diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..2b4f7b5fcf29f2e4affb2b03ab8cb767e2b55511
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,20 @@
+.github
+.DS_Store
+docs
+kubernetes
+node_modules
+/.svelte-kit
+/package
+.env
+.env.*
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+__pycache__
+.idea
+venv
+_old
+uploads
+.ipynb_checkpoints
+**/*.db
+_test
+backend/data/*
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000000000000000000000000000000000000..c38bf88bfb96e3a4f87e9f920096a93379a1e677
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,13 @@
+# Ollama URL for the backend to connect
+# The path '/ollama' will be redirected to the specified backend URL
+OLLAMA_BASE_URL='http://localhost:11434'
+
+OPENAI_API_BASE_URL=''
+OPENAI_API_KEY=''
+
+# AUTOMATIC1111_BASE_URL="http://localhost:7860"
+
+# DO NOT TRACK
+SCARF_NO_ANALYTICS=true
+DO_NOT_TRACK=true
+ANONYMIZED_TELEMETRY=false
\ No newline at end of file
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000000000000000000000000000000000000..38972655faff07d2cc0383044bbf9f43b22c2248
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,13 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+
+# Ignore files for PNPM, NPM and YARN
+pnpm-lock.yaml
+package-lock.json
+yarn.lock
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000000000000000000000000000000000000..cea095ea1aa19e444dc44264c138c95a82dfa04e
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,31 @@
+module.exports = {
+	root: true,
+	extends: [
+		'eslint:recommended',
+		'plugin:@typescript-eslint/recommended',
+		'plugin:svelte/recommended',
+		'plugin:cypress/recommended',
+		'prettier'
+	],
+	parser: '@typescript-eslint/parser',
+	plugins: ['@typescript-eslint'],
+	parserOptions: {
+		sourceType: 'module',
+		ecmaVersion: 2020,
+		extraFileExtensions: ['.svelte']
+	},
+	env: {
+		browser: true,
+		es2017: true,
+		node: true
+	},
+	overrides: [
+		{
+			files: ['*.svelte'],
+			parser: 'svelte-eslint-parser',
+			parserOptions: {
+				parser: '@typescript-eslint/parser'
+			}
+		}
+	]
+};
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000000000000000000000000000000000..36755c244e00997ca40d658e799c109d26adf6d9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+*.sh text eol=lf
+*.ttf filter=lfs diff=lfs merge=lfs -text
+*.jpg filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ef274fa9184f884a8f4af07f0c246d0592eafe42
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: tjbck
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000000000000000000000000000000000000..d0f38c2334e7c2ab89f1274df1ba91e37d2f89ef
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,80 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+---
+
+# Bug Report
+
+## Important Notes
+
+- **Before submitting a bug report**: Please check the Issues or Discussions section to see if a similar issue or feature request has already been posted. It's likely we're already tracking it! If you’re unsure, start a discussion post first. This will help us efficiently focus on improving the project.
+
+- **Collaborate respectfully**: We value a constructive attitude, so please be mindful of your communication. If negativity is part of your approach, our capacity to engage may be limited. We’re here to help if you’re open to learning and communicating positively. Remember, Open WebUI is a volunteer-driven project managed by a single maintainer and supported by contributors who also have full-time jobs. We appreciate your time and ask that you respect ours.
+
+- **Contributing**: If you encounter an issue, we highly encourage you to submit a pull request or fork the project. We actively work to prevent contributor burnout to maintain the quality and continuity of Open WebUI.
+
+- **Bug reproducibility**: If a bug cannot be reproduced with a `:main` or `:dev` Docker setup, or a pip install with Python 3.11, it may require additional help from the community. In such cases, we will move it to the "issues" Discussions section due to our limited resources. We encourage the community to assist with these issues. Remember, it’s not that the issue doesn’t exist; we need your help!
+
+Note: Please remove the notes above when submitting your post. Thank you for your understanding and support!
+
+---
+
+## Installation Method
+
+[Describe the method you used to install the project, e.g., git clone, Docker, pip, etc.]
+
+## Environment
+
+- **Open WebUI Version:** [e.g., v0.3.11]
+- **Ollama (if applicable):** [e.g., v0.2.0, v0.1.32-rc1]
+
+- **Operating System:** [e.g., Windows 10, macOS Big Sur, Ubuntu 20.04]
+- **Browser (if applicable):** [e.g., Chrome 100.0, Firefox 98.0]
+
+**Confirmation:**
+
+- [ ] I have read and followed all the instructions provided in the README.md.
+- [ ] I am on the latest version of both Open WebUI and Ollama.
+- [ ] I have included the browser console logs.
+- [ ] I have included the Docker container logs.
+- [ ] I have provided the exact steps to reproduce the bug in the "Steps to Reproduce" section below.
+
+## Expected Behavior:
+
+[Describe what you expected to happen.]
+
+## Actual Behavior:
+
+[Describe what actually happened.]
+
+## Description
+
+**Bug Summary:**
+[Provide a brief but clear summary of the bug]
+
+## Reproduction Details
+
+**Steps to Reproduce:**
+[Outline the steps to reproduce the bug. Be as detailed as possible.]
+
+## Logs and Screenshots
+
+**Browser Console Logs:**
+[Include relevant browser console logs, if applicable]
+
+**Docker Container Logs:**
+[Include relevant Docker container logs, if applicable]
+
+**Screenshots/Screen Recordings (if applicable):**
+[Attach any relevant screenshots to help illustrate the issue]
+
+## Additional Information
+
+[Include any additional details that may help in understanding and reproducing the issue. This could include specific configurations, error messages, or anything else relevant to the bug.]
+
+## Note
+
+If the bug report is incomplete or does not follow the provided instructions, it may not be addressed. Please ensure that you have followed the steps outlined in the README.md and troubleshooting.md documents, and provide all necessary information for us to reproduce and address the issue. Thank you!
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000000000000000000000000000000000000..5d6e9d708d634b9f5ad6fe066ac15928a6d85a2b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,35 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+---
+
+# Feature Request
+
+## Important Notes
+
+- **Before submitting a report**: Please check the Issues or Discussions section to see if a similar issue or feature request has already been posted. It's likely we're already tracking it! If you’re unsure, start a discussion post first. This will help us efficiently focus on improving the project.
+
+- **Collaborate respectfully**: We value a constructive attitude, so please be mindful of your communication. If negativity is part of your approach, our capacity to engage may be limited. We’re here to help if you’re open to learning and communicating positively. Remember, Open WebUI is a volunteer-driven project managed by a single maintainer and supported by contributors who also have full-time jobs. We appreciate your time and ask that you respect ours.
+
+- **Contributing**: If you encounter an issue, we highly encourage you to submit a pull request or fork the project. We actively work to prevent contributor burnout to maintain the quality and continuity of Open WebUI.
+
+- **Bug reproducibility**: If a bug cannot be reproduced with a `:main` or `:dev` Docker setup, or a pip install with Python 3.11, it may require additional help from the community. In such cases, we will move it to the "issues" Discussions section due to our limited resources. We encourage the community to assist with these issues. Remember, it’s not that the issue doesn’t exist; we need your help!
+
+Note: Please remove the notes above when submitting your post. Thank you for your understanding and support!
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000000000000000000000000000000000..af0a8ed0ee4cd85e0b381dc895baccb90f24f62a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+  - package-ecosystem: pip
+    directory: '/backend'
+    schedule:
+      interval: monthly
+    target-branch: 'dev'
+  - package-ecosystem: 'github-actions'
+    directory: '/'
+    schedule:
+      # Check for updates to GitHub Actions every week
+      interval: monthly
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000000000000000000000000000000000000..2a45c2c16e41cccaea1dd2e67f7568fef1206959
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,72 @@
+# Pull Request Checklist
+
+### Note to first-time contributors: Please open a discussion post in [Discussions](https://github.com/open-webui/open-webui/discussions) and describe your changes before submitting a pull request.
+
+**Before submitting, make sure you've checked the following:**
+
+- [ ] **Target branch:** Please verify that the pull request targets the `dev` branch.
+- [ ] **Description:** Provide a concise description of the changes made in this pull request.
+- [ ] **Changelog:** Ensure a changelog entry following the format of [Keep a Changelog](https://keepachangelog.com/) is added at the bottom of the PR description.
+- [ ] **Documentation:** Have you updated relevant documentation [Open WebUI Docs](https://github.com/open-webui/docs), or other documentation sources?
+- [ ] **Dependencies:** Are there any new dependencies? Have you updated the dependency versions in the documentation?
+- [ ] **Testing:** Have you written and run sufficient tests for validating the changes?
+- [ ] **Code review:** Have you performed a self-review of your code, addressing any coding standard issues and ensuring adherence to the project's coding standards?
+- [ ] **Prefix:** To cleary categorize this pull request, prefix the pull request title, using one of the following:
+  - **BREAKING CHANGE**: Significant changes that may affect compatibility
+  - **build**: Changes that affect the build system or external dependencies
+  - **ci**: Changes to our continuous integration processes or workflows
+  - **chore**: Refactor, cleanup, or other non-functional code changes
+  - **docs**: Documentation update or addition
+  - **feat**: Introduces a new feature or enhancement to the codebase
+  - **fix**: Bug fix or error correction
+  - **i18n**: Internationalization or localization changes
+  - **perf**: Performance improvement
+  - **refactor**: Code restructuring for better maintainability, readability, or scalability
+  - **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc.)
+  - **test**: Adding missing tests or correcting existing tests
+  - **WIP**: Work in progress, a temporary label for incomplete or ongoing work
+
+# Changelog Entry
+
+### Description
+
+- [Concisely describe the changes made in this pull request, including any relevant motivation and impact (e.g., fixing a bug, adding a feature, or improving performance)]
+
+### Added
+
+- [List any new features, functionalities, or additions]
+
+### Changed
+
+- [List any changes, updates, refactorings, or optimizations]
+
+### Deprecated
+
+- [List any deprecated functionality or features that have been removed]
+
+### Removed
+
+- [List any removed features, files, or functionalities]
+
+### Fixed
+
+- [List any fixes, corrections, or bug fixes]
+
+### Security
+
+- [List any new or updated security-related changes, including vulnerability fixes]
+
+### Breaking Changes
+
+- **BREAKING CHANGE**: [List any breaking changes affecting compatibility or functionality]
+
+---
+
+### Additional Information
+
+- [Insert any additional context, notes, or explanations for the changes]
+  - [Reference any related issues, commits, or other relevant information]
+
+### Screenshots or Videos
+
+- [Attach any relevant screenshots or videos demonstrating the changes]
diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml
new file mode 100644
index 0000000000000000000000000000000000000000..443d904199d7079fe12fc269af45debdc9d316f3
--- /dev/null
+++ b/.github/workflows/build-release.yml
@@ -0,0 +1,72 @@
+name: Release
+
+on:
+  push:
+    branches:
+      - main # or whatever branch you want to use
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Check for changes in package.json
+        run: |
+          git diff --cached --diff-filter=d package.json || {
+            echo "No changes to package.json"
+            exit 1
+          }
+
+      - name: Get version number from package.json
+        id: get_version
+        run: |
+          VERSION=$(jq -r '.version' package.json)
+          echo "::set-output name=version::$VERSION"
+
+      - name: Extract latest CHANGELOG entry
+        id: changelog
+        run: |
+          CHANGELOG_CONTENT=$(awk 'BEGIN {print_section=0;} /^## \[/ {if (print_section == 0) {print_section=1;} else {exit;}} print_section {print;}' CHANGELOG.md)
+          CHANGELOG_ESCAPED=$(echo "$CHANGELOG_CONTENT" | sed ':a;N;$!ba;s/\n/%0A/g')
+          echo "Extracted latest release notes from CHANGELOG.md:" 
+          echo -e "$CHANGELOG_CONTENT" 
+          echo "::set-output name=content::$CHANGELOG_ESCAPED"
+
+      - name: Create GitHub release
+        uses: actions/github-script@v7
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const changelog = `${{ steps.changelog.outputs.content }}`;
+            const release = await github.rest.repos.createRelease({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              tag_name: `v${{ steps.get_version.outputs.version }}`,
+              name: `v${{ steps.get_version.outputs.version }}`,
+              body: changelog,
+            })
+            console.log(`Created release ${release.data.html_url}`)
+
+      - name: Upload package to GitHub release
+        uses: actions/upload-artifact@v4
+        with:
+          name: package
+          path: |
+            .
+            !.git
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Trigger Docker build workflow
+        uses: actions/github-script@v7
+        with:
+          script: |
+            github.rest.actions.createWorkflowDispatch({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              workflow_id: 'docker-build.yaml',
+              ref: 'v${{ steps.get_version.outputs.version }}',
+            })
diff --git a/.github/workflows/deploy-to-hf-spaces.yml b/.github/workflows/deploy-to-hf-spaces.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7fc66acf5c40c490ab9dba840f10585563101d53
--- /dev/null
+++ b/.github/workflows/deploy-to-hf-spaces.yml
@@ -0,0 +1,63 @@
+name: Deploy to HuggingFace Spaces
+
+on:
+  push:
+    branches:
+      - dev
+      - main
+  workflow_dispatch:
+
+jobs:
+  check-secret:
+    runs-on: ubuntu-latest
+    outputs:
+      token-set: ${{ steps.check-key.outputs.defined }}
+    steps:
+      - id: check-key
+        env:
+          HF_TOKEN: ${{ secrets.HF_TOKEN }}
+        if: "${{ env.HF_TOKEN != '' }}"
+        run: echo "defined=true" >> $GITHUB_OUTPUT
+
+  deploy:
+    runs-on: ubuntu-latest
+    needs: [check-secret]
+    if: needs.check-secret.outputs.token-set == 'true'
+    env:
+      HF_TOKEN: ${{ secrets.HF_TOKEN }}
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          lfs: true
+
+      - name: Remove git history
+        run: rm -rf .git
+
+      - name: Prepend YAML front matter to README.md
+        run: |
+          echo "---" > temp_readme.md
+          echo "title: Open WebUI" >> temp_readme.md
+          echo "emoji: 🐳" >> temp_readme.md
+          echo "colorFrom: purple" >> temp_readme.md
+          echo "colorTo: gray" >> temp_readme.md
+          echo "sdk: docker" >> temp_readme.md
+          echo "app_port: 8080" >> temp_readme.md
+          echo "---" >> temp_readme.md
+          cat README.md >> temp_readme.md
+          mv temp_readme.md README.md
+
+      - name: Configure git
+        run: |
+          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
+          git config --global user.name "github-actions[bot]"
+      - name: Set up Git and push to Space
+        run: |
+          git init --initial-branch=main
+          git lfs install
+          git lfs track "*.ttf"
+          git lfs track "*.jpg"
+          rm demo.gif
+          git add .
+          git commit -m "GitHub deploy: ${{ github.sha }}"
+          git push --force https://open-webui:${HF_TOKEN}@huggingface.co/spaces/open-webui/open-webui main
diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..03dcf845567926f41da3b4c64de42fd94149c3c4
--- /dev/null
+++ b/.github/workflows/docker-build.yaml
@@ -0,0 +1,477 @@
+name: Create and publish Docker images with specific build args
+
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - main
+      - dev
+    tags:
+      - v*
+
+env:
+  REGISTRY: ghcr.io
+
+jobs:
+  build-main-image:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - linux/amd64
+          - linux/arm64
+
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Prepare
+        run: |
+          platform=${{ matrix.platform }}
+          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (default latest tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+
+      - name: Extract metadata for Docker cache
+        id: cache-meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            ${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
+          flavor: |
+            prefix=cache-${{ matrix.platform }}-
+            latest=false
+
+      - name: Build Docker image (latest)
+        uses: docker/build-push-action@v5
+        id: build
+        with:
+          context: .
+          push: true
+          platforms: ${{ matrix.platform }}
+          labels: ${{ steps.meta.outputs.labels }}
+          outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
+          cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
+          cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
+          build-args: |
+            BUILD_HASH=${{ github.sha }}
+
+      - name: Export digest
+        run: |
+          mkdir -p /tmp/digests
+          digest="${{ steps.build.outputs.digest }}"
+          touch "/tmp/digests/${digest#sha256:}"
+
+      - name: Upload digest
+        uses: actions/upload-artifact@v4
+        with:
+          name: digests-main-${{ env.PLATFORM_PAIR }}
+          path: /tmp/digests/*
+          if-no-files-found: error
+          retention-days: 1
+
+  build-cuda-image:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - linux/amd64
+          - linux/arm64
+
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Prepare
+        run: |
+          platform=${{ matrix.platform }}
+          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (cuda tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+            suffix=-cuda,onlatest=true
+
+      - name: Extract metadata for Docker cache
+        id: cache-meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            ${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
+          flavor: |
+            prefix=cache-cuda-${{ matrix.platform }}-
+            latest=false
+
+      - name: Build Docker image (cuda)
+        uses: docker/build-push-action@v5
+        id: build
+        with:
+          context: .
+          push: true
+          platforms: ${{ matrix.platform }}
+          labels: ${{ steps.meta.outputs.labels }}
+          outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
+          cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
+          cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
+          build-args: |
+            BUILD_HASH=${{ github.sha }}
+            USE_CUDA=true
+
+      - name: Export digest
+        run: |
+          mkdir -p /tmp/digests
+          digest="${{ steps.build.outputs.digest }}"
+          touch "/tmp/digests/${digest#sha256:}"
+
+      - name: Upload digest
+        uses: actions/upload-artifact@v4
+        with:
+          name: digests-cuda-${{ env.PLATFORM_PAIR }}
+          path: /tmp/digests/*
+          if-no-files-found: error
+          retention-days: 1
+
+  build-ollama-image:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - linux/amd64
+          - linux/arm64
+
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Prepare
+        run: |
+          platform=${{ matrix.platform }}
+          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
+
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (ollama tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=ollama
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+            suffix=-ollama,onlatest=true
+
+      - name: Extract metadata for Docker cache
+        id: cache-meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            ${{ github.ref_type == 'tag' && 'type=raw,value=main' || '' }}
+          flavor: |
+            prefix=cache-ollama-${{ matrix.platform }}-
+            latest=false
+
+      - name: Build Docker image (ollama)
+        uses: docker/build-push-action@v5
+        id: build
+        with:
+          context: .
+          push: true
+          platforms: ${{ matrix.platform }}
+          labels: ${{ steps.meta.outputs.labels }}
+          outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
+          cache-from: type=registry,ref=${{ steps.cache-meta.outputs.tags }}
+          cache-to: type=registry,ref=${{ steps.cache-meta.outputs.tags }},mode=max
+          build-args: |
+            BUILD_HASH=${{ github.sha }}
+            USE_OLLAMA=true
+
+      - name: Export digest
+        run: |
+          mkdir -p /tmp/digests
+          digest="${{ steps.build.outputs.digest }}"
+          touch "/tmp/digests/${digest#sha256:}"
+
+      - name: Upload digest
+        uses: actions/upload-artifact@v4
+        with:
+          name: digests-ollama-${{ env.PLATFORM_PAIR }}
+          path: /tmp/digests/*
+          if-no-files-found: error
+          retention-days: 1
+
+  merge-main-images:
+    runs-on: ubuntu-latest
+    needs: [build-main-image]
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Download digests
+        uses: actions/download-artifact@v4
+        with:
+          pattern: digests-main-*
+          path: /tmp/digests
+          merge-multiple: true
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (default latest tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+
+      - name: Create manifest list and push
+        working-directory: /tmp/digests
+        run: |
+          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
+            $(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
+
+      - name: Inspect image
+        run: |
+          docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
+
+  merge-cuda-images:
+    runs-on: ubuntu-latest
+    needs: [build-cuda-image]
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Download digests
+        uses: actions/download-artifact@v4
+        with:
+          pattern: digests-cuda-*
+          path: /tmp/digests
+          merge-multiple: true
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (default latest tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=cuda
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+            suffix=-cuda,onlatest=true
+
+      - name: Create manifest list and push
+        working-directory: /tmp/digests
+        run: |
+          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
+            $(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
+
+      - name: Inspect image
+        run: |
+          docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
+
+  merge-ollama-images:
+    runs-on: ubuntu-latest
+    needs: [build-ollama-image]
+    steps:
+      # GitHub Packages requires the entire repository name to be in lowercase
+      # although the repository owner has a lowercase username, this prevents some people from running actions after forking
+      - name: Set repository and image name to lowercase
+        run: |
+          echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
+          echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
+        env:
+          IMAGE_NAME: '${{ github.repository }}'
+
+      - name: Download digests
+        uses: actions/download-artifact@v4
+        with:
+          pattern: digests-ollama-*
+          path: /tmp/digests
+          merge-multiple: true
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker images (default ollama tag)
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_IMAGE_NAME }}
+          tags: |
+            type=ref,event=branch
+            type=ref,event=tag
+            type=sha,prefix=git-
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=raw,enable=${{ github.ref == 'refs/heads/main' }},prefix=,suffix=,value=ollama
+          flavor: |
+            latest=${{ github.ref == 'refs/heads/main' }}
+            suffix=-ollama,onlatest=true
+
+      - name: Create manifest list and push
+        working-directory: /tmp/digests
+        run: |
+          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
+            $(printf '${{ env.FULL_IMAGE_NAME }}@sha256:%s ' *)
+
+      - name: Inspect image
+        run: |
+          docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
diff --git a/.github/workflows/format-backend.yaml b/.github/workflows/format-backend.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..44587669753b484be551c1560075e4b36d39f270
--- /dev/null
+++ b/.github/workflows/format-backend.yaml
@@ -0,0 +1,39 @@
+name: Python CI
+
+on:
+  push:
+    branches:
+      - main
+      - dev
+  pull_request:
+    branches:
+      - main
+      - dev
+
+jobs:
+  build:
+    name: 'Format Backend'
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        python-version: [3.11]
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install black
+
+      - name: Format backend
+        run: npm run format:backend
+
+      - name: Check for changes after format
+        run: git diff --exit-code
diff --git a/.github/workflows/format-build-frontend.yaml b/.github/workflows/format-build-frontend.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..53d3aaa5ec830db736d167992bb2859332446f58
--- /dev/null
+++ b/.github/workflows/format-build-frontend.yaml
@@ -0,0 +1,57 @@
+name: Frontend Build
+
+on:
+  push:
+    branches:
+      - main
+      - dev
+  pull_request:
+    branches:
+      - main
+      - dev
+
+jobs:
+  build:
+    name: 'Format & Build Frontend'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Repository
+        uses: actions/checkout@v4
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v4
+        with:
+          node-version: '22' # Or specify any other version you want to use
+
+      - name: Install Dependencies
+        run: npm install
+
+      - name: Format Frontend
+        run: npm run format
+
+      - name: Run i18next
+        run: npm run i18n:parse
+
+      - name: Check for Changes After Format
+        run: git diff --exit-code
+
+      - name: Build Frontend
+        run: npm run build
+
+  test-frontend:
+    name: 'Frontend Unit Tests'
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Repository
+        uses: actions/checkout@v4
+
+      - name: Setup Node.js
+        uses: actions/setup-node@v4
+        with:
+          node-version: '22'
+
+      - name: Install Dependencies
+        run: npm ci
+
+      - name: Run vitest
+        run: npm run test:frontend
diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cb404f1fc1bd1671393a63f1c38c3da5e416468d
--- /dev/null
+++ b/.github/workflows/integration-test.yml
@@ -0,0 +1,253 @@
+name: Integration Test
+
+on:
+  push:
+    branches:
+      - main
+      - dev
+  pull_request:
+    branches:
+      - main
+      - dev
+
+jobs:
+  cypress-run:
+    name: Run Cypress Integration Tests
+    runs-on: ubuntu-latest
+    steps:
+      - name: Maximize build space
+        uses: AdityaGarg8/remove-unwanted-software@v4.1
+        with:
+          remove-android: 'true'
+          remove-haskell: 'true'
+          remove-codeql: 'true'
+
+      - name: Checkout Repository
+        uses: actions/checkout@v4
+
+      - name: Build and run Compose Stack
+        run: |
+          docker compose \
+            --file docker-compose.yaml \
+            --file docker-compose.api.yaml \
+            --file docker-compose.a1111-test.yaml \
+            up --detach --build
+
+      - name: Delete Docker build cache
+        run: |
+          docker builder prune --all --force
+
+      - name: Wait for Ollama to be up
+        timeout-minutes: 5
+        run: |
+          until curl --output /dev/null --silent --fail http://localhost:11434; do
+            printf '.'
+            sleep 1
+          done
+          echo "Service is up!"
+
+      - name: Preload Ollama model
+        run: |
+          docker exec ollama ollama pull qwen:0.5b-chat-v1.5-q2_K
+
+      - name: Cypress run
+        uses: cypress-io/github-action@v6
+        with:
+          browser: chrome
+          wait-on: 'http://localhost:3000'
+          config: baseUrl=http://localhost:3000
+
+      - uses: actions/upload-artifact@v4
+        if: always()
+        name: Upload Cypress videos
+        with:
+          name: cypress-videos
+          path: cypress/videos
+          if-no-files-found: ignore
+
+      - name: Extract Compose logs
+        if: always()
+        run: |
+          docker compose logs > compose-logs.txt
+
+      - uses: actions/upload-artifact@v4
+        if: always()
+        name: Upload Compose logs
+        with:
+          name: compose-logs
+          path: compose-logs.txt
+          if-no-files-found: ignore
+
+  # pytest:
+  #   name: Run Backend Tests
+  #   runs-on: ubuntu-latest
+  #   steps:
+  #     - uses: actions/checkout@v4
+
+  #     - name: Set up Python
+  #       uses: actions/setup-python@v5
+  #       with:
+  #         python-version: ${{ matrix.python-version }}
+
+  #     - name: Install dependencies
+  #       run: |
+  #         python -m pip install --upgrade pip
+  #         pip install -r backend/requirements.txt
+
+  #     - name: pytest run
+  #       run: |
+  #         ls -al
+  #         cd backend
+  #         PYTHONPATH=. pytest . -o log_cli=true -o log_cli_level=INFO
+
+  migration_test:
+    name: Run Migration Tests
+    runs-on: ubuntu-latest
+    services:
+      postgres:
+        image: postgres
+        env:
+          POSTGRES_PASSWORD: postgres
+        options: >-
+          --health-cmd pg_isready
+          --health-interval 10s
+          --health-timeout 5s
+          --health-retries 5
+        ports:
+          - 5432:5432
+    #      mysql:
+    #        image: mysql
+    #        env:
+    #          MYSQL_ROOT_PASSWORD: mysql
+    #          MYSQL_DATABASE: mysql
+    #        options: >-
+    #          --health-cmd "mysqladmin ping -h localhost"
+    #          --health-interval 10s
+    #          --health-timeout 5s
+    #          --health-retries 5
+    #        ports:
+    #          - 3306:3306
+    steps:
+      - name: Checkout Repository
+        uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: ${{ matrix.python-version }}
+
+      - name: Set up uv
+        uses: yezz123/setup-uv@v4
+        with:
+          uv-venv: venv
+
+      - name: Activate virtualenv
+        run: |
+          . venv/bin/activate
+          echo PATH=$PATH >> $GITHUB_ENV
+
+      - name: Install dependencies
+        run: |
+          uv pip install -r backend/requirements.txt
+
+      - name: Test backend with SQLite
+        id: sqlite
+        env:
+          WEBUI_SECRET_KEY: secret-key
+          GLOBAL_LOG_LEVEL: debug
+        run: |
+          cd backend
+          uvicorn open_webui.main:app --port "8080" --forwarded-allow-ips '*' &
+          UVICORN_PID=$!
+          # Wait up to 40 seconds for the server to start
+          for i in {1..40}; do
+              curl -s http://localhost:8080/api/config > /dev/null && break
+              sleep 1
+              if [ $i -eq 40 ]; then
+                  echo "Server failed to start"
+                  kill -9 $UVICORN_PID
+                  exit 1
+              fi
+          done
+          # Check that the server is still running after 5 seconds
+          sleep 5
+          if ! kill -0 $UVICORN_PID; then
+              echo "Server has stopped"
+              exit 1
+          fi
+
+      - name: Test backend with Postgres
+        if: success() || steps.sqlite.conclusion == 'failure'
+        env:
+          WEBUI_SECRET_KEY: secret-key
+          GLOBAL_LOG_LEVEL: debug
+          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
+          DATABASE_POOL_SIZE: 10
+          DATABASE_POOL_MAX_OVERFLOW: 10
+          DATABASE_POOL_TIMEOUT: 30
+        run: |
+          cd backend
+          uvicorn open_webui.main:app --port "8081" --forwarded-allow-ips '*' &
+          UVICORN_PID=$!
+          # Wait up to 20 seconds for the server to start
+          for i in {1..20}; do
+              curl -s http://localhost:8081/api/config > /dev/null && break
+              sleep 1
+              if [ $i -eq 20 ]; then
+                  echo "Server failed to start"
+                  kill -9 $UVICORN_PID
+                  exit 1
+              fi
+          done
+          # Check that the server is still running after 5 seconds
+          sleep 5
+          if ! kill -0 $UVICORN_PID; then
+              echo "Server has stopped"
+              exit 1
+          fi
+
+          # Check that service will reconnect to postgres when connection will be closed
+          status_code=$(curl --write-out %{http_code} -s --output /dev/null http://localhost:8081/health/db)
+          if [[ "$status_code" -ne 200 ]] ; then
+            echo "Server has failed before postgres reconnect check"
+            exit 1
+          fi
+
+          echo "Terminating all connections to postgres..."
+          python -c "import os, psycopg2 as pg2; \
+            conn = pg2.connect(dsn=os.environ['DATABASE_URL'].replace('+pool', '')); \
+            cur = conn.cursor(); \
+            cur.execute('SELECT pg_terminate_backend(psa.pid) FROM pg_stat_activity psa WHERE datname = current_database() AND pid <> pg_backend_pid();')"
+
+          status_code=$(curl --write-out %{http_code} -s --output /dev/null http://localhost:8081/health/db)
+          if [[ "$status_code" -ne 200 ]] ; then
+            echo "Server has not reconnected to postgres after connection was closed: returned status $status_code"
+            exit 1
+          fi
+
+#      - name: Test backend with MySQL
+#        if: success() || steps.sqlite.conclusion == 'failure' || steps.postgres.conclusion == 'failure'
+#        env:
+#          WEBUI_SECRET_KEY: secret-key
+#          GLOBAL_LOG_LEVEL: debug
+#          DATABASE_URL: mysql://root:mysql@localhost:3306/mysql
+#        run: |
+#          cd backend
+#          uvicorn open_webui.main:app --port "8083" --forwarded-allow-ips '*' &
+#          UVICORN_PID=$!
+#          # Wait up to 20 seconds for the server to start
+#          for i in {1..20}; do
+#              curl -s http://localhost:8083/api/config > /dev/null && break
+#              sleep 1
+#              if [ $i -eq 20 ]; then
+#                  echo "Server failed to start"
+#                  kill -9 $UVICORN_PID
+#                  exit 1
+#              fi
+#          done
+#          # Check that the server is still running after 5 seconds
+#          sleep 5
+#          if ! kill -0 $UVICORN_PID; then
+#              echo "Server has stopped"
+#              exit 1
+#          fi
diff --git a/.github/workflows/lint-backend.disabled b/.github/workflows/lint-backend.disabled
new file mode 100644
index 0000000000000000000000000000000000000000..dd0bdc7fa7bb123de740f6ee2df1d8aaa6dfa312
--- /dev/null
+++ b/.github/workflows/lint-backend.disabled
@@ -0,0 +1,27 @@
+name: Python CI
+on:
+  push:
+    branches: ['main']
+  pull_request:
+jobs:
+  build:
+    name: 'Lint Backend'
+    env:
+      PUBLIC_API_BASE_URL: ''
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        node-version:
+          - latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Use Python
+        uses: actions/setup-python@v5
+      - name: Use Bun
+        uses: oven-sh/setup-bun@v1
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install pylint
+      - name: Lint backend
+        run: bun run lint:backend
diff --git a/.github/workflows/lint-frontend.disabled b/.github/workflows/lint-frontend.disabled
new file mode 100644
index 0000000000000000000000000000000000000000..2c1cd3c5a574989def4319d07405f0bb621d74df
--- /dev/null
+++ b/.github/workflows/lint-frontend.disabled
@@ -0,0 +1,21 @@
+name: Bun CI
+on:
+  push:
+    branches: ['main']
+  pull_request:
+jobs:
+  build:
+    name: 'Lint Frontend'
+    env:
+      PUBLIC_API_BASE_URL: ''
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - name: Use Bun
+        uses: oven-sh/setup-bun@v1
+      - run: bun --version
+      - name: Install frontend dependencies
+        run: bun install --frozen-lockfile
+      - run: bun run lint:frontend
+      - run: bun run lint:types
+        if: success() || failure()
\ No newline at end of file
diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8a2e3438a6dfb242f665602b31da133b39f1f797
--- /dev/null
+++ b/.github/workflows/release-pypi.yml
@@ -0,0 +1,32 @@
+name: Release to PyPI
+
+on:
+  push:
+    branches:
+      - main # or whatever branch you want to use
+      - pypi-release
+
+jobs:
+  release:
+    runs-on: ubuntu-latest
+    environment:
+      name: pypi
+      url: https://pypi.org/p/open-webui
+    permissions:
+      id-token: write
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+      - uses: actions/setup-node@v4
+        with:
+          node-version: 18
+      - uses: actions/setup-python@v5
+        with:
+          python-version: 3.11
+      - name: Build
+        run: |
+          python -m pip install --upgrade pip
+          pip install build
+          python -m build .
+      - name: Publish package distributions to PyPI
+        uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..32271f8087e213e83089162bd0b1ec99c60d45ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,309 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Pyodide distribution
+static/pyodide/*
+!static/pyodide/pyodide-lock.json
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+#   This is especially recommended for binary packages to ensure reproducibility, and is more
+#   commonly ignored for libraries.
+#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+#   in version control.
+#   https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+#  and can be added to the global gitignore or merged into this file.  For a more nuclear
+#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
+.idea/
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+.cache
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# cypress artifacts
+cypress/videos
+cypress/screenshots
+.vscode/settings.json
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000000000000000000000000000000000..b6f27f135954640c8cc5bfd7b8c9922ca6eb2aad
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000000000000000000000000000000000000..82c49125724030d1010bb123df7dae758236ff11
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,316 @@
+# Ignore files for PNPM, NPM and YARN
+pnpm-lock.yaml
+package-lock.json
+yarn.lock
+
+kubernetes/
+
+# Copy of .gitignore
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+#   This is especially recommended for binary packages to ensure reproducibility, and is more
+#   commonly ignored for libraries.
+#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+#   in version control.
+#   https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+#  and can be added to the global gitignore or merged into this file.  For a more nuclear
+#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
+.idea/
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+.cache
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# cypress artifacts
+cypress/videos
+cypress/screenshots
+
+
+
+/static/*
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000000000000000000000000000000000000..a77fddea90975988d17a7e8b2f61720939a947f5
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,9 @@
+{
+	"useTabs": true,
+	"singleQuote": true,
+	"trailingComma": "none",
+	"printWidth": 100,
+	"plugins": ["prettier-plugin-svelte"],
+	"pluginSearchDirs": ["."],
+	"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..04a79ca432f46058f026532f5c9053dad702ec3f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,1375 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.4.7] - 2024-12-01
+
+### Added
+
+- **✨ Prompt Input Auto-Completion**: Type a prompt and let AI intelligently suggest and complete your inputs. Simply press 'Tab' or swipe right on mobile to confirm. Available only with Rich Text Input (default setting). Disable via Admin Settings for full control.
+- **🌍 Improved Translations**: Enhanced localization for multiple languages, ensuring a more polished and accessible experience for international users.
+
+### Fixed
+
+- **🛠️ Tools Export Issue**: Resolved a critical issue where exporting tools wasn’t functioning, restoring seamless export capabilities.
+- **🔗 Model ID Registration**: Fixed an issue where model IDs weren’t registering correctly in the model editor, ensuring reliable model setup and tracking.
+- **🖋️ Textarea Auto-Expansion**: Corrected a bug where textareas didn’t expand automatically on certain browsers, improving usability for multi-line inputs.
+- **🔧 Ollama Embed Endpoint**: Addressed the /ollama/embed endpoint malfunction, ensuring consistent performance and functionality.
+
+### Changed
+
+- **🎨 Knowledge Base Styling**: Refined knowledge base visuals for a cleaner, more modern look, laying the groundwork for further enhancements in upcoming releases.
+
+## [0.4.6] - 2024-11-26
+
+### Added
+
+- **🌍 Enhanced Translations**: Various language translations improved to make the WebUI more accessible and user-friendly worldwide.
+
+### Fixed
+
+- **✏️ Textarea Shifting Bug**: Resolved the issue where the textarea shifted unexpectedly, ensuring a smoother typing experience.
+- **⚙️ Model Configuration Modal**: Fixed the issue where the models configuration modal introduced in 0.4.5 wasn’t working for some users.
+- **🔍 Legacy Query Support**: Restored functionality for custom query generation in RAG when using legacy prompts, ensuring both default and custom templates now work seamlessly.
+- **⚡ Improved General Reliability**: Various minor fixes improve platform stability and ensure a smoother overall experience across workflows.
+
+## [0.4.5] - 2024-11-26
+
+### Added
+
+- **🎨 Model Order/Defaults Reintroduced**: Brought back the ability to set model order and default models, now configurable via Admin Settings > Models > Configure (Gear Icon).
+
+### Fixed
+
+- **🔍 Query Generation Issue**: Resolved an error in web search query generation, enhancing search accuracy and ensuring smoother search workflows.
+- **📏 Textarea Auto Height Bug**: Fixed a layout issue where textarea input height was shifting unpredictably, particularly when editing system prompts.
+- **🔑 Ollama Authentication**: Corrected an issue with Ollama’s authorization headers, guaranteeing reliable authentication across all endpoints.
+- **⚙️ Missing Min_P Save**: Resolved an issue where the 'min_p' parameter was not being saved in configurations.
+- **🛠️ Tools Description**: Fixed a key issue that omitted tool descriptions in tools payload.
+
+## [0.4.4] - 2024-11-22
+
+### Added
+
+- **🌐 Translation Updates**: Refreshed Catalan, Brazilian Portuguese, German, and Ukrainian translations, further enhancing the platform's accessibility and improving the experience for international users.
+
+### Fixed
+
+- **📱 Mobile Controls Visibility**: Resolved an issue where the controls button was not displaying on the new chats page for mobile users, ensuring smoother navigation and functionality on smaller screens.
+- **📷 LDAP Profile Image Issue**: Fixed an LDAP integration bug related to profile images, ensuring seamless authentication and a reliable login experience for users.
+- **⏳ RAG Query Generation Issue**: Addressed a significant problem where RAG query generation occurred unnecessarily without attached files, drastically improving speed and reducing delays during chat completions.
+
+### Changed
+
+- **⚙️ Legacy Event Emitter Support**: Reintroduced compatibility with legacy "citation" types for event emitters in tools and functions, providing smoother workflows and broader tool support for users.
+
+## [0.4.3] - 2024-11-21
+
+### Added
+
+- **📚 Inline Citations for RAG Results**: Get seamless inline citations for Retrieval-Augmented Generation (RAG) responses using the default RAG prompt. Note: This feature only supports newly uploaded files, improving traceability and providing source clarity.
+- **🎨 Better Rich Text Input Support**: Enjoy smoother and more reliable rich text formatting for chats, enhancing communication quality.
+- **⚡ Faster Model Retrieval**: Implemented caching optimizations for faster model loading, providing a noticeable speed boost across workflows. Further improvements are on the way!
+
+### Fixed
+
+- **🔗 Pipelines Feature Restored**: Resolved a critical issue that previously prevented Pipelines from functioning, ensuring seamless workflows.
+- **✏️ Missing Suffix Field in Ollama Form**: Added the missing "suffix" field to the Ollama generate form, enhancing customization options.
+
+### Changed
+
+- **🗂️ Renamed "Citations" to "Sources"**: Improved clarity and consistency by renaming the "citations" field to "sources" in messages.
+
+## [0.4.2] - 2024-11-20
+
+### Fixed
+
+- **📁 Knowledge Files Visibility Issue**: Resolved the bug preventing individual files in knowledge collections from displaying when referenced with '#'.
+- **🔗 OpenAI Endpoint Prefix**: Fixed the issue where certain OpenAI connections that deviate from the official API spec weren’t working correctly with prefixes.
+- **⚔️ Arena Model Access Control**: Corrected an issue where arena model access control settings were not being saved.
+- **🔧 Usage Capability Selector**: Fixed the broken usage capabilities selector in the model editor.
+
+## [0.4.1] - 2024-11-19
+
+### Added
+
+- **📊 Enhanced Feedback System**: Introduced a detailed 1-10 rating scale for feedback alongside thumbs up/down, preparing for more precise model fine-tuning and improving feedback quality.
+- **ℹ️ Tool Descriptions on Hover**: Easily access tool descriptions by hovering over the message input, providing a smoother workflow with more context when utilizing tools.
+
+### Fixed
+
+- **🗑️ Graceful Handling of Deleted Users**: Resolved an issue where deleted users caused workspace items (models, knowledge, prompts, tools) to fail, ensuring reliable workspace loading.
+- **🔑 API Key Creation**: Fixed an issue preventing users from creating new API keys, restoring secure and seamless API management.
+- **🔗 HTTPS Proxy Fix**: Corrected HTTPS proxy issues affecting the '/api/v1/models/' endpoint, ensuring smoother, uninterrupted model management.
+
+## [0.4.0] - 2024-11-19
+
+### Added
+
+- **👥 User Groups**: You can now create and manage user groups, making user organization seamless.
+- **🔐 Group-Based Access Control**: Set granular access to models, knowledge, prompts, and tools based on user groups, allowing for more controlled and secure environments.
+- **🛠️ Group-Based User Permissions**: Easily manage workspace permissions. Grant users the ability to upload files, delete, edit, or create temporary chats, as well as define their ability to create models, knowledge, prompts, and tools.
+- **🔑 LDAP Support**: Newly introduced LDAP authentication adds robust security and scalability to user management.
+- **🌐 Enhanced OpenAI-Compatible Connections**: Added prefix ID support to avoid model ID clashes, with explicit model ID support for APIs lacking '/models' endpoint support, ensuring smooth operation with custom setups.
+- **🔐 Ollama API Key Support**: Now manage credentials for Ollama when set behind proxies, including the option to utilize prefix ID for proper distinction across multiple Ollama instances.
+- **🔄 Connection Enable/Disable Toggle**: Easily enable or disable individual OpenAI and Ollama connections as needed.
+- **🎨 Redesigned Model Workspace**: Freshly redesigned to improve usability for managing models across users and groups.
+- **🎨 Redesigned Prompt Workspace**: A fresh UI to conveniently organize and manage prompts.
+- **🧩 Sorted Functions Workspace**: Functions are now automatically categorized by type (Action, Filter, Pipe), streamlining management.
+- **💻 Redesigned Collaborative Workspace**: Enhanced support for multiple users contributing to models, knowledge, prompts, or tools, improving collaboration.
+- **🔧 Auto-Selected Tools in Model Editor**: Tools enabled through the model editor are now automatically selected, whereas previously it only gave users the option to enable the tool, reducing manual steps and enhancing efficiency.
+- **🔔 Web Search & Tools Indicator**: A clear indication now shows when web search or tools are active, reducing confusion.
+- **🔑 Toggle API Key Auth**: Tighten security by easily enabling or disabling API key authentication option for Open WebUI.
+- **🗂️ Agentic Retrieval**: Improve RAG accuracy via smart pre-processing of chat history to determine the best queries before retrieval.
+- **📁 Large Text as File Option**: Optionally convert large pasted text into a file upload, keeping the chat interface cleaner.
+- **🗂️ Toggle Citations for Models**: Ability to disable citations has been introduced in the model editor.
+- **🔍 User Settings Search**: Quickly search for settings fields, improving ease of use and navigation.
+- **🗣️ Experimental SpeechT5 TTS**: Local SpeechT5 support added for improved text-to-speech capabilities.
+- **🔄 Unified Reset for Models**: A one-click option has been introduced to reset and remove all models from the Admin Settings.
+- **🛠️ Initial Setup Wizard**: The setup process now explicitly informs users that they are creating an admin account during the first-time setup, ensuring clarity. Previously, users encountered the login page right away without this distinction.
+- **🌐 Enhanced Translations**: Several language translations, including Ukrainian, Norwegian, and Brazilian Portuguese, were refined for better localization.
+
+### Fixed
+
+- **🎥 YouTube Video Attachments**: Fixed issues preventing proper loading and attachment of YouTube videos as files.
+- **🔄 Shared Chat Update**: Corrected issues where shared chats were not updating, improving collaboration consistency.
+- **🔍 DuckDuckGo Rate Limit Fix**: Addressed issues with DuckDuckGo search integration, enhancing search stability and performance when operating within rate limits.
+- **🧾 Citations Relevance Fix**: Adjusted the relevance percentage calculation for citations, so that Open WebUI properly reflect the accuracy of a retrieved document in RAG, ensuring users get clearer insights into sources.
+- **🔑 Jina Search API Key Requirement**: Added the option to input an API key for Jina Search, ensuring smooth functionality as keys are now mandatory.
+
+### Changed
+
+- **🛠️ Functions Moved to Admin Panel**: As Functions operate as advanced plugins, they are now accessible from the Admin Panel instead of the workspace.
+- **🛠️ Manage Ollama Connections**: The "Models" section in Admin Settings has been relocated to Admin Settings > "Connections" > Ollama Connections. You can now manage Ollama instances via a dedicated "Manage Ollama" modal from "Connections", streamlining the setup and configuration of Ollama models.
+- **📊 Base Models in Admin Settings**: Admins can now find all base models, both connections or functions, in the "Models" Admin setting. Global model accessibility can be enabled or disabled here. Models are private by default, requiring explicit permission assignment for user access.
+- **📌 Sticky Model Selection for New Chats**: The model chosen from a previous chat now persists when creating a new chat. If you click "New Chat" again from the new chat page, it will revert to your default model.
+- **🎨 Design Refactoring**: Overall design refinements across the platform have been made, providing a more cohesive and polished user experience.
+
+### Removed
+
+- **📂 Model List Reordering**: Temporarily removed and will be reintroduced in upcoming user group settings improvements.
+- **⚙️ Default Model Setting**: Removed the ability to set a default model for users, will be reintroduced with user group settings in the future.
+
+## [0.3.35] - 2024-10-26
+
+### Added
+
+- **🌐 Translation Update**: Added translation labels in the SearchInput and CreateCollection components and updated Brazilian Portuguese translation (pt-BR)
+- **📁 Robust File Handling**: Enhanced file input handling for chat. If the content extraction fails or is empty, users will now receive a clear warning, preventing silent failures and ensuring you always know what's happening with your uploads.
+- **🌍 New Language Support**: Introduced Hungarian translations and updated French translations, expanding the platform's language accessibility for a more global user base.
+
+### Fixed
+
+- **📚 Knowledge Base Loading Issue**: Resolved a critical bug where the Knowledge Base was not loading, ensuring smooth access to your stored documents and improving information retrieval in RAG-enhanced workflows.
+- **🛠️ Tool Parameters Issue**: Fixed an error where tools were not functioning correctly when required parameters were missing, ensuring reliable tool performance and more efficient task completions.
+- **🔗 Merged Response Loss in Multi-Model Chats**: Addressed an issue where responses in multi-model chat workflows were being deleted after follow-up queries, improving consistency and ensuring smoother interactions across models.
+
+## [0.3.34] - 2024-10-26
+
+### Added
+
+- **🔧 Feedback Export Enhancements**: Feedback history data can now be exported to JSON, allowing for seamless integration in RLHF processing and further analysis.
+- **🗂️ Embedding Model Lazy Loading**: Search functionality for leaderboard reranking is now more efficient, as embedding models are lazy-loaded only when needed, optimizing performance.
+- **🎨 Rich Text Input Toggle**: Users can now switch back to legacy textarea input for chat if they prefer simpler text input, though rich text is still the default until deprecation.
+- **🛠️ Improved Tool Calling Mechanism**: Enhanced method for parsing and calling tools, improving the reliability and robustness of tool function calls.
+- **🌐 Globalization Enhancements**: Updates to internationalization (i18n) support, further refining multi-language compatibility and accuracy.
+
+### Fixed
+
+- **🖥️ Folder Rename Fix for Firefox**: Addressed a persistent issue where users could not rename folders by pressing enter in Firefox, now ensuring seamless folder management across browsers.
+- **🔠 Tiktoken Model Text Splitter Issue**: Resolved an issue where the tiktoken text splitter wasn’t working in Docker installations, restoring full functionality for tokenized text editing.
+- **💼 S3 File Upload Issue**: Fixed a problem affecting S3 file uploads, ensuring smooth operations for those who store files on cloud storage.
+- **🔒 Strict-Transport-Security Crash**: Resolved a crash when setting the Strict-Transport-Security (HSTS) header, improving stability and security enhancements.
+- **🚫 OIDC Boolean Access Fix**: Addressed an issue with boolean values not being accessed correctly during OIDC logins, ensuring login reliability.
+- **⚙️ Rich Text Paste Behavior**: Refined paste behavior in rich text input to make it smoother and more intuitive when pasting various content types.
+- **🔨 Model Exclusion for Arena Fix**: Corrected the filter function that was not properly excluding models from the arena, improving model management.
+- **🏷️ "Tags Generation Prompt" Fix**: Addressed an issue preventing custom "tags generation prompts" from registering properly, ensuring custom prompt work seamlessly.
+
+## [0.3.33] - 2024-10-24
+
+### Added
+
+- **🏆 Evaluation Leaderboard**: Easily track your performance through a new leaderboard system where your ratings contribute to a real-time ranking based on the Elo system. Sibling responses (regenerations, many model chats) are required for your ratings to count in the leaderboard. Additionally, you can opt-in to share your feedback history and be part of the community-wide leaderboard. Expect further improvements as we refine the algorithm—help us build the best community leaderboard!
+- **⚔️ Arena Model Evaluation**: Enable blind A/B testing of models directly from Admin Settings > Evaluation for a true side-by-side comparison. Ideal for pinpointing the best model for your needs.
+- **🎯 Topic-Based Leaderboard**: Discover more accurate rankings with experimental topic-based reranking, which adjusts leaderboard standings based on tag similarity in feedback. Get more relevant insights based on specific topics!
+- **📁 Folders Support for Chats**: Organize your chats better by grouping them into folders. Drag and drop chats between folders and export them seamlessly for easy sharing or analysis.
+- **📤 Easy Chat Import via Drag & Drop**: Save time by simply dragging and dropping chat exports (JSON) directly onto the sidebar to import them into your workspace—streamlined, efficient, and intuitive!
+- **📚 Enhanced Knowledge Collection**: Now, you can reference individual files from a knowledge collection—ideal for more precise Retrieval-Augmented Generations (RAG) queries and document analysis.
+- **🏷️ Enhanced Tagging System**: Tags now take up less space! Utilize the new 'tag:' query system to manage, search, and organize your conversations more effectively without cluttering the interface.
+- **🧠 Auto-Tagging for Chats**: Your conversations are now automatically tagged for improved organization, mirroring the efficiency of auto-generated titles.
+- **🔍 Backend Chat Query System**: Chat filtering has become more efficient, now handled through the backend\*\* instead of your browser, improving search performance and accuracy.
+- **🎮 Revamped Playground**: Experience a refreshed and optimized Playground for smoother testing, tweaks, and experimentation of your models and tools.
+- **🧩 Token-Based Text Splitter**: Introducing token-based text splitting (tiktoken), giving you more precise control over how text is processed. Previously, only character-based splitting was available.
+- **🔢 Ollama Batch Embeddings**: Leverage new batch embedding support for improved efficiency and performance with Ollama embedding models.
+- **🔍 Enhanced Add Text Content Modal**: Enjoy a cleaner, more intuitive workflow for adding and curating knowledge content with an upgraded input modal from our Knowledge workspace.
+- **🖋️ Rich Text Input for Chats**: Make your chat inputs more dynamic with support for rich text formatting. Your conversations just got a lot more polished and professional.
+- **⚡ Faster Whisper Model Configurability**: Customize your local faster whisper model directly from the WebUI.
+- **☁️ Experimental S3 Support**: Enable stateless WebUI instances with S3 support, greatly enhancing scalability and balancing heavy workloads.
+- **🔕 Disable Update Toast**: Now you can streamline your workspace even further—choose to disable update notifications for a more focused experience.
+- **🌟 RAG Citation Relevance Percentage**: Easily assess citation accuracy with the addition of relevance percentages in RAG results.
+- **⚙️ Mermaid Copy Button**: Mermaid diagrams now come with a handy copy button, simplifying the extraction and use of diagram contents directly in your workflow.
+- **🎨 UI Redesign**: Major interface redesign that will make navigation smoother, keep your focus where it matters, and ensure a modern look.
+
+### Fixed
+
+- **🎙️ Voice Note Mic Stopping Issue**: Fixed the issue where the microphone stayed active after ending a voice note recording, ensuring your audio workflow runs smoothly.
+
+### Removed
+
+- **👋 Goodbye Sidebar Tags**: Sidebar tag clutter is gone. We’ve shifted tag buttons to more effective query-based tag filtering for a sleeker, more agile interface.
+
+## [0.3.32] - 2024-10-06
+
+### Added
+
+- **🔢 Workspace Enhancements**: Added a display count for models, prompts, tools, and functions in the workspace, providing a clear overview and easier management.
+
+### Fixed
+
+- **🖥️ Web and YouTube Attachment Fix**: Resolved an issue where attaching web links and YouTube videos was malfunctioning, ensuring seamless integration and display within chats.
+- **📞 Call Mode Activation on Landing Page**: Fixed a bug where call mode was not operational from the landing page.
+
+### Changed
+
+- **🔄 URL Parameter Refinement**: Updated the 'tool_ids' URL parameter to 'tools' or 'tool-ids' for more intuitive and consistent user experience.
+- **🎨 Floating Buttons Styling Update**: Refactored the styling of floating buttons to intelligently adjust to the left side when there isn't enough room on the right, improving interface usability and aesthetic.
+- **🔧 Enhanced Accessibility for Floating Buttons**: Implemented the ability to close floating buttons with the 'Esc' key, making workflow smoother and more efficient for users navigating via keyboard.
+- **🖇️ Updated Information URL**: Information URLs now direct users to a general release page rather than a version-specific URL, ensuring access to the latest and relevant details all in one place.
+- **📦 Library Dependencies Update**: Upgraded dependencies to ensure compatibility and performance optimization for pip installs.
+
+## [0.3.31] - 2024-10-06
+
+### Added
+
+- **📚 Knowledge Feature**: Reimagined documents feature, now more performant with a better UI for enhanced organization; includes streamlined API integration for Retrieval-Augmented Generation (RAG). Detailed documentation forthcoming: https://docs.openwebui.com/
+- **🌐 New Landing Page**: Freshly designed landing page; toggle between the new UI and the classic chat UI from Settings > Interface for a personalized experience.
+- **📁 Full Document Retrieval Mode**: Toggle between full document retrieval or traditional snippets by clicking on the file item. This mode enhances document capabilities and supports comprehensive tasks like summarization by utilizing the entire content instead of RAG.
+- **📄 Extracted File Content Display**: View extracted content directly by clicking on the file item, simplifying file analysis.
+- **🎨 Artifacts Feature**: Render web content and SVGs directly in the interface, supporting quick iterations and live changes.
+- **🖊️ Editable Code Blocks**: Supercharged code blocks now allow live editing directly in the LLM response, with live reloads supported by artifacts.
+- **🔧 Code Block Enhancements**: Introduced a floating copy button in code blocks to facilitate easier code copying without scrolling.
+- **🔍 SVG Pan/Zoom**: Enhanced interaction with SVG images, including Mermaid diagrams, via new pan and zoom capabilities.
+- **🔍 Text Select Quick Actions**: New floating buttons appear when text is highlighted in LLM responses, offering deeper interactions like "Ask a Question" or "Explain".
+- **🗃️ Database Pool Configuration**: Enhanced database handling to support scalable user growth.
+- **🔊 Experimental Audio Compression**: Compress audio files to navigate around the 25MB limit for OpenAI's speech-to-text processing.
+- **🔍 Query Embedding**: Adjusted embedding behavior to enhance system performance by not repeating query embedding.
+- **💾 Lazy Load Optimizations**: Implemented lazy loading of large dependencies to minimize initial memory usage, boosting performance.
+- **🍏 Apple Touch Icon Support**: Optimizes the display of icons for web bookmarks on Apple mobile devices.
+- **🔽 Expandable Content Markdown Support**: Introducing 'details', 'summary' tag support for creating expandable content sections in markdown, facilitating cleaner, organized documentation and interactive content display.
+
+### Fixed
+
+- **🔘 Action Button Issue**: Resolved a bug where action buttons were not functioning, enhancing UI reliability.
+- **🔄 Multi-Model Chat Loop**: Fixed an infinite loop issue in multi-model chat environments, ensuring smoother chat operations.
+- **📄 Chat PDF/TXT Export Issue**: Resolved problems with exporting chat logs to PDF and TXT formats.
+- **🔊 Call to Text-to-Speech Issues**: Rectified problems with text-to-speech functions to improve audio interactions.
+
+### Changed
+
+- **⚙️ Endpoint Renaming**: Renamed 'rag' endpoints to 'retrieval' for clearer function description.
+- **🎨 Styling and Interface Updates**: Multiple refinements across the platform to enhance visual appeal and user interaction.
+
+### Removed
+
+- **🗑️ Deprecated 'DOCS_DIR'**: Removed the outdated 'docs_dir' variable in favor of more direct file management solutions, with direct file directory syncing and API uploads for a more integrated experience.
+
+## [0.3.30] - 2024-09-26
+
+### Fixed
+
+- **🍞 Update Available Toast Dismissal**: Enhanced user experience by ensuring that once the update available notification is dismissed, it won't reappear for 24 hours.
+- **📋 Ollama /embed Form Data**: Adjusted the integration inaccuracies in the /embed form data to ensure it perfectly matches with Ollama's specifications.
+- **🔧 O1 Max Completion Tokens Issue**: Resolved compatibility issues with OpenAI's o1 models max_completion_tokens param to ensure smooth operation.
+- **🔄 Pip Install Database Issue**: Fixed a critical issue where database changes during pip installations were reverting and not saving chat logs, now ensuring data persistence and reliability in chat operations.
+- **🏷️ Chat Rename Tab Update**: Fixed the functionality to change the web browser's tab title simultaneously when a chat is renamed, keeping tab titles consistent.
+
+## [0.3.29] - 2023-09-25
+
+### Fixed
+
+- **🔧 KaTeX Rendering Improvement**: Resolved specific corner cases in KaTeX rendering to enhance the display of complex mathematical notation.
+- **📞 'Call' URL Parameter Fix**: Corrected functionality for 'call' URL search parameter ensuring reliable activation of voice calls through URL triggers.
+- **🔄 Configuration Reset Fix**: Fixed the RESET_CONFIG_ON_START to ensure settings revert to default correctly upon each startup, improving reliability in configuration management.
+- **🌍 Filter Outlet Hook Fix**: Addressed issues in the filter outlet hook, ensuring all filter functions operate as intended.
+
+## [0.3.28] - 2024-09-24
+
+### Fixed
+
+- **🔍 Web Search Functionality**: Corrected an issue where the web search option was not functioning properly.
+
+## [0.3.27] - 2024-09-24
+
+### Fixed
+
+- **🔄 Periodic Cleanup Error Resolved**: Fixed a critical RuntimeError related to the 'periodic_usage_pool_cleanup' coroutine, ensuring smooth and efficient performance post-pip install, correcting a persisting issue from version 0.3.26.
+- **📊 Enhanced LaTeX Rendering**: Improved rendering for LaTeX content, enhancing clarity and visual presentation in documents and mathematical models.
+
+## [0.3.26] - 2024-09-24
+
+### Fixed
+
+- **🔄 Event Loop Error Resolution**: Addressed a critical error where a missing running event loop caused 'periodic_usage_pool_cleanup' to fail with pip installs. This fix ensures smoother and more reliable updates and installations, enhancing overall system stability.
+
+## [0.3.25] - 2024-09-24
+
+### Fixed
+
+- **🖼️ Image Generation Functionality**: Resolved an issue where image generation was not functioning, restoring full capability for visual content creation.
+- **⚖️ Rate Response Corrections**: Addressed a problem where rate responses were not working, ensuring reliable feedback mechanisms are operational.
+
+## [0.3.24] - 2024-09-24
+
+### Added
+
+- **🚀 Rendering Optimization**: Significantly improved message rendering performance, enhancing user experience and webui responsiveness.
+- **💖 Favorite Response Feature in Chat Overview**: Users can now mark responses as favorite directly from the chat overview, enhancing ease of retrieval and organization of preferred responses.
+- **💬 Create Message Pairs with Shortcut**: Implemented creation of new message pairs using Cmd/Ctrl+Shift+Enter, making conversation editing faster and more intuitive.
+- **🌍 Expanded User Prompt Variables**: Added weekday, timezone, and language information variables to user prompts to match system prompt variables.
+- **🎵 Enhanced Audio Support**: Now includes support for 'audio/x-m4a' files, broadening compatibility with audio content within the platform.
+- **🔏 Model URL Search Parameter**: Added an ability to select a model directly via URL parameters, streamlining navigation and model access.
+- **📄 Enhanced PDF Citations**: PDF citations now open at the associated page, streamlining reference checks and document handling.
+- **🔧Use of Redis in Sockets**: Enhanced socket implementation to fully support Redis, enabling effective stateless instances suitable for scalable load balancing.
+- **🌍 Stream Individual Model Responses**: Allows specific models to have individualized streaming settings, enhancing performance and customization.
+- **🕒 Display Model Hash and Last Modified Timestamp for Ollama Models**: Provides critical model details directly in the Models workspace for enhanced tracking.
+- **❗ Update Info Notification for Admins**: Ensures administrators receive immediate updates upon login, keeping them informed of the latest changes and system statuses.
+
+### Fixed
+
+- **🗑️ Temporary File Handling On Windows**: Fixed an issue causing errors when accessing a temporary file being used by another process, Tools & Functions should now work as intended.
+- **🔓 Authentication Toggle Issue**: Resolved the malfunction where setting 'WEBUI_AUTH=False' did not appropriately disable authentication, ensuring that user experience and system security settings function as configured.
+- **🔧 Save As Copy Issue for Many Model Chats**: Resolved an error preventing users from save messages as copies in many model chats.
+- **🔒 Sidebar Closure on Mobile**: Resolved an issue where the mobile sidebar remained open after menu engagement, improving user interface responsivity and comfort.
+- **🛡️ Tooltip XSS Vulnerability**: Resolved a cross-site scripting (XSS) issue within tooltips, ensuring enhanced security and data integrity during user interactions.
+
+### Changed
+
+- **↩️ Deprecated Interface Stream Response Settings**: Moved to advanced parameters to streamline interface settings and enhance user clarity.
+- **⚙️ Renamed 'speedRate' to 'playbackRate'**: Standardizes terminology, improving usability and understanding in media settings.
+
+## [0.3.23] - 2024-09-21
+
+### Added
+
+- **🚀 WebSocket Redis Support**: Enhanced load balancing capabilities for multiple instance setups, promoting better performance and reliability in WebUI.
+- **🔧 Adjustable Chat Controls**: Introduced width-adjustable chat controls, enabling a personalized and more comfortable user interface.
+- **🌎 i18n Updates**: Improved and updated the Chinese translations.
+
+### Fixed
+
+- **🌐 Task Model Unloading Issue**: Modified task handling to use the Ollama /api/chat endpoint instead of OpenAI compatible endpoint, ensuring models stay loaded and ready with custom parameters, thus minimizing delays in task execution.
+- **📝 Title Generation Fix for OpenAI Compatible APIs**: Resolved an issue preventing the generation of titles, enhancing consistency and reliability when using multiple API providers.
+- **🗃️ RAG Duplicate Collection Issue**: Fixed a bug causing repeated processing of the same uploaded file. Now utilizes indexed files to prevent unnecessary duplications, optimizing resource usage.
+- **🖼️ Image Generation Enhancement**: Refactored OpenAI image generation endpoint to be asynchronous, preventing the WebUI from becoming unresponsive during processing, thus enhancing user experience.
+- **🔓 Downgrade Authlib**: Reverted Authlib to version 1.3.1 to address and resolve issues concerning OAuth functionality.
+
+### Changed
+
+- **🔍 Improved Message Interaction**: Enhanced the message node interface to allow for easier focus redirection with a simple click, streamlining user interaction.
+- **✨ Styling Refactor**: Updated WebUI styling for a cleaner, more modern look, enhancing user experience across the platform.
+
+## [0.3.22] - 2024-09-19
+
+### Added
+
+- **⭐ Chat Overview**: Introducing a node-based interactive messages diagram for improved visualization of conversation flows.
+- **🔗 Multiple Vector DB Support**: Now supports multiple vector databases, including the newly added Milvus support. Community contributions for additional database support are highly encouraged!
+- **📡 Experimental Non-Stream Chat Completion**: Experimental feature allowing the use of OpenAI o1 models, which do not support streaming, ensuring more versatile model deployment.
+- **🔍 Experimental Colbert-AI Reranker Integration**: Added support for "jinaai/jina-colbert-v2" as a reranker, enhancing search relevance and accuracy. Note: it may not function at all on low-spec computers.
+- **🕸️ ENABLE_WEBSOCKET_SUPPORT**: Added environment variable for instances to ignore websocket upgrades, stabilizing connections on platforms with websocket issues.
+- **🔊 Azure Speech Service Integration**: Added support for Azure Speech services for Text-to-Speech (TTS).
+- **🎚️ Customizable Playback Speed**: Playback speed control is now available in Call mode settings, allowing users to adjust audio playback speed to their preferences.
+- **🧠 Enhanced Error Messaging**: System now displays helpful error messages directly to users during chat completion issues.
+- **📂 Save Model as Transparent PNG**: Model profile images are now saved as PNGs, supporting transparency and improving visual integration.
+- **📱 iPhone Compatibility Adjustments**: Added padding to accommodate the iPhone navigation bar, improving UI display on these devices.
+- **🔗 Secure Response Headers**: Implemented security response headers, bolstering web application security.
+- **🔧 Enhanced AUTOMATIC1111 Settings**: Users can now configure 'CFG Scale', 'Sampler', and 'Scheduler' parameters directly in the admin settings, enhancing workflow flexibility without source code modifications.
+- **🌍 i18n Updates**: Enhanced translations for Chinese, Ukrainian, Russian, and French, fostering a better localized experience.
+
+### Fixed
+
+- **🛠️ Chat Message Deletion**: Resolved issues with chat message deletion, ensuring a smoother user interaction and system stability.
+- **🔢 Ordered List Numbering**: Fixed the incorrect ordering in lists.
+
+### Changed
+
+- **🎨 Transparent Icon Handling**: Allowed model icons to be displayed on transparent backgrounds, improving UI aesthetics.
+- **📝 Improved RAG Template**: Enhanced Retrieval-Augmented Generation template, optimizing context handling and error checking for more precise operation.
+
+## [0.3.21] - 2024-09-08
+
+### Added
+
+- **📊 Document Count Display**: Now displays the total number of documents directly within the dashboard.
+- **🚀 Ollama Embed API Endpoint**: Enabled /api/embed endpoint proxy support.
+
+### Fixed
+
+- **🐳 Docker Launch Issue**: Resolved the problem preventing Open-WebUI from launching correctly when using Docker.
+
+### Changed
+
+- **🔍 Enhanced Search Prompts**: Improved the search query generation prompts for better accuracy and user interaction, enhancing the overall search experience.
+
+## [0.3.20] - 2024-09-07
+
+### Added
+
+- **🌐 Translation Update**: Updated Catalan translations to improve user experience for Catalan speakers.
+
+### Fixed
+
+- **📄 PDF Download**: Resolved a configuration issue with fonts directory, ensuring PDFs are now downloaded with the correct formatting.
+- **🛠️ Installation of Tools & Functions Requirements**: Fixed a bug where necessary requirements for tools and functions were not properly installing.
+- **🔗 Inline Image Link Rendering**: Enabled rendering of images directly from links in chat.
+- **📞 Post-Call User Interface Cleanup**: Adjusted UI behavior to automatically close chat controls after a voice call ends, reducing screen clutter.
+- **🎙️ Microphone Deactivation Post-Call**: Addressed an issue where the microphone remained active after calls.
+- **✍️ Markdown Spacing Correction**: Corrected spacing in Markdown rendering, ensuring text appears neatly and as expected.
+- **🔄 Message Re-rendering**: Fixed an issue causing all response messages to re-render with each new message, now improving chat performance.
+
+### Changed
+
+- **🌐 Refined Web Search Integration**: Deprecated the Search Query Generation Prompt threshold; introduced a toggle button for "Enable Web Search Query Generation" allowing users to opt-in to using web search more judiciously.
+- **📝 Default Prompt Templates Update**: Emptied environment variable templates for search and title generation now default to the Open WebUI default prompt templates, simplifying configuration efforts.
+
+## [0.3.19] - 2024-09-05
+
+### Added
+
+- **🌐 Translation Update**: Improved Chinese translations.
+
+### Fixed
+
+- **📂 DATA_DIR Overriding**: Fixed an issue to avoid overriding DATA_DIR, preventing errors when directories are set identically, ensuring smoother operation and data management.
+- **🛠️ Frontmatter Extraction**: Fixed the extraction process for frontmatter in tools and functions.
+
+### Changed
+
+- **🎨 UI Styling**: Refined the user interface styling for enhanced visual coherence and user experience.
+
+## [0.3.18] - 2024-09-04
+
+### Added
+
+- **🛠️ Direct Database Execution for Tools & Functions**: Enhanced the execution of Python files for tools and functions, now directly loading from the database for a more streamlined backend process.
+
+### Fixed
+
+- **🔄 Automatic Rewrite of Import Statements in Tools & Functions**: Tool and function scripts that import 'utils', 'apps', 'main', 'config' will now automatically rename these with 'open_webui.', ensuring compatibility and consistency across different modules.
+- **🎨 Styling Adjustments**: Minor fixes in the visual styling to improve user experience and interface consistency.
+
+## [0.3.17] - 2024-09-04
+
+### Added
+
+- **🔄 Import/Export Configuration**: Users can now import and export webui configurations from admin settings > Database, simplifying setup replication across systems.
+- **🌍 Web Search via URL Parameter**: Added support for activating web search directly through URL by setting 'web-search=true'.
+- **🌐 SearchApi Integration**: Added support for SearchApi as an alternative web search provider, enhancing search capabilities within the platform.
+- **🔍 Literal Type Support in Tools**: Tools now support the Literal type.
+- **🌍 Updated Translations**: Improved translations for Chinese, Ukrainian, and Catalan.
+
+### Fixed
+
+- **🔧 Pip Install Issue**: Resolved the issue where pip install failed due to missing 'alembic.ini', ensuring smoother installation processes.
+- **🌃 Automatic Theme Update**: Fixed an issue where the color theme did not update dynamically with system changes.
+- **🛠️ User Agent in ComfyUI**: Added default headers in ComfyUI to fix access issues, improving reliability in network communications.
+- **🔄 Missing Chat Completion Response Headers**: Ensured proper return of proxied response headers during chat completion, improving API reliability.
+- **🔗 Websocket Connection Prioritization**: Modified socket.io configuration to prefer websockets and more reliably fallback to polling, enhancing connection stability.
+- **🎭 Accessibility Enhancements**: Added missing ARIA labels for buttons, improving accessibility for visually impaired users.
+- **⚖️ Advanced Parameter**: Fixed an issue ensuring that advanced parameters are correctly applied in all scenarios, ensuring consistent behavior of user-defined settings.
+
+### Changed
+
+- **🔁 Namespace Reorganization**: Reorganized all Python files under the 'open_webui' namespace to streamline the project structure and improve maintainability. Tools and functions importing from 'utils' should now use 'open_webui.utils'.
+- **🚧 Dependency Updates**: Updated several backend dependencies like 'aiohttp', 'authlib', 'duckduckgo-search', 'flask-cors', and 'langchain' to their latest versions, enhancing performance and security.
+
+## [0.3.16] - 2024-08-27
+
+### Added
+
+- **🚀 Config DB Migration**: Migrated configuration handling from config.json to the database, enabling high-availability setups and load balancing across multiple Open WebUI instances.
+- **🔗 Call Mode Activation via URL**: Added a 'call=true' URL search parameter enabling direct shortcuts to activate call mode, enhancing user interaction on mobile devices.
+- **✨ TTS Content Control**: Added functionality to control how message content is segmented for Text-to-Speech (TTS) generation requests, allowing for more flexible speech output options.
+- **😄 Show Knowledge Search Status**: Enhanced model usage transparency by displaying status when working with knowledge-augmented models, helping users understand the system's state during queries.
+- **👆 Click-to-Copy for Codespan**: Enhanced interactive experience in the WebUI by allowing users to click to copy content from code spans directly.
+- **🚫 API User Blocking via Model Filter**: Introduced the ability to block API users based on customized model filters, enhancing security and control over API access.
+- **🎬 Call Overlay Styling**: Adjusted call overlay styling on large screens to not cover the entire interface, but only the chat control area, for a more unobtrusive interaction experience.
+
+### Fixed
+
+- **🔧 LaTeX Rendering Issue**: Addressed an issue that affected the correct rendering of LaTeX.
+- **📁 File Leak Prevention**: Resolved the issue of uploaded files mistakenly being accessible across user chats.
+- **🔧 Pipe Functions with '**files**' Param**: Fixed issues with '**files**' parameter not functioning correctly in pipe functions.
+- **📝 Markdown Processing for RAG**: Fixed issues with processing Markdown in files.
+- **🚫 Duplicate System Prompts**: Fixed bugs causing system prompts to duplicate.
+
+### Changed
+
+- **🔋 Wakelock Permission**: Optimized the activation of wakelock to only engage during call mode, conserving device resources and improving battery performance during idle periods.
+- **🔍 Content-Type for Ollama Chats**: Added 'application/x-ndjson' content-type to '/api/chat' endpoint responses to match raw Ollama responses.
+- **✋ Disable Signups Conditionally**: Implemented conditional logic to disable sign-ups when 'ENABLE_LOGIN_FORM' is set to false.
+
+## [0.3.15] - 2024-08-21
+
+### Added
+
+- **🔗 Temporary Chat Activation**: Integrated a new URL parameter 'temporary-chat=true' to enable temporary chat sessions directly through the URL.
+- **🌄 ComfyUI Seed Node Support**: Introduced seed node support in ComfyUI for image generation, allowing users to specify node IDs for randomized seed assignment.
+
+### Fixed
+
+- **🛠️ Tools and Functions**: Resolved a critical issue where Tools and Functions were not properly functioning, restoring full capability and reliability to these essential features.
+- **🔘 Chat Action Button in Many Model Chat**: Fixed the malfunctioning of chat action buttons in many model chat environments, ensuring a smoother and more responsive user interaction.
+- **⏪ Many Model Chat Compatibility**: Restored backward compatibility for many model chats.
+
+## [0.3.14] - 2024-08-21
+
+### Added
+
+- **🛠️ Custom ComfyUI Workflow**: Deprecating several older environment variables, this enhancement introduces a new, customizable workflow for a more tailored user experience.
+- **🔀 Merge Responses in Many Model Chat**: Enhances the dialogue by merging responses from multiple models into a single, coherent reply, improving the interaction quality in many model chats.
+- **✅ Multiple Instances of Same Model in Chats**: Enhanced many model chat to support adding multiple instances of the same model.
+- **🔧 Quick Actions in Model Workspace**: Enhanced Shift key quick actions for hiding/unhiding and deleting models, facilitating a smoother workflow.
+- **🗨️ Markdown Rendering in User Messages**: User messages are now rendered in Markdown, enhancing readability and interaction.
+- **💬 Temporary Chat Feature**: Introduced a temporary chat feature, deprecating the old chat history setting to enhance user interaction flexibility.
+- **🖋️ User Message Editing**: Enhanced the user chat editing feature to allow saving changes without sending, providing more flexibility in message management.
+- **🛡️ Security Enhancements**: Various security improvements implemented across the platform to ensure safer user experiences.
+- **🌍 Updated Translations**: Enhanced translations for Chinese, Ukrainian, and Bahasa Malaysia, improving localization and user comprehension.
+
+### Fixed
+
+- **📑 Mermaid Rendering Issue**: Addressed issues with Mermaid chart rendering to ensure clean and clear visual data representation.
+- **🎭 PWA Icon Maskability**: Fixed the Progressive Web App icon to be maskable, ensuring proper display on various device home screens.
+- **🔀 Cloned Model Chat Freezing Issue**: Fixed a bug where cloning many model chats would cause freezing, enhancing stability and responsiveness.
+- **🔍 Generic Error Handling and Refinements**: Various minor fixes and refinements to address previously untracked issues, ensuring smoother operations.
+
+### Changed
+
+- **🖼️ Image Generation Refactor**: Overhauled image generation processes for improved efficiency and quality.
+- **🔨 Refactor Tool and Function Calling**: Refactored tool and function calling mechanisms for improved clarity and maintainability.
+- **🌐 Backend Library Updates**: Updated critical backend libraries including SQLAlchemy, uvicorn[standard], faster-whisper, bcrypt, and boto3 for enhanced performance and security.
+
+### Removed
+
+- **🚫 Deprecated ComfyUI Environment Variables**: Removed several outdated environment variables related to ComfyUI settings, simplifying configuration management.
+
+## [0.3.13] - 2024-08-14
+
+### Added
+
+- **🎨 Enhanced Markdown Rendering**: Significant improvements in rendering markdown, ensuring smooth and reliable display of LaTeX and Mermaid charts, enhancing user experience with more robust visual content.
+- **🔄 Auto-Install Tools & Functions Python Dependencies**: For 'Tools' and 'Functions', Open WebUI now automatically install extra python requirements specified in the frontmatter, streamlining setup processes and customization.
+- **🌀 OAuth Email Claim Customization**: Introduced an 'OAUTH_EMAIL_CLAIM' variable to allow customization of the default "email" claim within OAuth configurations, providing greater flexibility in authentication processes.
+- **📶 Websocket Reconnection**: Enhanced reliability with the capability to automatically reconnect when a websocket is closed, ensuring consistent and stable communication.
+- **🤳 Haptic Feedback on Support Devices**: Android devices now support haptic feedback for an immersive tactile experience during certain interactions.
+
+### Fixed
+
+- **🛠️ ComfyUI Performance Improvement**: Addressed an issue causing FastAPI to stall when ComfyUI image generation was active; now runs in a separate thread to prevent UI unresponsiveness.
+- **🔀 Session Handling**: Fixed an issue mandating session_id on client-side to ensure smoother session management and transitions.
+- **🖋️ Minor Bug Fixes and Format Corrections**: Various minor fixes including typo corrections, backend formatting improvements, and test amendments enhancing overall system stability and performance.
+
+### Changed
+
+- **🚀 Migration to SvelteKit 2**: Upgraded the underlying framework to SvelteKit version 2, offering enhanced speed, better code structure, and improved deployment capabilities.
+- **🧹 General Cleanup and Refactoring**: Performed broad cleanup and refactoring across the platform, improving code efficiency and maintaining high standards of code health.
+- **🚧 Integration Testing Improvements**: Modified how Cypress integration tests detect chat messages and updated sharing tests for better reliability and accuracy.
+- **📁 Standardized '.safetensors' File Extension**: Renamed the '.sft' file extension to '.safetensors' for ComfyUI workflows, standardizing file formats across the platform.
+
+### Removed
+
+- **🗑️ Deprecated Frontend Functions**: Removed frontend functions that were migrated to backend to declutter the codebase and reduce redundancy.
+
+## [0.3.12] - 2024-08-07
+
+### Added
+
+- **🔄 Sidebar Infinite Scroll**: Added an infinite scroll feature in the sidebar for more efficient chat navigation, reducing load times and enhancing user experience.
+- **🚀 Enhanced Markdown Rendering**: Support for rendering all code blocks and making images clickable for preview; codespan styling is also enhanced to improve readability and user interaction.
+- **🔒 Admin Shared Chat Visibility**: Admins no longer have default visibility over shared chats when ENABLE_ADMIN_CHAT_ACCESS is set to false, tightening security and privacy settings for users.
+- **🌍 Language Updates**: Added Malay (Bahasa Malaysia) translation and updated Catalan and Traditional Chinese translations to improve accessibility for more users.
+
+### Fixed
+
+- **📊 Markdown Rendering Issues**: Resolved issues with markdown rendering to ensure consistent and correct display across components.
+- **🛠️ Styling Issues**: Multiple fixes applied to styling throughout the application, improving the overall visual experience and interface consistency.
+- **🗃️ Modal Handling**: Fixed an issue where modals were not closing correctly in various model chat scenarios, enhancing usability and interface reliability.
+- **📄 Missing OpenAI Usage Information**: Resolved issues where usage statistics for OpenAI services were not being correctly displayed, ensuring users have access to crucial data for managing and monitoring their API consumption.
+- **🔧 Non-Streaming Support for Functions Plugin**: Fixed a functionality issue with the Functions plugin where non-streaming operations were not functioning as intended, restoring full capabilities for async and sync integration within the platform.
+- **🔄 Environment Variable Type Correction (COMFYUI_FLUX_FP8_CLIP)**: Corrected the data type of the 'COMFYUI_FLUX_FP8_CLIP' environment variable from string to boolean, ensuring environment settings apply correctly and enhance configuration management.
+
+### Changed
+
+- **🔧 Backend Dependency Updates**: Updated several backend dependencies such as boto3, pypdf, python-pptx, validators, and black, ensuring up-to-date security and performance optimizations.
+
+## [0.3.11] - 2024-08-02
+
+### Added
+
+- **📊 Model Information Display**: Added visuals for model selection, including images next to model names for more intuitive navigation.
+- **🗣 ElevenLabs Voice Adaptations**: Voice enhancements including support for ElevenLabs voice ID by name for personalized vocal interactions.
+- **⌨️ Arrow Keys Model Selection**: Users can now use arrow keys for quicker model selection, enhancing accessibility.
+- **🔍 Fuzzy Search in Model Selector**: Enhanced model selector with fuzzy search to locate models swiftly, including descriptions.
+- **🕹️ ComfyUI Flux Image Generation**: Added support for the new Flux image gen model; introduces environment controls like weight precision and CLIP model options in Settings.
+- **💾 Display File Size for Uploads**: Enhanced file interface now displays file size, preparing for upcoming upload restrictions.
+- **🎚️ Advanced Params "Min P"**: Added 'Min P' parameter in the advanced settings for customized model precision control.
+- **🔒 Enhanced OAuth**: Introduced custom redirect URI support for OAuth behind reverse proxies, enabling safer authentication processes.
+- **🖥 Enhanced Latex Rendering**: Adjustments made to latex rendering processes, now accurately detecting and presenting latex inputs from text.
+- **🌐 Internationalization**: Enhanced with new Romanian and updated Vietnamese and Ukrainian translations, helping broaden accessibility for international users.
+
+### Fixed
+
+- **🔧 Tags Handling in Document Upload**: Tags are now properly sent to the upload document handler, resolving issues with missing metadata.
+- **🖥️ Sensitive Input Fields**: Corrected browser misinterpretation of secure input fields, preventing misclassification as password fields.
+- **📂 Static Path Resolution in PDF Generation**: Fixed static paths that adjust dynamically to prevent issues across various environments.
+
+### Changed
+
+- **🎨 UI/UX Styling Enhancements**: Multiple minor styling updates for a cleaner and more intuitive user interface.
+- **🚧 Refactoring Various Components**: Numerous refactoring changes across styling, file handling, and function simplifications for clarity and performance.
+- **🎛️ User Valves Management**: Moved user valves from settings to direct chat controls for more user-friendly access during interactions.
+
+### Removed
+
+- **⚙️ Health Check Logging**: Removed verbose logging from the health checking processes to declutter logs and improve backend performance.
+
+## [0.3.10] - 2024-07-17
+
+### Fixed
+
+- **🔄 Improved File Upload**: Addressed the issue where file uploads lacked animation.
+- **💬 Chat Continuity**: Fixed a problem where existing chats were not functioning properly in some instances.
+- **🗂️ Chat File Reset**: Resolved the issue of chat files not resetting for new conversations, now ensuring a clean slate for each chat session.
+- **📁 Document Workspace Uploads**: Corrected the handling of document uploads in the workspace using the Files API.
+
+## [0.3.9] - 2024-07-17
+
+### Added
+
+- **📁 Files Chat Controls**: We've reverted to the old file handling behavior where uploaded files are always included. You can now manage files directly within the chat controls section, giving you the ability to remove files as needed.
+- **🔧 "Action" Function Support**: Introducing a new "Action" function to write custom buttons to the message toolbar. This feature enables more interactive messaging, with documentation coming soon.
+- **📜 Citations Handling**: For newly uploaded files in documents workspace, citations will now display the actual filename. Additionally, you can click on these filenames to open the file in a new tab for easier access.
+- **🛠️ Event Emitter and Call Updates**: Enhanced 'event_emitter' to allow message replacement and 'event_call' to support text input for Tools and Functions. Detailed documentation will be provided shortly.
+- **🎨 Styling Refactor**: Various styling updates for a cleaner and more cohesive user interface.
+- **🌐 Enhanced Translations**: Improved translations for Catalan, Ukrainian, and Brazilian Portuguese.
+
+### Fixed
+
+- **🔧 Chat Controls Priority**: Resolved an issue where Chat Controls values were being overridden by model information parameters. The priority is now Chat Controls, followed by Global Settings, then Model Settings.
+- **🪲 Debug Logs**: Fixed an issue where debug logs were not being logged properly.
+- **🔑 Automatic1111 Auth Key**: The auth key for Automatic1111 is no longer required.
+- **📝 Title Generation**: Ensured that the title generation runs only once, even when multiple models are in a chat.
+- **✅ Boolean Values in Params**: Added support for boolean values in parameters.
+- **🖼️ Files Overlay Styling**: Fixed the styling issue with the files overlay.
+
+### Changed
+
+- **⬆️ Dependency Updates**
+  - Upgraded 'pydantic' from version 2.7.1 to 2.8.2.
+  - Upgraded 'sqlalchemy' from version 2.0.30 to 2.0.31.
+  - Upgraded 'unstructured' from version 0.14.9 to 0.14.10.
+  - Upgraded 'chromadb' from version 0.5.3 to 0.5.4.
+
+## [0.3.8] - 2024-07-09
+
+### Added
+
+- **💬 Chat Controls**: Easily adjust parameters for each chat session, offering more precise control over your interactions.
+- **📌 Pinned Chats**: Support for pinned chats, allowing you to keep important conversations easily accessible.
+- **📄 Apache Tika Integration**: Added support for using Apache Tika as a document loader, enhancing document processing capabilities.
+- **🛠️ Custom Environment for OpenID Claims**: Allows setting custom claims for OpenID, providing more flexibility in user authentication.
+- **🔧 Enhanced Tools & Functions API**: Introduced 'event_emitter' and 'event_call', now you can also add citations for better documentation and tracking. Detailed documentation will be provided on our documentation website.
+- **↔️ Sideways Scrolling in Settings**: Settings tabs container now supports horizontal scrolling for easier navigation.
+- **🌑 Darker OLED Theme**: Includes a new, darker OLED theme and improved styling for the light theme, enhancing visual appeal.
+- **🌐 Language Updates**: Updated translations for Indonesian, German, French, and Catalan languages, expanding accessibility.
+
+### Fixed
+
+- **⏰ OpenAI Streaming Timeout**: Resolved issues with OpenAI streaming response using the 'AIOHTTP_CLIENT_TIMEOUT' setting, ensuring reliable performance.
+- **💡 User Valves**: Fixed malfunctioning user valves, ensuring proper functionality.
+- **🔄 Collapsible Components**: Addressed issues with collapsible components not working, restoring expected behavior.
+
+### Changed
+
+- **🗃️ Database Backend**: Switched from Peewee to SQLAlchemy for improved concurrency support, enhancing database performance.
+- **⬆️ ChromaDB Update**: Upgraded to version 0.5.3. Ensure your remote ChromaDB instance matches this version.
+- **🔤 Primary Font Styling**: Updated primary font to Archivo for better visual consistency.
+- **🔄 Font Change for Windows**: Replaced Arimo with Inter font for Windows users, improving readability.
+- **🚀 Lazy Loading**: Implemented lazy loading for 'faster_whisper' and 'sentence_transformers' to reduce startup memory usage.
+- **📋 Task Generation Payload**: Task generations now include only the "task" field in the body instead of "title".
+
+## [0.3.7] - 2024-06-29
+
+### Added
+
+- **🌐 Enhanced Internationalization (i18n)**: Newly introduced Indonesian translation, and updated translations for Turkish, Chinese, and Catalan languages to improve user accessibility.
+
+### Fixed
+
+- **🕵️‍♂️ Browser Language Detection**: Corrected the issue where the application was not properly detecting and adapting to the browser's language settings.
+- **🔐 OIDC Admin Role Assignment**: Fixed a bug where the admin role was not being assigned to the first user who signed up via OpenID Connect (OIDC).
+- **💬 Chat/Completions Endpoint**: Resolved an issue where the chat/completions endpoint was non-functional when the stream option was set to False.
+- **🚫 'WEBUI_AUTH' Configuration**: Addressed the problem where setting 'WEBUI_AUTH' to False was not being applied correctly.
+
+### Changed
+
+- **📦 Dependency Update**: Upgraded 'authlib' from version 1.3.0 to 1.3.1 to ensure better security and performance enhancements.
+
+## [0.3.6] - 2024-06-27
+
+### Added
+
+- **✨ "Functions" Feature**: You can now utilize "Functions" like filters (middleware) and pipe (model) functions directly within the WebUI. While largely compatible with Pipelines, these native functions can be executed easily within Open WebUI. Example use cases for filter functions include usage monitoring, real-time translation, moderation, and automemory. For pipe functions, the scope ranges from Cohere and Anthropic integration directly within Open WebUI, enabling "Valves" for per-user OpenAI API key usage, and much more. If you encounter issues, SAFE_MODE has been introduced.
+- **📁 Files API**: Compatible with OpenAI, this feature allows for custom Retrieval-Augmented Generation (RAG) in conjunction with the Filter Function. More examples will be shared on our community platform and official documentation website.
+- **🛠️ Tool Enhancements**: Tools now support citations and "Valves". Documentation will be available shortly.
+- **🔗 Iframe Support via Files API**: Enables rendering HTML directly into your chat interface using functions and tools. Use cases include playing games like DOOM and Snake, displaying a weather applet, and implementing Anthropic "artifacts"-like features. Stay tuned for updates on our community platform and documentation.
+- **🔒 Experimental OAuth Support**: New experimental OAuth support. Check our documentation for more details.
+- **🖼️ Custom Background Support**: Set a custom background from Settings > Interface to personalize your experience.
+- **🔑 AUTOMATIC1111_API_AUTH Support**: Enhanced security for the AUTOMATIC1111 API.
+- **🎨 Code Highlight Optimization**: Improved code highlighting features.
+- **🎙️ Voice Interruption Feature**: Reintroduced and now toggleable from Settings > Interface.
+- **💤 Wakelock API**: Now in use to prevent screen dimming during important tasks.
+- **🔐 API Key Privacy**: All API keys are now hidden by default for better security.
+- **🔍 New Web Search Provider**: Added jina_search as a new option.
+- **🌐 Enhanced Internationalization (i18n)**: Improved Korean translation and updated Chinese and Ukrainian translations.
+
+### Fixed
+
+- **🔧 Conversation Mode Issue**: Fixed the issue where Conversation Mode remained active after being removed from settings.
+- **📏 Scroll Button Obstruction**: Resolved the issue where the scrollToBottom button container obstructed clicks on buttons beneath it.
+
+### Changed
+
+- **⏲️ AIOHTTP_CLIENT_TIMEOUT**: Now set to 'None' by default for improved configuration flexibility.
+- **📞 Voice Call Enhancements**: Improved by skipping code blocks and expressions during calls.
+- **🚫 Error Message Handling**: Disabled the continuation of operations with error messages.
+- **🗂️ Playground Relocation**: Moved the Playground from the workspace to the user menu for better user experience.
+
+## [0.3.5] - 2024-06-16
+
+### Added
+
+- **📞 Enhanced Voice Call**: Text-to-speech (TTS) callback now operates in real-time for each sentence, reducing latency by not waiting for full completion.
+- **👆 Tap to Interrupt**: During a call, you can now stop the assistant from speaking by simply tapping, instead of using voice. This resolves the issue of the speaker's voice being mistakenly registered as input.
+- **😊 Emoji Call**: Toggle this feature on from the Settings > Interface, allowing LLMs to express emotions using emojis during voice calls for a more dynamic interaction.
+- **🖱️ Quick Archive/Delete**: Use the Shift key + mouseover on the chat list to swiftly archive or delete items.
+- **📝 Markdown Support in Model Descriptions**: You can now format model descriptions with markdown, enabling bold text, links, etc.
+- **🧠 Editable Memories**: Adds the capability to modify memories.
+- **📋 Admin Panel Sorting**: Introduces the ability to sort users/chats within the admin panel.
+- **🌑 Dark Mode for Quick Selectors**: Dark mode now available for chat quick selectors (prompts, models, documents).
+- **🔧 Advanced Parameters**: Adds 'num_keep' and 'num_batch' to advanced parameters for customization.
+- **📅 Dynamic System Prompts**: New variables '{{CURRENT_DATETIME}}', '{{CURRENT_TIME}}', '{{USER_LOCATION}}' added for system prompts. Ensure '{{USER_LOCATION}}' is toggled on from Settings > Interface.
+- **🌐 Tavily Web Search**: Includes Tavily as a web search provider option.
+- **🖊️ Federated Auth Usernames**: Ability to set user names for federated authentication.
+- **🔗 Auto Clean URLs**: When adding connection URLs, trailing slashes are now automatically removed.
+- **🌐 Enhanced Translations**: Improved Chinese and Swedish translations.
+
+### Fixed
+
+- **⏳ AIOHTTP_CLIENT_TIMEOUT**: Introduced a new environment variable 'AIOHTTP_CLIENT_TIMEOUT' for requests to Ollama lasting longer than 5 minutes. Default is 300 seconds; set to blank ('') for no timeout.
+- **❌ Message Delete Freeze**: Resolved an issue where message deletion would sometimes cause the web UI to freeze.
+
+## [0.3.4] - 2024-06-12
+
+### Fixed
+
+- **🔒 Mixed Content with HTTPS Issue**: Resolved a problem where mixed content (HTTP and HTTPS) was causing security warnings and blocking resources on HTTPS sites.
+- **🔍 Web Search Issue**: Addressed the problem where web search functionality was not working correctly. The 'ENABLE_RAG_LOCAL_WEB_FETCH' option has been reintroduced to restore proper web searching capabilities.
+- **💾 RAG Template Not Being Saved**: Fixed an issue where the RAG template was not being saved correctly, ensuring your custom templates are now preserved as expected.
+
+## [0.3.3] - 2024-06-12
+
+### Added
+
+- **🛠️ Native Python Function Calling**: Introducing native Python function calling within Open WebUI. We’ve also included a built-in code editor to seamlessly develop and integrate function code within the 'Tools' workspace. With this, you can significantly enhance your LLM’s capabilities by creating custom RAG pipelines, web search tools, and even agent-like features such as sending Discord messages.
+- **🌐 DuckDuckGo Integration**: Added DuckDuckGo as a web search provider, giving you more search options.
+- **🌏 Enhanced Translations**: Improved translations for Vietnamese and Chinese languages, making the interface more accessible.
+
+### Fixed
+
+- **🔗 Web Search URL Error Handling**: Fixed the issue where a single URL error would disrupt the data loading process in Web Search mode. Now, such errors will be handled gracefully to ensure uninterrupted data loading.
+- **🖥️ Frontend Responsiveness**: Resolved the problem where the frontend would stop responding if the backend encounters an error while downloading a model. Improved error handling to maintain frontend stability.
+- **🔧 Dependency Issues in pip**: Fixed issues related to pip installations, ensuring all dependencies are correctly managed to prevent installation errors.
+
+## [0.3.2] - 2024-06-10
+
+### Added
+
+- **🔍 Web Search Query Status**: The web search query will now persist in the results section to aid in easier debugging and tracking of search queries.
+- **🌐 New Web Search Provider**: We have added Serply as a new option for web search providers, giving you more choices for your search needs.
+- **🌏 Improved Translations**: We've enhanced translations for Chinese and Portuguese.
+
+### Fixed
+
+- **🎤 Audio File Upload Issue**: The bug that prevented audio files from being uploaded in chat input has been fixed, ensuring smooth communication.
+- **💬 Message Input Handling**: Improved the handling of message inputs by instantly clearing images and text after sending, along with immediate visual indications when a response message is loading, enhancing user feedback.
+- **⚙️ Parameter Registration and Validation**: Fixed the issue where parameters were not registering in certain cases and addressed the problem where users were unable to save due to invalid input errors.
+
+## [0.3.1] - 2024-06-09
+
+### Fixed
+
+- **💬 Chat Functionality**: Resolved the issue where chat functionality was not working for specific models.
+
+## [0.3.0] - 2024-06-09
+
+### Added
+
+- **📚 Knowledge Support for Models**: Attach documents directly to models from the models workspace, enhancing the information available to each model.
+- **🎙️ Hands-Free Voice Call Feature**: Initiate voice calls without needing to use your hands, making interactions more seamless.
+- **📹 Video Call Feature**: Enable video calls with supported vision models like Llava and GPT-4o, adding a visual dimension to your communications.
+- **🎛️ Enhanced UI for Voice Recording**: Improved user interface for the voice recording feature, making it more intuitive and user-friendly.
+- **🌐 External STT Support**: Now support for external Speech-To-Text services, providing more flexibility in choosing your STT provider.
+- **⚙️ Unified Settings**: Consolidated settings including document settings under a new admin settings section for easier management.
+- **🌑 Dark Mode Splash Screen**: A new splash screen for dark mode, ensuring a consistent and visually appealing experience for dark mode users.
+- **📥 Upload Pipeline**: Directly upload pipelines from the admin settings > pipelines section, streamlining the pipeline management process.
+- **🌍 Improved Language Support**: Enhanced support for Chinese and Ukrainian languages, better catering to a global user base.
+
+### Fixed
+
+- **🛠️ Playground Issue**: Fixed the playground not functioning properly, ensuring a smoother user experience.
+- **🔥 Temperature Parameter Issue**: Corrected the issue where the temperature value '0' was not being passed correctly.
+- **📝 Prompt Input Clearing**: Resolved prompt input textarea not being cleared right away, ensuring a clean slate for new inputs.
+- **✨ Various UI Styling Issues**: Fixed numerous user interface styling problems for a more cohesive look.
+- **👥 Active Users Display**: Fixed active users showing active sessions instead of actual users, now reflecting accurate user activity.
+- **🌐 Community Platform Compatibility**: The Community Platform is back online and fully compatible with Open WebUI.
+
+### Changed
+
+- **📝 RAG Implementation**: Updated the RAG (Retrieval-Augmented Generation) implementation to use a system prompt for context, instead of overriding the user's prompt.
+- **🔄 Settings Relocation**: Moved Models, Connections, Audio, and Images settings to the admin settings for better organization.
+- **✍️ Improved Title Generation**: Enhanced the default prompt for title generation, yielding better results.
+- **🔧 Backend Task Management**: Tasks like title generation and search query generation are now managed on the backend side and controlled only by the admin.
+- **🔍 Editable Search Query Prompt**: You can now edit the search query generation prompt, offering more control over how queries are generated.
+- **📏 Prompt Length Threshold**: Set the prompt length threshold for search query generation from the admin settings, giving more customization options.
+- **📣 Settings Consolidation**: Merged the Banners admin setting with the Interface admin setting for a more streamlined settings area.
+
+## [0.2.5] - 2024-06-05
+
+### Added
+
+- **👥 Active Users Indicator**: Now you can see how many people are currently active and what they are running. This helps you gauge when performance might slow down due to a high number of users.
+- **🗂️ Create Ollama Modelfile**: The option to create a modelfile for Ollama has been reintroduced in the Settings > Models section, making it easier to manage your models.
+- **⚙️ Default Model Setting**: Added an option to set the default model from Settings > Interface. This feature is now easily accessible, especially convenient for mobile users as it was previously hidden.
+- **🌐 Enhanced Translations**: We've improved the Chinese translations and added support for Turkmen and Norwegian languages to make the interface more accessible globally.
+
+### Fixed
+
+- **📱 Mobile View Improvements**: The UI now uses dvh (dynamic viewport height) instead of vh (viewport height), providing a better and more responsive experience for mobile users.
+
+## [0.2.4] - 2024-06-03
+
+### Added
+
+- **👤 Improved Account Pending Page**: The account pending page now displays admin details by default to avoid confusion. You can disable this feature in the admin settings if needed.
+- **🌐 HTTP Proxy Support**: We have enabled the use of the 'http_proxy' environment variable in OpenAI and Ollama API calls, making it easier to configure network settings.
+- **❓ Quick Access to Documentation**: You can now easily access Open WebUI documents via a question mark button located at the bottom right corner of the screen (available on larger screens like PCs).
+- **🌍 Enhanced Translation**: Improvements have been made to translations.
+
+### Fixed
+
+- **🔍 SearxNG Web Search**: Fixed the issue where the SearxNG web search functionality was not working properly.
+
+## [0.2.3] - 2024-06-03
+
+### Added
+
+- **📁 Export Chat as JSON**: You can now export individual chats as JSON files from the navbar menu by navigating to 'Download > Export Chat'. This makes sharing specific conversations easier.
+- **✏️ Edit Titles with Double Click**: Double-click on titles to rename them quickly and efficiently.
+- **🧩 Batch Multiple Embeddings**: Introduced 'RAG_EMBEDDING_OPENAI_BATCH_SIZE' to process multiple embeddings in a batch, enhancing performance for large datasets.
+- **🌍 Improved Translations**: Enhanced the translation quality across various languages for a better user experience.
+
+### Fixed
+
+- **🛠️ Modelfile Migration Script**: Fixed an issue where the modelfile migration script would fail if an invalid modelfile was encountered.
+- **💬 Zhuyin Input Method on Mac**: Resolved an issue where using the Zhuyin input method in the Web UI on a Mac caused text to send immediately upon pressing the enter key, leading to incorrect input.
+- **🔊 Local TTS Voice Selection**: Fixed the issue where the selected local Text-to-Speech (TTS) voice was not being displayed in settings.
+
+## [0.2.2] - 2024-06-02
+
+### Added
+
+- **🌊 Mermaid Rendering Support**: We've included support for Mermaid rendering. This allows you to create beautiful diagrams and flowcharts directly within Open WebUI.
+- **🔄 New Environment Variable 'RESET_CONFIG_ON_START'**: Introducing a new environment variable: 'RESET_CONFIG_ON_START'. Set this variable to reset your configuration settings upon starting the application, making it easier to revert to default settings.
+
+### Fixed
+
+- **🔧 Pipelines Filter Issue**: We've addressed an issue with the pipelines where filters were not functioning as expected.
+
+## [0.2.1] - 2024-06-02
+
+### Added
+
+- **🖱️ Single Model Export Button**: Easily export models with just one click using the new single model export button.
+- **🖥️ Advanced Parameters Support**: Added support for 'num_thread', 'use_mmap', and 'use_mlock' parameters for Ollama.
+- **🌐 Improved Vietnamese Translation**: Enhanced Vietnamese language support for a better user experience for our Vietnamese-speaking community.
+
+### Fixed
+
+- **🔧 OpenAI URL API Save Issue**: Corrected a problem preventing the saving of OpenAI URL API settings.
+- **🚫 Display Issue with Disabled Ollama API**: Fixed the display bug causing models to appear in settings when the Ollama API was disabled.
+
+### Changed
+
+- **💡 Versioning Update**: As a reminder from our previous update, version 0.2.y will focus primarily on bug fixes, while major updates will be designated as 0.x from now on for better version tracking.
+
+## [0.2.0] - 2024-06-01
+
+### Added
+
+- **🔧 Pipelines Support**: Open WebUI now includes a plugin framework for enhanced customization and functionality (https://github.com/open-webui/pipelines). Easily add custom logic and integrate Python libraries, from AI agents to home automation APIs.
+- **🔗 Function Calling via Pipelines**: Integrate function calling seamlessly through Pipelines.
+- **⚖️ User Rate Limiting via Pipelines**: Implement user-specific rate limits to manage API usage efficiently.
+- **📊 Usage Monitoring with Langfuse**: Track and analyze usage statistics with Langfuse integration through Pipelines.
+- **🕒 Conversation Turn Limits**: Set limits on conversation turns to manage interactions better through Pipelines.
+- **🛡️ Toxic Message Filtering**: Automatically filter out toxic messages to maintain a safe environment using Pipelines.
+- **🔍 Web Search Support**: Introducing built-in web search capabilities via RAG API, allowing users to search using SearXNG, Google Programmatic Search Engine, Brave Search, serpstack, and serper. Activate it effortlessly by adding necessary variables from Document settings > Web Params.
+- **🗂️ Models Workspace**: Create and manage model presets for both Ollama/OpenAI API. Note: The old Modelfiles workspace is deprecated.
+- **🛠️ Model Builder Feature**: Build and edit all models with persistent builder mode.
+- **🏷️ Model Tagging Support**: Organize models with tagging features in the models workspace.
+- **📋 Model Ordering Support**: Effortlessly organize models by dragging and dropping them into the desired positions within the models workspace.
+- **📈 OpenAI Generation Stats**: Access detailed generation statistics for OpenAI models.
+- **📅 System Prompt Variables**: New variables added: '{{CURRENT_DATE}}' and '{{USER_NAME}}' for dynamic prompts.
+- **📢 Global Banner Support**: Manage global banners from admin settings > banners.
+- **🗃️ Enhanced Archived Chats Modal**: Search and export archived chats easily.
+- **📂 Archive All Button**: Quickly archive all chats from settings > chats.
+- **🌐 Improved Translations**: Added and improved translations for French, Croatian, Cebuano, and Vietnamese.
+
+### Fixed
+
+- **🔍 Archived Chats Visibility**: Resolved issue with archived chats not showing in the admin panel.
+- **💬 Message Styling**: Fixed styling issues affecting message appearance.
+- **🔗 Shared Chat Responses**: Corrected the issue where shared chat response messages were not readonly.
+- **🖥️ UI Enhancement**: Fixed the scrollbar overlapping issue with the message box in the user interface.
+
+### Changed
+
+- **💾 User Settings Storage**: User settings are now saved on the backend, ensuring consistency across all devices.
+- **📡 Unified API Requests**: The API request for getting models is now unified to '/api/models' for easier usage.
+- **🔄 Versioning Update**: Our versioning will now follow the format 0.x for major updates and 0.x.y for patches.
+- **📦 Export All Chats (All Users)**: Moved this functionality to the Admin Panel settings for better organization and accessibility.
+
+### Removed
+
+- **🚫 Bundled LiteLLM Support Deprecated**: Migrate your LiteLLM config.yaml to a self-hosted LiteLLM instance. LiteLLM can still be added via OpenAI Connections. Download the LiteLLM config.yaml from admin settings > database > export LiteLLM config.yaml.
+
+## [0.1.125] - 2024-05-19
+
+### Added
+
+- **🔄 Updated UI**: Chat interface revamped with chat bubbles. Easily switch back to the old style via settings > interface > chat bubble UI.
+- **📂 Enhanced Sidebar UI**: Model files, documents, prompts, and playground merged into Workspace for streamlined access.
+- **🚀 Improved Many Model Interaction**: All responses now displayed simultaneously for a smoother experience.
+- **🐍 Python Code Execution**: Execute Python code locally in the browser with libraries like 'requests', 'beautifulsoup4', 'numpy', 'pandas', 'seaborn', 'matplotlib', 'scikit-learn', 'scipy', 'regex'.
+- **🧠 Experimental Memory Feature**: Manually input personal information you want LLMs to remember via settings > personalization > memory.
+- **💾 Persistent Settings**: Settings now saved as config.json for convenience.
+- **🩺 Health Check Endpoint**: Added for Docker deployment.
+- **↕️ RTL Support**: Toggle chat direction via settings > interface > chat direction.
+- **🖥️ PowerPoint Support**: RAG pipeline now supports PowerPoint documents.
+- **🌐 Language Updates**: Ukrainian, Turkish, Arabic, Chinese, Serbian, Vietnamese updated; Punjabi added.
+
+### Changed
+
+- **👤 Shared Chat Update**: Shared chat now includes creator user information.
+
+## [0.1.124] - 2024-05-08
+
+### Added
+
+- **🖼️ Improved Chat Sidebar**: Now conveniently displays time ranges and organizes chats by today, yesterday, and more.
+- **📜 Citations in RAG Feature**: Easily track the context fed to the LLM with added citations in the RAG feature.
+- **🔒 Auth Disable Option**: Introducing the ability to disable authentication. Set 'WEBUI_AUTH' to False to disable authentication. Note: Only applicable for fresh installations without existing users.
+- **📹 Enhanced YouTube RAG Pipeline**: Now supports non-English videos for an enriched experience.
+- **🔊 Specify OpenAI TTS Models**: Customize your TTS experience by specifying OpenAI TTS models.
+- **🔧 Additional Environment Variables**: Discover more environment variables in our comprehensive documentation at Open WebUI Documentation (https://docs.openwebui.com).
+- **🌐 Language Support**: Arabic, Finnish, and Hindi added; Improved support for German, Vietnamese, and Chinese.
+
+### Fixed
+
+- **🛠️ Model Selector Styling**: Addressed styling issues for improved user experience.
+- **⚠️ Warning Messages**: Resolved backend warning messages.
+
+### Changed
+
+- **📝 Title Generation**: Limited output to 50 tokens.
+- **📦 Helm Charts**: Removed Helm charts, now available in a separate repository (https://github.com/open-webui/helm-charts).
+
+## [0.1.123] - 2024-05-02
+
+### Added
+
+- **🎨 New Landing Page Design**: Refreshed design for a more modern look and optimized use of screen space.
+- **📹 Youtube RAG Pipeline**: Introduces dedicated RAG pipeline for Youtube videos, enabling interaction with video transcriptions directly.
+- **🔧 Enhanced Admin Panel**: Streamlined user management with options to add users directly or in bulk via CSV import.
+- **👥 '@' Model Integration**: Easily switch to specific models during conversations; old collaborative chat feature phased out.
+- **🌐 Language Enhancements**: Swedish translation added, plus improvements to German, Spanish, and the addition of Doge translation.
+
+### Fixed
+
+- **🗑️ Delete Chat Shortcut**: Addressed issue where shortcut wasn't functioning.
+- **🖼️ Modal Closing Bug**: Resolved unexpected closure of modal when dragging from within.
+- **✏️ Edit Button Styling**: Fixed styling inconsistency with edit buttons.
+- **🌐 Image Generation Compatibility Issue**: Rectified image generation compatibility issue with third-party APIs.
+- **📱 iOS PWA Icon Fix**: Corrected iOS PWA home screen icon shape.
+- **🔍 Scroll Gesture Bug**: Adjusted gesture sensitivity to prevent accidental activation when scrolling through code on mobile; now requires scrolling from the leftmost side to open the sidebar.
+
+### Changed
+
+- **🔄 Unlimited Context Length**: Advanced settings now allow unlimited max context length (previously limited to 16000).
+- **👑 Super Admin Assignment**: The first signup is automatically assigned a super admin role, unchangeable by other admins.
+- **🛡️ Admin User Restrictions**: User action buttons from the admin panel are now disabled for users with admin roles.
+- **🔝 Default Model Selector**: Set as default model option now exclusively available on the landing page.
+
+## [0.1.122] - 2024-04-27
+
+### Added
+
+- **🌟 Enhanced RAG Pipeline**: Now with hybrid searching via 'BM25', reranking powered by 'CrossEncoder', and configurable relevance score thresholds.
+- **🛢️ External Database Support**: Seamlessly connect to custom SQLite or Postgres databases using the 'DATABASE_URL' environment variable.
+- **🌐 Remote ChromaDB Support**: Introducing the capability to connect to remote ChromaDB servers.
+- **👨‍💼 Improved Admin Panel**: Admins can now conveniently check users' chat lists and last active status directly from the admin panel.
+- **🎨 Splash Screen**: Introducing a loading splash screen for a smoother user experience.
+- **🌍 Language Support Expansion**: Added support for Bangla (bn-BD), along with enhancements to Chinese, Spanish, and Ukrainian translations.
+- **💻 Improved LaTeX Rendering Performance**: Enjoy faster rendering times for LaTeX equations.
+- **🔧 More Environment Variables**: Explore additional environment variables in our documentation (https://docs.openwebui.com), including the 'ENABLE_LITELLM' option to manage memory usage.
+
+### Fixed
+
+- **🔧 Ollama Compatibility**: Resolved errors occurring when Ollama server version isn't an integer, such as SHA builds or RCs.
+- **🐛 Various OpenAI API Issues**: Addressed several issues related to the OpenAI API.
+- **🛑 Stop Sequence Issue**: Fixed the problem where the stop sequence with a backslash '\' was not functioning.
+- **🔤 Font Fallback**: Corrected font fallback issue.
+
+### Changed
+
+- **⌨️ Prompt Input Behavior on Mobile**: Enter key prompt submission disabled on mobile devices for improved user experience.
+
+## [0.1.121] - 2024-04-24
+
+### Fixed
+
+- **🔧 Translation Issues**: Addressed various translation discrepancies.
+- **🔒 LiteLLM Security Fix**: Updated LiteLLM version to resolve a security vulnerability.
+- **🖥️ HTML Tag Display**: Rectified the issue where the '< br >' tag wasn't displaying correctly.
+- **🔗 WebSocket Connection**: Resolved the failure of WebSocket connection under HTTPS security for ComfyUI server.
+- **📜 FileReader Optimization**: Implemented FileReader initialization per image in multi-file drag & drop to ensure reusability.
+- **🏷️ Tag Display**: Corrected tag display inconsistencies.
+- **📦 Archived Chat Styling**: Fixed styling issues in archived chat.
+- **🔖 Safari Copy Button Bug**: Addressed the bug where the copy button failed to copy links in Safari.
+
+## [0.1.120] - 2024-04-20
+
+### Added
+
+- **📦 Archive Chat Feature**: Easily archive chats with a new sidebar button, and access archived chats via the profile button > archived chats.
+- **🔊 Configurable Text-to-Speech Endpoint**: Customize your Text-to-Speech experience with configurable OpenAI endpoints.
+- **🛠️ Improved Error Handling**: Enhanced error message handling for connection failures.
+- **⌨️ Enhanced Shortcut**: When editing messages, use ctrl/cmd+enter to save and submit, and esc to close.
+- **🌐 Language Support**: Added support for Georgian and enhanced translations for Portuguese and Vietnamese.
+
+### Fixed
+
+- **🔧 Model Selector**: Resolved issue where default model selection was not saving.
+- **🔗 Share Link Copy Button**: Fixed bug where the copy button wasn't copying links in Safari.
+- **🎨 Light Theme Styling**: Addressed styling issue with the light theme.
+
+## [0.1.119] - 2024-04-16
+
+### Added
+
+- **🌟 Enhanced RAG Embedding Support**: Ollama, and OpenAI models can now be used for RAG embedding model.
+- **🔄 Seamless Integration**: Copy 'ollama run <model name>' directly from Ollama page to easily select and pull models.
+- **🏷️ Tagging Feature**: Add tags to chats directly via the sidebar chat menu.
+- **📱 Mobile Accessibility**: Swipe left and right on mobile to effortlessly open and close the sidebar.
+- **🔍 Improved Navigation**: Admin panel now supports pagination for user list.
+- **🌍 Additional Language Support**: Added Polish language support.
+
+### Fixed
+
+- **🌍 Language Enhancements**: Vietnamese and Spanish translations have been improved.
+- **🔧 Helm Fixes**: Resolved issues with Helm trailing slash and manifest.json.
+
+### Changed
+
+- **🐳 Docker Optimization**: Updated docker image build process to utilize 'uv' for significantly faster builds compared to 'pip3'.
+
+## [0.1.118] - 2024-04-10
+
+### Added
+
+- **🦙 Ollama and CUDA Images**: Added support for ':ollama' and ':cuda' tagged images.
+- **👍 Enhanced Response Rating**: Now you can annotate your ratings for better feedback.
+- **👤 User Initials Profile Photo**: User initials are now the default profile photo.
+- **🔍 Update RAG Embedding Model**: Customize RAG embedding model directly in document settings.
+- **🌍 Additional Language Support**: Added Turkish language support.
+
+### Fixed
+
+- **🔒 Share Chat Permission**: Resolved issue with chat sharing permissions.
+- **🛠 Modal Close**: Modals can now be closed using the Esc key.
+
+### Changed
+
+- **🎨 Admin Panel Styling**: Refreshed styling for the admin panel.
+- **🐳 Docker Image Build**: Updated docker image build process for improved efficiency.
+
+## [0.1.117] - 2024-04-03
+
+### Added
+
+- 🗨️ **Local Chat Sharing**: Share chat links seamlessly between users.
+- 🔑 **API Key Generation Support**: Generate secret keys to leverage Open WebUI with OpenAI libraries.
+- 📄 **Chat Download as PDF**: Easily download chats in PDF format.
+- 📝 **Improved Logging**: Enhancements to logging functionality.
+- 📧 **Trusted Email Authentication**: Authenticate using a trusted email header.
+
+### Fixed
+
+- 🌷 **Enhanced Dutch Translation**: Improved translation for Dutch users.
+- ⚪ **White Theme Styling**: Resolved styling issue with the white theme.
+- 📜 **LaTeX Chat Screen Overflow**: Fixed screen overflow issue with LaTeX rendering.
+- 🔒 **Security Patches**: Applied necessary security patches.
+
+## [0.1.116] - 2024-03-31
+
+### Added
+
+- **🔄 Enhanced UI**: Model selector now conveniently located in the navbar, enabling seamless switching between multiple models during conversations.
+- **🔍 Improved Model Selector**: Directly pull a model from the selector/Models now display detailed information for better understanding.
+- **💬 Webhook Support**: Now compatible with Google Chat and Microsoft Teams.
+- **🌐 Localization**: Korean translation (I18n) now available.
+- **🌑 Dark Theme**: OLED dark theme introduced for reduced strain during prolonged usage.
+- **🏷️ Tag Autocomplete**: Dropdown feature added for effortless chat tagging.
+
+### Fixed
+
+- **🔽 Auto-Scrolling**: Addressed OpenAI auto-scrolling issue.
+- **🏷️ Tag Validation**: Implemented tag validation to prevent empty string tags.
+- **🚫 Model Whitelisting**: Resolved LiteLLM model whitelisting issue.
+- **✅ Spelling**: Corrected various spelling issues for improved readability.
+
+## [0.1.115] - 2024-03-24
+
+### Added
+
+- **🔍 Custom Model Selector**: Easily find and select custom models with the new search filter feature.
+- **🛑 Cancel Model Download**: Added the ability to cancel model downloads.
+- **🎨 Image Generation ComfyUI**: Image generation now supports ComfyUI.
+- **🌟 Updated Light Theme**: Updated the light theme for a fresh look.
+- **🌍 Additional Language Support**: Now supporting Bulgarian, Italian, Portuguese, Japanese, and Dutch.
+
+### Fixed
+
+- **🔧 Fixed Broken Experimental GGUF Upload**: Resolved issues with experimental GGUF upload functionality.
+
+### Changed
+
+- **🔄 Vector Storage Reset Button**: Moved the reset vector storage button to document settings.
+
+## [0.1.114] - 2024-03-20
+
+### Added
+
+- **🔗 Webhook Integration**: Now you can subscribe to new user sign-up events via webhook. Simply navigate to the admin panel > admin settings > webhook URL.
+- **🛡️ Enhanced Model Filtering**: Alongside Ollama, OpenAI proxy model whitelisting, we've added model filtering functionality for LiteLLM proxy.
+- **🌍 Expanded Language Support**: Spanish, Catalan, and Vietnamese languages are now available, with improvements made to others.
+
+### Fixed
+
+- **🔧 Input Field Spelling**: Resolved issue with spelling mistakes in input fields.
+- **🖊️ Light Mode Styling**: Fixed styling issue with light mode in document adding.
+
+### Changed
+
+- **🔄 Language Sorting**: Languages are now sorted alphabetically by their code for improved organization.
+
+## [0.1.113] - 2024-03-18
+
+### Added
+
+- 🌍 **Localization**: You can now change the UI language in Settings > General. We support Ukrainian, German, Farsi (Persian), Traditional and Simplified Chinese and French translations. You can help us to translate the UI into your language! More info in our [CONTRIBUTION.md](https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization).
+- 🎨 **System-wide Theme**: Introducing a new system-wide theme for enhanced visual experience.
+
+### Fixed
+
+- 🌑 **Dark Background on Select Fields**: Improved readability by adding a dark background to select fields, addressing issues on certain browsers/devices.
+- **Multiple OPENAI_API_BASE_URLS Issue**: Resolved issue where multiple base URLs caused conflicts when one wasn't functioning.
+- **RAG Encoding Issue**: Fixed encoding problem in RAG.
+- **npm Audit Fix**: Addressed npm audit findings.
+- **Reduced Scroll Threshold**: Improved auto-scroll experience by reducing the scroll threshold from 50px to 5px.
+
+### Changed
+
+- 🔄 **Sidebar UI Update**: Updated sidebar UI to feature a chat menu dropdown, replacing two icons for improved navigation.
+
+## [0.1.112] - 2024-03-15
+
+### Fixed
+
+- 🗨️ Resolved chat malfunction after image generation.
+- 🎨 Fixed various RAG issues.
+- 🧪 Rectified experimental broken GGUF upload logic.
+
+## [0.1.111] - 2024-03-10
+
+### Added
+
+- 🛡️ **Model Whitelisting**: Admins now have the ability to whitelist models for users with the 'user' role.
+- 🔄 **Update All Models**: Added a convenient button to update all models at once.
+- 📄 **Toggle PDF OCR**: Users can now toggle PDF OCR option for improved parsing performance.
+- 🎨 **DALL-E Integration**: Introduced DALL-E integration for image generation alongside automatic1111.
+- 🛠️ **RAG API Refactoring**: Refactored RAG logic and exposed its API, with additional documentation to follow.
+
+### Fixed
+
+- 🔒 **Max Token Settings**: Added max token settings for anthropic/claude-3-sonnet-20240229 (Issue #1094).
+- 🔧 **Misalignment Issue**: Corrected misalignment of Edit and Delete Icons when Chat Title is Empty (Issue #1104).
+- 🔄 **Context Loss Fix**: Resolved RAG losing context on model response regeneration with Groq models via API key (Issue #1105).
+- 📁 **File Handling Bug**: Addressed File Not Found Notification when Dropping a Conversation Element (Issue #1098).
+- 🖱️ **Dragged File Styling**: Fixed dragged file layover styling issue.
+
+## [0.1.110] - 2024-03-06
+
+### Added
+
+- **🌐 Multiple OpenAI Servers Support**: Enjoy seamless integration with multiple OpenAI-compatible APIs, now supported natively.
+
+### Fixed
+
+- **🔍 OCR Issue**: Resolved PDF parsing issue caused by OCR malfunction.
+- **🚫 RAG Issue**: Fixed the RAG functionality, ensuring it operates smoothly.
+- **📄 "Add Docs" Model Button**: Addressed the non-functional behavior of the "Add Docs" model button.
+
+## [0.1.109] - 2024-03-06
+
+### Added
+
+- **🔄 Multiple Ollama Servers Support**: Enjoy enhanced scalability and performance with support for multiple Ollama servers in a single WebUI. Load balancing features are now available, providing improved efficiency (#788, #278).
+- **🔧 Support for Claude 3 and Gemini**: Responding to user requests, we've expanded our toolset to include Claude 3 and Gemini, offering a wider range of functionalities within our platform (#1064).
+- **🔍 OCR Functionality for PDF Loader**: We've augmented our PDF loader with Optical Character Recognition (OCR) capabilities. Now, extract text from scanned documents and images within PDFs, broadening the scope of content processing (#1050).
+
+### Fixed
+
+- **🛠️ RAG Collection**: Implemented a dynamic mechanism to recreate RAG collections, ensuring users have up-to-date and accurate data (#1031).
+- **📝 User Agent Headers**: Fixed issue of RAG web requests being sent with empty user_agent headers, reducing rejections from certain websites. Realistic headers are now utilized for these requests (#1024).
+- **⏹️ Playground Cancel Functionality**: Introducing a new "Cancel" option for stopping Ollama generation in the Playground, enhancing user control and usability (#1006).
+- **🔤 Typographical Error in 'ASSISTANT' Field**: Corrected a typographical error in the 'ASSISTANT' field within the GGUF model upload template for accuracy and consistency (#1061).
+
+### Changed
+
+- **🔄 Refactored Message Deletion Logic**: Streamlined message deletion process for improved efficiency and user experience, simplifying interactions within the platform (#1004).
+- **⚠️ Deprecation of `OLLAMA_API_BASE_URL`**: Deprecated `OLLAMA_API_BASE_URL` environment variable; recommend using `OLLAMA_BASE_URL` instead. Refer to our documentation for further details.
+
+## [0.1.108] - 2024-03-02
+
+### Added
+
+- **🎮 Playground Feature (Beta)**: Explore the full potential of the raw API through an intuitive UI with our new playground feature, accessible to admins. Simply click on the bottom name area of the sidebar to access it. The playground feature offers two modes text completion (notebook) and chat completion. As it's in beta, please report any issues you encounter.
+- **🛠️ Direct Database Download for Admins**: Admins can now download the database directly from the WebUI via the admin settings.
+- **🎨 Additional RAG Settings**: Customize your RAG process with the ability to edit the TOP K value. Navigate to Documents > Settings > General to make changes.
+- **🖥️ UI Improvements**: Tooltips now available in the input area and sidebar handle. More tooltips will be added across other parts of the UI.
+
+### Fixed
+
+- Resolved input autofocus issue on mobile when the sidebar is open, making it easier to use.
+- Corrected numbered list display issue in Safari (#963).
+- Restricted user ability to delete chats without proper permissions (#993).
+
+### Changed
+
+- **Simplified Ollama Settings**: Ollama settings now don't require the `/api` suffix. You can now utilize the Ollama base URL directly, e.g., `http://localhost:11434`. Also, an `OLLAMA_BASE_URL` environment variable has been added.
+- **Database Renaming**: Starting from this release, `ollama.db` will be automatically renamed to `webui.db`.
+
+## [0.1.107] - 2024-03-01
+
+### Added
+
+- **🚀 Makefile and LLM Update Script**: Included Makefile and a script for LLM updates in the repository.
+
+### Fixed
+
+- Corrected issue where links in the settings modal didn't appear clickable (#960).
+- Fixed problem with web UI port not taking effect due to incorrect environment variable name in run-compose.sh (#996).
+- Enhanced user experience by displaying chat in browser title and enabling automatic scrolling to the bottom (#992).
+
+### Changed
+
+- Upgraded toast library from `svelte-french-toast` to `svelte-sonner` for a more polished UI.
+- Enhanced accessibility with the addition of dark mode on the authentication page.
+
+## [0.1.106] - 2024-02-27
+
+### Added
+
+- **🎯 Auto-focus Feature**: The input area now automatically focuses when initiating or opening a chat conversation.
+
+### Fixed
+
+- Corrected typo from "HuggingFace" to "Hugging Face" (Issue #924).
+- Resolved bug causing errors in chat completion API calls to OpenAI due to missing "num_ctx" parameter (Issue #927).
+- Fixed issues preventing text editing, selection, and cursor retention in the input field (Issue #940).
+- Fixed a bug where defining an OpenAI-compatible API server using 'OPENAI_API_BASE_URL' containing 'openai' string resulted in hiding models not containing 'gpt' string from the model menu. (Issue #930)
+
+## [0.1.105] - 2024-02-25
+
+### Added
+
+- **📄 Document Selection**: Now you can select and delete multiple documents at once for easier management.
+
+### Changed
+
+- **🏷️ Document Pre-tagging**: Simply click the "+" button at the top, enter tag names in the popup window, or select from a list of existing tags. Then, upload files with the added tags for streamlined organization.
+
+## [0.1.104] - 2024-02-25
+
+### Added
+
+- **🔄 Check for Updates**: Keep your system current by checking for updates conveniently located in Settings > About.
+- **🗑️ Automatic Tag Deletion**: Unused tags on the sidebar will now be deleted automatically with just a click.
+
+### Changed
+
+- **🎨 Modernized Styling**: Enjoy a refreshed look with updated styling for a more contemporary experience.
+
+## [0.1.103] - 2024-02-25
+
+### Added
+
+- **🔗 Built-in LiteLLM Proxy**: Now includes LiteLLM proxy within Open WebUI for enhanced functionality.
+
+  - Easily integrate existing LiteLLM configurations using `-v /path/to/config.yaml:/app/backend/data/litellm/config.yaml` flag.
+  - When utilizing Docker container to run Open WebUI, ensure connections to localhost use `host.docker.internal`.
+
+- **🖼️ Image Generation Enhancements**: Introducing Advanced Settings with Image Preview Feature.
+  - Customize image generation by setting the number of steps; defaults to A1111 value.
+
+### Fixed
+
+- Resolved issue with RAG scan halting document loading upon encountering unsupported MIME types or exceptions (Issue #866).
+
+### Changed
+
+- Ollama is no longer required to run Open WebUI.
+- Access our comprehensive documentation at [Open WebUI Documentation](https://docs.openwebui.com/).
+
+## [0.1.102] - 2024-02-22
+
+### Added
+
+- **🖼️ Image Generation**: Generate Images using the AUTOMATIC1111/stable-diffusion-webui API. You can set this up in Settings > Images.
+- **📝 Change title generation prompt**: Change the prompt used to generate titles for your chats. You can set this up in the Settings > Interface.
+- **🤖 Change embedding model**: Change the embedding model used to generate embeddings for your chats in the Dockerfile. Use any sentence transformer model from huggingface.co.
+- **📢 CHANGELOG.md/Popup**: This popup will show you the latest changes.
+
+## [0.1.101] - 2024-02-22
+
+### Fixed
+
+- LaTex output formatting issue (#828)
+
+### Changed
+
+- Instead of having the previous 1.0.0-alpha.101, we switched to semantic versioning as a way to respect global conventions.
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000000000000000000000000000000000..b1c7b56a338449bbdbf44a0ecc39ecf947df0715
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,99 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+As members, contributors, and leaders of this community, we pledge to make participation in our open-source project a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+We are committed to creating and maintaining an open, respectful, and professional environment where positive contributions and meaningful discussions can flourish. By participating in this project, you agree to uphold these values and align your behavior to the standards outlined in this Code of Conduct.
+
+## Why These Standards Are Important
+
+Open-source projects rely on a community of volunteers dedicating their time, expertise, and effort toward a shared goal. These projects are inherently collaborative but also fragile, as the success of the project depends on the goodwill, energy, and productivity of those involved.
+
+Maintaining a positive and respectful environment is essential to safeguarding the integrity of this project and protecting contributors' efforts. Behavior that disrupts this atmosphere—whether through hostility, entitlement, or unprofessional conduct—can severely harm the morale and productivity of the community. **Strict enforcement of these standards ensures a safe and supportive space for meaningful collaboration.**
+
+This is a community where **respect and professionalism are mandatory.** Violations of these standards will result in **zero tolerance** and immediate enforcement to prevent disruption and ensure the well-being of all participants.
+
+## Our Standards
+
+Examples of behavior that contribute to a positive and professional community include:
+
+- **Respecting others.** Be considerate, listen actively, and engage with empathy toward others' viewpoints and experiences.
+- **Constructive feedback.** Provide actionable, thoughtful, and respectful feedback that helps improve the project and encourages collaboration. Avoid unproductive negativity or hypercriticism.
+- **Recognizing volunteer contributions.** Appreciate that contributors dedicate their free time and resources selflessly. Approach them with gratitude and patience.
+- **Focusing on shared goals.** Collaborate in ways that prioritize the health, success, and sustainability of the community over individual agendas.
+
+Examples of unacceptable behavior include:
+
+- The use of discriminatory, demeaning, or sexualized language or behavior.
+- Personal attacks, derogatory comments, trolling, or inflammatory political or ideological arguments.
+- Harassment, intimidation, or any behavior intended to create a hostile, uncomfortable, or unsafe environment.
+- Publishing others' private information (e.g., physical or email addresses) without explicit permission.
+- **Entitlement, demand, or aggression toward contributors.** Volunteers are under no obligation to provide immediate or personalized support. Rude or dismissive behavior will not be tolerated.
+- **Unproductive or destructive behavior.** This includes venting frustration as hostility ("tantrums"), hypercriticism, attention-seeking negativity, or anything that distracts from the project's goals.
+- **Spamming and promotional exploitation.** Sharing irrelevant product promotions or self-promotion in the community is not allowed unless it directly contributes value to the discussion.
+
+### Feedback and Community Engagement
+
+- **Constructive feedback is encouraged, but hostile or entitled behavior will result in immediate action.** If you disagree with elements of the project, we encourage you to offer meaningful improvements or fork the project if necessary. Healthy discussions and technical disagreements are welcome only when handled with professionalism.
+- **Respect contributors' time and efforts.** No one is entitled to personalized or on-demand assistance. This is a community built on collaboration and shared effort; demanding or demeaning behavior undermines that trust and will not be allowed.
+
+### Zero Tolerance: No Warnings, Immediate Action
+
+This community operates under a **zero-tolerance policy.** Any behavior deemed unacceptable under this Code of Conduct will result in **immediate enforcement, without prior warning.**
+
+We employ this approach to ensure that unproductive or disruptive behavior does not escalate further or cause unnecessary harm to other contributors. The standards are clear, and violations of any kind—whether mild or severe—will be addressed decisively to protect the community.
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for upholding and enforcing these standards. They are empowered to take **immediate and appropriate action** to address any behaviors they deem unacceptable under this Code of Conduct. These actions are taken with the goal of protecting the community and preserving its safe, positive, and productive environment.
+
+## Scope
+
+This Code of Conduct applies to all community spaces, including forums, repositories, social media accounts, and in-person events. It also applies when an individual represents the community in public settings, such as conferences or official communications.
+
+Additionally, any behavior outside of these defined spaces that negatively impacts the community or its members may fall within the scope of this Code of Conduct.
+
+## Reporting Violations
+
+Instances of unacceptable behavior can be reported to the leadership team at **hello@openwebui.com**. Reports will be handled promptly, confidentially, and with consideration for the safety and well-being of the reporter.
+
+All community leaders are required to uphold confidentiality and impartiality when addressing reports of violations.
+
+## Enforcement Guidelines
+
+### Ban
+
+**Community Impact**: Community leaders will issue a ban to any participant whose behavior is deemed unacceptable according to this Code of Conduct. Bans are enforced immediately and without prior notice.
+
+A ban may be temporary or permanent, depending on the severity of the violation. This includes—but is not limited to—behavior such as:
+
+- Harassment or abusive behavior toward contributors.
+- Persistent negativity or hostility that disrupts the collaborative environment.
+- Disrespectful, demanding, or aggressive interactions with others.
+- Attempts to cause harm or sabotage the community.
+
+**Consequence**: A banned individual is immediately removed from access to all community spaces, communication channels, and events. Community leaders reserve the right to enforce either a time-limited suspension or a permanent ban based on the specific circumstances of the violation.
+
+This approach ensures that disruptive behaviors are addressed swiftly and decisively in order to maintain the integrity and productivity of the community.
+
+## Why Zero Tolerance Is Necessary
+
+Open-source projects thrive on collaboration, goodwill, and mutual respect. Toxic behaviors—such as entitlement, hostility, or persistent negativity—threaten not just individual contributors but the health of the project as a whole. Allowing such behaviors to persist robs contributors of their time, energy, and enthusiasm for the work they do.
+
+By enforcing a zero-tolerance policy, we ensure that the community remains a safe, welcoming space for all participants. These measures are not about harshness—they are about protecting contributors and fostering a productive environment where innovation can thrive.
+
+Our expectations are clear, and our enforcement reflects our commitment to this project's long-term success.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at  
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at  
+https://www.contributor-covenant.org/faq. Translations are available at  
+https://www.contributor-covenant.org/translations.
diff --git a/Caddyfile.localhost b/Caddyfile.localhost
new file mode 100644
index 0000000000000000000000000000000000000000..80728eedf6ac60dc6454ef6933b490eeed4648be
--- /dev/null
+++ b/Caddyfile.localhost
@@ -0,0 +1,64 @@
+# Run with
+#    caddy run --envfile ./example.env --config ./Caddyfile.localhost
+#
+# This is configured for
+#    - Automatic HTTPS (even for localhost)
+#    - Reverse Proxying to Ollama API Base URL (http://localhost:11434/api)
+#    - CORS
+#    - HTTP Basic Auth API Tokens (uncomment basicauth section)
+
+
+# CORS Preflight (OPTIONS) + Request (GET, POST, PATCH, PUT, DELETE)
+(cors-api) {
+	@match-cors-api-preflight method OPTIONS
+	handle @match-cors-api-preflight {
+		header {
+			Access-Control-Allow-Origin "{http.request.header.origin}"
+			Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
+			Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
+			Access-Control-Allow-Credentials "true"
+			Access-Control-Max-Age "3600"
+			defer
+		}
+		respond "" 204
+	}
+
+	@match-cors-api-request {
+		not {
+			header Origin "{http.request.scheme}://{http.request.host}"
+		}
+		header Origin "{http.request.header.origin}"
+	}
+	handle @match-cors-api-request {
+		header {
+			Access-Control-Allow-Origin "{http.request.header.origin}"
+			Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
+			Access-Control-Allow-Headers "Origin, Accept, Authorization, Content-Type, X-Requested-With"
+			Access-Control-Allow-Credentials "true"
+			Access-Control-Max-Age "3600"
+			defer
+		}
+	}
+}
+
+# replace localhost with example.com or whatever
+localhost {
+	## HTTP Basic Auth
+	## (uncomment to enable)
+	# basicauth {
+	# 	# see .example.env for how to generate tokens
+	# 	{env.OLLAMA_API_ID} {env.OLLAMA_API_TOKEN_DIGEST}
+	# }
+
+	handle /api/* {
+		# Comment to disable CORS
+		import cors-api
+
+		reverse_proxy localhost:11434
+	}
+
+	# Same-Origin Static Web Server
+	file_server {
+		root ./build/
+	}
+}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..274e23dbfc79c0a2fb5c01da0b26f26ad9f7dec8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,176 @@
+# syntax=docker/dockerfile:1
+# Initialize device type args
+# use build args in the docker build command with --build-arg="BUILDARG=true"
+ARG USE_CUDA=false
+ARG USE_OLLAMA=false
+# Tested with cu117 for CUDA 11 and cu121 for CUDA 12 (default)
+ARG USE_CUDA_VER=cu121
+# any sentence transformer model; models to use can be found at https://huggingface.co/models?library=sentence-transformers
+# Leaderboard: https://huggingface.co/spaces/mteb/leaderboard 
+# for better performance and multilangauge support use "intfloat/multilingual-e5-large" (~2.5GB) or "intfloat/multilingual-e5-base" (~1.5GB)
+# IMPORTANT: If you change the embedding model (sentence-transformers/all-MiniLM-L6-v2) and vice versa, you aren't able to use RAG Chat with your previous documents loaded in the WebUI! You need to re-embed them.
+ARG USE_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
+ARG USE_RERANKING_MODEL=""
+
+# Tiktoken encoding name; models to use can be found at https://huggingface.co/models?library=tiktoken
+ARG USE_TIKTOKEN_ENCODING_NAME="cl100k_base"
+
+ARG BUILD_HASH=dev-build
+# Override at your own risk - non-root configurations are untested
+ARG UID=0
+ARG GID=0
+
+######## WebUI frontend ########
+FROM --platform=$BUILDPLATFORM node:22-alpine3.20 AS build
+ARG BUILD_HASH
+
+WORKDIR /app
+
+COPY package.json package-lock.json ./
+RUN npm ci
+
+COPY . .
+ENV APP_BUILD_HASH=${BUILD_HASH}
+RUN npm run build
+
+######## WebUI backend ########
+FROM python:3.11-slim-bookworm AS base
+
+# Use args
+ARG USE_CUDA
+ARG USE_OLLAMA
+ARG USE_CUDA_VER
+ARG USE_EMBEDDING_MODEL
+ARG USE_RERANKING_MODEL
+ARG UID
+ARG GID
+
+## Basis ##
+ENV ENV=prod \
+    PORT=8080 \
+    # pass build args to the build
+    USE_OLLAMA_DOCKER=${USE_OLLAMA} \
+    USE_CUDA_DOCKER=${USE_CUDA} \
+    USE_CUDA_DOCKER_VER=${USE_CUDA_VER} \
+    USE_EMBEDDING_MODEL_DOCKER=${USE_EMBEDDING_MODEL} \
+    USE_RERANKING_MODEL_DOCKER=${USE_RERANKING_MODEL}
+
+## Basis URL Config ##
+ENV OLLAMA_BASE_URL="/ollama" \
+    OPENAI_API_BASE_URL=""
+
+## API Key and Security Config ##
+ENV OPENAI_API_KEY="" \
+    WEBUI_SECRET_KEY="" \
+    SCARF_NO_ANALYTICS=true \
+    DO_NOT_TRACK=true \
+    ANONYMIZED_TELEMETRY=false
+
+#### Other models #########################################################
+## whisper TTS model settings ##
+ENV WHISPER_MODEL="base" \
+    WHISPER_MODEL_DIR="/app/backend/data/cache/whisper/models"
+
+## RAG Embedding model settings ##
+ENV RAG_EMBEDDING_MODEL="$USE_EMBEDDING_MODEL_DOCKER" \
+    RAG_RERANKING_MODEL="$USE_RERANKING_MODEL_DOCKER" \
+    SENTENCE_TRANSFORMERS_HOME="/app/backend/data/cache/embedding/models"
+
+## Tiktoken model settings ##
+ENV TIKTOKEN_ENCODING_NAME="cl100k_base" \
+    TIKTOKEN_CACHE_DIR="/app/backend/data/cache/tiktoken"
+
+## Hugging Face download cache ##
+ENV HF_HOME="/app/backend/data/cache/embedding/models"
+
+## Torch Extensions ##
+# ENV TORCH_EXTENSIONS_DIR="/.cache/torch_extensions"
+
+#### Other models ##########################################################
+
+WORKDIR /app/backend
+
+ENV HOME=/root
+# Create user and group if not root
+RUN if [ $UID -ne 0 ]; then \
+    if [ $GID -ne 0 ]; then \
+    addgroup --gid $GID app; \
+    fi; \
+    adduser --uid $UID --gid $GID --home $HOME --disabled-password --no-create-home app; \
+    fi
+
+RUN mkdir -p $HOME/.cache/chroma
+RUN echo -n 00000000-0000-0000-0000-000000000000 > $HOME/.cache/chroma/telemetry_user_id
+
+# Make sure the user has access to the app and root directory
+RUN chown -R $UID:$GID /app $HOME
+
+RUN if [ "$USE_OLLAMA" = "true" ]; then \
+    apt-get update && \
+    # Install pandoc and netcat
+    apt-get install -y --no-install-recommends git build-essential pandoc netcat-openbsd curl && \
+    apt-get install -y --no-install-recommends gcc python3-dev && \
+    # for RAG OCR
+    apt-get install -y --no-install-recommends ffmpeg libsm6 libxext6 && \
+    # install helper tools
+    apt-get install -y --no-install-recommends curl jq && \
+    # install ollama
+    curl -fsSL https://ollama.com/install.sh | sh && \
+    # cleanup
+    rm -rf /var/lib/apt/lists/*; \
+    else \
+    apt-get update && \
+    # Install pandoc, netcat and gcc
+    apt-get install -y --no-install-recommends git build-essential pandoc gcc netcat-openbsd curl jq && \
+    apt-get install -y --no-install-recommends gcc python3-dev && \
+    # for RAG OCR
+    apt-get install -y --no-install-recommends ffmpeg libsm6 libxext6 && \
+    # cleanup
+    rm -rf /var/lib/apt/lists/*; \
+    fi
+
+# install python dependencies
+COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt
+
+RUN pip3 install uv && \
+    if [ "$USE_CUDA" = "true" ]; then \
+    # If you use CUDA the whisper and embedding model will be downloaded on first use
+    pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/$USE_CUDA_DOCKER_VER --no-cache-dir && \
+    uv pip install --system -r requirements.txt --no-cache-dir && \
+    python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
+    python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
+    python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
+    else \
+    pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --no-cache-dir && \
+    uv pip install --system -r requirements.txt --no-cache-dir && \
+    python -c "import os; from sentence_transformers import SentenceTransformer; SentenceTransformer(os.environ['RAG_EMBEDDING_MODEL'], device='cpu')" && \
+    python -c "import os; from faster_whisper import WhisperModel; WhisperModel(os.environ['WHISPER_MODEL'], device='cpu', compute_type='int8', download_root=os.environ['WHISPER_MODEL_DIR'])"; \
+    python -c "import os; import tiktoken; tiktoken.get_encoding(os.environ['TIKTOKEN_ENCODING_NAME'])"; \
+    fi; \
+    chown -R $UID:$GID /app/backend/data/
+
+
+
+# copy embedding weight from build
+# RUN mkdir -p /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2
+# COPY --from=build /app/onnx /root/.cache/chroma/onnx_models/all-MiniLM-L6-v2/onnx
+
+# copy built frontend files
+COPY --chown=$UID:$GID --from=build /app/build /app/build
+COPY --chown=$UID:$GID --from=build /app/CHANGELOG.md /app/CHANGELOG.md
+COPY --chown=$UID:$GID --from=build /app/package.json /app/package.json
+
+# copy backend files
+COPY --chown=$UID:$GID ./backend .
+
+EXPOSE 8080
+
+HEALTHCHECK CMD curl --silent --fail http://localhost:${PORT:-8080}/health | jq -ne 'input.status == true' || exit 1
+
+USER $UID:$GID
+
+ARG BUILD_HASH
+ENV WEBUI_BUILD_VERSION=${BUILD_HASH}
+ENV DOCKER=true
+
+CMD [ "bash", "start.sh"]
diff --git a/INSTALLATION.md b/INSTALLATION.md
new file mode 100644
index 0000000000000000000000000000000000000000..4298b173e9fd5c372a81c86fc4121de6a57fb81b
--- /dev/null
+++ b/INSTALLATION.md
@@ -0,0 +1,35 @@
+### Installing Both Ollama and Open WebUI Using Kustomize
+
+For cpu-only pod
+
+```bash
+kubectl apply -f ./kubernetes/manifest/base
+```
+
+For gpu-enabled pod
+
+```bash
+kubectl apply -k ./kubernetes/manifest
+```
+
+### Installing Both Ollama and Open WebUI Using Helm
+
+Package Helm file first
+
+```bash
+helm package ./kubernetes/helm/
+```
+
+For cpu-only pod
+
+```bash
+helm install ollama-webui ./ollama-webui-*.tgz
+```
+
+For gpu-enabled pod
+
+```bash
+helm install ollama-webui ./ollama-webui-*.tgz --set ollama.resources.limits.nvidia.com/gpu="1"
+```
+
+Check the `kubernetes/helm/values.yaml` file to know which parameters are available for customization
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..515e64df6c00c937379ad187de6204770db0fe18
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Timothy Jaeryang Baek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4b60b049658173c1e8b64dd79919fdd04d5d4f24
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+
+ifneq ($(shell which docker-compose 2>/dev/null),)
+    DOCKER_COMPOSE := docker-compose
+else
+    DOCKER_COMPOSE := docker compose
+endif
+
+install:
+	$(DOCKER_COMPOSE) up -d
+
+remove:
+	@chmod +x confirm_remove.sh
+	@./confirm_remove.sh
+
+start:
+	$(DOCKER_COMPOSE) start
+startAndBuild: 
+	$(DOCKER_COMPOSE) up -d --build
+
+stop:
+	$(DOCKER_COMPOSE) stop
+
+update:
+	# Calls the LLM update script
+	chmod +x update_ollama_models.sh
+	@./update_ollama_models.sh
+	@git pull
+	$(DOCKER_COMPOSE) down
+	# Make sure the ollama-webui container is stopped before rebuilding
+	@docker stop open-webui || true
+	$(DOCKER_COMPOSE) up --build -d
+	$(DOCKER_COMPOSE) start
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..42360b244972d3b6703cfd1ba63e446080173a2b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,221 @@
+---
+title: Open WebUI
+emoji: 🐳
+colorFrom: purple
+colorTo: gray
+sdk: docker
+app_port: 8080
+---
+# Open WebUI 👋
+
+![GitHub stars](https://img.shields.io/github/stars/open-webui/open-webui?style=social)
+![GitHub forks](https://img.shields.io/github/forks/open-webui/open-webui?style=social)
+![GitHub watchers](https://img.shields.io/github/watchers/open-webui/open-webui?style=social)
+![GitHub repo size](https://img.shields.io/github/repo-size/open-webui/open-webui)
+![GitHub language count](https://img.shields.io/github/languages/count/open-webui/open-webui)
+![GitHub top language](https://img.shields.io/github/languages/top/open-webui/open-webui)
+![GitHub last commit](https://img.shields.io/github/last-commit/open-webui/open-webui?color=red)
+![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Follama-webui%2Follama-wbui&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)
+[![Discord](https://img.shields.io/badge/Discord-Open_WebUI-blue?logo=discord&logoColor=white)](https://discord.gg/5rJgQTnV4s)
+[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/tjbck)
+
+Open WebUI is an [extensible](https://github.com/open-webui/pipelines), feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. For more information, be sure to check out our [Open WebUI Documentation](https://docs.openwebui.com/).
+
+![Open WebUI Demo](./demo.gif)
+
+## Key Features of Open WebUI ⭐
+
+- 🚀 **Effortless Setup**: Install seamlessly using Docker or Kubernetes (kubectl, kustomize or helm) for a hassle-free experience with support for both `:ollama` and `:cuda` tagged images.
+
+- 🤝 **Ollama/OpenAI API Integration**: Effortlessly integrate OpenAI-compatible APIs for versatile conversations alongside Ollama models. Customize the OpenAI API URL to link with **LMStudio, GroqCloud, Mistral, OpenRouter, and more**.
+
+- 🛡️ **Granular Permissions and User Groups**: By allowing administrators to create detailed user roles and permissions, we ensure a secure user environment. This granularity not only enhances security but also allows for customized user experiences, fostering a sense of ownership and responsibility amongst users.
+
+- 📱 **Responsive Design**: Enjoy a seamless experience across Desktop PC, Laptop, and Mobile devices.
+
+- 📱 **Progressive Web App (PWA) for Mobile**: Enjoy a native app-like experience on your mobile device with our PWA, providing offline access on localhost and a seamless user interface.
+
+- ✒️🔢 **Full Markdown and LaTeX Support**: Elevate your LLM experience with comprehensive Markdown and LaTeX capabilities for enriched interaction.
+
+- 🎤📹 **Hands-Free Voice/Video Call**: Experience seamless communication with integrated hands-free voice and video call features, allowing for a more dynamic and interactive chat environment.
+
+- 🛠️ **Model Builder**: Easily create Ollama models via the Web UI. Create and add custom characters/agents, customize chat elements, and import models effortlessly through [Open WebUI Community](https://openwebui.com/) integration.
+
+- 🐍 **Native Python Function Calling Tool**: Enhance your LLMs with built-in code editor support in the tools workspace. Bring Your Own Function (BYOF) by simply adding your pure Python functions, enabling seamless integration with LLMs.
+
+- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support. This feature seamlessly integrates document interactions into your chat experience. You can load documents directly into the chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
+
+- 🔍 **Web Search for RAG**: Perform web searches using providers like `SearXNG`, `Google PSE`, `Brave Search`, `serpstack`, `serper`, `Serply`, `DuckDuckGo`, `TavilySearch`, `SearchApi` and `Bing` and inject the results directly into your chat experience.
+
+- 🌐 **Web Browsing Capability**: Seamlessly integrate websites into your chat experience using the `#` command followed by a URL. This feature allows you to incorporate web content directly into your conversations, enhancing the richness and depth of your interactions.
+
+- 🎨 **Image Generation Integration**: Seamlessly incorporate image generation capabilities using options such as AUTOMATIC1111 API or ComfyUI (local), and OpenAI's DALL-E (external), enriching your chat experience with dynamic visual content.
+
+- ⚙️ **Many Models Conversations**: Effortlessly engage with various models simultaneously, harnessing their unique strengths for optimal responses. Enhance your experience by leveraging a diverse set of models in parallel.
+
+- 🔐 **Role-Based Access Control (RBAC)**: Ensure secure access with restricted permissions; only authorized individuals can access your Ollama, and exclusive model creation/pulling rights are reserved for administrators.
+
+- 🌐🌍 **Multilingual Support**: Experience Open WebUI in your preferred language with our internationalization (i18n) support. Join us in expanding our supported languages! We're actively seeking contributors!
+
+- 🧩 **Pipelines, Open WebUI Plugin Support**: Seamlessly integrate custom logic and Python libraries into Open WebUI using [Pipelines Plugin Framework](https://github.com/open-webui/pipelines). Launch your Pipelines instance, set the OpenAI URL to the Pipelines URL, and explore endless possibilities. [Examples](https://github.com/open-webui/pipelines/tree/main/examples) include **Function Calling**, User **Rate Limiting** to control access, **Usage Monitoring** with tools like Langfuse, **Live Translation with LibreTranslate** for multilingual support, **Toxic Message Filtering** and much more.
+
+- 🌟 **Continuous Updates**: We are committed to improving Open WebUI with regular updates, fixes, and new features.
+
+Want to learn more about Open WebUI's features? Check out our [Open WebUI documentation](https://docs.openwebui.com/features) for a comprehensive overview!
+
+## 🔗 Also Check Out Open WebUI Community!
+
+Don't forget to explore our sibling project, [Open WebUI Community](https://openwebui.com/), where you can discover, download, and explore customized Modelfiles. Open WebUI Community offers a wide range of exciting possibilities for enhancing your chat interactions with Open WebUI! 🚀
+
+## How to Install 🚀
+
+### Installation via Python pip 🐍
+
+Open WebUI can be installed using pip, the Python package installer. Before proceeding, ensure you're using **Python 3.11** to avoid compatibility issues.
+
+1. **Install Open WebUI**:
+   Open your terminal and run the following command to install Open WebUI:
+
+   ```bash
+   pip install open-webui
+   ```
+
+2. **Running Open WebUI**:
+   After installation, you can start Open WebUI by executing:
+
+   ```bash
+   open-webui serve
+   ```
+
+This will start the Open WebUI server, which you can access at [http://localhost:8080](http://localhost:8080)
+
+### Quick Start with Docker 🐳
+
+> [!NOTE]  
+> Please note that for certain Docker environments, additional configurations might be needed. If you encounter any connection issues, our detailed guide on [Open WebUI Documentation](https://docs.openwebui.com/) is ready to assist you.
+
+> [!WARNING]
+> When using Docker to install Open WebUI, make sure to include the `-v open-webui:/app/backend/data` in your Docker command. This step is crucial as it ensures your database is properly mounted and prevents any loss of data.
+
+> [!TIP]  
+> If you wish to utilize Open WebUI with Ollama included or CUDA acceleration, we recommend utilizing our official images tagged with either `:cuda` or `:ollama`. To enable CUDA, you must install the [Nvidia CUDA container toolkit](https://docs.nvidia.com/dgx/nvidia-container-runtime-upgrade/) on your Linux/WSL system.
+
+### Installation with Default Configuration
+
+- **If Ollama is on your computer**, use this command:
+
+  ```bash
+  docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
+  ```
+
+- **If Ollama is on a Different Server**, use this command:
+
+  To connect to Ollama on another server, change the `OLLAMA_BASE_URL` to the server's URL:
+
+  ```bash
+  docker run -d -p 3000:8080 -e OLLAMA_BASE_URL=https://example.com -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
+  ```
+
+- **To run Open WebUI with Nvidia GPU support**, use this command:
+
+  ```bash
+  docker run -d -p 3000:8080 --gpus all --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:cuda
+  ```
+
+### Installation for OpenAI API Usage Only
+
+- **If you're only using OpenAI API**, use this command:
+
+  ```bash
+  docker run -d -p 3000:8080 -e OPENAI_API_KEY=your_secret_key -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
+  ```
+
+### Installing Open WebUI with Bundled Ollama Support
+
+This installation method uses a single container image that bundles Open WebUI with Ollama, allowing for a streamlined setup via a single command. Choose the appropriate command based on your hardware setup:
+
+- **With GPU Support**:
+  Utilize GPU resources by running the following command:
+
+  ```bash
+  docker run -d -p 3000:8080 --gpus=all -v ollama:/root/.ollama -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:ollama
+  ```
+
+- **For CPU Only**:
+  If you're not using a GPU, use this command instead:
+
+  ```bash
+  docker run -d -p 3000:8080 -v ollama:/root/.ollama -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:ollama
+  ```
+
+Both commands facilitate a built-in, hassle-free installation of both Open WebUI and Ollama, ensuring that you can get everything up and running swiftly.
+
+After installation, you can access Open WebUI at [http://localhost:3000](http://localhost:3000). Enjoy! 😄
+
+### Other Installation Methods
+
+We offer various installation alternatives, including non-Docker native installation methods, Docker Compose, Kustomize, and Helm. Visit our [Open WebUI Documentation](https://docs.openwebui.com/getting-started/) or join our [Discord community](https://discord.gg/5rJgQTnV4s) for comprehensive guidance.
+
+### Troubleshooting
+
+Encountering connection issues? Our [Open WebUI Documentation](https://docs.openwebui.com/troubleshooting/) has got you covered. For further assistance and to join our vibrant community, visit the [Open WebUI Discord](https://discord.gg/5rJgQTnV4s).
+
+#### Open WebUI: Server Connection Error
+
+If you're experiencing connection issues, it’s often due to the WebUI docker container not being able to reach the Ollama server at 127.0.0.1:11434 (host.docker.internal:11434) inside the container . Use the `--network=host` flag in your docker command to resolve this. Note that the port changes from 3000 to 8080, resulting in the link: `http://localhost:8080`.
+
+**Example Docker Command**:
+
+```bash
+docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main
+```
+
+### Keeping Your Docker Installation Up-to-Date
+
+In case you want to update your local Docker installation to the latest version, you can do it with [Watchtower](https://containrrr.dev/watchtower/):
+
+```bash
+docker run --rm --volume /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --run-once open-webui
+```
+
+In the last part of the command, replace `open-webui` with your container name if it is different.
+
+Check our Migration Guide available in our [Open WebUI Documentation](https://docs.openwebui.com/tutorials/migration/).
+
+### Using the Dev Branch 🌙
+
+> [!WARNING]
+> The `:dev` branch contains the latest unstable features and changes. Use it at your own risk as it may have bugs or incomplete features.
+
+If you want to try out the latest bleeding-edge features and are okay with occasional instability, you can use the `:dev` tag like this:
+
+```bash
+docker run -d -p 3000:8080 -v open-webui:/app/backend/data --name open-webui --add-host=host.docker.internal:host-gateway --restart always ghcr.io/open-webui/open-webui:dev
+```
+
+## What's Next? 🌟
+
+Discover upcoming features on our roadmap in the [Open WebUI Documentation](https://docs.openwebui.com/roadmap/).
+
+## License 📜
+
+This project is licensed under the [MIT License](LICENSE) - see the [LICENSE](LICENSE) file for details. 📄
+
+## Support 💬
+
+If you have any questions, suggestions, or need assistance, please open an issue or join our
+[Open WebUI Discord community](https://discord.gg/5rJgQTnV4s) to connect with us! 🤝
+
+## Star History
+
+<a href="https://star-history.com/#open-webui/open-webui&Date">
+  <picture>
+    <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=open-webui/open-webui&type=Date&theme=dark" />
+    <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=open-webui/open-webui&type=Date" />
+    <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=open-webui/open-webui&type=Date" />
+  </picture>
+</a>
+
+---
+
+Created by [Timothy Jaeryang Baek](https://github.com/tjbck) - Let's make Open WebUI even more amazing together! 💪
diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..83251a3a91ee25b4952e70e335fe7df1dfcbde69
--- /dev/null
+++ b/TROUBLESHOOTING.md
@@ -0,0 +1,36 @@
+# Open WebUI Troubleshooting Guide
+
+## Understanding the Open WebUI Architecture
+
+The Open WebUI system is designed to streamline interactions between the client (your browser) and the Ollama API. At the heart of this design is a backend reverse proxy, enhancing security and resolving CORS issues.
+
+- **How it Works**: The Open WebUI is designed to interact with the Ollama API through a specific route. When a request is made from the WebUI to Ollama, it is not directly sent to the Ollama API. Initially, the request is sent to the Open WebUI backend via `/ollama` route. From there, the backend is responsible for forwarding the request to the Ollama API. This forwarding is accomplished by using the route specified in the `OLLAMA_BASE_URL` environment variable. Therefore, a request made to `/ollama` in the WebUI is effectively the same as making a request to `OLLAMA_BASE_URL` in the backend. For instance, a request to `/ollama/api/tags` in the WebUI is equivalent to `OLLAMA_BASE_URL/api/tags` in the backend.
+
+- **Security Benefits**: This design prevents direct exposure of the Ollama API to the frontend, safeguarding against potential CORS (Cross-Origin Resource Sharing) issues and unauthorized access. Requiring authentication to access the Ollama API further enhances this security layer.
+
+## Open WebUI: Server Connection Error
+
+If you're experiencing connection issues, it’s often due to the WebUI docker container not being able to reach the Ollama server at 127.0.0.1:11434 (host.docker.internal:11434) inside the container . Use the `--network=host` flag in your docker command to resolve this. Note that the port changes from 3000 to 8080, resulting in the link: `http://localhost:8080`.
+
+**Example Docker Command**:
+
+```bash
+docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 --name open-webui --restart always ghcr.io/open-webui/open-webui:main
+```
+
+### Error on Slow Responses for Ollama
+
+Open WebUI has a default timeout of 5 minutes for Ollama to finish generating the response. If needed, this can be adjusted via the environment variable AIOHTTP_CLIENT_TIMEOUT, which sets the timeout in seconds.
+
+### General Connection Errors
+
+**Ensure Ollama Version is Up-to-Date**: Always start by checking that you have the latest version of Ollama. Visit [Ollama's official site](https://ollama.com/) for the latest updates.
+
+**Troubleshooting Steps**:
+
+1. **Verify Ollama URL Format**:
+   - When running the Web UI container, ensure the `OLLAMA_BASE_URL` is correctly set. (e.g., `http://192.168.1.1:11434` for different host setups).
+   - In the Open WebUI, navigate to "Settings" > "General".
+   - Confirm that the Ollama Server URL is correctly set to `[OLLAMA URL]` (e.g., `http://localhost:11434`).
+
+By following these enhanced troubleshooting steps, connection issues should be effectively resolved. For further assistance or queries, feel free to reach out to us on our community Discord.
diff --git a/backend/.dockerignore b/backend/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..97ab32835d90e779180b09b866c7eecbdb1433ac
--- /dev/null
+++ b/backend/.dockerignore
@@ -0,0 +1,14 @@
+__pycache__
+.env
+_old
+uploads
+.ipynb_checkpoints
+*.db
+_test
+!/data
+/data/*
+!/data/litellm
+/data/litellm/*
+!data/litellm/config.yaml
+
+!data/config.json
\ No newline at end of file
diff --git a/backend/.gitignore b/backend/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..614a5f7465676c7bdc5d90f117fa6c18c75c4476
--- /dev/null
+++ b/backend/.gitignore
@@ -0,0 +1,12 @@
+__pycache__
+.env
+_old
+uploads
+.ipynb_checkpoints
+*.db
+_test
+Pipfile
+!/data
+/data/*
+/open_webui/data/*
+.webui_secret_key
\ No newline at end of file
diff --git a/backend/dev.sh b/backend/dev.sh
new file mode 100755
index 0000000000000000000000000000000000000000..5449ab77777fa604f6569fa59b68f29756e94a1d
--- /dev/null
+++ b/backend/dev.sh
@@ -0,0 +1,2 @@
+PORT="${PORT:-8080}"
+uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --forwarded-allow-ips '*' --reload
\ No newline at end of file
diff --git a/backend/open_webui/__init__.py b/backend/open_webui/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..de34a8bc769c70de9a097ad863f1e6a1dbb29dbb
--- /dev/null
+++ b/backend/open_webui/__init__.py
@@ -0,0 +1,77 @@
+import base64
+import os
+import random
+from pathlib import Path
+
+import typer
+import uvicorn
+
+app = typer.Typer()
+
+KEY_FILE = Path.cwd() / ".webui_secret_key"
+
+
+@app.command()
+def serve(
+    host: str = "0.0.0.0",
+    port: int = 8080,
+):
+    os.environ["FROM_INIT_PY"] = "true"
+    if os.getenv("WEBUI_SECRET_KEY") is None:
+        typer.echo(
+            "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
+        )
+        if not KEY_FILE.exists():
+            typer.echo(f"Generating a new secret key and saving it to {KEY_FILE}")
+            KEY_FILE.write_bytes(base64.b64encode(random.randbytes(12)))
+        typer.echo(f"Loading WEBUI_SECRET_KEY from {KEY_FILE}")
+        os.environ["WEBUI_SECRET_KEY"] = KEY_FILE.read_text()
+
+    if os.getenv("USE_CUDA_DOCKER", "false") == "true":
+        typer.echo(
+            "CUDA is enabled, appending LD_LIBRARY_PATH to include torch/cudnn & cublas libraries."
+        )
+        LD_LIBRARY_PATH = os.getenv("LD_LIBRARY_PATH", "").split(":")
+        os.environ["LD_LIBRARY_PATH"] = ":".join(
+            LD_LIBRARY_PATH
+            + [
+                "/usr/local/lib/python3.11/site-packages/torch/lib",
+                "/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib",
+            ]
+        )
+        try:
+            import torch
+
+            assert torch.cuda.is_available(), "CUDA not available"
+            typer.echo("CUDA seems to be working")
+        except Exception as e:
+            typer.echo(
+                "Error when testing CUDA but USE_CUDA_DOCKER is true. "
+                "Resetting USE_CUDA_DOCKER to false and removing "
+                f"LD_LIBRARY_PATH modifications: {e}"
+            )
+            os.environ["USE_CUDA_DOCKER"] = "false"
+            os.environ["LD_LIBRARY_PATH"] = ":".join(LD_LIBRARY_PATH)
+
+    import open_webui.main  # we need set environment variables before importing main
+
+    uvicorn.run(open_webui.main.app, host=host, port=port, forwarded_allow_ips="*")
+
+
+@app.command()
+def dev(
+    host: str = "0.0.0.0",
+    port: int = 8080,
+    reload: bool = True,
+):
+    uvicorn.run(
+        "open_webui.main:app",
+        host=host,
+        port=port,
+        reload=reload,
+        forwarded_allow_ips="*",
+    )
+
+
+if __name__ == "__main__":
+    app()
diff --git a/backend/open_webui/alembic.ini b/backend/open_webui/alembic.ini
new file mode 100644
index 0000000000000000000000000000000000000000..4eff85f0c621c16f5afc80a4d92dc75da1483ac8
--- /dev/null
+++ b/backend/open_webui/alembic.ini
@@ -0,0 +1,114 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = migrations
+
+# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
+# Uncomment the line below if you want the files to be prepended with date and time
+# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python>=3.9 or backports.zoneinfo library.
+# Any required deps can installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to ZoneInfo()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to migrations/versions.  When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator" below.
+# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
+# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
+# Valid values for version_path_separator are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+version_path_separator = os  # Use os.pathsep. Default configuration used for new projects.
+
+# set to 'true' to search source files recursively
+# in each "version_locations" directory
+# new in Alembic version 1.10
+# recursive_version_locations = false
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+# sqlalchemy.url = REPLACE_WITH_DATABASE_URL
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts.  See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
+# hooks = ruff
+# ruff.type = exec
+# ruff.executable = %(here)s/.venv/bin/ruff
+# ruff.options = --fix REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/backend/open_webui/apps/audio/main.py b/backend/open_webui/apps/audio/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c24c2633272de62736e2b1cd85b76a6fed29b17
--- /dev/null
+++ b/backend/open_webui/apps/audio/main.py
@@ -0,0 +1,703 @@
+import hashlib
+import json
+import logging
+import os
+import uuid
+from functools import lru_cache
+from pathlib import Path
+from pydub import AudioSegment
+from pydub.silence import split_on_silence
+
+import aiohttp
+import aiofiles
+import requests
+from open_webui.config import (
+    AUDIO_STT_ENGINE,
+    AUDIO_STT_MODEL,
+    AUDIO_STT_OPENAI_API_BASE_URL,
+    AUDIO_STT_OPENAI_API_KEY,
+    AUDIO_TTS_API_KEY,
+    AUDIO_TTS_ENGINE,
+    AUDIO_TTS_MODEL,
+    AUDIO_TTS_OPENAI_API_BASE_URL,
+    AUDIO_TTS_OPENAI_API_KEY,
+    AUDIO_TTS_SPLIT_ON,
+    AUDIO_TTS_VOICE,
+    AUDIO_TTS_AZURE_SPEECH_REGION,
+    AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT,
+    CACHE_DIR,
+    CORS_ALLOW_ORIGIN,
+    WHISPER_MODEL,
+    WHISPER_MODEL_AUTO_UPDATE,
+    WHISPER_MODEL_DIR,
+    AppConfig,
+)
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import (
+    ENV,
+    SRC_LOG_LEVELS,
+    DEVICE_TYPE,
+    ENABLE_FORWARD_USER_INFO_HEADERS,
+)
+
+from fastapi import Depends, FastAPI, File, HTTPException, Request, UploadFile, status
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import FileResponse
+from pydantic import BaseModel
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+# Constants
+MAX_FILE_SIZE_MB = 25
+MAX_FILE_SIZE = MAX_FILE_SIZE_MB * 1024 * 1024  # Convert MB to bytes
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["AUDIO"])
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.state.config = AppConfig()
+
+app.state.config.STT_OPENAI_API_BASE_URL = AUDIO_STT_OPENAI_API_BASE_URL
+app.state.config.STT_OPENAI_API_KEY = AUDIO_STT_OPENAI_API_KEY
+app.state.config.STT_ENGINE = AUDIO_STT_ENGINE
+app.state.config.STT_MODEL = AUDIO_STT_MODEL
+
+app.state.config.WHISPER_MODEL = WHISPER_MODEL
+app.state.faster_whisper_model = None
+
+app.state.config.TTS_OPENAI_API_BASE_URL = AUDIO_TTS_OPENAI_API_BASE_URL
+app.state.config.TTS_OPENAI_API_KEY = AUDIO_TTS_OPENAI_API_KEY
+app.state.config.TTS_ENGINE = AUDIO_TTS_ENGINE
+app.state.config.TTS_MODEL = AUDIO_TTS_MODEL
+app.state.config.TTS_VOICE = AUDIO_TTS_VOICE
+app.state.config.TTS_API_KEY = AUDIO_TTS_API_KEY
+app.state.config.TTS_SPLIT_ON = AUDIO_TTS_SPLIT_ON
+
+
+app.state.speech_synthesiser = None
+app.state.speech_speaker_embeddings_dataset = None
+
+app.state.config.TTS_AZURE_SPEECH_REGION = AUDIO_TTS_AZURE_SPEECH_REGION
+app.state.config.TTS_AZURE_SPEECH_OUTPUT_FORMAT = AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT
+
+# setting device type for whisper model
+whisper_device_type = DEVICE_TYPE if DEVICE_TYPE and DEVICE_TYPE == "cuda" else "cpu"
+log.info(f"whisper_device_type: {whisper_device_type}")
+
+SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/")
+SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
+
+
+def set_faster_whisper_model(model: str, auto_update: bool = False):
+    if model and app.state.config.STT_ENGINE == "":
+        from faster_whisper import WhisperModel
+
+        faster_whisper_kwargs = {
+            "model_size_or_path": model,
+            "device": whisper_device_type,
+            "compute_type": "int8",
+            "download_root": WHISPER_MODEL_DIR,
+            "local_files_only": not auto_update,
+        }
+
+        try:
+            app.state.faster_whisper_model = WhisperModel(**faster_whisper_kwargs)
+        except Exception:
+            log.warning(
+                "WhisperModel initialization failed, attempting download with local_files_only=False"
+            )
+            faster_whisper_kwargs["local_files_only"] = False
+            app.state.faster_whisper_model = WhisperModel(**faster_whisper_kwargs)
+
+    else:
+        app.state.faster_whisper_model = None
+
+
+class TTSConfigForm(BaseModel):
+    OPENAI_API_BASE_URL: str
+    OPENAI_API_KEY: str
+    API_KEY: str
+    ENGINE: str
+    MODEL: str
+    VOICE: str
+    SPLIT_ON: str
+    AZURE_SPEECH_REGION: str
+    AZURE_SPEECH_OUTPUT_FORMAT: str
+
+
+class STTConfigForm(BaseModel):
+    OPENAI_API_BASE_URL: str
+    OPENAI_API_KEY: str
+    ENGINE: str
+    MODEL: str
+    WHISPER_MODEL: str
+
+
+class AudioConfigUpdateForm(BaseModel):
+    tts: TTSConfigForm
+    stt: STTConfigForm
+
+
+from pydub import AudioSegment
+from pydub.utils import mediainfo
+
+
+def is_mp4_audio(file_path):
+    """Check if the given file is an MP4 audio file."""
+    if not os.path.isfile(file_path):
+        print(f"File not found: {file_path}")
+        return False
+
+    info = mediainfo(file_path)
+    if (
+        info.get("codec_name") == "aac"
+        and info.get("codec_type") == "audio"
+        and info.get("codec_tag_string") == "mp4a"
+    ):
+        return True
+    return False
+
+
+def convert_mp4_to_wav(file_path, output_path):
+    """Convert MP4 audio file to WAV format."""
+    audio = AudioSegment.from_file(file_path, format="mp4")
+    audio.export(output_path, format="wav")
+    print(f"Converted {file_path} to {output_path}")
+
+
+@app.get("/config")
+async def get_audio_config(user=Depends(get_admin_user)):
+    return {
+        "tts": {
+            "OPENAI_API_BASE_URL": app.state.config.TTS_OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.TTS_OPENAI_API_KEY,
+            "API_KEY": app.state.config.TTS_API_KEY,
+            "ENGINE": app.state.config.TTS_ENGINE,
+            "MODEL": app.state.config.TTS_MODEL,
+            "VOICE": app.state.config.TTS_VOICE,
+            "SPLIT_ON": app.state.config.TTS_SPLIT_ON,
+            "AZURE_SPEECH_REGION": app.state.config.TTS_AZURE_SPEECH_REGION,
+            "AZURE_SPEECH_OUTPUT_FORMAT": app.state.config.TTS_AZURE_SPEECH_OUTPUT_FORMAT,
+        },
+        "stt": {
+            "OPENAI_API_BASE_URL": app.state.config.STT_OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.STT_OPENAI_API_KEY,
+            "ENGINE": app.state.config.STT_ENGINE,
+            "MODEL": app.state.config.STT_MODEL,
+            "WHISPER_MODEL": app.state.config.WHISPER_MODEL,
+        },
+    }
+
+
+@app.post("/config/update")
+async def update_audio_config(
+    form_data: AudioConfigUpdateForm, user=Depends(get_admin_user)
+):
+    app.state.config.TTS_OPENAI_API_BASE_URL = form_data.tts.OPENAI_API_BASE_URL
+    app.state.config.TTS_OPENAI_API_KEY = form_data.tts.OPENAI_API_KEY
+    app.state.config.TTS_API_KEY = form_data.tts.API_KEY
+    app.state.config.TTS_ENGINE = form_data.tts.ENGINE
+    app.state.config.TTS_MODEL = form_data.tts.MODEL
+    app.state.config.TTS_VOICE = form_data.tts.VOICE
+    app.state.config.TTS_SPLIT_ON = form_data.tts.SPLIT_ON
+    app.state.config.TTS_AZURE_SPEECH_REGION = form_data.tts.AZURE_SPEECH_REGION
+    app.state.config.TTS_AZURE_SPEECH_OUTPUT_FORMAT = (
+        form_data.tts.AZURE_SPEECH_OUTPUT_FORMAT
+    )
+
+    app.state.config.STT_OPENAI_API_BASE_URL = form_data.stt.OPENAI_API_BASE_URL
+    app.state.config.STT_OPENAI_API_KEY = form_data.stt.OPENAI_API_KEY
+    app.state.config.STT_ENGINE = form_data.stt.ENGINE
+    app.state.config.STT_MODEL = form_data.stt.MODEL
+    app.state.config.WHISPER_MODEL = form_data.stt.WHISPER_MODEL
+    set_faster_whisper_model(form_data.stt.WHISPER_MODEL, WHISPER_MODEL_AUTO_UPDATE)
+
+    return {
+        "tts": {
+            "OPENAI_API_BASE_URL": app.state.config.TTS_OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.TTS_OPENAI_API_KEY,
+            "API_KEY": app.state.config.TTS_API_KEY,
+            "ENGINE": app.state.config.TTS_ENGINE,
+            "MODEL": app.state.config.TTS_MODEL,
+            "VOICE": app.state.config.TTS_VOICE,
+            "SPLIT_ON": app.state.config.TTS_SPLIT_ON,
+            "AZURE_SPEECH_REGION": app.state.config.TTS_AZURE_SPEECH_REGION,
+            "AZURE_SPEECH_OUTPUT_FORMAT": app.state.config.TTS_AZURE_SPEECH_OUTPUT_FORMAT,
+        },
+        "stt": {
+            "OPENAI_API_BASE_URL": app.state.config.STT_OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.STT_OPENAI_API_KEY,
+            "ENGINE": app.state.config.STT_ENGINE,
+            "MODEL": app.state.config.STT_MODEL,
+            "WHISPER_MODEL": app.state.config.WHISPER_MODEL,
+        },
+    }
+
+
+def load_speech_pipeline():
+    from transformers import pipeline
+    from datasets import load_dataset
+
+    if app.state.speech_synthesiser is None:
+        app.state.speech_synthesiser = pipeline(
+            "text-to-speech", "microsoft/speecht5_tts"
+        )
+
+    if app.state.speech_speaker_embeddings_dataset is None:
+        app.state.speech_speaker_embeddings_dataset = load_dataset(
+            "Matthijs/cmu-arctic-xvectors", split="validation"
+        )
+
+
+@app.post("/speech")
+async def speech(request: Request, user=Depends(get_verified_user)):
+    body = await request.body()
+    name = hashlib.sha256(body).hexdigest()
+
+    file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3")
+    file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json")
+
+    # Check if the file already exists in the cache
+    if file_path.is_file():
+        return FileResponse(file_path)
+
+    if app.state.config.TTS_ENGINE == "openai":
+        headers = {}
+        headers["Authorization"] = f"Bearer {app.state.config.TTS_OPENAI_API_KEY}"
+        headers["Content-Type"] = "application/json"
+
+        if ENABLE_FORWARD_USER_INFO_HEADERS:
+            headers["X-OpenWebUI-User-Name"] = user.name
+            headers["X-OpenWebUI-User-Id"] = user.id
+            headers["X-OpenWebUI-User-Email"] = user.email
+            headers["X-OpenWebUI-User-Role"] = user.role
+
+        try:
+            body = body.decode("utf-8")
+            body = json.loads(body)
+            body["model"] = app.state.config.TTS_MODEL
+            body = json.dumps(body).encode("utf-8")
+        except Exception:
+            pass
+
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.post(
+                    url=f"{app.state.config.TTS_OPENAI_API_BASE_URL}/audio/speech",
+                    data=body,
+                    headers=headers,
+                ) as r:
+                    r.raise_for_status()
+                    async with aiofiles.open(file_path, "wb") as f:
+                        await f.write(await r.read())
+
+                    async with aiofiles.open(file_body_path, "w") as f:
+                        await f.write(json.dumps(json.loads(body.decode("utf-8"))))
+
+            return FileResponse(file_path)
+
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            try:
+                if r.status != 200:
+                    res = await r.json()
+                    if "error" in res:
+                        error_detail = f"External: {res['error']['message']}"
+            except Exception:
+                error_detail = f"External: {e}"
+
+            raise HTTPException(
+                status_code=getattr(r, "status", 500),
+                detail=error_detail,
+            )
+
+    elif app.state.config.TTS_ENGINE == "elevenlabs":
+        try:
+            payload = json.loads(body.decode("utf-8"))
+        except Exception as e:
+            log.exception(e)
+            raise HTTPException(status_code=400, detail="Invalid JSON payload")
+
+        voice_id = payload.get("voice", "")
+        if voice_id not in get_available_voices():
+            raise HTTPException(
+                status_code=400,
+                detail="Invalid voice id",
+            )
+
+        url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}"
+        headers = {
+            "Accept": "audio/mpeg",
+            "Content-Type": "application/json",
+            "xi-api-key": app.state.config.TTS_API_KEY,
+        }
+        data = {
+            "text": payload["input"],
+            "model_id": app.state.config.TTS_MODEL,
+            "voice_settings": {"stability": 0.5, "similarity_boost": 0.5},
+        }
+
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.post(url, json=data, headers=headers) as r:
+                    r.raise_for_status()
+                    async with aiofiles.open(file_path, "wb") as f:
+                        await f.write(await r.read())
+
+                    async with aiofiles.open(file_body_path, "w") as f:
+                        await f.write(json.dumps(json.loads(body.decode("utf-8"))))
+
+            return FileResponse(file_path)
+
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            try:
+                if r.status != 200:
+                    res = await r.json()
+                    if "error" in res:
+                        error_detail = f"External: {res['error']['message']}"
+            except Exception:
+                error_detail = f"External: {e}"
+
+            raise HTTPException(
+                status_code=getattr(r, "status", 500),
+                detail=error_detail,
+            )
+
+    elif app.state.config.TTS_ENGINE == "azure":
+        try:
+            payload = json.loads(body.decode("utf-8"))
+        except Exception as e:
+            log.exception(e)
+            raise HTTPException(status_code=400, detail="Invalid JSON payload")
+
+        region = app.state.config.TTS_AZURE_SPEECH_REGION
+        language = app.state.config.TTS_VOICE
+        locale = "-".join(app.state.config.TTS_VOICE.split("-")[:1])
+        output_format = app.state.config.TTS_AZURE_SPEECH_OUTPUT_FORMAT
+        url = f"https://{region}.tts.speech.microsoft.com/cognitiveservices/v1"
+
+        headers = {
+            "Ocp-Apim-Subscription-Key": app.state.config.TTS_API_KEY,
+            "Content-Type": "application/ssml+xml",
+            "X-Microsoft-OutputFormat": output_format,
+        }
+
+        data = f"""<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="{locale}">
+                <voice name="{language}">{payload["input"]}</voice>
+            </speak>"""
+
+        try:
+            async with aiohttp.ClientSession() as session:
+                async with session.post(url, headers=headers, data=data) as response:
+                    if response.status == 200:
+                        async with aiofiles.open(file_path, "wb") as f:
+                            await f.write(await response.read())
+                        return FileResponse(file_path)
+                    else:
+                        error_msg = f"Error synthesizing speech - {response.reason}"
+                        log.error(error_msg)
+                        raise HTTPException(status_code=500, detail=error_msg)
+        except Exception as e:
+            log.exception(e)
+            raise HTTPException(status_code=500, detail=str(e))
+    elif app.state.config.TTS_ENGINE == "transformers":
+        payload = None
+        try:
+            payload = json.loads(body.decode("utf-8"))
+        except Exception as e:
+            log.exception(e)
+            raise HTTPException(status_code=400, detail="Invalid JSON payload")
+
+        import torch
+        import soundfile as sf
+
+        load_speech_pipeline()
+
+        embeddings_dataset = app.state.speech_speaker_embeddings_dataset
+
+        speaker_index = 6799
+        try:
+            speaker_index = embeddings_dataset["filename"].index(
+                app.state.config.TTS_MODEL
+            )
+        except Exception:
+            pass
+
+        speaker_embedding = torch.tensor(
+            embeddings_dataset[speaker_index]["xvector"]
+        ).unsqueeze(0)
+
+        speech = app.state.speech_synthesiser(
+            payload["input"],
+            forward_params={"speaker_embeddings": speaker_embedding},
+        )
+
+        sf.write(file_path, speech["audio"], samplerate=speech["sampling_rate"])
+        with open(file_body_path, "w") as f:
+            json.dump(json.loads(body.decode("utf-8")), f)
+
+        return FileResponse(file_path)
+
+
+def transcribe(file_path):
+    print("transcribe", file_path)
+    filename = os.path.basename(file_path)
+    file_dir = os.path.dirname(file_path)
+    id = filename.split(".")[0]
+
+    if app.state.config.STT_ENGINE == "":
+        if app.state.faster_whisper_model is None:
+            set_faster_whisper_model(app.state.config.WHISPER_MODEL)
+
+        model = app.state.faster_whisper_model
+        segments, info = model.transcribe(file_path, beam_size=5)
+        log.info(
+            "Detected language '%s' with probability %f"
+            % (info.language, info.language_probability)
+        )
+
+        transcript = "".join([segment.text for segment in list(segments)])
+        data = {"text": transcript.strip()}
+
+        # save the transcript to a json file
+        transcript_file = f"{file_dir}/{id}.json"
+        with open(transcript_file, "w") as f:
+            json.dump(data, f)
+
+        log.debug(data)
+        return data
+    elif app.state.config.STT_ENGINE == "openai":
+        if is_mp4_audio(file_path):
+            print("is_mp4_audio")
+            os.rename(file_path, file_path.replace(".wav", ".mp4"))
+            # Convert MP4 audio file to WAV format
+            convert_mp4_to_wav(file_path.replace(".wav", ".mp4"), file_path)
+
+        headers = {"Authorization": f"Bearer {app.state.config.STT_OPENAI_API_KEY}"}
+
+        files = {"file": (filename, open(file_path, "rb"))}
+        data = {"model": app.state.config.STT_MODEL}
+
+        log.debug(files, data)
+
+        r = None
+        try:
+            r = requests.post(
+                url=f"{app.state.config.STT_OPENAI_API_BASE_URL}/audio/transcriptions",
+                headers=headers,
+                files=files,
+                data=data,
+            )
+
+            r.raise_for_status()
+
+            data = r.json()
+
+            # save the transcript to a json file
+            transcript_file = f"{file_dir}/{id}.json"
+            with open(transcript_file, "w") as f:
+                json.dump(data, f)
+
+            print(data)
+            return data
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            if r is not None:
+                try:
+                    res = r.json()
+                    if "error" in res:
+                        error_detail = f"External: {res['error']['message']}"
+                except Exception:
+                    error_detail = f"External: {e}"
+
+            raise Exception(error_detail)
+
+
+@app.post("/transcriptions")
+def transcription(
+    file: UploadFile = File(...),
+    user=Depends(get_verified_user),
+):
+    log.info(f"file.content_type: {file.content_type}")
+
+    if file.content_type not in ["audio/mpeg", "audio/wav", "audio/ogg", "audio/x-m4a"]:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.FILE_NOT_SUPPORTED,
+        )
+
+    try:
+        ext = file.filename.split(".")[-1]
+        id = uuid.uuid4()
+
+        filename = f"{id}.{ext}"
+        contents = file.file.read()
+
+        file_dir = f"{CACHE_DIR}/audio/transcriptions"
+        os.makedirs(file_dir, exist_ok=True)
+        file_path = f"{file_dir}/{filename}"
+
+        with open(file_path, "wb") as f:
+            f.write(contents)
+
+        try:
+            if os.path.getsize(file_path) > MAX_FILE_SIZE:  # file is bigger than 25MB
+                log.debug(f"File size is larger than {MAX_FILE_SIZE_MB}MB")
+                audio = AudioSegment.from_file(file_path)
+                audio = audio.set_frame_rate(16000).set_channels(1)  # Compress audio
+                compressed_path = f"{file_dir}/{id}_compressed.opus"
+                audio.export(compressed_path, format="opus", bitrate="32k")
+                log.debug(f"Compressed audio to {compressed_path}")
+                file_path = compressed_path
+
+                if (
+                    os.path.getsize(file_path) > MAX_FILE_SIZE
+                ):  # Still larger than 25MB after compression
+                    log.debug(
+                        f"Compressed file size is still larger than {MAX_FILE_SIZE_MB}MB: {os.path.getsize(file_path)}"
+                    )
+                    raise HTTPException(
+                        status_code=status.HTTP_400_BAD_REQUEST,
+                        detail=ERROR_MESSAGES.FILE_TOO_LARGE(
+                            size=f"{MAX_FILE_SIZE_MB}MB"
+                        ),
+                    )
+
+                data = transcribe(file_path)
+            else:
+                data = transcribe(file_path)
+
+            file_path = file_path.split("/")[-1]
+            return {**data, "filename": file_path}
+        except Exception as e:
+            log.exception(e)
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+
+    except Exception as e:
+        log.exception(e)
+
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+def get_available_models() -> list[dict]:
+    if app.state.config.TTS_ENGINE == "openai":
+        return [{"id": "tts-1"}, {"id": "tts-1-hd"}]
+    elif app.state.config.TTS_ENGINE == "elevenlabs":
+        headers = {
+            "xi-api-key": app.state.config.TTS_API_KEY,
+            "Content-Type": "application/json",
+        }
+
+        try:
+            response = requests.get(
+                "https://api.elevenlabs.io/v1/models", headers=headers, timeout=5
+            )
+            response.raise_for_status()
+            models = response.json()
+            return [
+                {"name": model["name"], "id": model["model_id"]} for model in models
+            ]
+        except requests.RequestException as e:
+            log.error(f"Error fetching voices: {str(e)}")
+    return []
+
+
+@app.get("/models")
+async def get_models(user=Depends(get_verified_user)):
+    return {"models": get_available_models()}
+
+
+def get_available_voices() -> dict:
+    """Returns {voice_id: voice_name} dict"""
+    ret = {}
+    if app.state.config.TTS_ENGINE == "openai":
+        ret = {
+            "alloy": "alloy",
+            "echo": "echo",
+            "fable": "fable",
+            "onyx": "onyx",
+            "nova": "nova",
+            "shimmer": "shimmer",
+        }
+    elif app.state.config.TTS_ENGINE == "elevenlabs":
+        try:
+            ret = get_elevenlabs_voices()
+        except Exception:
+            # Avoided @lru_cache with exception
+            pass
+    elif app.state.config.TTS_ENGINE == "azure":
+        try:
+            region = app.state.config.TTS_AZURE_SPEECH_REGION
+            url = f"https://{region}.tts.speech.microsoft.com/cognitiveservices/voices/list"
+            headers = {"Ocp-Apim-Subscription-Key": app.state.config.TTS_API_KEY}
+
+            response = requests.get(url, headers=headers)
+            response.raise_for_status()
+            voices = response.json()
+            for voice in voices:
+                ret[voice["ShortName"]] = (
+                    f"{voice['DisplayName']} ({voice['ShortName']})"
+                )
+        except requests.RequestException as e:
+            log.error(f"Error fetching voices: {str(e)}")
+
+    return ret
+
+
+@lru_cache
+def get_elevenlabs_voices() -> dict:
+    """
+    Note, set the following in your .env file to use Elevenlabs:
+    AUDIO_TTS_ENGINE=elevenlabs
+    AUDIO_TTS_API_KEY=sk_...  # Your Elevenlabs API key
+    AUDIO_TTS_VOICE=EXAVITQu4vr4xnSDxMaL  # From https://api.elevenlabs.io/v1/voices
+    AUDIO_TTS_MODEL=eleven_multilingual_v2
+    """
+    headers = {
+        "xi-api-key": app.state.config.TTS_API_KEY,
+        "Content-Type": "application/json",
+    }
+    try:
+        # TODO: Add retries
+        response = requests.get("https://api.elevenlabs.io/v1/voices", headers=headers)
+        response.raise_for_status()
+        voices_data = response.json()
+
+        voices = {}
+        for voice in voices_data.get("voices", []):
+            voices[voice["voice_id"]] = voice["name"]
+    except requests.RequestException as e:
+        # Avoid @lru_cache with exception
+        log.error(f"Error fetching voices: {str(e)}")
+        raise RuntimeError(f"Error fetching voices: {str(e)}")
+
+    return voices
+
+
+@app.get("/voices")
+async def get_voices(user=Depends(get_verified_user)):
+    return {"voices": [{"id": k, "name": v} for k, v in get_available_voices().items()]}
diff --git a/backend/open_webui/apps/images/main.py b/backend/open_webui/apps/images/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..62c76425d8cea8fd88a5c9698a11b28ce1d209eb
--- /dev/null
+++ b/backend/open_webui/apps/images/main.py
@@ -0,0 +1,609 @@
+import asyncio
+import base64
+import json
+import logging
+import mimetypes
+import re
+import uuid
+from pathlib import Path
+from typing import Optional
+
+import requests
+from open_webui.apps.images.utils.comfyui import (
+    ComfyUIGenerateImageForm,
+    ComfyUIWorkflow,
+    comfyui_generate_image,
+)
+from open_webui.config import (
+    AUTOMATIC1111_API_AUTH,
+    AUTOMATIC1111_BASE_URL,
+    AUTOMATIC1111_CFG_SCALE,
+    AUTOMATIC1111_SAMPLER,
+    AUTOMATIC1111_SCHEDULER,
+    CACHE_DIR,
+    COMFYUI_BASE_URL,
+    COMFYUI_WORKFLOW,
+    COMFYUI_WORKFLOW_NODES,
+    CORS_ALLOW_ORIGIN,
+    ENABLE_IMAGE_GENERATION,
+    IMAGE_GENERATION_ENGINE,
+    IMAGE_GENERATION_MODEL,
+    IMAGE_SIZE,
+    IMAGE_STEPS,
+    IMAGES_OPENAI_API_BASE_URL,
+    IMAGES_OPENAI_API_KEY,
+    AppConfig,
+)
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import ENV, SRC_LOG_LEVELS, ENABLE_FORWARD_USER_INFO_HEADERS
+
+from fastapi import Depends, FastAPI, HTTPException, Request
+from fastapi.middleware.cors import CORSMiddleware
+from pydantic import BaseModel
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["IMAGES"])
+
+IMAGE_CACHE_DIR = Path(CACHE_DIR).joinpath("./image/generations/")
+IMAGE_CACHE_DIR.mkdir(parents=True, exist_ok=True)
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.state.config = AppConfig()
+
+app.state.config.ENGINE = IMAGE_GENERATION_ENGINE
+app.state.config.ENABLED = ENABLE_IMAGE_GENERATION
+
+app.state.config.OPENAI_API_BASE_URL = IMAGES_OPENAI_API_BASE_URL
+app.state.config.OPENAI_API_KEY = IMAGES_OPENAI_API_KEY
+
+app.state.config.MODEL = IMAGE_GENERATION_MODEL
+
+app.state.config.AUTOMATIC1111_BASE_URL = AUTOMATIC1111_BASE_URL
+app.state.config.AUTOMATIC1111_API_AUTH = AUTOMATIC1111_API_AUTH
+app.state.config.AUTOMATIC1111_CFG_SCALE = AUTOMATIC1111_CFG_SCALE
+app.state.config.AUTOMATIC1111_SAMPLER = AUTOMATIC1111_SAMPLER
+app.state.config.AUTOMATIC1111_SCHEDULER = AUTOMATIC1111_SCHEDULER
+app.state.config.COMFYUI_BASE_URL = COMFYUI_BASE_URL
+app.state.config.COMFYUI_WORKFLOW = COMFYUI_WORKFLOW
+app.state.config.COMFYUI_WORKFLOW_NODES = COMFYUI_WORKFLOW_NODES
+
+app.state.config.IMAGE_SIZE = IMAGE_SIZE
+app.state.config.IMAGE_STEPS = IMAGE_STEPS
+
+
+@app.get("/config")
+async def get_config(request: Request, user=Depends(get_admin_user)):
+    return {
+        "enabled": app.state.config.ENABLED,
+        "engine": app.state.config.ENGINE,
+        "openai": {
+            "OPENAI_API_BASE_URL": app.state.config.OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.OPENAI_API_KEY,
+        },
+        "automatic1111": {
+            "AUTOMATIC1111_BASE_URL": app.state.config.AUTOMATIC1111_BASE_URL,
+            "AUTOMATIC1111_API_AUTH": app.state.config.AUTOMATIC1111_API_AUTH,
+            "AUTOMATIC1111_CFG_SCALE": app.state.config.AUTOMATIC1111_CFG_SCALE,
+            "AUTOMATIC1111_SAMPLER": app.state.config.AUTOMATIC1111_SAMPLER,
+            "AUTOMATIC1111_SCHEDULER": app.state.config.AUTOMATIC1111_SCHEDULER,
+        },
+        "comfyui": {
+            "COMFYUI_BASE_URL": app.state.config.COMFYUI_BASE_URL,
+            "COMFYUI_WORKFLOW": app.state.config.COMFYUI_WORKFLOW,
+            "COMFYUI_WORKFLOW_NODES": app.state.config.COMFYUI_WORKFLOW_NODES,
+        },
+    }
+
+
+class OpenAIConfigForm(BaseModel):
+    OPENAI_API_BASE_URL: str
+    OPENAI_API_KEY: str
+
+
+class Automatic1111ConfigForm(BaseModel):
+    AUTOMATIC1111_BASE_URL: str
+    AUTOMATIC1111_API_AUTH: str
+    AUTOMATIC1111_CFG_SCALE: Optional[str]
+    AUTOMATIC1111_SAMPLER: Optional[str]
+    AUTOMATIC1111_SCHEDULER: Optional[str]
+
+
+class ComfyUIConfigForm(BaseModel):
+    COMFYUI_BASE_URL: str
+    COMFYUI_WORKFLOW: str
+    COMFYUI_WORKFLOW_NODES: list[dict]
+
+
+class ConfigForm(BaseModel):
+    enabled: bool
+    engine: str
+    openai: OpenAIConfigForm
+    automatic1111: Automatic1111ConfigForm
+    comfyui: ComfyUIConfigForm
+
+
+@app.post("/config/update")
+async def update_config(form_data: ConfigForm, user=Depends(get_admin_user)):
+    app.state.config.ENGINE = form_data.engine
+    app.state.config.ENABLED = form_data.enabled
+
+    app.state.config.OPENAI_API_BASE_URL = form_data.openai.OPENAI_API_BASE_URL
+    app.state.config.OPENAI_API_KEY = form_data.openai.OPENAI_API_KEY
+
+    app.state.config.AUTOMATIC1111_BASE_URL = (
+        form_data.automatic1111.AUTOMATIC1111_BASE_URL
+    )
+    app.state.config.AUTOMATIC1111_API_AUTH = (
+        form_data.automatic1111.AUTOMATIC1111_API_AUTH
+    )
+
+    app.state.config.AUTOMATIC1111_CFG_SCALE = (
+        float(form_data.automatic1111.AUTOMATIC1111_CFG_SCALE)
+        if form_data.automatic1111.AUTOMATIC1111_CFG_SCALE
+        else None
+    )
+    app.state.config.AUTOMATIC1111_SAMPLER = (
+        form_data.automatic1111.AUTOMATIC1111_SAMPLER
+        if form_data.automatic1111.AUTOMATIC1111_SAMPLER
+        else None
+    )
+    app.state.config.AUTOMATIC1111_SCHEDULER = (
+        form_data.automatic1111.AUTOMATIC1111_SCHEDULER
+        if form_data.automatic1111.AUTOMATIC1111_SCHEDULER
+        else None
+    )
+
+    app.state.config.COMFYUI_BASE_URL = form_data.comfyui.COMFYUI_BASE_URL.strip("/")
+    app.state.config.COMFYUI_WORKFLOW = form_data.comfyui.COMFYUI_WORKFLOW
+    app.state.config.COMFYUI_WORKFLOW_NODES = form_data.comfyui.COMFYUI_WORKFLOW_NODES
+
+    return {
+        "enabled": app.state.config.ENABLED,
+        "engine": app.state.config.ENGINE,
+        "openai": {
+            "OPENAI_API_BASE_URL": app.state.config.OPENAI_API_BASE_URL,
+            "OPENAI_API_KEY": app.state.config.OPENAI_API_KEY,
+        },
+        "automatic1111": {
+            "AUTOMATIC1111_BASE_URL": app.state.config.AUTOMATIC1111_BASE_URL,
+            "AUTOMATIC1111_API_AUTH": app.state.config.AUTOMATIC1111_API_AUTH,
+            "AUTOMATIC1111_CFG_SCALE": app.state.config.AUTOMATIC1111_CFG_SCALE,
+            "AUTOMATIC1111_SAMPLER": app.state.config.AUTOMATIC1111_SAMPLER,
+            "AUTOMATIC1111_SCHEDULER": app.state.config.AUTOMATIC1111_SCHEDULER,
+        },
+        "comfyui": {
+            "COMFYUI_BASE_URL": app.state.config.COMFYUI_BASE_URL,
+            "COMFYUI_WORKFLOW": app.state.config.COMFYUI_WORKFLOW,
+            "COMFYUI_WORKFLOW_NODES": app.state.config.COMFYUI_WORKFLOW_NODES,
+        },
+    }
+
+
+def get_automatic1111_api_auth():
+    if app.state.config.AUTOMATIC1111_API_AUTH is None:
+        return ""
+    else:
+        auth1111_byte_string = app.state.config.AUTOMATIC1111_API_AUTH.encode("utf-8")
+        auth1111_base64_encoded_bytes = base64.b64encode(auth1111_byte_string)
+        auth1111_base64_encoded_string = auth1111_base64_encoded_bytes.decode("utf-8")
+        return f"Basic {auth1111_base64_encoded_string}"
+
+
+@app.get("/config/url/verify")
+async def verify_url(user=Depends(get_admin_user)):
+    if app.state.config.ENGINE == "automatic1111":
+        try:
+            r = requests.get(
+                url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
+                headers={"authorization": get_automatic1111_api_auth()},
+            )
+            r.raise_for_status()
+            return True
+        except Exception:
+            app.state.config.ENABLED = False
+            raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
+    elif app.state.config.ENGINE == "comfyui":
+        try:
+            r = requests.get(url=f"{app.state.config.COMFYUI_BASE_URL}/object_info")
+            r.raise_for_status()
+            return True
+        except Exception:
+            app.state.config.ENABLED = False
+            raise HTTPException(status_code=400, detail=ERROR_MESSAGES.INVALID_URL)
+    else:
+        return True
+
+
+def set_image_model(model: str):
+    log.info(f"Setting image model to {model}")
+    app.state.config.MODEL = model
+    if app.state.config.ENGINE in ["", "automatic1111"]:
+        api_auth = get_automatic1111_api_auth()
+        r = requests.get(
+            url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
+            headers={"authorization": api_auth},
+        )
+        options = r.json()
+        if model != options["sd_model_checkpoint"]:
+            options["sd_model_checkpoint"] = model
+            r = requests.post(
+                url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
+                json=options,
+                headers={"authorization": api_auth},
+            )
+    return app.state.config.MODEL
+
+
+def get_image_model():
+    if app.state.config.ENGINE == "openai":
+        return app.state.config.MODEL if app.state.config.MODEL else "dall-e-2"
+    elif app.state.config.ENGINE == "comfyui":
+        return app.state.config.MODEL if app.state.config.MODEL else ""
+    elif app.state.config.ENGINE == "automatic1111" or app.state.config.ENGINE == "":
+        try:
+            r = requests.get(
+                url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/options",
+                headers={"authorization": get_automatic1111_api_auth()},
+            )
+            options = r.json()
+            return options["sd_model_checkpoint"]
+        except Exception as e:
+            app.state.config.ENABLED = False
+            raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(e))
+
+
+class ImageConfigForm(BaseModel):
+    MODEL: str
+    IMAGE_SIZE: str
+    IMAGE_STEPS: int
+
+
+@app.get("/image/config")
+async def get_image_config(user=Depends(get_admin_user)):
+    return {
+        "MODEL": app.state.config.MODEL,
+        "IMAGE_SIZE": app.state.config.IMAGE_SIZE,
+        "IMAGE_STEPS": app.state.config.IMAGE_STEPS,
+    }
+
+
+@app.post("/image/config/update")
+async def update_image_config(form_data: ImageConfigForm, user=Depends(get_admin_user)):
+
+    set_image_model(form_data.MODEL)
+
+    pattern = r"^\d+x\d+$"
+    if re.match(pattern, form_data.IMAGE_SIZE):
+        app.state.config.IMAGE_SIZE = form_data.IMAGE_SIZE
+    else:
+        raise HTTPException(
+            status_code=400,
+            detail=ERROR_MESSAGES.INCORRECT_FORMAT("  (e.g., 512x512)."),
+        )
+
+    if form_data.IMAGE_STEPS >= 0:
+        app.state.config.IMAGE_STEPS = form_data.IMAGE_STEPS
+    else:
+        raise HTTPException(
+            status_code=400,
+            detail=ERROR_MESSAGES.INCORRECT_FORMAT("  (e.g., 50)."),
+        )
+
+    return {
+        "MODEL": app.state.config.MODEL,
+        "IMAGE_SIZE": app.state.config.IMAGE_SIZE,
+        "IMAGE_STEPS": app.state.config.IMAGE_STEPS,
+    }
+
+
+@app.get("/models")
+def get_models(user=Depends(get_verified_user)):
+    try:
+        if app.state.config.ENGINE == "openai":
+            return [
+                {"id": "dall-e-2", "name": "DALL·E 2"},
+                {"id": "dall-e-3", "name": "DALL·E 3"},
+            ]
+        elif app.state.config.ENGINE == "comfyui":
+            # TODO - get models from comfyui
+            r = requests.get(url=f"{app.state.config.COMFYUI_BASE_URL}/object_info")
+            info = r.json()
+
+            workflow = json.loads(app.state.config.COMFYUI_WORKFLOW)
+            model_node_id = None
+
+            for node in app.state.config.COMFYUI_WORKFLOW_NODES:
+                if node["type"] == "model":
+                    if node["node_ids"]:
+                        model_node_id = node["node_ids"][0]
+                    break
+
+            if model_node_id:
+                model_list_key = None
+
+                print(workflow[model_node_id]["class_type"])
+                for key in info[workflow[model_node_id]["class_type"]]["input"][
+                    "required"
+                ]:
+                    if "_name" in key:
+                        model_list_key = key
+                        break
+
+                if model_list_key:
+                    return list(
+                        map(
+                            lambda model: {"id": model, "name": model},
+                            info[workflow[model_node_id]["class_type"]]["input"][
+                                "required"
+                            ][model_list_key][0],
+                        )
+                    )
+            else:
+                return list(
+                    map(
+                        lambda model: {"id": model, "name": model},
+                        info["CheckpointLoaderSimple"]["input"]["required"][
+                            "ckpt_name"
+                        ][0],
+                    )
+                )
+        elif (
+            app.state.config.ENGINE == "automatic1111" or app.state.config.ENGINE == ""
+        ):
+            r = requests.get(
+                url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/sd-models",
+                headers={"authorization": get_automatic1111_api_auth()},
+            )
+            models = r.json()
+            return list(
+                map(
+                    lambda model: {"id": model["title"], "name": model["model_name"]},
+                    models,
+                )
+            )
+    except Exception as e:
+        app.state.config.ENABLED = False
+        raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(e))
+
+
+class GenerateImageForm(BaseModel):
+    model: Optional[str] = None
+    prompt: str
+    size: Optional[str] = None
+    n: int = 1
+    negative_prompt: Optional[str] = None
+
+
+def save_b64_image(b64_str):
+    try:
+        image_id = str(uuid.uuid4())
+
+        if "," in b64_str:
+            header, encoded = b64_str.split(",", 1)
+            mime_type = header.split(";")[0]
+
+            img_data = base64.b64decode(encoded)
+            image_format = mimetypes.guess_extension(mime_type)
+
+            image_filename = f"{image_id}{image_format}"
+            file_path = IMAGE_CACHE_DIR / f"{image_filename}"
+            with open(file_path, "wb") as f:
+                f.write(img_data)
+            return image_filename
+        else:
+            image_filename = f"{image_id}.png"
+            file_path = IMAGE_CACHE_DIR.joinpath(image_filename)
+
+            img_data = base64.b64decode(b64_str)
+
+            # Write the image data to a file
+            with open(file_path, "wb") as f:
+                f.write(img_data)
+            return image_filename
+
+    except Exception as e:
+        log.exception(f"Error saving image: {e}")
+        return None
+
+
+def save_url_image(url):
+    image_id = str(uuid.uuid4())
+    try:
+        r = requests.get(url)
+        r.raise_for_status()
+        if r.headers["content-type"].split("/")[0] == "image":
+            mime_type = r.headers["content-type"]
+            image_format = mimetypes.guess_extension(mime_type)
+
+            if not image_format:
+                raise ValueError("Could not determine image type from MIME type")
+
+            image_filename = f"{image_id}{image_format}"
+
+            file_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}")
+            with open(file_path, "wb") as image_file:
+                for chunk in r.iter_content(chunk_size=8192):
+                    image_file.write(chunk)
+            return image_filename
+        else:
+            log.error("Url does not point to an image.")
+            return None
+
+    except Exception as e:
+        log.exception(f"Error saving image: {e}")
+        return None
+
+
+@app.post("/generations")
+async def image_generations(
+    form_data: GenerateImageForm,
+    user=Depends(get_verified_user),
+):
+    width, height = tuple(map(int, app.state.config.IMAGE_SIZE.split("x")))
+
+    r = None
+    try:
+        if app.state.config.ENGINE == "openai":
+            headers = {}
+            headers["Authorization"] = f"Bearer {app.state.config.OPENAI_API_KEY}"
+            headers["Content-Type"] = "application/json"
+
+            if ENABLE_FORWARD_USER_INFO_HEADERS:
+                headers["X-OpenWebUI-User-Name"] = user.name
+                headers["X-OpenWebUI-User-Id"] = user.id
+                headers["X-OpenWebUI-User-Email"] = user.email
+                headers["X-OpenWebUI-User-Role"] = user.role
+
+            data = {
+                "model": (
+                    app.state.config.MODEL
+                    if app.state.config.MODEL != ""
+                    else "dall-e-2"
+                ),
+                "prompt": form_data.prompt,
+                "n": form_data.n,
+                "size": (
+                    form_data.size if form_data.size else app.state.config.IMAGE_SIZE
+                ),
+                "response_format": "b64_json",
+            }
+
+            # Use asyncio.to_thread for the requests.post call
+            r = await asyncio.to_thread(
+                requests.post,
+                url=f"{app.state.config.OPENAI_API_BASE_URL}/images/generations",
+                json=data,
+                headers=headers,
+            )
+
+            r.raise_for_status()
+            res = r.json()
+
+            images = []
+
+            for image in res["data"]:
+                image_filename = save_b64_image(image["b64_json"])
+                images.append({"url": f"/cache/image/generations/{image_filename}"})
+                file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
+
+                with open(file_body_path, "w") as f:
+                    json.dump(data, f)
+
+            return images
+
+        elif app.state.config.ENGINE == "comfyui":
+            data = {
+                "prompt": form_data.prompt,
+                "width": width,
+                "height": height,
+                "n": form_data.n,
+            }
+
+            if app.state.config.IMAGE_STEPS is not None:
+                data["steps"] = app.state.config.IMAGE_STEPS
+
+            if form_data.negative_prompt is not None:
+                data["negative_prompt"] = form_data.negative_prompt
+
+            form_data = ComfyUIGenerateImageForm(
+                **{
+                    "workflow": ComfyUIWorkflow(
+                        **{
+                            "workflow": app.state.config.COMFYUI_WORKFLOW,
+                            "nodes": app.state.config.COMFYUI_WORKFLOW_NODES,
+                        }
+                    ),
+                    **data,
+                }
+            )
+            res = await comfyui_generate_image(
+                app.state.config.MODEL,
+                form_data,
+                user.id,
+                app.state.config.COMFYUI_BASE_URL,
+            )
+            log.debug(f"res: {res}")
+
+            images = []
+
+            for image in res["data"]:
+                image_filename = save_url_image(image["url"])
+                images.append({"url": f"/cache/image/generations/{image_filename}"})
+                file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
+
+                with open(file_body_path, "w") as f:
+                    json.dump(form_data.model_dump(exclude_none=True), f)
+
+            log.debug(f"images: {images}")
+            return images
+        elif (
+            app.state.config.ENGINE == "automatic1111" or app.state.config.ENGINE == ""
+        ):
+            if form_data.model:
+                set_image_model(form_data.model)
+
+            data = {
+                "prompt": form_data.prompt,
+                "batch_size": form_data.n,
+                "width": width,
+                "height": height,
+            }
+
+            if app.state.config.IMAGE_STEPS is not None:
+                data["steps"] = app.state.config.IMAGE_STEPS
+
+            if form_data.negative_prompt is not None:
+                data["negative_prompt"] = form_data.negative_prompt
+
+            if app.state.config.AUTOMATIC1111_CFG_SCALE:
+                data["cfg_scale"] = app.state.config.AUTOMATIC1111_CFG_SCALE
+
+            if app.state.config.AUTOMATIC1111_SAMPLER:
+                data["sampler_name"] = app.state.config.AUTOMATIC1111_SAMPLER
+
+            if app.state.config.AUTOMATIC1111_SCHEDULER:
+                data["scheduler"] = app.state.config.AUTOMATIC1111_SCHEDULER
+
+            # Use asyncio.to_thread for the requests.post call
+            r = await asyncio.to_thread(
+                requests.post,
+                url=f"{app.state.config.AUTOMATIC1111_BASE_URL}/sdapi/v1/txt2img",
+                json=data,
+                headers={"authorization": get_automatic1111_api_auth()},
+            )
+
+            res = r.json()
+            log.debug(f"res: {res}")
+
+            images = []
+
+            for image in res["images"]:
+                image_filename = save_b64_image(image)
+                images.append({"url": f"/cache/image/generations/{image_filename}"})
+                file_body_path = IMAGE_CACHE_DIR.joinpath(f"{image_filename}.json")
+
+                with open(file_body_path, "w") as f:
+                    json.dump({**data, "info": res["info"]}, f)
+
+            return images
+    except Exception as e:
+        error = e
+        if r != None:
+            data = r.json()
+            if "error" in data:
+                error = data["error"]["message"]
+        raise HTTPException(status_code=400, detail=ERROR_MESSAGES.DEFAULT(error))
diff --git a/backend/open_webui/apps/images/utils/comfyui.py b/backend/open_webui/apps/images/utils/comfyui.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c421d7c5290ba7564acc69303241dcdb67dae18
--- /dev/null
+++ b/backend/open_webui/apps/images/utils/comfyui.py
@@ -0,0 +1,186 @@
+import asyncio
+import json
+import logging
+import random
+import urllib.parse
+import urllib.request
+from typing import Optional
+
+import websocket  # NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["COMFYUI"])
+
+default_headers = {"User-Agent": "Mozilla/5.0"}
+
+
+def queue_prompt(prompt, client_id, base_url):
+    log.info("queue_prompt")
+    p = {"prompt": prompt, "client_id": client_id}
+    data = json.dumps(p).encode("utf-8")
+    log.debug(f"queue_prompt data: {data}")
+    try:
+        req = urllib.request.Request(
+            f"{base_url}/prompt", data=data, headers=default_headers
+        )
+        response = urllib.request.urlopen(req).read()
+        return json.loads(response)
+    except Exception as e:
+        log.exception(f"Error while queuing prompt: {e}")
+        raise e
+
+
+def get_image(filename, subfolder, folder_type, base_url):
+    log.info("get_image")
+    data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
+    url_values = urllib.parse.urlencode(data)
+    req = urllib.request.Request(
+        f"{base_url}/view?{url_values}", headers=default_headers
+    )
+    with urllib.request.urlopen(req) as response:
+        return response.read()
+
+
+def get_image_url(filename, subfolder, folder_type, base_url):
+    log.info("get_image")
+    data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
+    url_values = urllib.parse.urlencode(data)
+    return f"{base_url}/view?{url_values}"
+
+
+def get_history(prompt_id, base_url):
+    log.info("get_history")
+
+    req = urllib.request.Request(
+        f"{base_url}/history/{prompt_id}", headers=default_headers
+    )
+    with urllib.request.urlopen(req) as response:
+        return json.loads(response.read())
+
+
+def get_images(ws, prompt, client_id, base_url):
+    prompt_id = queue_prompt(prompt, client_id, base_url)["prompt_id"]
+    output_images = []
+    while True:
+        out = ws.recv()
+        if isinstance(out, str):
+            message = json.loads(out)
+            if message["type"] == "executing":
+                data = message["data"]
+                if data["node"] is None and data["prompt_id"] == prompt_id:
+                    break  # Execution is done
+        else:
+            continue  # previews are binary data
+
+    history = get_history(prompt_id, base_url)[prompt_id]
+    for o in history["outputs"]:
+        for node_id in history["outputs"]:
+            node_output = history["outputs"][node_id]
+            if "images" in node_output:
+                for image in node_output["images"]:
+                    url = get_image_url(
+                        image["filename"], image["subfolder"], image["type"], base_url
+                    )
+                    output_images.append({"url": url})
+    return {"data": output_images}
+
+
+class ComfyUINodeInput(BaseModel):
+    type: Optional[str] = None
+    node_ids: list[str] = []
+    key: Optional[str] = "text"
+    value: Optional[str] = None
+
+
+class ComfyUIWorkflow(BaseModel):
+    workflow: str
+    nodes: list[ComfyUINodeInput]
+
+
+class ComfyUIGenerateImageForm(BaseModel):
+    workflow: ComfyUIWorkflow
+
+    prompt: str
+    negative_prompt: Optional[str] = None
+    width: int
+    height: int
+    n: int = 1
+
+    steps: Optional[int] = None
+    seed: Optional[int] = None
+
+
+async def comfyui_generate_image(
+    model: str, payload: ComfyUIGenerateImageForm, client_id, base_url
+):
+    ws_url = base_url.replace("http://", "ws://").replace("https://", "wss://")
+    workflow = json.loads(payload.workflow.workflow)
+
+    for node in payload.workflow.nodes:
+        if node.type:
+            if node.type == "model":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][node.key] = model
+            elif node.type == "prompt":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "text"
+                    ] = payload.prompt
+            elif node.type == "negative_prompt":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "text"
+                    ] = payload.negative_prompt
+            elif node.type == "width":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "width"
+                    ] = payload.width
+            elif node.type == "height":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "height"
+                    ] = payload.height
+            elif node.type == "n":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "batch_size"
+                    ] = payload.n
+            elif node.type == "steps":
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][
+                        node.key if node.key else "steps"
+                    ] = payload.steps
+            elif node.type == "seed":
+                seed = (
+                    payload.seed
+                    if payload.seed
+                    else random.randint(0, 18446744073709551614)
+                )
+                for node_id in node.node_ids:
+                    workflow[node_id]["inputs"][node.key] = seed
+        else:
+            for node_id in node.node_ids:
+                workflow[node_id]["inputs"][node.key] = node.value
+
+    try:
+        ws = websocket.WebSocket()
+        ws.connect(f"{ws_url}/ws?clientId={client_id}")
+        log.info("WebSocket connection established.")
+    except Exception as e:
+        log.exception(f"Failed to connect to WebSocket server: {e}")
+        return None
+
+    try:
+        log.info("Sending workflow to WebSocket server.")
+        log.info(f"Workflow: {workflow}")
+        images = await asyncio.to_thread(get_images, ws, workflow, client_id, base_url)
+    except Exception as e:
+        log.exception(f"Error while receiving images: {e}")
+        images = None
+
+    ws.close()
+
+    return images
diff --git a/backend/open_webui/apps/ollama/main.py b/backend/open_webui/apps/ollama/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..82a37a752812a14e1259de8aa89e9513bf0b45e2
--- /dev/null
+++ b/backend/open_webui/apps/ollama/main.py
@@ -0,0 +1,1351 @@
+import asyncio
+import json
+import logging
+import os
+import random
+import re
+import time
+from typing import Optional, Union
+from urllib.parse import urlparse
+
+import aiohttp
+from aiocache import cached
+
+import requests
+from open_webui.apps.webui.models.models import Models
+from open_webui.config import (
+    CORS_ALLOW_ORIGIN,
+    ENABLE_OLLAMA_API,
+    OLLAMA_BASE_URLS,
+    OLLAMA_API_CONFIGS,
+    UPLOAD_DIR,
+    AppConfig,
+)
+from open_webui.env import (
+    AIOHTTP_CLIENT_TIMEOUT,
+    AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST,
+    BYPASS_MODEL_ACCESS_CONTROL,
+)
+
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import ENV, SRC_LOG_LEVELS
+from fastapi import Depends, FastAPI, File, HTTPException, Request, UploadFile
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import StreamingResponse
+from pydantic import BaseModel, ConfigDict
+from starlette.background import BackgroundTask
+
+
+from open_webui.utils.misc import (
+    calculate_sha256,
+)
+from open_webui.utils.payload import (
+    apply_model_params_to_body_ollama,
+    apply_model_params_to_body_openai,
+    apply_model_system_prompt_to_body,
+)
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["OLLAMA"])
+
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.state.config = AppConfig()
+
+app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
+app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
+app.state.config.OLLAMA_API_CONFIGS = OLLAMA_API_CONFIGS
+
+
+# TODO: Implement a more intelligent load balancing mechanism for distributing requests among multiple backend instances.
+# Current implementation uses a simple round-robin approach (random.choice). Consider incorporating algorithms like weighted round-robin,
+# least connections, or least response time for better resource utilization and performance optimization.
+
+
+@app.head("/")
+@app.get("/")
+async def get_status():
+    return {"status": True}
+
+
+class ConnectionVerificationForm(BaseModel):
+    url: str
+    key: Optional[str] = None
+
+
+@app.post("/verify")
+async def verify_connection(
+    form_data: ConnectionVerificationForm, user=Depends(get_admin_user)
+):
+    url = form_data.url
+    key = form_data.key
+
+    headers = {}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST)
+    async with aiohttp.ClientSession(timeout=timeout) as session:
+        try:
+            async with session.get(f"{url}/api/version", headers=headers) as r:
+                if r.status != 200:
+                    # Extract response error details if available
+                    error_detail = f"HTTP Error: {r.status}"
+                    res = await r.json()
+                    if "error" in res:
+                        error_detail = f"External Error: {res['error']}"
+                    raise Exception(error_detail)
+
+                response_data = await r.json()
+                return response_data
+
+        except aiohttp.ClientError as e:
+            # ClientError covers all aiohttp requests issues
+            log.exception(f"Client error: {str(e)}")
+            # Handle aiohttp-specific connection issues, timeout etc.
+            raise HTTPException(
+                status_code=500, detail="Open WebUI: Server Connection Error"
+            )
+        except Exception as e:
+            log.exception(f"Unexpected error: {e}")
+            # Generic error handler in case parsing JSON or other steps fail
+            error_detail = f"Unexpected error: {str(e)}"
+            raise HTTPException(status_code=500, detail=error_detail)
+
+
+@app.get("/config")
+async def get_config(user=Depends(get_admin_user)):
+    return {
+        "ENABLE_OLLAMA_API": app.state.config.ENABLE_OLLAMA_API,
+        "OLLAMA_BASE_URLS": app.state.config.OLLAMA_BASE_URLS,
+        "OLLAMA_API_CONFIGS": app.state.config.OLLAMA_API_CONFIGS,
+    }
+
+
+class OllamaConfigForm(BaseModel):
+    ENABLE_OLLAMA_API: Optional[bool] = None
+    OLLAMA_BASE_URLS: list[str]
+    OLLAMA_API_CONFIGS: dict
+
+
+@app.post("/config/update")
+async def update_config(form_data: OllamaConfigForm, user=Depends(get_admin_user)):
+    app.state.config.ENABLE_OLLAMA_API = form_data.ENABLE_OLLAMA_API
+    app.state.config.OLLAMA_BASE_URLS = form_data.OLLAMA_BASE_URLS
+
+    app.state.config.OLLAMA_API_CONFIGS = form_data.OLLAMA_API_CONFIGS
+
+    # Remove any extra configs
+    config_urls = app.state.config.OLLAMA_API_CONFIGS.keys()
+    for url in list(app.state.config.OLLAMA_BASE_URLS):
+        if url not in config_urls:
+            app.state.config.OLLAMA_API_CONFIGS.pop(url, None)
+
+    return {
+        "ENABLE_OLLAMA_API": app.state.config.ENABLE_OLLAMA_API,
+        "OLLAMA_BASE_URLS": app.state.config.OLLAMA_BASE_URLS,
+        "OLLAMA_API_CONFIGS": app.state.config.OLLAMA_API_CONFIGS,
+    }
+
+
+async def aiohttp_get(url, key=None):
+    timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST)
+    try:
+        headers = {"Authorization": f"Bearer {key}"} if key else {}
+        async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
+            async with session.get(url, headers=headers) as response:
+                return await response.json()
+    except Exception as e:
+        # Handle connection error here
+        log.error(f"Connection error: {e}")
+        return None
+
+
+async def cleanup_response(
+    response: Optional[aiohttp.ClientResponse],
+    session: Optional[aiohttp.ClientSession],
+):
+    if response:
+        response.close()
+    if session:
+        await session.close()
+
+
+async def post_streaming_url(
+    url: str, payload: Union[str, bytes], stream: bool = True, content_type=None
+):
+    r = None
+    try:
+        session = aiohttp.ClientSession(
+            trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
+        )
+
+        parsed_url = urlparse(url)
+        base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+        api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+        key = api_config.get("key", None)
+
+        headers = {"Content-Type": "application/json"}
+        if key:
+            headers["Authorization"] = f"Bearer {key}"
+
+        r = await session.post(
+            url,
+            data=payload,
+            headers=headers,
+        )
+        r.raise_for_status()
+
+        if stream:
+            response_headers = dict(r.headers)
+            if content_type:
+                response_headers["Content-Type"] = content_type
+            return StreamingResponse(
+                r.content,
+                status_code=r.status,
+                headers=response_headers,
+                background=BackgroundTask(
+                    cleanup_response, response=r, session=session
+                ),
+            )
+        else:
+            res = await r.json()
+            await cleanup_response(r, session)
+            return res
+
+    except Exception as e:
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = await r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise HTTPException(
+            status_code=r.status if r else 500,
+            detail=error_detail,
+        )
+
+
+def merge_models_lists(model_lists):
+    merged_models = {}
+
+    for idx, model_list in enumerate(model_lists):
+        if model_list is not None:
+            for model in model_list:
+                id = model["model"]
+                if id not in merged_models:
+                    model["urls"] = [idx]
+                    merged_models[id] = model
+                else:
+                    merged_models[id]["urls"].append(idx)
+
+    return list(merged_models.values())
+
+
+@cached(ttl=3)
+async def get_all_models():
+    log.info("get_all_models()")
+    if app.state.config.ENABLE_OLLAMA_API:
+        tasks = []
+        for idx, url in enumerate(app.state.config.OLLAMA_BASE_URLS):
+            if url not in app.state.config.OLLAMA_API_CONFIGS:
+                tasks.append(aiohttp_get(f"{url}/api/tags"))
+            else:
+                api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {})
+                enable = api_config.get("enable", True)
+                key = api_config.get("key", None)
+
+                if enable:
+                    tasks.append(aiohttp_get(f"{url}/api/tags", key))
+                else:
+                    tasks.append(asyncio.ensure_future(asyncio.sleep(0, None)))
+
+        responses = await asyncio.gather(*tasks)
+
+        for idx, response in enumerate(responses):
+            if response:
+                url = app.state.config.OLLAMA_BASE_URLS[idx]
+                api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {})
+
+                prefix_id = api_config.get("prefix_id", None)
+                model_ids = api_config.get("model_ids", [])
+
+                if len(model_ids) != 0 and "models" in response:
+                    response["models"] = list(
+                        filter(
+                            lambda model: model["model"] in model_ids,
+                            response["models"],
+                        )
+                    )
+
+                if prefix_id:
+                    for model in response.get("models", []):
+                        model["model"] = f"{prefix_id}.{model['model']}"
+
+        models = {
+            "models": merge_models_lists(
+                map(
+                    lambda response: response.get("models", []) if response else None,
+                    responses,
+                )
+            )
+        }
+
+    else:
+        models = {"models": []}
+
+    return models
+
+
+@app.get("/api/tags")
+@app.get("/api/tags/{url_idx}")
+async def get_ollama_tags(
+    url_idx: Optional[int] = None, user=Depends(get_verified_user)
+):
+    models = []
+    if url_idx is None:
+        models = await get_all_models()
+    else:
+        url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+
+        parsed_url = urlparse(url)
+        base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+        api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+        key = api_config.get("key", None)
+
+        headers = {}
+        if key:
+            headers["Authorization"] = f"Bearer {key}"
+
+        r = None
+        try:
+            r = requests.request(method="GET", url=f"{url}/api/tags", headers=headers)
+            r.raise_for_status()
+
+            models = r.json()
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            if r is not None:
+                try:
+                    res = r.json()
+                    if "error" in res:
+                        error_detail = f"Ollama: {res['error']}"
+                except Exception:
+                    error_detail = f"Ollama: {e}"
+
+            raise HTTPException(
+                status_code=r.status_code if r else 500,
+                detail=error_detail,
+            )
+
+    if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+        # Filter models based on user access control
+        filtered_models = []
+        for model in models.get("models", []):
+            model_info = Models.get_model_by_id(model["model"])
+            if model_info:
+                if user.id == model_info.user_id or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                ):
+                    filtered_models.append(model)
+        models["models"] = filtered_models
+
+    return models
+
+
+@app.get("/api/version")
+@app.get("/api/version/{url_idx}")
+async def get_ollama_versions(url_idx: Optional[int] = None):
+    if app.state.config.ENABLE_OLLAMA_API:
+        if url_idx is None:
+            # returns lowest version
+            tasks = [
+                aiohttp_get(
+                    f"{url}/api/version",
+                    app.state.config.OLLAMA_API_CONFIGS.get(url, {}).get("key", None),
+                )
+                for url in app.state.config.OLLAMA_BASE_URLS
+            ]
+            responses = await asyncio.gather(*tasks)
+            responses = list(filter(lambda x: x is not None, responses))
+
+            if len(responses) > 0:
+                lowest_version = min(
+                    responses,
+                    key=lambda x: tuple(
+                        map(int, re.sub(r"^v|-.*", "", x["version"]).split("."))
+                    ),
+                )
+
+                return {"version": lowest_version["version"]}
+            else:
+                raise HTTPException(
+                    status_code=500,
+                    detail=ERROR_MESSAGES.OLLAMA_NOT_FOUND,
+                )
+        else:
+            url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+
+            r = None
+            try:
+                r = requests.request(method="GET", url=f"{url}/api/version")
+                r.raise_for_status()
+
+                return r.json()
+            except Exception as e:
+                log.exception(e)
+                error_detail = "Open WebUI: Server Connection Error"
+                if r is not None:
+                    try:
+                        res = r.json()
+                        if "error" in res:
+                            error_detail = f"Ollama: {res['error']}"
+                    except Exception:
+                        error_detail = f"Ollama: {e}"
+
+                raise HTTPException(
+                    status_code=r.status_code if r else 500,
+                    detail=error_detail,
+                )
+    else:
+        return {"version": False}
+
+
+class ModelNameForm(BaseModel):
+    name: str
+
+
+@app.post("/api/pull")
+@app.post("/api/pull/{url_idx}")
+async def pull_model(
+    form_data: ModelNameForm, url_idx: int = 0, user=Depends(get_admin_user)
+):
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    # Admin should be able to pull models from any source
+    payload = {**form_data.model_dump(exclude_none=True), "insecure": True}
+
+    return await post_streaming_url(f"{url}/api/pull", json.dumps(payload))
+
+
+class PushModelForm(BaseModel):
+    name: str
+    insecure: Optional[bool] = None
+    stream: Optional[bool] = None
+
+
+@app.delete("/api/push")
+@app.delete("/api/push/{url_idx}")
+async def push_model(
+    form_data: PushModelForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_admin_user),
+):
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        if form_data.name in models:
+            url_idx = models[form_data.name]["urls"][0]
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.name),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.debug(f"url: {url}")
+
+    return await post_streaming_url(
+        f"{url}/api/push", form_data.model_dump_json(exclude_none=True).encode()
+    )
+
+
+class CreateModelForm(BaseModel):
+    name: str
+    modelfile: Optional[str] = None
+    stream: Optional[bool] = None
+    path: Optional[str] = None
+
+
+@app.post("/api/create")
+@app.post("/api/create/{url_idx}")
+async def create_model(
+    form_data: CreateModelForm, url_idx: int = 0, user=Depends(get_admin_user)
+):
+    log.debug(f"form_data: {form_data}")
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    return await post_streaming_url(
+        f"{url}/api/create", form_data.model_dump_json(exclude_none=True).encode()
+    )
+
+
+class CopyModelForm(BaseModel):
+    source: str
+    destination: str
+
+
+@app.post("/api/copy")
+@app.post("/api/copy/{url_idx}")
+async def copy_model(
+    form_data: CopyModelForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_admin_user),
+):
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        if form_data.source in models:
+            url_idx = models[form_data.source]["urls"][0]
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.source),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    key = api_config.get("key", None)
+
+    headers = {"Content-Type": "application/json"}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    r = requests.request(
+        method="POST",
+        url=f"{url}/api/copy",
+        headers=headers,
+        data=form_data.model_dump_json(exclude_none=True).encode(),
+    )
+
+    try:
+        r.raise_for_status()
+
+        log.debug(f"r.text: {r.text}")
+
+        return True
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise HTTPException(
+            status_code=r.status_code if r else 500,
+            detail=error_detail,
+        )
+
+
+@app.delete("/api/delete")
+@app.delete("/api/delete/{url_idx}")
+async def delete_model(
+    form_data: ModelNameForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_admin_user),
+):
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        if form_data.name in models:
+            url_idx = models[form_data.name]["urls"][0]
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.name),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    key = api_config.get("key", None)
+
+    headers = {"Content-Type": "application/json"}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    r = requests.request(
+        method="DELETE",
+        url=f"{url}/api/delete",
+        data=form_data.model_dump_json(exclude_none=True).encode(),
+        headers=headers,
+    )
+    try:
+        r.raise_for_status()
+
+        log.debug(f"r.text: {r.text}")
+
+        return True
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise HTTPException(
+            status_code=r.status_code if r else 500,
+            detail=error_detail,
+        )
+
+
+@app.post("/api/show")
+async def show_model_info(form_data: ModelNameForm, user=Depends(get_verified_user)):
+    model_list = await get_all_models()
+    models = {model["model"]: model for model in model_list["models"]}
+
+    if form_data.name not in models:
+        raise HTTPException(
+            status_code=400,
+            detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.name),
+        )
+
+    url_idx = random.choice(models[form_data.name]["urls"])
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    key = api_config.get("key", None)
+
+    headers = {"Content-Type": "application/json"}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    r = requests.request(
+        method="POST",
+        url=f"{url}/api/show",
+        headers=headers,
+        data=form_data.model_dump_json(exclude_none=True).encode(),
+    )
+    try:
+        r.raise_for_status()
+
+        return r.json()
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise HTTPException(
+            status_code=r.status_code if r else 500,
+            detail=error_detail,
+        )
+
+
+class GenerateEmbeddingsForm(BaseModel):
+    model: str
+    prompt: str
+    options: Optional[dict] = None
+    keep_alive: Optional[Union[int, str]] = None
+
+
+class GenerateEmbedForm(BaseModel):
+    model: str
+    input: list[str] | str
+    truncate: Optional[bool] = None
+    options: Optional[dict] = None
+    keep_alive: Optional[Union[int, str]] = None
+
+
+@app.post("/api/embed")
+@app.post("/api/embed/{url_idx}")
+async def generate_embeddings(
+    form_data: GenerateEmbedForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+):
+    return await generate_ollama_batch_embeddings(form_data, url_idx)
+
+
+@app.post("/api/embeddings")
+@app.post("/api/embeddings/{url_idx}")
+async def generate_embeddings(
+    form_data: GenerateEmbeddingsForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+):
+    return await generate_ollama_embeddings(form_data=form_data, url_idx=url_idx)
+
+
+async def generate_ollama_embeddings(
+    form_data: GenerateEmbeddingsForm,
+    url_idx: Optional[int] = None,
+):
+    log.info(f"generate_ollama_embeddings {form_data}")
+
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        model = form_data.model
+
+        if ":" not in model:
+            model = f"{model}:latest"
+
+        if model in models:
+            url_idx = random.choice(models[model]["urls"])
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    key = api_config.get("key", None)
+
+    headers = {"Content-Type": "application/json"}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    r = requests.request(
+        method="POST",
+        url=f"{url}/api/embeddings",
+        headers=headers,
+        data=form_data.model_dump_json(exclude_none=True).encode(),
+    )
+    try:
+        r.raise_for_status()
+
+        data = r.json()
+
+        log.info(f"generate_ollama_embeddings {data}")
+
+        if "embedding" in data:
+            return data
+        else:
+            raise Exception("Something went wrong :/")
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise HTTPException(
+            status_code=r.status_code if r else 500,
+            detail=error_detail,
+        )
+
+
+async def generate_ollama_batch_embeddings(
+    form_data: GenerateEmbedForm,
+    url_idx: Optional[int] = None,
+):
+    log.info(f"generate_ollama_batch_embeddings {form_data}")
+
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        model = form_data.model
+
+        if ":" not in model:
+            model = f"{model}:latest"
+
+        if model in models:
+            url_idx = random.choice(models[model]["urls"])
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    log.info(f"url: {url}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    key = api_config.get("key", None)
+
+    headers = {"Content-Type": "application/json"}
+    if key:
+        headers["Authorization"] = f"Bearer {key}"
+
+    r = requests.request(
+        method="POST",
+        url=f"{url}/api/embed",
+        headers=headers,
+        data=form_data.model_dump_json(exclude_none=True).encode(),
+    )
+    try:
+        r.raise_for_status()
+
+        data = r.json()
+
+        log.info(f"generate_ollama_batch_embeddings {data}")
+
+        if "embeddings" in data:
+            return data
+        else:
+            raise Exception("Something went wrong :/")
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = r.json()
+                if "error" in res:
+                    error_detail = f"Ollama: {res['error']}"
+            except Exception:
+                error_detail = f"Ollama: {e}"
+
+        raise Exception(error_detail)
+
+
+class GenerateCompletionForm(BaseModel):
+    model: str
+    prompt: str
+    suffix: Optional[str] = None
+    images: Optional[list[str]] = None
+    format: Optional[str] = None
+    options: Optional[dict] = None
+    system: Optional[str] = None
+    template: Optional[str] = None
+    context: Optional[list[int]] = None
+    stream: Optional[bool] = True
+    raw: Optional[bool] = None
+    keep_alive: Optional[Union[int, str]] = None
+
+
+@app.post("/api/generate")
+@app.post("/api/generate/{url_idx}")
+async def generate_completion(
+    form_data: GenerateCompletionForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+):
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        model = form_data.model
+
+        if ":" not in model:
+            model = f"{model}:latest"
+
+        if model in models:
+            url_idx = random.choice(models[model]["urls"])
+        else:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(form_data.model),
+            )
+
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {})
+    prefix_id = api_config.get("prefix_id", None)
+    if prefix_id:
+        form_data.model = form_data.model.replace(f"{prefix_id}.", "")
+    log.info(f"url: {url}")
+
+    return await post_streaming_url(
+        f"{url}/api/generate", form_data.model_dump_json(exclude_none=True).encode()
+    )
+
+
+class ChatMessage(BaseModel):
+    role: str
+    content: str
+    images: Optional[list[str]] = None
+
+
+class GenerateChatCompletionForm(BaseModel):
+    model: str
+    messages: list[ChatMessage]
+    format: Optional[str] = None
+    options: Optional[dict] = None
+    template: Optional[str] = None
+    stream: Optional[bool] = True
+    keep_alive: Optional[Union[int, str]] = None
+
+
+async def get_ollama_url(url_idx: Optional[int], model: str):
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = {model["model"]: model for model in model_list["models"]}
+
+        if model not in models:
+            raise HTTPException(
+                status_code=400,
+                detail=ERROR_MESSAGES.MODEL_NOT_FOUND(model),
+            )
+        url_idx = random.choice(models[model]["urls"])
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+    return url
+
+
+@app.post("/api/chat")
+@app.post("/api/chat/{url_idx}")
+async def generate_chat_completion(
+    form_data: GenerateChatCompletionForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+    bypass_filter: Optional[bool] = False,
+):
+    payload = {**form_data.model_dump(exclude_none=True)}
+    log.debug(f"generate_chat_completion() - 1.payload = {payload}")
+    if "metadata" in payload:
+        del payload["metadata"]
+
+    model_id = payload["model"]
+    model_info = Models.get_model_by_id(model_id)
+
+    if model_info:
+        if model_info.base_model_id:
+            payload["model"] = model_info.base_model_id
+
+        params = model_info.params.model_dump()
+
+        if params:
+            if payload.get("options") is None:
+                payload["options"] = {}
+
+            payload["options"] = apply_model_params_to_body_ollama(
+                params, payload["options"]
+            )
+            payload = apply_model_system_prompt_to_body(params, payload, user)
+
+        # Check if user has access to the model
+        if not bypass_filter and user.role == "user":
+            if not (
+                user.id == model_info.user_id
+                or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                )
+            ):
+                raise HTTPException(
+                    status_code=403,
+                    detail="Model not found",
+                )
+    elif not bypass_filter:
+        if user.role != "admin":
+            raise HTTPException(
+                status_code=403,
+                detail="Model not found",
+            )
+
+    if ":" not in payload["model"]:
+        payload["model"] = f"{payload['model']}:latest"
+
+    url = await get_ollama_url(url_idx, payload["model"])
+    log.info(f"url: {url}")
+    log.debug(f"generate_chat_completion() - 2.payload = {payload}")
+
+    parsed_url = urlparse(url)
+    base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(base_url, {})
+    prefix_id = api_config.get("prefix_id", None)
+    if prefix_id:
+        payload["model"] = payload["model"].replace(f"{prefix_id}.", "")
+
+    return await post_streaming_url(
+        f"{url}/api/chat",
+        json.dumps(payload),
+        stream=form_data.stream,
+        content_type="application/x-ndjson",
+    )
+
+
+# TODO: we should update this part once Ollama supports other types
+class OpenAIChatMessageContent(BaseModel):
+    type: str
+    model_config = ConfigDict(extra="allow")
+
+
+class OpenAIChatMessage(BaseModel):
+    role: str
+    content: Union[str, list[OpenAIChatMessageContent]]
+
+    model_config = ConfigDict(extra="allow")
+
+
+class OpenAIChatCompletionForm(BaseModel):
+    model: str
+    messages: list[OpenAIChatMessage]
+
+    model_config = ConfigDict(extra="allow")
+
+
+@app.post("/v1/chat/completions")
+@app.post("/v1/chat/completions/{url_idx}")
+async def generate_openai_chat_completion(
+    form_data: dict,
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+):
+    try:
+        completion_form = OpenAIChatCompletionForm(**form_data)
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=400,
+            detail=str(e),
+        )
+
+    payload = {**completion_form.model_dump(exclude_none=True, exclude=["metadata"])}
+    if "metadata" in payload:
+        del payload["metadata"]
+
+    model_id = completion_form.model
+    if ":" not in model_id:
+        model_id = f"{model_id}:latest"
+
+    model_info = Models.get_model_by_id(model_id)
+    if model_info:
+        if model_info.base_model_id:
+            payload["model"] = model_info.base_model_id
+
+        params = model_info.params.model_dump()
+
+        if params:
+            payload = apply_model_params_to_body_openai(params, payload)
+            payload = apply_model_system_prompt_to_body(params, payload, user)
+
+        # Check if user has access to the model
+        if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+            if not (
+                user.id == model_info.user_id
+                or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                )
+            ):
+                raise HTTPException(
+                    status_code=403,
+                    detail="Model not found",
+                )
+    else:
+        if user.role != "admin":
+            raise HTTPException(
+                status_code=403,
+                detail="Model not found",
+            )
+
+    if ":" not in payload["model"]:
+        payload["model"] = f"{payload['model']}:latest"
+
+    url = await get_ollama_url(url_idx, payload["model"])
+    log.info(f"url: {url}")
+
+    api_config = app.state.config.OLLAMA_API_CONFIGS.get(url, {})
+    prefix_id = api_config.get("prefix_id", None)
+    if prefix_id:
+        payload["model"] = payload["model"].replace(f"{prefix_id}.", "")
+
+    return await post_streaming_url(
+        f"{url}/v1/chat/completions",
+        json.dumps(payload),
+        stream=payload.get("stream", False),
+    )
+
+
+@app.get("/v1/models")
+@app.get("/v1/models/{url_idx}")
+async def get_openai_models(
+    url_idx: Optional[int] = None,
+    user=Depends(get_verified_user),
+):
+
+    models = []
+    if url_idx is None:
+        model_list = await get_all_models()
+        models = [
+            {
+                "id": model["model"],
+                "object": "model",
+                "created": int(time.time()),
+                "owned_by": "openai",
+            }
+            for model in model_list["models"]
+        ]
+
+    else:
+        url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+        try:
+            r = requests.request(method="GET", url=f"{url}/api/tags")
+            r.raise_for_status()
+
+            model_list = r.json()
+
+            models = [
+                {
+                    "id": model["model"],
+                    "object": "model",
+                    "created": int(time.time()),
+                    "owned_by": "openai",
+                }
+                for model in models["models"]
+            ]
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            if r is not None:
+                try:
+                    res = r.json()
+                    if "error" in res:
+                        error_detail = f"Ollama: {res['error']}"
+                except Exception:
+                    error_detail = f"Ollama: {e}"
+
+            raise HTTPException(
+                status_code=r.status_code if r else 500,
+                detail=error_detail,
+            )
+
+    if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+        # Filter models based on user access control
+        filtered_models = []
+        for model in models:
+            model_info = Models.get_model_by_id(model["id"])
+            if model_info:
+                if user.id == model_info.user_id or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                ):
+                    filtered_models.append(model)
+        models = filtered_models
+
+    return {
+        "data": models,
+        "object": "list",
+    }
+
+
+class UrlForm(BaseModel):
+    url: str
+
+
+class UploadBlobForm(BaseModel):
+    filename: str
+
+
+def parse_huggingface_url(hf_url):
+    try:
+        # Parse the URL
+        parsed_url = urlparse(hf_url)
+
+        # Get the path and split it into components
+        path_components = parsed_url.path.split("/")
+
+        # Extract the desired output
+        model_file = path_components[-1]
+
+        return model_file
+    except ValueError:
+        return None
+
+
+async def download_file_stream(
+    ollama_url, file_url, file_path, file_name, chunk_size=1024 * 1024
+):
+    done = False
+
+    if os.path.exists(file_path):
+        current_size = os.path.getsize(file_path)
+    else:
+        current_size = 0
+
+    headers = {"Range": f"bytes={current_size}-"} if current_size > 0 else {}
+
+    timeout = aiohttp.ClientTimeout(total=600)  # Set the timeout
+
+    async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
+        async with session.get(file_url, headers=headers) as response:
+            total_size = int(response.headers.get("content-length", 0)) + current_size
+
+            with open(file_path, "ab+") as file:
+                async for data in response.content.iter_chunked(chunk_size):
+                    current_size += len(data)
+                    file.write(data)
+
+                    done = current_size == total_size
+                    progress = round((current_size / total_size) * 100, 2)
+
+                    yield f'data: {{"progress": {progress}, "completed": {current_size}, "total": {total_size}}}\n\n'
+
+                if done:
+                    file.seek(0)
+                    hashed = calculate_sha256(file)
+                    file.seek(0)
+
+                    url = f"{ollama_url}/api/blobs/sha256:{hashed}"
+                    response = requests.post(url, data=file)
+
+                    if response.ok:
+                        res = {
+                            "done": done,
+                            "blob": f"sha256:{hashed}",
+                            "name": file_name,
+                        }
+                        os.remove(file_path)
+
+                        yield f"data: {json.dumps(res)}\n\n"
+                    else:
+                        raise "Ollama: Could not create blob, Please try again."
+
+
+# url = "https://huggingface.co/TheBloke/stablelm-zephyr-3b-GGUF/resolve/main/stablelm-zephyr-3b.Q2_K.gguf"
+@app.post("/models/download")
+@app.post("/models/download/{url_idx}")
+async def download_model(
+    form_data: UrlForm,
+    url_idx: Optional[int] = None,
+    user=Depends(get_admin_user),
+):
+    allowed_hosts = ["https://huggingface.co/", "https://github.com/"]
+
+    if not any(form_data.url.startswith(host) for host in allowed_hosts):
+        raise HTTPException(
+            status_code=400,
+            detail="Invalid file_url. Only URLs from allowed hosts are permitted.",
+        )
+
+    if url_idx is None:
+        url_idx = 0
+    url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+
+    file_name = parse_huggingface_url(form_data.url)
+
+    if file_name:
+        file_path = f"{UPLOAD_DIR}/{file_name}"
+
+        return StreamingResponse(
+            download_file_stream(url, form_data.url, file_path, file_name),
+        )
+    else:
+        return None
+
+
+@app.post("/models/upload")
+@app.post("/models/upload/{url_idx}")
+def upload_model(
+    file: UploadFile = File(...),
+    url_idx: Optional[int] = None,
+    user=Depends(get_admin_user),
+):
+    if url_idx is None:
+        url_idx = 0
+    ollama_url = app.state.config.OLLAMA_BASE_URLS[url_idx]
+
+    file_path = f"{UPLOAD_DIR}/{file.filename}"
+
+    # Save file in chunks
+    with open(file_path, "wb+") as f:
+        for chunk in file.file:
+            f.write(chunk)
+
+    def file_process_stream():
+        nonlocal ollama_url
+        total_size = os.path.getsize(file_path)
+        chunk_size = 1024 * 1024
+        try:
+            with open(file_path, "rb") as f:
+                total = 0
+                done = False
+
+                while not done:
+                    chunk = f.read(chunk_size)
+                    if not chunk:
+                        done = True
+                        continue
+
+                    total += len(chunk)
+                    progress = round((total / total_size) * 100, 2)
+
+                    res = {
+                        "progress": progress,
+                        "total": total_size,
+                        "completed": total,
+                    }
+                    yield f"data: {json.dumps(res)}\n\n"
+
+                if done:
+                    f.seek(0)
+                    hashed = calculate_sha256(f)
+                    f.seek(0)
+
+                    url = f"{ollama_url}/api/blobs/sha256:{hashed}"
+                    response = requests.post(url, data=f)
+
+                    if response.ok:
+                        res = {
+                            "done": done,
+                            "blob": f"sha256:{hashed}",
+                            "name": file.filename,
+                        }
+                        os.remove(file_path)
+                        yield f"data: {json.dumps(res)}\n\n"
+                    else:
+                        raise Exception(
+                            "Ollama: Could not create blob, Please try again."
+                        )
+
+        except Exception as e:
+            res = {"error": str(e)}
+            yield f"data: {json.dumps(res)}\n\n"
+
+    return StreamingResponse(file_process_stream(), media_type="text/event-stream")
diff --git a/backend/open_webui/apps/openai/main.py b/backend/open_webui/apps/openai/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9193c2be650baab00eff51a6a613c817aa510b99
--- /dev/null
+++ b/backend/open_webui/apps/openai/main.py
@@ -0,0 +1,719 @@
+import asyncio
+import hashlib
+import json
+import logging
+from pathlib import Path
+from typing import Literal, Optional, overload
+
+import aiohttp
+from aiocache import cached
+import requests
+
+
+from open_webui.apps.webui.models.models import Models
+from open_webui.config import (
+    CACHE_DIR,
+    CORS_ALLOW_ORIGIN,
+    ENABLE_OPENAI_API,
+    OPENAI_API_BASE_URLS,
+    OPENAI_API_KEYS,
+    OPENAI_API_CONFIGS,
+    AppConfig,
+)
+from open_webui.env import (
+    AIOHTTP_CLIENT_TIMEOUT,
+    AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST,
+    ENABLE_FORWARD_USER_INFO_HEADERS,
+    BYPASS_MODEL_ACCESS_CONTROL,
+)
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import ENV, SRC_LOG_LEVELS
+from fastapi import Depends, FastAPI, HTTPException, Request
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import FileResponse, StreamingResponse
+from pydantic import BaseModel
+from starlette.background import BackgroundTask
+
+from open_webui.utils.payload import (
+    apply_model_params_to_body_openai,
+    apply_model_system_prompt_to_body,
+)
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["OPENAI"])
+
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.state.config = AppConfig()
+
+app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
+app.state.config.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS
+app.state.config.OPENAI_API_KEYS = OPENAI_API_KEYS
+app.state.config.OPENAI_API_CONFIGS = OPENAI_API_CONFIGS
+
+
+@app.get("/config")
+async def get_config(user=Depends(get_admin_user)):
+    return {
+        "ENABLE_OPENAI_API": app.state.config.ENABLE_OPENAI_API,
+        "OPENAI_API_BASE_URLS": app.state.config.OPENAI_API_BASE_URLS,
+        "OPENAI_API_KEYS": app.state.config.OPENAI_API_KEYS,
+        "OPENAI_API_CONFIGS": app.state.config.OPENAI_API_CONFIGS,
+    }
+
+
+class OpenAIConfigForm(BaseModel):
+    ENABLE_OPENAI_API: Optional[bool] = None
+    OPENAI_API_BASE_URLS: list[str]
+    OPENAI_API_KEYS: list[str]
+    OPENAI_API_CONFIGS: dict
+
+
+@app.post("/config/update")
+async def update_config(form_data: OpenAIConfigForm, user=Depends(get_admin_user)):
+    app.state.config.ENABLE_OPENAI_API = form_data.ENABLE_OPENAI_API
+
+    app.state.config.OPENAI_API_BASE_URLS = form_data.OPENAI_API_BASE_URLS
+    app.state.config.OPENAI_API_KEYS = form_data.OPENAI_API_KEYS
+
+    # Check if API KEYS length is same than API URLS length
+    if len(app.state.config.OPENAI_API_KEYS) != len(
+        app.state.config.OPENAI_API_BASE_URLS
+    ):
+        if len(app.state.config.OPENAI_API_KEYS) > len(
+            app.state.config.OPENAI_API_BASE_URLS
+        ):
+            app.state.config.OPENAI_API_KEYS = app.state.config.OPENAI_API_KEYS[
+                : len(app.state.config.OPENAI_API_BASE_URLS)
+            ]
+        else:
+            app.state.config.OPENAI_API_KEYS += [""] * (
+                len(app.state.config.OPENAI_API_BASE_URLS)
+                - len(app.state.config.OPENAI_API_KEYS)
+            )
+
+    app.state.config.OPENAI_API_CONFIGS = form_data.OPENAI_API_CONFIGS
+
+    # Remove any extra configs
+    config_urls = app.state.config.OPENAI_API_CONFIGS.keys()
+    for idx, url in enumerate(app.state.config.OPENAI_API_BASE_URLS):
+        if url not in config_urls:
+            app.state.config.OPENAI_API_CONFIGS.pop(url, None)
+
+    return {
+        "ENABLE_OPENAI_API": app.state.config.ENABLE_OPENAI_API,
+        "OPENAI_API_BASE_URLS": app.state.config.OPENAI_API_BASE_URLS,
+        "OPENAI_API_KEYS": app.state.config.OPENAI_API_KEYS,
+        "OPENAI_API_CONFIGS": app.state.config.OPENAI_API_CONFIGS,
+    }
+
+
+@app.post("/audio/speech")
+async def speech(request: Request, user=Depends(get_verified_user)):
+    idx = None
+    try:
+        idx = app.state.config.OPENAI_API_BASE_URLS.index("https://api.openai.com/v1")
+        body = await request.body()
+        name = hashlib.sha256(body).hexdigest()
+
+        SPEECH_CACHE_DIR = Path(CACHE_DIR).joinpath("./audio/speech/")
+        SPEECH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
+        file_path = SPEECH_CACHE_DIR.joinpath(f"{name}.mp3")
+        file_body_path = SPEECH_CACHE_DIR.joinpath(f"{name}.json")
+
+        # Check if the file already exists in the cache
+        if file_path.is_file():
+            return FileResponse(file_path)
+
+        headers = {}
+        headers["Authorization"] = f"Bearer {app.state.config.OPENAI_API_KEYS[idx]}"
+        headers["Content-Type"] = "application/json"
+        if "openrouter.ai" in app.state.config.OPENAI_API_BASE_URLS[idx]:
+            headers["HTTP-Referer"] = "https://openwebui.com/"
+            headers["X-Title"] = "Open WebUI"
+        if ENABLE_FORWARD_USER_INFO_HEADERS:
+            headers["X-OpenWebUI-User-Name"] = user.name
+            headers["X-OpenWebUI-User-Id"] = user.id
+            headers["X-OpenWebUI-User-Email"] = user.email
+            headers["X-OpenWebUI-User-Role"] = user.role
+        r = None
+        try:
+            r = requests.post(
+                url=f"{app.state.config.OPENAI_API_BASE_URLS[idx]}/audio/speech",
+                data=body,
+                headers=headers,
+                stream=True,
+            )
+
+            r.raise_for_status()
+
+            # Save the streaming content to a file
+            with open(file_path, "wb") as f:
+                for chunk in r.iter_content(chunk_size=8192):
+                    f.write(chunk)
+
+            with open(file_body_path, "w") as f:
+                json.dump(json.loads(body.decode("utf-8")), f)
+
+            # Return the saved file
+            return FileResponse(file_path)
+
+        except Exception as e:
+            log.exception(e)
+            error_detail = "Open WebUI: Server Connection Error"
+            if r is not None:
+                try:
+                    res = r.json()
+                    if "error" in res:
+                        error_detail = f"External: {res['error']}"
+                except Exception:
+                    error_detail = f"External: {e}"
+
+            raise HTTPException(
+                status_code=r.status_code if r else 500, detail=error_detail
+            )
+
+    except ValueError:
+        raise HTTPException(status_code=401, detail=ERROR_MESSAGES.OPENAI_NOT_FOUND)
+
+
+async def aiohttp_get(url, key=None):
+    timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST)
+    try:
+        headers = {"Authorization": f"Bearer {key}"} if key else {}
+        async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
+            async with session.get(url, headers=headers) as response:
+                return await response.json()
+    except Exception as e:
+        # Handle connection error here
+        log.error(f"Connection error: {e}")
+        return None
+
+
+async def cleanup_response(
+    response: Optional[aiohttp.ClientResponse],
+    session: Optional[aiohttp.ClientSession],
+):
+    if response:
+        response.close()
+    if session:
+        await session.close()
+
+
+def merge_models_lists(model_lists):
+    log.debug(f"merge_models_lists {model_lists}")
+    merged_list = []
+
+    for idx, models in enumerate(model_lists):
+        if models is not None and "error" not in models:
+            merged_list.extend(
+                [
+                    {
+                        **model,
+                        "name": model.get("name", model["id"]),
+                        "owned_by": "openai",
+                        "openai": model,
+                        "urlIdx": idx,
+                    }
+                    for model in models
+                    if "api.openai.com"
+                    not in app.state.config.OPENAI_API_BASE_URLS[idx]
+                    or not any(
+                        name in model["id"]
+                        for name in [
+                            "babbage",
+                            "dall-e",
+                            "davinci",
+                            "embedding",
+                            "tts",
+                            "whisper",
+                        ]
+                    )
+                ]
+            )
+
+    return merged_list
+
+
+async def get_all_models_responses() -> list:
+    if not app.state.config.ENABLE_OPENAI_API:
+        return []
+
+    # Check if API KEYS length is same than API URLS length
+    num_urls = len(app.state.config.OPENAI_API_BASE_URLS)
+    num_keys = len(app.state.config.OPENAI_API_KEYS)
+
+    if num_keys != num_urls:
+        # if there are more keys than urls, remove the extra keys
+        if num_keys > num_urls:
+            new_keys = app.state.config.OPENAI_API_KEYS[:num_urls]
+            app.state.config.OPENAI_API_KEYS = new_keys
+        # if there are more urls than keys, add empty keys
+        else:
+            app.state.config.OPENAI_API_KEYS += [""] * (num_urls - num_keys)
+
+    tasks = []
+    for idx, url in enumerate(app.state.config.OPENAI_API_BASE_URLS):
+        if url not in app.state.config.OPENAI_API_CONFIGS:
+            tasks.append(
+                aiohttp_get(f"{url}/models", app.state.config.OPENAI_API_KEYS[idx])
+            )
+        else:
+            api_config = app.state.config.OPENAI_API_CONFIGS.get(url, {})
+
+            enable = api_config.get("enable", True)
+            model_ids = api_config.get("model_ids", [])
+
+            if enable:
+                if len(model_ids) == 0:
+                    tasks.append(
+                        aiohttp_get(
+                            f"{url}/models", app.state.config.OPENAI_API_KEYS[idx]
+                        )
+                    )
+                else:
+                    model_list = {
+                        "object": "list",
+                        "data": [
+                            {
+                                "id": model_id,
+                                "name": model_id,
+                                "owned_by": "openai",
+                                "openai": {"id": model_id},
+                                "urlIdx": idx,
+                            }
+                            for model_id in model_ids
+                        ],
+                    }
+
+                    tasks.append(asyncio.ensure_future(asyncio.sleep(0, model_list)))
+            else:
+                tasks.append(asyncio.ensure_future(asyncio.sleep(0, None)))
+
+    responses = await asyncio.gather(*tasks)
+
+    for idx, response in enumerate(responses):
+        if response:
+            url = app.state.config.OPENAI_API_BASE_URLS[idx]
+            api_config = app.state.config.OPENAI_API_CONFIGS.get(url, {})
+
+            prefix_id = api_config.get("prefix_id", None)
+
+            if prefix_id:
+                for model in (
+                    response if isinstance(response, list) else response.get("data", [])
+                ):
+                    model["id"] = f"{prefix_id}.{model['id']}"
+
+    log.debug(f"get_all_models:responses() {responses}")
+
+    return responses
+
+
+@cached(ttl=3)
+async def get_all_models() -> dict[str, list]:
+    log.info("get_all_models()")
+
+    if not app.state.config.ENABLE_OPENAI_API:
+        return {"data": []}
+
+    responses = await get_all_models_responses()
+
+    def extract_data(response):
+        if response and "data" in response:
+            return response["data"]
+        if isinstance(response, list):
+            return response
+        return None
+
+    models = {"data": merge_models_lists(map(extract_data, responses))}
+    log.debug(f"models: {models}")
+
+    return models
+
+
+@app.get("/models")
+@app.get("/models/{url_idx}")
+async def get_models(url_idx: Optional[int] = None, user=Depends(get_verified_user)):
+    models = {
+        "data": [],
+    }
+
+    if url_idx is None:
+        models = await get_all_models()
+    else:
+        url = app.state.config.OPENAI_API_BASE_URLS[url_idx]
+        key = app.state.config.OPENAI_API_KEYS[url_idx]
+
+        headers = {}
+        headers["Authorization"] = f"Bearer {key}"
+        headers["Content-Type"] = "application/json"
+
+        if ENABLE_FORWARD_USER_INFO_HEADERS:
+            headers["X-OpenWebUI-User-Name"] = user.name
+            headers["X-OpenWebUI-User-Id"] = user.id
+            headers["X-OpenWebUI-User-Email"] = user.email
+            headers["X-OpenWebUI-User-Role"] = user.role
+
+        r = None
+
+        timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST)
+        async with aiohttp.ClientSession(timeout=timeout) as session:
+            try:
+                async with session.get(f"{url}/models", headers=headers) as r:
+                    if r.status != 200:
+                        # Extract response error details if available
+                        error_detail = f"HTTP Error: {r.status}"
+                        res = await r.json()
+                        if "error" in res:
+                            error_detail = f"External Error: {res['error']}"
+                        raise Exception(error_detail)
+
+                    response_data = await r.json()
+
+                    # Check if we're calling OpenAI API based on the URL
+                    if "api.openai.com" in url:
+                        # Filter models according to the specified conditions
+                        response_data["data"] = [
+                            model
+                            for model in response_data.get("data", [])
+                            if not any(
+                                name in model["id"]
+                                for name in [
+                                    "babbage",
+                                    "dall-e",
+                                    "davinci",
+                                    "embedding",
+                                    "tts",
+                                    "whisper",
+                                ]
+                            )
+                        ]
+
+                    models = response_data
+            except aiohttp.ClientError as e:
+                # ClientError covers all aiohttp requests issues
+                log.exception(f"Client error: {str(e)}")
+                # Handle aiohttp-specific connection issues, timeout etc.
+                raise HTTPException(
+                    status_code=500, detail="Open WebUI: Server Connection Error"
+                )
+            except Exception as e:
+                log.exception(f"Unexpected error: {e}")
+                # Generic error handler in case parsing JSON or other steps fail
+                error_detail = f"Unexpected error: {str(e)}"
+                raise HTTPException(status_code=500, detail=error_detail)
+
+    if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+        # Filter models based on user access control
+        filtered_models = []
+        for model in models.get("data", []):
+            model_info = Models.get_model_by_id(model["id"])
+            if model_info:
+                if user.id == model_info.user_id or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                ):
+                    filtered_models.append(model)
+        models["data"] = filtered_models
+
+    return models
+
+
+class ConnectionVerificationForm(BaseModel):
+    url: str
+    key: str
+
+
+@app.post("/verify")
+async def verify_connection(
+    form_data: ConnectionVerificationForm, user=Depends(get_admin_user)
+):
+    url = form_data.url
+    key = form_data.key
+
+    headers = {}
+    headers["Authorization"] = f"Bearer {key}"
+    headers["Content-Type"] = "application/json"
+
+    timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST)
+    async with aiohttp.ClientSession(timeout=timeout) as session:
+        try:
+            async with session.get(f"{url}/models", headers=headers) as r:
+                if r.status != 200:
+                    # Extract response error details if available
+                    error_detail = f"HTTP Error: {r.status}"
+                    res = await r.json()
+                    if "error" in res:
+                        error_detail = f"External Error: {res['error']}"
+                    raise Exception(error_detail)
+
+                response_data = await r.json()
+                return response_data
+
+        except aiohttp.ClientError as e:
+            # ClientError covers all aiohttp requests issues
+            log.exception(f"Client error: {str(e)}")
+            # Handle aiohttp-specific connection issues, timeout etc.
+            raise HTTPException(
+                status_code=500, detail="Open WebUI: Server Connection Error"
+            )
+        except Exception as e:
+            log.exception(f"Unexpected error: {e}")
+            # Generic error handler in case parsing JSON or other steps fail
+            error_detail = f"Unexpected error: {str(e)}"
+            raise HTTPException(status_code=500, detail=error_detail)
+
+
+@app.post("/chat/completions")
+async def generate_chat_completion(
+    form_data: dict,
+    user=Depends(get_verified_user),
+    bypass_filter: Optional[bool] = False,
+):
+    idx = 0
+    payload = {**form_data}
+
+    if "metadata" in payload:
+        del payload["metadata"]
+
+    model_id = form_data.get("model")
+    model_info = Models.get_model_by_id(model_id)
+
+    # Check model info and override the payload
+    if model_info:
+        if model_info.base_model_id:
+            payload["model"] = model_info.base_model_id
+
+        params = model_info.params.model_dump()
+        payload = apply_model_params_to_body_openai(params, payload)
+        payload = apply_model_system_prompt_to_body(params, payload, user)
+
+        # Check if user has access to the model
+        if not bypass_filter and user.role == "user":
+            if not (
+                user.id == model_info.user_id
+                or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                )
+            ):
+                raise HTTPException(
+                    status_code=403,
+                    detail="Model not found",
+                )
+    elif not bypass_filter:
+        if user.role != "admin":
+            raise HTTPException(
+                status_code=403,
+                detail="Model not found",
+            )
+
+    # Attemp to get urlIdx from the model
+    models = await get_all_models()
+
+    # Find the model from the list
+    model = next(
+        (model for model in models["data"] if model["id"] == payload.get("model")),
+        None,
+    )
+
+    if model:
+        idx = model["urlIdx"]
+    else:
+        raise HTTPException(
+            status_code=404,
+            detail="Model not found",
+        )
+
+    # Get the API config for the model
+    api_config = app.state.config.OPENAI_API_CONFIGS.get(
+        app.state.config.OPENAI_API_BASE_URLS[idx], {}
+    )
+    prefix_id = api_config.get("prefix_id", None)
+
+    if prefix_id:
+        payload["model"] = payload["model"].replace(f"{prefix_id}.", "")
+
+    # Add user info to the payload if the model is a pipeline
+    if "pipeline" in model and model.get("pipeline"):
+        payload["user"] = {
+            "name": user.name,
+            "id": user.id,
+            "email": user.email,
+            "role": user.role,
+        }
+
+    url = app.state.config.OPENAI_API_BASE_URLS[idx]
+    key = app.state.config.OPENAI_API_KEYS[idx]
+
+    # Fix: O1 does not support the "max_tokens" parameter, Modify "max_tokens" to "max_completion_tokens"
+    is_o1 = payload["model"].lower().startswith("o1-")
+    # Change max_completion_tokens to max_tokens (Backward compatible)
+    if "api.openai.com" not in url and not is_o1:
+        if "max_completion_tokens" in payload:
+            # Remove "max_completion_tokens" from the payload
+            payload["max_tokens"] = payload["max_completion_tokens"]
+            del payload["max_completion_tokens"]
+    else:
+        if is_o1 and "max_tokens" in payload:
+            payload["max_completion_tokens"] = payload["max_tokens"]
+            del payload["max_tokens"]
+        if "max_tokens" in payload and "max_completion_tokens" in payload:
+            del payload["max_tokens"]
+
+    # Fix: O1 does not support the "system" parameter, Modify "system" to "user"
+    if is_o1 and payload["messages"][0]["role"] == "system":
+        payload["messages"][0]["role"] = "user"
+
+    # Convert the modified body back to JSON
+    payload = json.dumps(payload)
+
+    headers = {}
+    headers["Authorization"] = f"Bearer {key}"
+    headers["Content-Type"] = "application/json"
+    if "openrouter.ai" in app.state.config.OPENAI_API_BASE_URLS[idx]:
+        headers["HTTP-Referer"] = "https://openwebui.com/"
+        headers["X-Title"] = "Open WebUI"
+    if ENABLE_FORWARD_USER_INFO_HEADERS:
+        headers["X-OpenWebUI-User-Name"] = user.name
+        headers["X-OpenWebUI-User-Id"] = user.id
+        headers["X-OpenWebUI-User-Email"] = user.email
+        headers["X-OpenWebUI-User-Role"] = user.role
+
+    r = None
+    session = None
+    streaming = False
+    response = None
+
+    try:
+        session = aiohttp.ClientSession(
+            trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT)
+        )
+        r = await session.request(
+            method="POST",
+            url=f"{url}/chat/completions",
+            data=payload,
+            headers=headers,
+        )
+
+        # Check if response is SSE
+        if "text/event-stream" in r.headers.get("Content-Type", ""):
+            streaming = True
+            return StreamingResponse(
+                r.content,
+                status_code=r.status,
+                headers=dict(r.headers),
+                background=BackgroundTask(
+                    cleanup_response, response=r, session=session
+                ),
+            )
+        else:
+            try:
+                response = await r.json()
+            except Exception as e:
+                log.error(e)
+                response = await r.text()
+
+            r.raise_for_status()
+            return response
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if isinstance(response, dict):
+            if "error" in response:
+                error_detail = f"{response['error']['message'] if 'message' in response['error'] else response['error']}"
+        elif isinstance(response, str):
+            error_detail = response
+
+        raise HTTPException(status_code=r.status if r else 500, detail=error_detail)
+    finally:
+        if not streaming and session:
+            if r:
+                r.close()
+            await session.close()
+
+
+@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
+async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
+    idx = 0
+
+    body = await request.body()
+
+    url = app.state.config.OPENAI_API_BASE_URLS[idx]
+    key = app.state.config.OPENAI_API_KEYS[idx]
+
+    target_url = f"{url}/{path}"
+
+    headers = {}
+    headers["Authorization"] = f"Bearer {key}"
+    headers["Content-Type"] = "application/json"
+    if ENABLE_FORWARD_USER_INFO_HEADERS:
+        headers["X-OpenWebUI-User-Name"] = user.name
+        headers["X-OpenWebUI-User-Id"] = user.id
+        headers["X-OpenWebUI-User-Email"] = user.email
+        headers["X-OpenWebUI-User-Role"] = user.role
+
+    r = None
+    session = None
+    streaming = False
+
+    try:
+        session = aiohttp.ClientSession(trust_env=True)
+        r = await session.request(
+            method=request.method,
+            url=target_url,
+            data=body,
+            headers=headers,
+        )
+
+        r.raise_for_status()
+
+        # Check if response is SSE
+        if "text/event-stream" in r.headers.get("Content-Type", ""):
+            streaming = True
+            return StreamingResponse(
+                r.content,
+                status_code=r.status,
+                headers=dict(r.headers),
+                background=BackgroundTask(
+                    cleanup_response, response=r, session=session
+                ),
+            )
+        else:
+            response_data = await r.json()
+            return response_data
+    except Exception as e:
+        log.exception(e)
+        error_detail = "Open WebUI: Server Connection Error"
+        if r is not None:
+            try:
+                res = await r.json()
+                print(res)
+                if "error" in res:
+                    error_detail = f"External: {res['error']['message'] if 'message' in res['error'] else res['error']}"
+            except Exception:
+                error_detail = f"External: {e}"
+        raise HTTPException(status_code=r.status if r else 500, detail=error_detail)
+    finally:
+        if not streaming and session:
+            if r:
+                r.close()
+            await session.close()
diff --git a/backend/open_webui/apps/retrieval/loaders/main.py b/backend/open_webui/apps/retrieval/loaders/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..36f03cbb27bca17c4e8797df329a3901c3b444a5
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/loaders/main.py
@@ -0,0 +1,190 @@
+import requests
+import logging
+import ftfy
+
+from langchain_community.document_loaders import (
+    BSHTMLLoader,
+    CSVLoader,
+    Docx2txtLoader,
+    OutlookMessageLoader,
+    PyPDFLoader,
+    TextLoader,
+    UnstructuredEPubLoader,
+    UnstructuredExcelLoader,
+    UnstructuredMarkdownLoader,
+    UnstructuredPowerPointLoader,
+    UnstructuredRSTLoader,
+    UnstructuredXMLLoader,
+    YoutubeLoader,
+)
+from langchain_core.documents import Document
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+known_source_ext = [
+    "go",
+    "py",
+    "java",
+    "sh",
+    "bat",
+    "ps1",
+    "cmd",
+    "js",
+    "ts",
+    "css",
+    "cpp",
+    "hpp",
+    "h",
+    "c",
+    "cs",
+    "sql",
+    "log",
+    "ini",
+    "pl",
+    "pm",
+    "r",
+    "dart",
+    "dockerfile",
+    "env",
+    "php",
+    "hs",
+    "hsc",
+    "lua",
+    "nginxconf",
+    "conf",
+    "m",
+    "mm",
+    "plsql",
+    "perl",
+    "rb",
+    "rs",
+    "db2",
+    "scala",
+    "bash",
+    "swift",
+    "vue",
+    "svelte",
+    "msg",
+    "ex",
+    "exs",
+    "erl",
+    "tsx",
+    "jsx",
+    "hs",
+    "lhs",
+]
+
+
+class TikaLoader:
+    def __init__(self, url, file_path, mime_type=None):
+        self.url = url
+        self.file_path = file_path
+        self.mime_type = mime_type
+
+    def load(self) -> list[Document]:
+        with open(self.file_path, "rb") as f:
+            data = f.read()
+
+        if self.mime_type is not None:
+            headers = {"Content-Type": self.mime_type}
+        else:
+            headers = {}
+
+        endpoint = self.url
+        if not endpoint.endswith("/"):
+            endpoint += "/"
+        endpoint += "tika/text"
+
+        r = requests.put(endpoint, data=data, headers=headers)
+
+        if r.ok:
+            raw_metadata = r.json()
+            text = raw_metadata.get("X-TIKA:content", "<No text content found>")
+
+            if "Content-Type" in raw_metadata:
+                headers["Content-Type"] = raw_metadata["Content-Type"]
+
+            log.info("Tika extracted text: %s", text)
+
+            return [Document(page_content=text, metadata=headers)]
+        else:
+            raise Exception(f"Error calling Tika: {r.reason}")
+
+
+class Loader:
+    def __init__(self, engine: str = "", **kwargs):
+        self.engine = engine
+        self.kwargs = kwargs
+
+    def load(
+        self, filename: str, file_content_type: str, file_path: str
+    ) -> list[Document]:
+        loader = self._get_loader(filename, file_content_type, file_path)
+        docs = loader.load()
+
+        return [
+            Document(
+                page_content=ftfy.fix_text(doc.page_content), metadata=doc.metadata
+            )
+            for doc in docs
+        ]
+
+    def _get_loader(self, filename: str, file_content_type: str, file_path: str):
+        file_ext = filename.split(".")[-1].lower()
+
+        if self.engine == "tika" and self.kwargs.get("TIKA_SERVER_URL"):
+            if file_ext in known_source_ext or (
+                file_content_type and file_content_type.find("text/") >= 0
+            ):
+                loader = TextLoader(file_path, autodetect_encoding=True)
+            else:
+                loader = TikaLoader(
+                    url=self.kwargs.get("TIKA_SERVER_URL"),
+                    file_path=file_path,
+                    mime_type=file_content_type,
+                )
+        else:
+            if file_ext == "pdf":
+                loader = PyPDFLoader(
+                    file_path, extract_images=self.kwargs.get("PDF_EXTRACT_IMAGES")
+                )
+            elif file_ext == "csv":
+                loader = CSVLoader(file_path)
+            elif file_ext == "rst":
+                loader = UnstructuredRSTLoader(file_path, mode="elements")
+            elif file_ext == "xml":
+                loader = UnstructuredXMLLoader(file_path)
+            elif file_ext in ["htm", "html"]:
+                loader = BSHTMLLoader(file_path, open_encoding="unicode_escape")
+            elif file_ext == "md":
+                loader = TextLoader(file_path, autodetect_encoding=True)
+            elif file_content_type == "application/epub+zip":
+                loader = UnstructuredEPubLoader(file_path)
+            elif (
+                file_content_type
+                == "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
+                or file_ext == "docx"
+            ):
+                loader = Docx2txtLoader(file_path)
+            elif file_content_type in [
+                "application/vnd.ms-excel",
+                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+            ] or file_ext in ["xls", "xlsx"]:
+                loader = UnstructuredExcelLoader(file_path)
+            elif file_content_type in [
+                "application/vnd.ms-powerpoint",
+                "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+            ] or file_ext in ["ppt", "pptx"]:
+                loader = UnstructuredPowerPointLoader(file_path)
+            elif file_ext == "msg":
+                loader = OutlookMessageLoader(file_path)
+            elif file_ext in known_source_ext or (
+                file_content_type and file_content_type.find("text/") >= 0
+            ):
+                loader = TextLoader(file_path, autodetect_encoding=True)
+            else:
+                loader = TextLoader(file_path, autodetect_encoding=True)
+
+        return loader
diff --git a/backend/open_webui/apps/retrieval/loaders/youtube.py b/backend/open_webui/apps/retrieval/loaders/youtube.py
new file mode 100644
index 0000000000000000000000000000000000000000..8eb48488b24abed09819babba5f205e5dbf352df
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/loaders/youtube.py
@@ -0,0 +1,117 @@
+import logging
+
+from typing import Any, Dict, Generator, List, Optional, Sequence, Union
+from urllib.parse import parse_qs, urlparse
+from langchain_core.documents import Document
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+ALLOWED_SCHEMES = {"http", "https"}
+ALLOWED_NETLOCS = {
+    "youtu.be",
+    "m.youtube.com",
+    "youtube.com",
+    "www.youtube.com",
+    "www.youtube-nocookie.com",
+    "vid.plus",
+}
+
+
+def _parse_video_id(url: str) -> Optional[str]:
+    """Parse a YouTube URL and return the video ID if valid, otherwise None."""
+    parsed_url = urlparse(url)
+
+    if parsed_url.scheme not in ALLOWED_SCHEMES:
+        return None
+
+    if parsed_url.netloc not in ALLOWED_NETLOCS:
+        return None
+
+    path = parsed_url.path
+
+    if path.endswith("/watch"):
+        query = parsed_url.query
+        parsed_query = parse_qs(query)
+        if "v" in parsed_query:
+            ids = parsed_query["v"]
+            video_id = ids if isinstance(ids, str) else ids[0]
+        else:
+            return None
+    else:
+        path = parsed_url.path.lstrip("/")
+        video_id = path.split("/")[-1]
+
+    if len(video_id) != 11:  # Video IDs are 11 characters long
+        return None
+
+    return video_id
+
+
+class YoutubeLoader:
+    """Load `YouTube` video transcripts."""
+
+    def __init__(
+        self,
+        video_id: str,
+        language: Union[str, Sequence[str]] = "en",
+        proxy_url: Optional[str] = None,
+    ):
+        """Initialize with YouTube video ID."""
+        _video_id = _parse_video_id(video_id)
+        self.video_id = _video_id if _video_id is not None else video_id
+        self._metadata = {"source": video_id}
+        self.language = language
+        self.proxy_url = proxy_url
+        if isinstance(language, str):
+            self.language = [language]
+        else:
+            self.language = language
+
+    def load(self) -> List[Document]:
+        """Load YouTube transcripts into `Document` objects."""
+        try:
+            from youtube_transcript_api import (
+                NoTranscriptFound,
+                TranscriptsDisabled,
+                YouTubeTranscriptApi,
+            )
+        except ImportError:
+            raise ImportError(
+                'Could not import "youtube_transcript_api" Python package. '
+                "Please install it with `pip install youtube-transcript-api`."
+            )
+
+        if self.proxy_url:
+            youtube_proxies = {
+                "http": self.proxy_url,
+                "https": self.proxy_url,
+            }
+            # Don't log complete URL because it might contain secrets
+            log.debug(f"Using proxy URL: {self.proxy_url[:14]}...")
+        else:
+            youtube_proxies = None
+
+        try:
+            transcript_list = YouTubeTranscriptApi.list_transcripts(
+                self.video_id, proxies=youtube_proxies
+            )
+        except Exception as e:
+            log.exception("Loading YouTube transcript failed")
+            return []
+
+        try:
+            transcript = transcript_list.find_transcript(self.language)
+        except NoTranscriptFound:
+            transcript = transcript_list.find_transcript(["en"])
+
+        transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
+
+        transcript = " ".join(
+            map(
+                lambda transcript_piece: transcript_piece["text"].strip(" "),
+                transcript_pieces,
+            )
+        )
+        return [Document(page_content=transcript, metadata=self._metadata)]
diff --git a/backend/open_webui/apps/retrieval/main.py b/backend/open_webui/apps/retrieval/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..341f4f500b2fcf03f5d9c8ca98d0778caab9af84
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/main.py
@@ -0,0 +1,1494 @@
+# TODO: Merge this with the webui_app and make it a single app
+
+import json
+import logging
+import mimetypes
+import os
+import shutil
+
+import uuid
+from datetime import datetime
+from pathlib import Path
+from typing import Iterator, Optional, Sequence, Union
+
+from fastapi import Depends, FastAPI, File, Form, HTTPException, UploadFile, status
+from fastapi.middleware.cors import CORSMiddleware
+from pydantic import BaseModel
+import tiktoken
+
+
+from open_webui.storage.provider import Storage
+from open_webui.apps.webui.models.knowledge import Knowledges
+from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
+
+# Document loaders
+from open_webui.apps.retrieval.loaders.main import Loader
+from open_webui.apps.retrieval.loaders.youtube import YoutubeLoader
+
+# Web search engines
+from open_webui.apps.retrieval.web.main import SearchResult
+from open_webui.apps.retrieval.web.utils import get_web_loader
+from open_webui.apps.retrieval.web.brave import search_brave
+from open_webui.apps.retrieval.web.mojeek import search_mojeek
+from open_webui.apps.retrieval.web.duckduckgo import search_duckduckgo
+from open_webui.apps.retrieval.web.google_pse import search_google_pse
+from open_webui.apps.retrieval.web.jina_search import search_jina
+from open_webui.apps.retrieval.web.searchapi import search_searchapi
+from open_webui.apps.retrieval.web.searxng import search_searxng
+from open_webui.apps.retrieval.web.serper import search_serper
+from open_webui.apps.retrieval.web.serply import search_serply
+from open_webui.apps.retrieval.web.serpstack import search_serpstack
+from open_webui.apps.retrieval.web.tavily import search_tavily
+from open_webui.apps.retrieval.web.bing import search_bing
+
+
+from open_webui.apps.retrieval.utils import (
+    get_embedding_function,
+    get_model_path,
+    query_collection,
+    query_collection_with_hybrid_search,
+    query_doc,
+    query_doc_with_hybrid_search,
+)
+
+from open_webui.apps.webui.models.files import Files
+from open_webui.config import (
+    BRAVE_SEARCH_API_KEY,
+    MOJEEK_SEARCH_API_KEY,
+    TIKTOKEN_ENCODING_NAME,
+    RAG_TEXT_SPLITTER,
+    CHUNK_OVERLAP,
+    CHUNK_SIZE,
+    CONTENT_EXTRACTION_ENGINE,
+    CORS_ALLOW_ORIGIN,
+    ENABLE_RAG_HYBRID_SEARCH,
+    ENABLE_RAG_LOCAL_WEB_FETCH,
+    ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
+    ENABLE_RAG_WEB_SEARCH,
+    ENV,
+    GOOGLE_PSE_API_KEY,
+    GOOGLE_PSE_ENGINE_ID,
+    PDF_EXTRACT_IMAGES,
+    RAG_EMBEDDING_ENGINE,
+    RAG_EMBEDDING_MODEL,
+    RAG_EMBEDDING_MODEL_AUTO_UPDATE,
+    RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
+    RAG_EMBEDDING_BATCH_SIZE,
+    RAG_FILE_MAX_COUNT,
+    RAG_FILE_MAX_SIZE,
+    RAG_OPENAI_API_BASE_URL,
+    RAG_OPENAI_API_KEY,
+    RAG_OLLAMA_BASE_URL,
+    RAG_OLLAMA_API_KEY,
+    RAG_RELEVANCE_THRESHOLD,
+    RAG_RERANKING_MODEL,
+    RAG_RERANKING_MODEL_AUTO_UPDATE,
+    RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
+    DEFAULT_RAG_TEMPLATE,
+    RAG_TEMPLATE,
+    RAG_TOP_K,
+    RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
+    RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+    RAG_WEB_SEARCH_ENGINE,
+    RAG_WEB_SEARCH_RESULT_COUNT,
+    JINA_API_KEY,
+    SEARCHAPI_API_KEY,
+    SEARCHAPI_ENGINE,
+    SEARXNG_QUERY_URL,
+    SERPER_API_KEY,
+    SERPLY_API_KEY,
+    SERPSTACK_API_KEY,
+    SERPSTACK_HTTPS,
+    TAVILY_API_KEY,
+    BING_SEARCH_V7_ENDPOINT,
+    BING_SEARCH_V7_SUBSCRIPTION_KEY,
+    TIKA_SERVER_URL,
+    UPLOAD_DIR,
+    YOUTUBE_LOADER_LANGUAGE,
+    YOUTUBE_LOADER_PROXY_URL,
+    DEFAULT_LOCALE,
+    AppConfig,
+)
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import (
+    SRC_LOG_LEVELS,
+    DEVICE_TYPE,
+    DOCKER,
+)
+from open_webui.utils.misc import (
+    calculate_sha256,
+    calculate_sha256_string,
+    extract_folders_after_data_docs,
+    sanitize_filename,
+)
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+from langchain.text_splitter import RecursiveCharacterTextSplitter, TokenTextSplitter
+from langchain_core.documents import Document
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+app.state.config = AppConfig()
+
+app.state.config.TOP_K = RAG_TOP_K
+app.state.config.RELEVANCE_THRESHOLD = RAG_RELEVANCE_THRESHOLD
+app.state.config.FILE_MAX_SIZE = RAG_FILE_MAX_SIZE
+app.state.config.FILE_MAX_COUNT = RAG_FILE_MAX_COUNT
+
+app.state.config.ENABLE_RAG_HYBRID_SEARCH = ENABLE_RAG_HYBRID_SEARCH
+app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = (
+    ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION
+)
+
+app.state.config.CONTENT_EXTRACTION_ENGINE = CONTENT_EXTRACTION_ENGINE
+app.state.config.TIKA_SERVER_URL = TIKA_SERVER_URL
+
+app.state.config.TEXT_SPLITTER = RAG_TEXT_SPLITTER
+app.state.config.TIKTOKEN_ENCODING_NAME = TIKTOKEN_ENCODING_NAME
+
+app.state.config.CHUNK_SIZE = CHUNK_SIZE
+app.state.config.CHUNK_OVERLAP = CHUNK_OVERLAP
+
+app.state.config.RAG_EMBEDDING_ENGINE = RAG_EMBEDDING_ENGINE
+app.state.config.RAG_EMBEDDING_MODEL = RAG_EMBEDDING_MODEL
+app.state.config.RAG_EMBEDDING_BATCH_SIZE = RAG_EMBEDDING_BATCH_SIZE
+app.state.config.RAG_RERANKING_MODEL = RAG_RERANKING_MODEL
+app.state.config.RAG_TEMPLATE = RAG_TEMPLATE
+
+app.state.config.OPENAI_API_BASE_URL = RAG_OPENAI_API_BASE_URL
+app.state.config.OPENAI_API_KEY = RAG_OPENAI_API_KEY
+
+app.state.config.OLLAMA_BASE_URL = RAG_OLLAMA_BASE_URL
+app.state.config.OLLAMA_API_KEY = RAG_OLLAMA_API_KEY
+
+app.state.config.PDF_EXTRACT_IMAGES = PDF_EXTRACT_IMAGES
+
+app.state.config.YOUTUBE_LOADER_LANGUAGE = YOUTUBE_LOADER_LANGUAGE
+app.state.config.YOUTUBE_LOADER_PROXY_URL = YOUTUBE_LOADER_PROXY_URL
+app.state.YOUTUBE_LOADER_TRANSLATION = None
+
+
+app.state.config.ENABLE_RAG_WEB_SEARCH = ENABLE_RAG_WEB_SEARCH
+app.state.config.RAG_WEB_SEARCH_ENGINE = RAG_WEB_SEARCH_ENGINE
+app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = RAG_WEB_SEARCH_DOMAIN_FILTER_LIST
+
+app.state.config.SEARXNG_QUERY_URL = SEARXNG_QUERY_URL
+app.state.config.GOOGLE_PSE_API_KEY = GOOGLE_PSE_API_KEY
+app.state.config.GOOGLE_PSE_ENGINE_ID = GOOGLE_PSE_ENGINE_ID
+app.state.config.BRAVE_SEARCH_API_KEY = BRAVE_SEARCH_API_KEY
+app.state.config.MOJEEK_SEARCH_API_KEY = MOJEEK_SEARCH_API_KEY
+app.state.config.SERPSTACK_API_KEY = SERPSTACK_API_KEY
+app.state.config.SERPSTACK_HTTPS = SERPSTACK_HTTPS
+app.state.config.SERPER_API_KEY = SERPER_API_KEY
+app.state.config.SERPLY_API_KEY = SERPLY_API_KEY
+app.state.config.TAVILY_API_KEY = TAVILY_API_KEY
+app.state.config.SEARCHAPI_API_KEY = SEARCHAPI_API_KEY
+app.state.config.SEARCHAPI_ENGINE = SEARCHAPI_ENGINE
+app.state.config.JINA_API_KEY = JINA_API_KEY
+app.state.config.BING_SEARCH_V7_ENDPOINT = BING_SEARCH_V7_ENDPOINT
+app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY = BING_SEARCH_V7_SUBSCRIPTION_KEY
+
+app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = RAG_WEB_SEARCH_RESULT_COUNT
+app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS = RAG_WEB_SEARCH_CONCURRENT_REQUESTS
+
+
+def update_embedding_model(
+    embedding_model: str,
+    auto_update: bool = False,
+):
+    if embedding_model and app.state.config.RAG_EMBEDDING_ENGINE == "":
+        from sentence_transformers import SentenceTransformer
+
+        try:
+            app.state.sentence_transformer_ef = SentenceTransformer(
+                get_model_path(embedding_model, auto_update),
+                device=DEVICE_TYPE,
+                trust_remote_code=RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE,
+            )
+        except Exception as e:
+            log.debug(f"Error loading SentenceTransformer: {e}")
+            app.state.sentence_transformer_ef = None
+    else:
+        app.state.sentence_transformer_ef = None
+
+
+def update_reranking_model(
+    reranking_model: str,
+    auto_update: bool = False,
+):
+    if reranking_model:
+        if any(model in reranking_model for model in ["jinaai/jina-colbert-v2"]):
+            try:
+                from open_webui.apps.retrieval.models.colbert import ColBERT
+
+                app.state.sentence_transformer_rf = ColBERT(
+                    get_model_path(reranking_model, auto_update),
+                    env="docker" if DOCKER else None,
+                )
+            except Exception as e:
+                log.error(f"ColBERT: {e}")
+                app.state.sentence_transformer_rf = None
+                app.state.config.ENABLE_RAG_HYBRID_SEARCH = False
+        else:
+            import sentence_transformers
+
+            try:
+                app.state.sentence_transformer_rf = sentence_transformers.CrossEncoder(
+                    get_model_path(reranking_model, auto_update),
+                    device=DEVICE_TYPE,
+                    trust_remote_code=RAG_RERANKING_MODEL_TRUST_REMOTE_CODE,
+                )
+            except:
+                log.error("CrossEncoder error")
+                app.state.sentence_transformer_rf = None
+                app.state.config.ENABLE_RAG_HYBRID_SEARCH = False
+    else:
+        app.state.sentence_transformer_rf = None
+
+
+update_embedding_model(
+    app.state.config.RAG_EMBEDDING_MODEL,
+    RAG_EMBEDDING_MODEL_AUTO_UPDATE,
+)
+
+update_reranking_model(
+    app.state.config.RAG_RERANKING_MODEL,
+    RAG_RERANKING_MODEL_AUTO_UPDATE,
+)
+
+
+app.state.EMBEDDING_FUNCTION = get_embedding_function(
+    app.state.config.RAG_EMBEDDING_ENGINE,
+    app.state.config.RAG_EMBEDDING_MODEL,
+    app.state.sentence_transformer_ef,
+    (
+        app.state.config.OPENAI_API_BASE_URL
+        if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+        else app.state.config.OLLAMA_BASE_URL
+    ),
+    (
+        app.state.config.OPENAI_API_KEY
+        if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+        else app.state.config.OLLAMA_API_KEY
+    ),
+    app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+)
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+
+class CollectionNameForm(BaseModel):
+    collection_name: Optional[str] = None
+
+
+class ProcessUrlForm(CollectionNameForm):
+    url: str
+
+
+class SearchForm(CollectionNameForm):
+    query: str
+
+
+@app.get("/")
+async def get_status():
+    return {
+        "status": True,
+        "chunk_size": app.state.config.CHUNK_SIZE,
+        "chunk_overlap": app.state.config.CHUNK_OVERLAP,
+        "template": app.state.config.RAG_TEMPLATE,
+        "embedding_engine": app.state.config.RAG_EMBEDDING_ENGINE,
+        "embedding_model": app.state.config.RAG_EMBEDDING_MODEL,
+        "reranking_model": app.state.config.RAG_RERANKING_MODEL,
+        "embedding_batch_size": app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+    }
+
+
+@app.get("/embedding")
+async def get_embedding_config(user=Depends(get_admin_user)):
+    return {
+        "status": True,
+        "embedding_engine": app.state.config.RAG_EMBEDDING_ENGINE,
+        "embedding_model": app.state.config.RAG_EMBEDDING_MODEL,
+        "embedding_batch_size": app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+        "openai_config": {
+            "url": app.state.config.OPENAI_API_BASE_URL,
+            "key": app.state.config.OPENAI_API_KEY,
+        },
+        "ollama_config": {
+            "url": app.state.config.OLLAMA_BASE_URL,
+            "key": app.state.config.OLLAMA_API_KEY,
+        },
+    }
+
+
+@app.get("/reranking")
+async def get_reraanking_config(user=Depends(get_admin_user)):
+    return {
+        "status": True,
+        "reranking_model": app.state.config.RAG_RERANKING_MODEL,
+    }
+
+
+class OpenAIConfigForm(BaseModel):
+    url: str
+    key: str
+
+
+class OllamaConfigForm(BaseModel):
+    url: str
+    key: str
+
+
+class EmbeddingModelUpdateForm(BaseModel):
+    openai_config: Optional[OpenAIConfigForm] = None
+    ollama_config: Optional[OllamaConfigForm] = None
+    embedding_engine: str
+    embedding_model: str
+    embedding_batch_size: Optional[int] = 1
+
+
+@app.post("/embedding/update")
+async def update_embedding_config(
+    form_data: EmbeddingModelUpdateForm, user=Depends(get_admin_user)
+):
+    log.info(
+        f"Updating embedding model: {app.state.config.RAG_EMBEDDING_MODEL} to {form_data.embedding_model}"
+    )
+    try:
+        app.state.config.RAG_EMBEDDING_ENGINE = form_data.embedding_engine
+        app.state.config.RAG_EMBEDDING_MODEL = form_data.embedding_model
+
+        if app.state.config.RAG_EMBEDDING_ENGINE in ["ollama", "openai"]:
+            if form_data.openai_config is not None:
+                app.state.config.OPENAI_API_BASE_URL = form_data.openai_config.url
+                app.state.config.OPENAI_API_KEY = form_data.openai_config.key
+
+            if form_data.ollama_config is not None:
+                app.state.config.OLLAMA_BASE_URL = form_data.ollama_config.url
+                app.state.config.OLLAMA_API_KEY = form_data.ollama_config.key
+
+            app.state.config.RAG_EMBEDDING_BATCH_SIZE = form_data.embedding_batch_size
+
+        update_embedding_model(app.state.config.RAG_EMBEDDING_MODEL)
+
+        app.state.EMBEDDING_FUNCTION = get_embedding_function(
+            app.state.config.RAG_EMBEDDING_ENGINE,
+            app.state.config.RAG_EMBEDDING_MODEL,
+            app.state.sentence_transformer_ef,
+            (
+                app.state.config.OPENAI_API_BASE_URL
+                if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+                else app.state.config.OLLAMA_BASE_URL
+            ),
+            (
+                app.state.config.OPENAI_API_KEY
+                if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+                else app.state.config.OLLAMA_API_KEY
+            ),
+            app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+        )
+
+        return {
+            "status": True,
+            "embedding_engine": app.state.config.RAG_EMBEDDING_ENGINE,
+            "embedding_model": app.state.config.RAG_EMBEDDING_MODEL,
+            "embedding_batch_size": app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+            "openai_config": {
+                "url": app.state.config.OPENAI_API_BASE_URL,
+                "key": app.state.config.OPENAI_API_KEY,
+            },
+            "ollama_config": {
+                "url": app.state.config.OLLAMA_BASE_URL,
+                "key": app.state.config.OLLAMA_API_KEY,
+            },
+        }
+    except Exception as e:
+        log.exception(f"Problem updating embedding model: {e}")
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+class RerankingModelUpdateForm(BaseModel):
+    reranking_model: str
+
+
+@app.post("/reranking/update")
+async def update_reranking_config(
+    form_data: RerankingModelUpdateForm, user=Depends(get_admin_user)
+):
+    log.info(
+        f"Updating reranking model: {app.state.config.RAG_RERANKING_MODEL} to {form_data.reranking_model}"
+    )
+    try:
+        app.state.config.RAG_RERANKING_MODEL = form_data.reranking_model
+
+        update_reranking_model(app.state.config.RAG_RERANKING_MODEL, True)
+
+        return {
+            "status": True,
+            "reranking_model": app.state.config.RAG_RERANKING_MODEL,
+        }
+    except Exception as e:
+        log.exception(f"Problem updating reranking model: {e}")
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+@app.get("/config")
+async def get_rag_config(user=Depends(get_admin_user)):
+    return {
+        "status": True,
+        "pdf_extract_images": app.state.config.PDF_EXTRACT_IMAGES,
+        "content_extraction": {
+            "engine": app.state.config.CONTENT_EXTRACTION_ENGINE,
+            "tika_server_url": app.state.config.TIKA_SERVER_URL,
+        },
+        "chunk": {
+            "text_splitter": app.state.config.TEXT_SPLITTER,
+            "chunk_size": app.state.config.CHUNK_SIZE,
+            "chunk_overlap": app.state.config.CHUNK_OVERLAP,
+        },
+        "file": {
+            "max_size": app.state.config.FILE_MAX_SIZE,
+            "max_count": app.state.config.FILE_MAX_COUNT,
+        },
+        "youtube": {
+            "language": app.state.config.YOUTUBE_LOADER_LANGUAGE,
+            "translation": app.state.YOUTUBE_LOADER_TRANSLATION,
+            "proxy_url": app.state.config.YOUTUBE_LOADER_PROXY_URL,
+        },
+        "web": {
+            "web_loader_ssl_verification": app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
+            "search": {
+                "enabled": app.state.config.ENABLE_RAG_WEB_SEARCH,
+                "engine": app.state.config.RAG_WEB_SEARCH_ENGINE,
+                "searxng_query_url": app.state.config.SEARXNG_QUERY_URL,
+                "google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY,
+                "google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID,
+                "brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY,
+                "mojeek_search_api_key": app.state.config.MOJEEK_SEARCH_API_KEY,
+                "serpstack_api_key": app.state.config.SERPSTACK_API_KEY,
+                "serpstack_https": app.state.config.SERPSTACK_HTTPS,
+                "serper_api_key": app.state.config.SERPER_API_KEY,
+                "serply_api_key": app.state.config.SERPLY_API_KEY,
+                "tavily_api_key": app.state.config.TAVILY_API_KEY,
+                "searchapi_api_key": app.state.config.SEARCHAPI_API_KEY,
+                "seaarchapi_engine": app.state.config.SEARCHAPI_ENGINE,
+                "jina_api_key": app.state.config.JINA_API_KEY,
+                "bing_search_v7_endpoint": app.state.config.BING_SEARCH_V7_ENDPOINT,
+                "bing_search_v7_subscription_key": app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
+                "result_count": app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                "concurrent_requests": app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
+            },
+        },
+    }
+
+
+class FileConfig(BaseModel):
+    max_size: Optional[int] = None
+    max_count: Optional[int] = None
+
+
+class ContentExtractionConfig(BaseModel):
+    engine: str = ""
+    tika_server_url: Optional[str] = None
+
+
+class ChunkParamUpdateForm(BaseModel):
+    text_splitter: Optional[str] = None
+    chunk_size: int
+    chunk_overlap: int
+
+
+class YoutubeLoaderConfig(BaseModel):
+    language: list[str]
+    translation: Optional[str] = None
+    proxy_url: str = ""
+
+
+class WebSearchConfig(BaseModel):
+    enabled: bool
+    engine: Optional[str] = None
+    searxng_query_url: Optional[str] = None
+    google_pse_api_key: Optional[str] = None
+    google_pse_engine_id: Optional[str] = None
+    brave_search_api_key: Optional[str] = None
+    mojeek_search_api_key: Optional[str] = None
+    serpstack_api_key: Optional[str] = None
+    serpstack_https: Optional[bool] = None
+    serper_api_key: Optional[str] = None
+    serply_api_key: Optional[str] = None
+    tavily_api_key: Optional[str] = None
+    searchapi_api_key: Optional[str] = None
+    searchapi_engine: Optional[str] = None
+    jina_api_key: Optional[str] = None
+    bing_search_v7_endpoint: Optional[str] = None
+    bing_search_v7_subscription_key: Optional[str] = None
+    result_count: Optional[int] = None
+    concurrent_requests: Optional[int] = None
+
+
+class WebConfig(BaseModel):
+    search: WebSearchConfig
+    web_loader_ssl_verification: Optional[bool] = None
+
+
+class ConfigUpdateForm(BaseModel):
+    pdf_extract_images: Optional[bool] = None
+    file: Optional[FileConfig] = None
+    content_extraction: Optional[ContentExtractionConfig] = None
+    chunk: Optional[ChunkParamUpdateForm] = None
+    youtube: Optional[YoutubeLoaderConfig] = None
+    web: Optional[WebConfig] = None
+
+
+@app.post("/config/update")
+async def update_rag_config(form_data: ConfigUpdateForm, user=Depends(get_admin_user)):
+    app.state.config.PDF_EXTRACT_IMAGES = (
+        form_data.pdf_extract_images
+        if form_data.pdf_extract_images is not None
+        else app.state.config.PDF_EXTRACT_IMAGES
+    )
+
+    if form_data.file is not None:
+        app.state.config.FILE_MAX_SIZE = form_data.file.max_size
+        app.state.config.FILE_MAX_COUNT = form_data.file.max_count
+
+    if form_data.content_extraction is not None:
+        log.info(f"Updating text settings: {form_data.content_extraction}")
+        app.state.config.CONTENT_EXTRACTION_ENGINE = form_data.content_extraction.engine
+        app.state.config.TIKA_SERVER_URL = form_data.content_extraction.tika_server_url
+
+    if form_data.chunk is not None:
+        app.state.config.TEXT_SPLITTER = form_data.chunk.text_splitter
+        app.state.config.CHUNK_SIZE = form_data.chunk.chunk_size
+        app.state.config.CHUNK_OVERLAP = form_data.chunk.chunk_overlap
+
+    if form_data.youtube is not None:
+        app.state.config.YOUTUBE_LOADER_LANGUAGE = form_data.youtube.language
+        app.state.config.YOUTUBE_LOADER_PROXY_URL = form_data.youtube.proxy_url
+        app.state.YOUTUBE_LOADER_TRANSLATION = form_data.youtube.translation
+
+    if form_data.web is not None:
+        app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = (
+            # Note: When UI "Bypass SSL verification for Websites"=True then ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION=False
+            form_data.web.web_loader_ssl_verification
+        )
+
+        app.state.config.ENABLE_RAG_WEB_SEARCH = form_data.web.search.enabled
+        app.state.config.RAG_WEB_SEARCH_ENGINE = form_data.web.search.engine
+        app.state.config.SEARXNG_QUERY_URL = form_data.web.search.searxng_query_url
+        app.state.config.GOOGLE_PSE_API_KEY = form_data.web.search.google_pse_api_key
+        app.state.config.GOOGLE_PSE_ENGINE_ID = (
+            form_data.web.search.google_pse_engine_id
+        )
+        app.state.config.BRAVE_SEARCH_API_KEY = (
+            form_data.web.search.brave_search_api_key
+        )
+        app.state.config.MOJEEK_SEARCH_API_KEY = (
+            form_data.web.search.mojeek_search_api_key
+        )
+        app.state.config.SERPSTACK_API_KEY = form_data.web.search.serpstack_api_key
+        app.state.config.SERPSTACK_HTTPS = form_data.web.search.serpstack_https
+        app.state.config.SERPER_API_KEY = form_data.web.search.serper_api_key
+        app.state.config.SERPLY_API_KEY = form_data.web.search.serply_api_key
+        app.state.config.TAVILY_API_KEY = form_data.web.search.tavily_api_key
+        app.state.config.SEARCHAPI_API_KEY = form_data.web.search.searchapi_api_key
+        app.state.config.SEARCHAPI_ENGINE = form_data.web.search.searchapi_engine
+
+        app.state.config.JINA_API_KEY = form_data.web.search.jina_api_key
+        app.state.config.BING_SEARCH_V7_ENDPOINT = (
+            form_data.web.search.bing_search_v7_endpoint
+        )
+        app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY = (
+            form_data.web.search.bing_search_v7_subscription_key
+        )
+
+        app.state.config.RAG_WEB_SEARCH_RESULT_COUNT = form_data.web.search.result_count
+        app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS = (
+            form_data.web.search.concurrent_requests
+        )
+
+    return {
+        "status": True,
+        "pdf_extract_images": app.state.config.PDF_EXTRACT_IMAGES,
+        "file": {
+            "max_size": app.state.config.FILE_MAX_SIZE,
+            "max_count": app.state.config.FILE_MAX_COUNT,
+        },
+        "content_extraction": {
+            "engine": app.state.config.CONTENT_EXTRACTION_ENGINE,
+            "tika_server_url": app.state.config.TIKA_SERVER_URL,
+        },
+        "chunk": {
+            "text_splitter": app.state.config.TEXT_SPLITTER,
+            "chunk_size": app.state.config.CHUNK_SIZE,
+            "chunk_overlap": app.state.config.CHUNK_OVERLAP,
+        },
+        "youtube": {
+            "language": app.state.config.YOUTUBE_LOADER_LANGUAGE,
+            "proxy_url": app.state.config.YOUTUBE_LOADER_PROXY_URL,
+            "translation": app.state.YOUTUBE_LOADER_TRANSLATION,
+        },
+        "web": {
+            "web_loader_ssl_verification": app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
+            "search": {
+                "enabled": app.state.config.ENABLE_RAG_WEB_SEARCH,
+                "engine": app.state.config.RAG_WEB_SEARCH_ENGINE,
+                "searxng_query_url": app.state.config.SEARXNG_QUERY_URL,
+                "google_pse_api_key": app.state.config.GOOGLE_PSE_API_KEY,
+                "google_pse_engine_id": app.state.config.GOOGLE_PSE_ENGINE_ID,
+                "brave_search_api_key": app.state.config.BRAVE_SEARCH_API_KEY,
+                "mojeek_search_api_key": app.state.config.MOJEEK_SEARCH_API_KEY,
+                "serpstack_api_key": app.state.config.SERPSTACK_API_KEY,
+                "serpstack_https": app.state.config.SERPSTACK_HTTPS,
+                "serper_api_key": app.state.config.SERPER_API_KEY,
+                "serply_api_key": app.state.config.SERPLY_API_KEY,
+                "serachapi_api_key": app.state.config.SEARCHAPI_API_KEY,
+                "searchapi_engine": app.state.config.SEARCHAPI_ENGINE,
+                "tavily_api_key": app.state.config.TAVILY_API_KEY,
+                "jina_api_key": app.state.config.JINA_API_KEY,
+                "bing_search_v7_endpoint": app.state.config.BING_SEARCH_V7_ENDPOINT,
+                "bing_search_v7_subscription_key": app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
+                "result_count": app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                "concurrent_requests": app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
+            },
+        },
+    }
+
+
+@app.get("/template")
+async def get_rag_template(user=Depends(get_verified_user)):
+    return {
+        "status": True,
+        "template": app.state.config.RAG_TEMPLATE,
+    }
+
+
+@app.get("/query/settings")
+async def get_query_settings(user=Depends(get_admin_user)):
+    return {
+        "status": True,
+        "template": app.state.config.RAG_TEMPLATE,
+        "k": app.state.config.TOP_K,
+        "r": app.state.config.RELEVANCE_THRESHOLD,
+        "hybrid": app.state.config.ENABLE_RAG_HYBRID_SEARCH,
+    }
+
+
+class QuerySettingsForm(BaseModel):
+    k: Optional[int] = None
+    r: Optional[float] = None
+    template: Optional[str] = None
+    hybrid: Optional[bool] = None
+
+
+@app.post("/query/settings/update")
+async def update_query_settings(
+    form_data: QuerySettingsForm, user=Depends(get_admin_user)
+):
+    app.state.config.RAG_TEMPLATE = form_data.template
+    app.state.config.TOP_K = form_data.k if form_data.k else 4
+    app.state.config.RELEVANCE_THRESHOLD = form_data.r if form_data.r else 0.0
+
+    app.state.config.ENABLE_RAG_HYBRID_SEARCH = (
+        form_data.hybrid if form_data.hybrid else False
+    )
+
+    return {
+        "status": True,
+        "template": app.state.config.RAG_TEMPLATE,
+        "k": app.state.config.TOP_K,
+        "r": app.state.config.RELEVANCE_THRESHOLD,
+        "hybrid": app.state.config.ENABLE_RAG_HYBRID_SEARCH,
+    }
+
+
+####################################
+#
+# Document process and retrieval
+#
+####################################
+
+
+def _get_docs_info(docs: list[Document]) -> str:
+    docs_info = set()
+
+    # Trying to select relevant metadata identifying the document.
+    for doc in docs:
+        metadata = getattr(doc, "metadata", {})
+        doc_name = metadata.get("name", "")
+        if not doc_name:
+            doc_name = metadata.get("title", "")
+        if not doc_name:
+            doc_name = metadata.get("source", "")
+        if doc_name:
+            docs_info.add(doc_name)
+
+    return ", ".join(docs_info)
+
+
+def save_docs_to_vector_db(
+    docs,
+    collection_name,
+    metadata: Optional[dict] = None,
+    overwrite: bool = False,
+    split: bool = True,
+    add: bool = False,
+) -> bool:
+    log.info(
+        f"save_docs_to_vector_db: document {_get_docs_info(docs)} {collection_name}"
+    )
+
+    # Check if entries with the same hash (metadata.hash) already exist
+    if metadata and "hash" in metadata:
+        result = VECTOR_DB_CLIENT.query(
+            collection_name=collection_name,
+            filter={"hash": metadata["hash"]},
+        )
+
+        if result is not None:
+            existing_doc_ids = result.ids[0]
+            if existing_doc_ids:
+                log.info(f"Document with hash {metadata['hash']} already exists")
+                raise ValueError(ERROR_MESSAGES.DUPLICATE_CONTENT)
+
+    if split:
+        if app.state.config.TEXT_SPLITTER in ["", "character"]:
+            text_splitter = RecursiveCharacterTextSplitter(
+                chunk_size=app.state.config.CHUNK_SIZE,
+                chunk_overlap=app.state.config.CHUNK_OVERLAP,
+                add_start_index=True,
+            )
+        elif app.state.config.TEXT_SPLITTER == "token":
+            log.info(
+                f"Using token text splitter: {app.state.config.TIKTOKEN_ENCODING_NAME}"
+            )
+
+            tiktoken.get_encoding(str(app.state.config.TIKTOKEN_ENCODING_NAME))
+            text_splitter = TokenTextSplitter(
+                encoding_name=str(app.state.config.TIKTOKEN_ENCODING_NAME),
+                chunk_size=app.state.config.CHUNK_SIZE,
+                chunk_overlap=app.state.config.CHUNK_OVERLAP,
+                add_start_index=True,
+            )
+        else:
+            raise ValueError(ERROR_MESSAGES.DEFAULT("Invalid text splitter"))
+
+        docs = text_splitter.split_documents(docs)
+
+    if len(docs) == 0:
+        raise ValueError(ERROR_MESSAGES.EMPTY_CONTENT)
+
+    texts = [doc.page_content for doc in docs]
+    metadatas = [
+        {
+            **doc.metadata,
+            **(metadata if metadata else {}),
+            "embedding_config": json.dumps(
+                {
+                    "engine": app.state.config.RAG_EMBEDDING_ENGINE,
+                    "model": app.state.config.RAG_EMBEDDING_MODEL,
+                }
+            ),
+        }
+        for doc in docs
+    ]
+
+    # ChromaDB does not like datetime formats
+    # for meta-data so convert them to string.
+    for metadata in metadatas:
+        for key, value in metadata.items():
+            if isinstance(value, datetime):
+                metadata[key] = str(value)
+
+    try:
+        if VECTOR_DB_CLIENT.has_collection(collection_name=collection_name):
+            log.info(f"collection {collection_name} already exists")
+
+            if overwrite:
+                VECTOR_DB_CLIENT.delete_collection(collection_name=collection_name)
+                log.info(f"deleting existing collection {collection_name}")
+            elif add is False:
+                log.info(
+                    f"collection {collection_name} already exists, overwrite is False and add is False"
+                )
+                return True
+
+        log.info(f"adding to collection {collection_name}")
+        embedding_function = get_embedding_function(
+            app.state.config.RAG_EMBEDDING_ENGINE,
+            app.state.config.RAG_EMBEDDING_MODEL,
+            app.state.sentence_transformer_ef,
+            (
+                app.state.config.OPENAI_API_BASE_URL
+                if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+                else app.state.config.OLLAMA_BASE_URL
+            ),
+            (
+                app.state.config.OPENAI_API_KEY
+                if app.state.config.RAG_EMBEDDING_ENGINE == "openai"
+                else app.state.config.OLLAMA_API_KEY
+            ),
+            app.state.config.RAG_EMBEDDING_BATCH_SIZE,
+        )
+
+        embeddings = embedding_function(
+            list(map(lambda x: x.replace("\n", " "), texts))
+        )
+
+        items = [
+            {
+                "id": str(uuid.uuid4()),
+                "text": text,
+                "vector": embeddings[idx],
+                "metadata": metadatas[idx],
+            }
+            for idx, text in enumerate(texts)
+        ]
+
+        VECTOR_DB_CLIENT.insert(
+            collection_name=collection_name,
+            items=items,
+        )
+
+        return True
+    except Exception as e:
+        log.exception(e)
+        raise e
+
+
+class ProcessFileForm(BaseModel):
+    file_id: str
+    content: Optional[str] = None
+    collection_name: Optional[str] = None
+
+
+@app.post("/process/file")
+def process_file(
+    form_data: ProcessFileForm,
+    user=Depends(get_verified_user),
+):
+    try:
+        file = Files.get_file_by_id(form_data.file_id)
+
+        collection_name = form_data.collection_name
+
+        if collection_name is None:
+            collection_name = f"file-{file.id}"
+
+        if form_data.content:
+            # Update the content in the file
+            # Usage: /files/{file_id}/data/content/update
+
+            VECTOR_DB_CLIENT.delete_collection(collection_name=f"file-{file.id}")
+
+            docs = [
+                Document(
+                    page_content=form_data.content.replace("<br/>", "\n"),
+                    metadata={
+                        **file.meta,
+                        "name": file.filename,
+                        "created_by": file.user_id,
+                        "file_id": file.id,
+                        "source": file.filename,
+                    },
+                )
+            ]
+
+            text_content = form_data.content
+        elif form_data.collection_name:
+            # Check if the file has already been processed and save the content
+            # Usage: /knowledge/{id}/file/add, /knowledge/{id}/file/update
+
+            result = VECTOR_DB_CLIENT.query(
+                collection_name=f"file-{file.id}", filter={"file_id": file.id}
+            )
+
+            if result is not None and len(result.ids[0]) > 0:
+                docs = [
+                    Document(
+                        page_content=result.documents[0][idx],
+                        metadata=result.metadatas[0][idx],
+                    )
+                    for idx, id in enumerate(result.ids[0])
+                ]
+            else:
+                docs = [
+                    Document(
+                        page_content=file.data.get("content", ""),
+                        metadata={
+                            **file.meta,
+                            "name": file.filename,
+                            "created_by": file.user_id,
+                            "file_id": file.id,
+                            "source": file.filename,
+                        },
+                    )
+                ]
+
+            text_content = file.data.get("content", "")
+        else:
+            # Process the file and save the content
+            # Usage: /files/
+            file_path = file.path
+            if file_path:
+                file_path = Storage.get_file(file_path)
+                loader = Loader(
+                    engine=app.state.config.CONTENT_EXTRACTION_ENGINE,
+                    TIKA_SERVER_URL=app.state.config.TIKA_SERVER_URL,
+                    PDF_EXTRACT_IMAGES=app.state.config.PDF_EXTRACT_IMAGES,
+                )
+                docs = loader.load(
+                    file.filename, file.meta.get("content_type"), file_path
+                )
+
+                docs = [
+                    Document(
+                        page_content=doc.page_content,
+                        metadata={
+                            **doc.metadata,
+                            "name": file.filename,
+                            "created_by": file.user_id,
+                            "file_id": file.id,
+                            "source": file.filename,
+                        },
+                    )
+                    for doc in docs
+                ]
+            else:
+                docs = [
+                    Document(
+                        page_content=file.data.get("content", ""),
+                        metadata={
+                            **file.meta,
+                            "name": file.filename,
+                            "created_by": file.user_id,
+                            "file_id": file.id,
+                            "source": file.filename,
+                        },
+                    )
+                ]
+            text_content = " ".join([doc.page_content for doc in docs])
+
+        log.debug(f"text_content: {text_content}")
+        Files.update_file_data_by_id(
+            file.id,
+            {"content": text_content},
+        )
+
+        hash = calculate_sha256_string(text_content)
+        Files.update_file_hash_by_id(file.id, hash)
+
+        try:
+            result = save_docs_to_vector_db(
+                docs=docs,
+                collection_name=collection_name,
+                metadata={
+                    "file_id": file.id,
+                    "name": file.filename,
+                    "hash": hash,
+                },
+                add=(True if form_data.collection_name else False),
+            )
+
+            if result:
+                Files.update_file_metadata_by_id(
+                    file.id,
+                    {
+                        "collection_name": collection_name,
+                    },
+                )
+
+                return {
+                    "status": True,
+                    "collection_name": collection_name,
+                    "filename": file.filename,
+                    "content": text_content,
+                }
+        except Exception as e:
+            raise e
+    except Exception as e:
+        log.exception(e)
+        if "No pandoc was found" in str(e):
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.PANDOC_NOT_INSTALLED,
+            )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=str(e),
+            )
+
+
+class ProcessTextForm(BaseModel):
+    name: str
+    content: str
+    collection_name: Optional[str] = None
+
+
+@app.post("/process/text")
+def process_text(
+    form_data: ProcessTextForm,
+    user=Depends(get_verified_user),
+):
+    collection_name = form_data.collection_name
+    if collection_name is None:
+        collection_name = calculate_sha256_string(form_data.content)
+
+    docs = [
+        Document(
+            page_content=form_data.content,
+            metadata={"name": form_data.name, "created_by": user.id},
+        )
+    ]
+    text_content = form_data.content
+    log.debug(f"text_content: {text_content}")
+
+    result = save_docs_to_vector_db(docs, collection_name)
+
+    if result:
+        return {
+            "status": True,
+            "collection_name": collection_name,
+            "content": text_content,
+        }
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+            detail=ERROR_MESSAGES.DEFAULT(),
+        )
+
+
+@app.post("/process/youtube")
+def process_youtube_video(form_data: ProcessUrlForm, user=Depends(get_verified_user)):
+    try:
+        collection_name = form_data.collection_name
+        if not collection_name:
+            collection_name = calculate_sha256_string(form_data.url)[:63]
+
+        loader = YoutubeLoader(
+            form_data.url,
+            language=app.state.config.YOUTUBE_LOADER_LANGUAGE,
+            proxy_url=app.state.config.YOUTUBE_LOADER_PROXY_URL,
+        )
+
+        docs = loader.load()
+        content = " ".join([doc.page_content for doc in docs])
+        log.debug(f"text_content: {content}")
+        save_docs_to_vector_db(docs, collection_name, overwrite=True)
+
+        return {
+            "status": True,
+            "collection_name": collection_name,
+            "filename": form_data.url,
+            "file": {
+                "data": {
+                    "content": content,
+                },
+                "meta": {
+                    "name": form_data.url,
+                },
+            },
+        }
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+@app.post("/process/web")
+def process_web(form_data: ProcessUrlForm, user=Depends(get_verified_user)):
+    try:
+        collection_name = form_data.collection_name
+        if not collection_name:
+            collection_name = calculate_sha256_string(form_data.url)[:63]
+
+        loader = get_web_loader(
+            form_data.url,
+            verify_ssl=app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
+            requests_per_second=app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
+        )
+        docs = loader.load()
+        content = " ".join([doc.page_content for doc in docs])
+        log.debug(f"text_content: {content}")
+        save_docs_to_vector_db(docs, collection_name, overwrite=True)
+
+        return {
+            "status": True,
+            "collection_name": collection_name,
+            "filename": form_data.url,
+            "file": {
+                "data": {
+                    "content": content,
+                },
+                "meta": {
+                    "name": form_data.url,
+                },
+            },
+        }
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+def search_web(engine: str, query: str) -> list[SearchResult]:
+    """Search the web using a search engine and return the results as a list of SearchResult objects.
+    Will look for a search engine API key in environment variables in the following order:
+    - SEARXNG_QUERY_URL
+    - GOOGLE_PSE_API_KEY + GOOGLE_PSE_ENGINE_ID
+    - BRAVE_SEARCH_API_KEY
+    - MOJEEK_SEARCH_API_KEY
+    - SERPSTACK_API_KEY
+    - SERPER_API_KEY
+    - SERPLY_API_KEY
+    - TAVILY_API_KEY
+    - SEARCHAPI_API_KEY + SEARCHAPI_ENGINE (by default `google`)
+    Args:
+        query (str): The query to search for
+    """
+
+    # TODO: add playwright to search the web
+    if engine == "searxng":
+        if app.state.config.SEARXNG_QUERY_URL:
+            return search_searxng(
+                app.state.config.SEARXNG_QUERY_URL,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No SEARXNG_QUERY_URL found in environment variables")
+    elif engine == "google_pse":
+        if (
+            app.state.config.GOOGLE_PSE_API_KEY
+            and app.state.config.GOOGLE_PSE_ENGINE_ID
+        ):
+            return search_google_pse(
+                app.state.config.GOOGLE_PSE_API_KEY,
+                app.state.config.GOOGLE_PSE_ENGINE_ID,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception(
+                "No GOOGLE_PSE_API_KEY or GOOGLE_PSE_ENGINE_ID found in environment variables"
+            )
+    elif engine == "brave":
+        if app.state.config.BRAVE_SEARCH_API_KEY:
+            return search_brave(
+                app.state.config.BRAVE_SEARCH_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No BRAVE_SEARCH_API_KEY found in environment variables")
+    elif engine == "mojeek":
+        if app.state.config.MOJEEK_SEARCH_API_KEY:
+            return search_mojeek(
+                app.state.config.MOJEEK_SEARCH_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No MOJEEK_SEARCH_API_KEY found in environment variables")
+    elif engine == "serpstack":
+        if app.state.config.SERPSTACK_API_KEY:
+            return search_serpstack(
+                app.state.config.SERPSTACK_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+                https_enabled=app.state.config.SERPSTACK_HTTPS,
+            )
+        else:
+            raise Exception("No SERPSTACK_API_KEY found in environment variables")
+    elif engine == "serper":
+        if app.state.config.SERPER_API_KEY:
+            return search_serper(
+                app.state.config.SERPER_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No SERPER_API_KEY found in environment variables")
+    elif engine == "serply":
+        if app.state.config.SERPLY_API_KEY:
+            return search_serply(
+                app.state.config.SERPLY_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No SERPLY_API_KEY found in environment variables")
+    elif engine == "duckduckgo":
+        return search_duckduckgo(
+            query,
+            app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+            app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+        )
+    elif engine == "tavily":
+        if app.state.config.TAVILY_API_KEY:
+            return search_tavily(
+                app.state.config.TAVILY_API_KEY,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+            )
+        else:
+            raise Exception("No TAVILY_API_KEY found in environment variables")
+    elif engine == "searchapi":
+        if app.state.config.SEARCHAPI_API_KEY:
+            return search_searchapi(
+                app.state.config.SEARCHAPI_API_KEY,
+                app.state.config.SEARCHAPI_ENGINE,
+                query,
+                app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+                app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+            )
+        else:
+            raise Exception("No SEARCHAPI_API_KEY found in environment variables")
+    elif engine == "jina":
+        return search_jina(
+            app.state.config.JINA_API_KEY,
+            query,
+            app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+        )
+    elif engine == "bing":
+        return search_bing(
+            app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
+            app.state.config.BING_SEARCH_V7_ENDPOINT,
+            str(DEFAULT_LOCALE),
+            query,
+            app.state.config.RAG_WEB_SEARCH_RESULT_COUNT,
+            app.state.config.RAG_WEB_SEARCH_DOMAIN_FILTER_LIST,
+        )
+    else:
+        raise Exception("No search engine API key found in environment variables")
+
+
+@app.post("/process/web/search")
+def process_web_search(form_data: SearchForm, user=Depends(get_verified_user)):
+    try:
+        logging.info(
+            f"trying to web search with {app.state.config.RAG_WEB_SEARCH_ENGINE, form_data.query}"
+        )
+        web_results = search_web(
+            app.state.config.RAG_WEB_SEARCH_ENGINE, form_data.query
+        )
+    except Exception as e:
+        log.exception(e)
+
+        print(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e),
+        )
+
+    try:
+        collection_name = form_data.collection_name
+        if collection_name == "":
+            collection_name = calculate_sha256_string(form_data.query)[:63]
+
+        urls = [result.link for result in web_results]
+
+        loader = get_web_loader(
+            urls,
+            verify_ssl=app.state.config.ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION,
+            requests_per_second=app.state.config.RAG_WEB_SEARCH_CONCURRENT_REQUESTS,
+        )
+        docs = loader.aload()
+
+        save_docs_to_vector_db(docs, collection_name, overwrite=True)
+
+        return {
+            "status": True,
+            "collection_name": collection_name,
+            "filenames": urls,
+        }
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+class QueryDocForm(BaseModel):
+    collection_name: str
+    query: str
+    k: Optional[int] = None
+    r: Optional[float] = None
+    hybrid: Optional[bool] = None
+
+
+@app.post("/query/doc")
+def query_doc_handler(
+    form_data: QueryDocForm,
+    user=Depends(get_verified_user),
+):
+    try:
+        if app.state.config.ENABLE_RAG_HYBRID_SEARCH:
+            return query_doc_with_hybrid_search(
+                collection_name=form_data.collection_name,
+                query=form_data.query,
+                embedding_function=app.state.EMBEDDING_FUNCTION,
+                k=form_data.k if form_data.k else app.state.config.TOP_K,
+                reranking_function=app.state.sentence_transformer_rf,
+                r=(
+                    form_data.r if form_data.r else app.state.config.RELEVANCE_THRESHOLD
+                ),
+            )
+        else:
+            return query_doc(
+                collection_name=form_data.collection_name,
+                query=form_data.query,
+                embedding_function=app.state.EMBEDDING_FUNCTION,
+                k=form_data.k if form_data.k else app.state.config.TOP_K,
+            )
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+class QueryCollectionsForm(BaseModel):
+    collection_names: list[str]
+    query: str
+    k: Optional[int] = None
+    r: Optional[float] = None
+    hybrid: Optional[bool] = None
+
+
+@app.post("/query/collection")
+def query_collection_handler(
+    form_data: QueryCollectionsForm,
+    user=Depends(get_verified_user),
+):
+    try:
+        if app.state.config.ENABLE_RAG_HYBRID_SEARCH:
+            return query_collection_with_hybrid_search(
+                collection_names=form_data.collection_names,
+                queries=[form_data.query],
+                embedding_function=app.state.EMBEDDING_FUNCTION,
+                k=form_data.k if form_data.k else app.state.config.TOP_K,
+                reranking_function=app.state.sentence_transformer_rf,
+                r=(
+                    form_data.r if form_data.r else app.state.config.RELEVANCE_THRESHOLD
+                ),
+            )
+        else:
+            return query_collection(
+                collection_names=form_data.collection_names,
+                queries=[form_data.query],
+                embedding_function=app.state.EMBEDDING_FUNCTION,
+                k=form_data.k if form_data.k else app.state.config.TOP_K,
+            )
+
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+####################################
+#
+# Vector DB operations
+#
+####################################
+
+
+class DeleteForm(BaseModel):
+    collection_name: str
+    file_id: str
+
+
+@app.post("/delete")
+def delete_entries_from_collection(form_data: DeleteForm, user=Depends(get_admin_user)):
+    try:
+        if VECTOR_DB_CLIENT.has_collection(collection_name=form_data.collection_name):
+            file = Files.get_file_by_id(form_data.file_id)
+            hash = file.hash
+
+            VECTOR_DB_CLIENT.delete(
+                collection_name=form_data.collection_name,
+                metadata={"hash": hash},
+            )
+            return {"status": True}
+        else:
+            return {"status": False}
+    except Exception as e:
+        log.exception(e)
+        return {"status": False}
+
+
+@app.post("/reset/db")
+def reset_vector_db(user=Depends(get_admin_user)):
+    VECTOR_DB_CLIENT.reset()
+    Knowledges.delete_all_knowledge()
+
+
+@app.post("/reset/uploads")
+def reset_upload_dir(user=Depends(get_admin_user)) -> bool:
+    folder = f"{UPLOAD_DIR}"
+    try:
+        # Check if the directory exists
+        if os.path.exists(folder):
+            # Iterate over all the files and directories in the specified directory
+            for filename in os.listdir(folder):
+                file_path = os.path.join(folder, filename)
+                try:
+                    if os.path.isfile(file_path) or os.path.islink(file_path):
+                        os.unlink(file_path)  # Remove the file or link
+                    elif os.path.isdir(file_path):
+                        shutil.rmtree(file_path)  # Remove the directory
+                except Exception as e:
+                    print(f"Failed to delete {file_path}. Reason: {e}")
+        else:
+            print(f"The directory {folder} does not exist")
+    except Exception as e:
+        print(f"Failed to process the directory {folder}. Reason: {e}")
+    return True
+
+
+if ENV == "dev":
+
+    @app.get("/ef")
+    async def get_embeddings():
+        return {"result": app.state.EMBEDDING_FUNCTION("hello world")}
+
+    @app.get("/ef/{text}")
+    async def get_embeddings_text(text: str):
+        return {"result": app.state.EMBEDDING_FUNCTION(text)}
diff --git a/backend/open_webui/apps/retrieval/models/colbert.py b/backend/open_webui/apps/retrieval/models/colbert.py
new file mode 100644
index 0000000000000000000000000000000000000000..ea3204cb8bf91d7dbae4370d0f3628de7b6e031b
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/models/colbert.py
@@ -0,0 +1,81 @@
+import os
+import torch
+import numpy as np
+from colbert.infra import ColBERTConfig
+from colbert.modeling.checkpoint import Checkpoint
+
+
+class ColBERT:
+    def __init__(self, name, **kwargs) -> None:
+        print("ColBERT: Loading model", name)
+        self.device = "cuda" if torch.cuda.is_available() else "cpu"
+
+        DOCKER = kwargs.get("env") == "docker"
+        if DOCKER:
+            # This is a workaround for the issue with the docker container
+            # where the torch extension is not loaded properly
+            # and the following error is thrown:
+            # /root/.cache/torch_extensions/py311_cpu/segmented_maxsim_cpp/segmented_maxsim_cpp.so: cannot open shared object file: No such file or directory
+
+            lock_file = (
+                "/root/.cache/torch_extensions/py311_cpu/segmented_maxsim_cpp/lock"
+            )
+            if os.path.exists(lock_file):
+                os.remove(lock_file)
+
+        self.ckpt = Checkpoint(
+            name,
+            colbert_config=ColBERTConfig(model_name=name),
+        ).to(self.device)
+        pass
+
+    def calculate_similarity_scores(self, query_embeddings, document_embeddings):
+
+        query_embeddings = query_embeddings.to(self.device)
+        document_embeddings = document_embeddings.to(self.device)
+
+        # Validate dimensions to ensure compatibility
+        if query_embeddings.dim() != 3:
+            raise ValueError(
+                f"Expected query embeddings to have 3 dimensions, but got {query_embeddings.dim()}."
+            )
+        if document_embeddings.dim() != 3:
+            raise ValueError(
+                f"Expected document embeddings to have 3 dimensions, but got {document_embeddings.dim()}."
+            )
+        if query_embeddings.size(0) not in [1, document_embeddings.size(0)]:
+            raise ValueError(
+                "There should be either one query or queries equal to the number of documents."
+            )
+
+        # Transpose the query embeddings to align for matrix multiplication
+        transposed_query_embeddings = query_embeddings.permute(0, 2, 1)
+        # Compute similarity scores using batch matrix multiplication
+        computed_scores = torch.matmul(document_embeddings, transposed_query_embeddings)
+        # Apply max pooling to extract the highest semantic similarity across each document's sequence
+        maximum_scores = torch.max(computed_scores, dim=1).values
+
+        # Sum up the maximum scores across features to get the overall document relevance scores
+        final_scores = maximum_scores.sum(dim=1)
+
+        normalized_scores = torch.softmax(final_scores, dim=0)
+
+        return normalized_scores.detach().cpu().numpy().astype(np.float32)
+
+    def predict(self, sentences):
+
+        query = sentences[0][0]
+        docs = [i[1] for i in sentences]
+
+        # Embedding the documents
+        embedded_docs = self.ckpt.docFromText(docs, bsize=32)[0]
+        # Embedding the queries
+        embedded_queries = self.ckpt.queryFromText([query], bsize=32)
+        embedded_query = embedded_queries[0]
+
+        # Calculate retrieval scores for the query against all documents
+        scores = self.calculate_similarity_scores(
+            embedded_query.unsqueeze(0), embedded_docs
+        )
+
+        return scores
diff --git a/backend/open_webui/apps/retrieval/utils.py b/backend/open_webui/apps/retrieval/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf939ecf1e3859abebc57a1fdbc8a4ac5c85c6e9
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/utils.py
@@ -0,0 +1,532 @@
+import logging
+import os
+import uuid
+from typing import Optional, Union
+
+import asyncio
+import requests
+
+from huggingface_hub import snapshot_download
+from langchain.retrievers import ContextualCompressionRetriever, EnsembleRetriever
+from langchain_community.retrievers import BM25Retriever
+from langchain_core.documents import Document
+
+from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
+from open_webui.utils.misc import get_last_user_message
+
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+from typing import Any
+
+from langchain_core.callbacks import CallbackManagerForRetrieverRun
+from langchain_core.retrievers import BaseRetriever
+
+
+class VectorSearchRetriever(BaseRetriever):
+    collection_name: Any
+    embedding_function: Any
+    top_k: int
+
+    def _get_relevant_documents(
+        self,
+        query: str,
+        *,
+        run_manager: CallbackManagerForRetrieverRun,
+    ) -> list[Document]:
+        result = VECTOR_DB_CLIENT.search(
+            collection_name=self.collection_name,
+            vectors=[self.embedding_function(query)],
+            limit=self.top_k,
+        )
+
+        ids = result.ids[0]
+        metadatas = result.metadatas[0]
+        documents = result.documents[0]
+
+        results = []
+        for idx in range(len(ids)):
+            results.append(
+                Document(
+                    metadata=metadatas[idx],
+                    page_content=documents[idx],
+                )
+            )
+        return results
+
+
+def query_doc(
+    collection_name: str,
+    query_embedding: list[float],
+    k: int,
+):
+    try:
+        result = VECTOR_DB_CLIENT.search(
+            collection_name=collection_name,
+            vectors=[query_embedding],
+            limit=k,
+        )
+
+        log.info(f"query_doc:result {result.ids} {result.metadatas}")
+        return result
+    except Exception as e:
+        print(e)
+        raise e
+
+
+def query_doc_with_hybrid_search(
+    collection_name: str,
+    query: str,
+    embedding_function,
+    k: int,
+    reranking_function,
+    r: float,
+) -> dict:
+    try:
+        result = VECTOR_DB_CLIENT.get(collection_name=collection_name)
+
+        bm25_retriever = BM25Retriever.from_texts(
+            texts=result.documents[0],
+            metadatas=result.metadatas[0],
+        )
+        bm25_retriever.k = k
+
+        vector_search_retriever = VectorSearchRetriever(
+            collection_name=collection_name,
+            embedding_function=embedding_function,
+            top_k=k,
+        )
+
+        ensemble_retriever = EnsembleRetriever(
+            retrievers=[bm25_retriever, vector_search_retriever], weights=[0.5, 0.5]
+        )
+        compressor = RerankCompressor(
+            embedding_function=embedding_function,
+            top_n=k,
+            reranking_function=reranking_function,
+            r_score=r,
+        )
+
+        compression_retriever = ContextualCompressionRetriever(
+            base_compressor=compressor, base_retriever=ensemble_retriever
+        )
+
+        result = compression_retriever.invoke(query)
+        result = {
+            "distances": [[d.metadata.get("score") for d in result]],
+            "documents": [[d.page_content for d in result]],
+            "metadatas": [[d.metadata for d in result]],
+        }
+
+        log.info(
+            "query_doc_with_hybrid_search:result "
+            + f'{result["metadatas"]} {result["distances"]}'
+        )
+        return result
+    except Exception as e:
+        raise e
+
+
+def merge_and_sort_query_results(
+    query_results: list[dict], k: int, reverse: bool = False
+) -> list[dict]:
+    # Initialize lists to store combined data
+    combined_distances = []
+    combined_documents = []
+    combined_metadatas = []
+
+    for data in query_results:
+        combined_distances.extend(data["distances"][0])
+        combined_documents.extend(data["documents"][0])
+        combined_metadatas.extend(data["metadatas"][0])
+
+    # Create a list of tuples (distance, document, metadata)
+    combined = list(zip(combined_distances, combined_documents, combined_metadatas))
+
+    # Sort the list based on distances
+    combined.sort(key=lambda x: x[0], reverse=reverse)
+
+    # We don't have anything :-(
+    if not combined:
+        sorted_distances = []
+        sorted_documents = []
+        sorted_metadatas = []
+    else:
+        # Unzip the sorted list
+        sorted_distances, sorted_documents, sorted_metadatas = zip(*combined)
+
+        # Slicing the lists to include only k elements
+        sorted_distances = list(sorted_distances)[:k]
+        sorted_documents = list(sorted_documents)[:k]
+        sorted_metadatas = list(sorted_metadatas)[:k]
+
+    # Create the output dictionary
+    result = {
+        "distances": [sorted_distances],
+        "documents": [sorted_documents],
+        "metadatas": [sorted_metadatas],
+    }
+
+    return result
+
+
+def query_collection(
+    collection_names: list[str],
+    queries: list[str],
+    embedding_function,
+    k: int,
+) -> dict:
+    results = []
+    for query in queries:
+        query_embedding = embedding_function(query)
+        for collection_name in collection_names:
+            if collection_name:
+                try:
+                    result = query_doc(
+                        collection_name=collection_name,
+                        k=k,
+                        query_embedding=query_embedding,
+                    )
+                    if result is not None:
+                        results.append(result.model_dump())
+                except Exception as e:
+                    log.exception(f"Error when querying the collection: {e}")
+            else:
+                pass
+
+    return merge_and_sort_query_results(results, k=k)
+
+
+def query_collection_with_hybrid_search(
+    collection_names: list[str],
+    queries: list[str],
+    embedding_function,
+    k: int,
+    reranking_function,
+    r: float,
+) -> dict:
+    results = []
+    error = False
+    for collection_name in collection_names:
+        try:
+            for query in queries:
+                result = query_doc_with_hybrid_search(
+                    collection_name=collection_name,
+                    query=query,
+                    embedding_function=embedding_function,
+                    k=k,
+                    reranking_function=reranking_function,
+                    r=r,
+                )
+                results.append(result)
+        except Exception as e:
+            log.exception(
+                "Error when querying the collection with " f"hybrid_search: {e}"
+            )
+            error = True
+
+    if error:
+        raise Exception(
+            "Hybrid search failed for all collections. Using Non hybrid search as fallback."
+        )
+
+    return merge_and_sort_query_results(results, k=k, reverse=True)
+
+
+def get_embedding_function(
+    embedding_engine,
+    embedding_model,
+    embedding_function,
+    url,
+    key,
+    embedding_batch_size,
+):
+    if embedding_engine == "":
+        return lambda query: embedding_function.encode(query).tolist()
+    elif embedding_engine in ["ollama", "openai"]:
+        func = lambda query: generate_embeddings(
+            engine=embedding_engine,
+            model=embedding_model,
+            text=query,
+            url=url,
+            key=key,
+        )
+
+        def generate_multiple(query, func):
+            if isinstance(query, list):
+                embeddings = []
+                for i in range(0, len(query), embedding_batch_size):
+                    embeddings.extend(func(query[i : i + embedding_batch_size]))
+                return embeddings
+            else:
+                return func(query)
+
+        return lambda query: generate_multiple(query, func)
+
+
+def get_sources_from_files(
+    files,
+    queries,
+    embedding_function,
+    k,
+    reranking_function,
+    r,
+    hybrid_search,
+):
+    log.debug(f"files: {files} {queries} {embedding_function} {reranking_function}")
+
+    extracted_collections = []
+    relevant_contexts = []
+
+    for file in files:
+        if file.get("context") == "full":
+            context = {
+                "documents": [[file.get("file").get("data", {}).get("content")]],
+                "metadatas": [[{"file_id": file.get("id"), "name": file.get("name")}]],
+            }
+        else:
+            context = None
+
+            collection_names = []
+            if file.get("type") == "collection":
+                if file.get("legacy"):
+                    collection_names = file.get("collection_names", [])
+                else:
+                    collection_names.append(file["id"])
+            elif file.get("collection_name"):
+                collection_names.append(file["collection_name"])
+            elif file.get("id"):
+                if file.get("legacy"):
+                    collection_names.append(f"{file['id']}")
+                else:
+                    collection_names.append(f"file-{file['id']}")
+
+            collection_names = set(collection_names).difference(extracted_collections)
+            if not collection_names:
+                log.debug(f"skipping {file} as it has already been extracted")
+                continue
+
+            try:
+                context = None
+                if file.get("type") == "text":
+                    context = file["content"]
+                else:
+                    if hybrid_search:
+                        try:
+                            context = query_collection_with_hybrid_search(
+                                collection_names=collection_names,
+                                queries=queries,
+                                embedding_function=embedding_function,
+                                k=k,
+                                reranking_function=reranking_function,
+                                r=r,
+                            )
+                        except Exception as e:
+                            log.debug(
+                                "Error when using hybrid search, using"
+                                " non hybrid search as fallback."
+                            )
+
+                    if (not hybrid_search) or (context is None):
+                        context = query_collection(
+                            collection_names=collection_names,
+                            queries=queries,
+                            embedding_function=embedding_function,
+                            k=k,
+                        )
+            except Exception as e:
+                log.exception(e)
+
+            extracted_collections.extend(collection_names)
+
+        if context:
+            if "data" in file:
+                del file["data"]
+            relevant_contexts.append({**context, "file": file})
+
+    sources = []
+    for context in relevant_contexts:
+        try:
+            if "documents" in context:
+                if "metadatas" in context:
+                    source = {
+                        "source": context["file"],
+                        "document": context["documents"][0],
+                        "metadata": context["metadatas"][0],
+                    }
+                    if "distances" in context and context["distances"]:
+                        source["distances"] = context["distances"][0]
+
+                    sources.append(source)
+        except Exception as e:
+            log.exception(e)
+
+    return sources
+
+
+def get_model_path(model: str, update_model: bool = False):
+    # Construct huggingface_hub kwargs with local_files_only to return the snapshot path
+    cache_dir = os.getenv("SENTENCE_TRANSFORMERS_HOME")
+
+    local_files_only = not update_model
+
+    snapshot_kwargs = {
+        "cache_dir": cache_dir,
+        "local_files_only": local_files_only,
+    }
+
+    log.debug(f"model: {model}")
+    log.debug(f"snapshot_kwargs: {snapshot_kwargs}")
+
+    # Inspiration from upstream sentence_transformers
+    if (
+        os.path.exists(model)
+        or ("\\" in model or model.count("/") > 1)
+        and local_files_only
+    ):
+        # If fully qualified path exists, return input, else set repo_id
+        return model
+    elif "/" not in model:
+        # Set valid repo_id for model short-name
+        model = "sentence-transformers" + "/" + model
+
+    snapshot_kwargs["repo_id"] = model
+
+    # Attempt to query the huggingface_hub library to determine the local path and/or to update
+    try:
+        model_repo_path = snapshot_download(**snapshot_kwargs)
+        log.debug(f"model_repo_path: {model_repo_path}")
+        return model_repo_path
+    except Exception as e:
+        log.exception(f"Cannot determine model snapshot path: {e}")
+        return model
+
+
+def generate_openai_batch_embeddings(
+    model: str, texts: list[str], url: str = "https://api.openai.com/v1", key: str = ""
+) -> Optional[list[list[float]]]:
+    try:
+        r = requests.post(
+            f"{url}/embeddings",
+            headers={
+                "Content-Type": "application/json",
+                "Authorization": f"Bearer {key}",
+            },
+            json={"input": texts, "model": model},
+        )
+        r.raise_for_status()
+        data = r.json()
+        if "data" in data:
+            return [elem["embedding"] for elem in data["data"]]
+        else:
+            raise "Something went wrong :/"
+    except Exception as e:
+        print(e)
+        return None
+
+
+def generate_ollama_batch_embeddings(
+    model: str, texts: list[str], url: str, key: str = ""
+) -> Optional[list[list[float]]]:
+    try:
+        r = requests.post(
+            f"{url}/api/embed",
+            headers={
+                "Content-Type": "application/json",
+                "Authorization": f"Bearer {key}",
+            },
+            json={"input": texts, "model": model},
+        )
+        r.raise_for_status()
+        data = r.json()
+
+        if "embeddings" in data:
+            return data["embeddings"]
+        else:
+            raise "Something went wrong :/"
+    except Exception as e:
+        print(e)
+        return None
+
+
+def generate_embeddings(engine: str, model: str, text: Union[str, list[str]], **kwargs):
+    url = kwargs.get("url", "")
+    key = kwargs.get("key", "")
+
+    if engine == "ollama":
+        if isinstance(text, list):
+            embeddings = generate_ollama_batch_embeddings(
+                **{"model": model, "texts": text, "url": url, "key": key}
+            )
+        else:
+            embeddings = generate_ollama_batch_embeddings(
+                **{"model": model, "texts": [text], "url": url, "key": key}
+            )
+        return embeddings[0] if isinstance(text, str) else embeddings
+    elif engine == "openai":
+        if isinstance(text, list):
+            embeddings = generate_openai_batch_embeddings(model, text, url, key)
+        else:
+            embeddings = generate_openai_batch_embeddings(model, [text], url, key)
+
+        return embeddings[0] if isinstance(text, str) else embeddings
+
+
+import operator
+from typing import Optional, Sequence
+
+from langchain_core.callbacks import Callbacks
+from langchain_core.documents import BaseDocumentCompressor, Document
+
+
+class RerankCompressor(BaseDocumentCompressor):
+    embedding_function: Any
+    top_n: int
+    reranking_function: Any
+    r_score: float
+
+    class Config:
+        extra = "forbid"
+        arbitrary_types_allowed = True
+
+    def compress_documents(
+        self,
+        documents: Sequence[Document],
+        query: str,
+        callbacks: Optional[Callbacks] = None,
+    ) -> Sequence[Document]:
+        reranking = self.reranking_function is not None
+
+        if reranking:
+            scores = self.reranking_function.predict(
+                [(query, doc.page_content) for doc in documents]
+            )
+        else:
+            from sentence_transformers import util
+
+            query_embedding = self.embedding_function(query)
+            document_embedding = self.embedding_function(
+                [doc.page_content for doc in documents]
+            )
+            scores = util.cos_sim(query_embedding, document_embedding)[0]
+
+        docs_with_scores = list(zip(documents, scores.tolist()))
+        if self.r_score:
+            docs_with_scores = [
+                (d, s) for d, s in docs_with_scores if s >= self.r_score
+            ]
+
+        result = sorted(docs_with_scores, key=operator.itemgetter(1), reverse=True)
+        final_results = []
+        for doc, doc_score in result[: self.top_n]:
+            metadata = doc.metadata
+            metadata["score"] = doc_score
+            doc = Document(
+                page_content=doc.page_content,
+                metadata=metadata,
+            )
+            final_results.append(doc)
+        return final_results
diff --git a/backend/open_webui/apps/retrieval/vector/connector.py b/backend/open_webui/apps/retrieval/vector/connector.py
new file mode 100644
index 0000000000000000000000000000000000000000..528835b567c0c5f88fb41119130cd9e99fa9633a
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/connector.py
@@ -0,0 +1,22 @@
+from open_webui.config import VECTOR_DB
+
+if VECTOR_DB == "milvus":
+    from open_webui.apps.retrieval.vector.dbs.milvus import MilvusClient
+
+    VECTOR_DB_CLIENT = MilvusClient()
+elif VECTOR_DB == "qdrant":
+    from open_webui.apps.retrieval.vector.dbs.qdrant import QdrantClient
+
+    VECTOR_DB_CLIENT = QdrantClient()
+elif VECTOR_DB == "opensearch":
+    from open_webui.apps.retrieval.vector.dbs.opensearch import OpenSearchClient
+
+    VECTOR_DB_CLIENT = OpenSearchClient()
+elif VECTOR_DB == "pgvector":
+    from open_webui.apps.retrieval.vector.dbs.pgvector import PgvectorClient
+
+    VECTOR_DB_CLIENT = PgvectorClient()
+else:
+    from open_webui.apps.retrieval.vector.dbs.chroma import ChromaClient
+
+    VECTOR_DB_CLIENT = ChromaClient()
diff --git a/backend/open_webui/apps/retrieval/vector/dbs/chroma.py b/backend/open_webui/apps/retrieval/vector/dbs/chroma.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2fcdd16ab500ccd596e190746cae52c8fb74fa5
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/dbs/chroma.py
@@ -0,0 +1,174 @@
+import chromadb
+from chromadb import Settings
+from chromadb.utils.batch_utils import create_batches
+
+from typing import Optional
+
+from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
+from open_webui.config import (
+    CHROMA_DATA_PATH,
+    CHROMA_HTTP_HOST,
+    CHROMA_HTTP_PORT,
+    CHROMA_HTTP_HEADERS,
+    CHROMA_HTTP_SSL,
+    CHROMA_TENANT,
+    CHROMA_DATABASE,
+    CHROMA_CLIENT_AUTH_PROVIDER,
+    CHROMA_CLIENT_AUTH_CREDENTIALS,
+)
+
+
+class ChromaClient:
+    def __init__(self):
+        settings_dict = {
+            "allow_reset": True,
+            "anonymized_telemetry": False,
+        }
+        if CHROMA_CLIENT_AUTH_PROVIDER is not None:
+            settings_dict["chroma_client_auth_provider"] = CHROMA_CLIENT_AUTH_PROVIDER
+        if CHROMA_CLIENT_AUTH_CREDENTIALS is not None:
+            settings_dict["chroma_client_auth_credentials"] = (
+                CHROMA_CLIENT_AUTH_CREDENTIALS
+            )
+
+        if CHROMA_HTTP_HOST != "":
+            self.client = chromadb.HttpClient(
+                host=CHROMA_HTTP_HOST,
+                port=CHROMA_HTTP_PORT,
+                headers=CHROMA_HTTP_HEADERS,
+                ssl=CHROMA_HTTP_SSL,
+                tenant=CHROMA_TENANT,
+                database=CHROMA_DATABASE,
+                settings=Settings(**settings_dict),
+            )
+        else:
+            self.client = chromadb.PersistentClient(
+                path=CHROMA_DATA_PATH,
+                settings=Settings(**settings_dict),
+                tenant=CHROMA_TENANT,
+                database=CHROMA_DATABASE,
+            )
+
+    def has_collection(self, collection_name: str) -> bool:
+        # Check if the collection exists based on the collection name.
+        collections = self.client.list_collections()
+        return collection_name in [collection.name for collection in collections]
+
+    def delete_collection(self, collection_name: str):
+        # Delete the collection based on the collection name.
+        return self.client.delete_collection(name=collection_name)
+
+    def search(
+        self, collection_name: str, vectors: list[list[float | int]], limit: int
+    ) -> Optional[SearchResult]:
+        # Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
+        try:
+            collection = self.client.get_collection(name=collection_name)
+            if collection:
+                result = collection.query(
+                    query_embeddings=vectors,
+                    n_results=limit,
+                )
+
+                return SearchResult(
+                    **{
+                        "ids": result["ids"],
+                        "distances": result["distances"],
+                        "documents": result["documents"],
+                        "metadatas": result["metadatas"],
+                    }
+                )
+            return None
+        except Exception as e:
+            return None
+
+    def query(
+        self, collection_name: str, filter: dict, limit: Optional[int] = None
+    ) -> Optional[GetResult]:
+        # Query the items from the collection based on the filter.
+        try:
+            collection = self.client.get_collection(name=collection_name)
+            if collection:
+                result = collection.get(
+                    where=filter,
+                    limit=limit,
+                )
+
+                return GetResult(
+                    **{
+                        "ids": [result["ids"]],
+                        "documents": [result["documents"]],
+                        "metadatas": [result["metadatas"]],
+                    }
+                )
+            return None
+        except Exception as e:
+            print(e)
+            return None
+
+    def get(self, collection_name: str) -> Optional[GetResult]:
+        # Get all the items in the collection.
+        collection = self.client.get_collection(name=collection_name)
+        if collection:
+            result = collection.get()
+            return GetResult(
+                **{
+                    "ids": [result["ids"]],
+                    "documents": [result["documents"]],
+                    "metadatas": [result["metadatas"]],
+                }
+            )
+        return None
+
+    def insert(self, collection_name: str, items: list[VectorItem]):
+        # Insert the items into the collection, if the collection does not exist, it will be created.
+        collection = self.client.get_or_create_collection(
+            name=collection_name, metadata={"hnsw:space": "cosine"}
+        )
+
+        ids = [item["id"] for item in items]
+        documents = [item["text"] for item in items]
+        embeddings = [item["vector"] for item in items]
+        metadatas = [item["metadata"] for item in items]
+
+        for batch in create_batches(
+            api=self.client,
+            documents=documents,
+            embeddings=embeddings,
+            ids=ids,
+            metadatas=metadatas,
+        ):
+            collection.add(*batch)
+
+    def upsert(self, collection_name: str, items: list[VectorItem]):
+        # Update the items in the collection, if the items are not present, insert them. If the collection does not exist, it will be created.
+        collection = self.client.get_or_create_collection(
+            name=collection_name, metadata={"hnsw:space": "cosine"}
+        )
+
+        ids = [item["id"] for item in items]
+        documents = [item["text"] for item in items]
+        embeddings = [item["vector"] for item in items]
+        metadatas = [item["metadata"] for item in items]
+
+        collection.upsert(
+            ids=ids, documents=documents, embeddings=embeddings, metadatas=metadatas
+        )
+
+    def delete(
+        self,
+        collection_name: str,
+        ids: Optional[list[str]] = None,
+        filter: Optional[dict] = None,
+    ):
+        # Delete the items from the collection based on the ids.
+        collection = self.client.get_collection(name=collection_name)
+        if collection:
+            if ids:
+                collection.delete(ids=ids)
+            elif filter:
+                collection.delete(where=filter)
+
+    def reset(self):
+        # Resets the database. This will delete all collections and item entries.
+        return self.client.reset()
diff --git a/backend/open_webui/apps/retrieval/vector/dbs/milvus.py b/backend/open_webui/apps/retrieval/vector/dbs/milvus.py
new file mode 100644
index 0000000000000000000000000000000000000000..5351f860e045fc55b93a8524fc1a4e5697f8142d
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/dbs/milvus.py
@@ -0,0 +1,286 @@
+from pymilvus import MilvusClient as Client
+from pymilvus import FieldSchema, DataType
+import json
+
+from typing import Optional
+
+from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
+from open_webui.config import (
+    MILVUS_URI,
+)
+
+
+class MilvusClient:
+    def __init__(self):
+        self.collection_prefix = "open_webui"
+        self.client = Client(uri=MILVUS_URI)
+
+    def _result_to_get_result(self, result) -> GetResult:
+        ids = []
+        documents = []
+        metadatas = []
+
+        for match in result:
+            _ids = []
+            _documents = []
+            _metadatas = []
+            for item in match:
+                _ids.append(item.get("id"))
+                _documents.append(item.get("data", {}).get("text"))
+                _metadatas.append(item.get("metadata"))
+
+            ids.append(_ids)
+            documents.append(_documents)
+            metadatas.append(_metadatas)
+
+        return GetResult(
+            **{
+                "ids": ids,
+                "documents": documents,
+                "metadatas": metadatas,
+            }
+        )
+
+    def _result_to_search_result(self, result) -> SearchResult:
+        ids = []
+        distances = []
+        documents = []
+        metadatas = []
+
+        for match in result:
+            _ids = []
+            _distances = []
+            _documents = []
+            _metadatas = []
+
+            for item in match:
+                _ids.append(item.get("id"))
+                _distances.append(item.get("distance"))
+                _documents.append(item.get("entity", {}).get("data", {}).get("text"))
+                _metadatas.append(item.get("entity", {}).get("metadata"))
+
+            ids.append(_ids)
+            distances.append(_distances)
+            documents.append(_documents)
+            metadatas.append(_metadatas)
+
+        return SearchResult(
+            **{
+                "ids": ids,
+                "distances": distances,
+                "documents": documents,
+                "metadatas": metadatas,
+            }
+        )
+
+    def _create_collection(self, collection_name: str, dimension: int):
+        schema = self.client.create_schema(
+            auto_id=False,
+            enable_dynamic_field=True,
+        )
+        schema.add_field(
+            field_name="id",
+            datatype=DataType.VARCHAR,
+            is_primary=True,
+            max_length=65535,
+        )
+        schema.add_field(
+            field_name="vector",
+            datatype=DataType.FLOAT_VECTOR,
+            dim=dimension,
+            description="vector",
+        )
+        schema.add_field(field_name="data", datatype=DataType.JSON, description="data")
+        schema.add_field(
+            field_name="metadata", datatype=DataType.JSON, description="metadata"
+        )
+
+        index_params = self.client.prepare_index_params()
+        index_params.add_index(
+            field_name="vector",
+            index_type="HNSW",
+            metric_type="COSINE",
+            params={"M": 16, "efConstruction": 100},
+        )
+
+        self.client.create_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            schema=schema,
+            index_params=index_params,
+        )
+
+    def has_collection(self, collection_name: str) -> bool:
+        # Check if the collection exists based on the collection name.
+        collection_name = collection_name.replace("-", "_")
+        return self.client.has_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}"
+        )
+
+    def delete_collection(self, collection_name: str):
+        # Delete the collection based on the collection name.
+        collection_name = collection_name.replace("-", "_")
+        return self.client.drop_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}"
+        )
+
+    def search(
+        self, collection_name: str, vectors: list[list[float | int]], limit: int
+    ) -> Optional[SearchResult]:
+        # Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
+        collection_name = collection_name.replace("-", "_")
+        result = self.client.search(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            data=vectors,
+            limit=limit,
+            output_fields=["data", "metadata"],
+        )
+
+        return self._result_to_search_result(result)
+
+    def query(self, collection_name: str, filter: dict, limit: Optional[int] = None):
+        # Construct the filter string for querying
+        collection_name = collection_name.replace("-", "_")
+        if not self.has_collection(collection_name):
+            return None
+
+        filter_string = " && ".join(
+            [
+                f'metadata["{key}"] == {json.dumps(value)}'
+                for key, value in filter.items()
+            ]
+        )
+
+        max_limit = 16383  # The maximum number of records per request
+        all_results = []
+
+        if limit is None:
+            limit = float("inf")  # Use infinity as a placeholder for no limit
+
+        # Initialize offset and remaining to handle pagination
+        offset = 0
+        remaining = limit
+
+        try:
+            # Loop until there are no more items to fetch or the desired limit is reached
+            while remaining > 0:
+                print("remaining", remaining)
+                current_fetch = min(
+                    max_limit, remaining
+                )  # Determine how many items to fetch in this iteration
+
+                results = self.client.query(
+                    collection_name=f"{self.collection_prefix}_{collection_name}",
+                    filter=filter_string,
+                    output_fields=["*"],
+                    limit=current_fetch,
+                    offset=offset,
+                )
+
+                if not results:
+                    break
+
+                all_results.extend(results)
+                results_count = len(results)
+                remaining -= (
+                    results_count  # Decrease remaining by the number of items fetched
+                )
+                offset += results_count
+
+                # Break the loop if the results returned are less than the requested fetch count
+                if results_count < current_fetch:
+                    break
+
+            print(all_results)
+            return self._result_to_get_result([all_results])
+        except Exception as e:
+            print(e)
+            return None
+
+    def get(self, collection_name: str) -> Optional[GetResult]:
+        # Get all the items in the collection.
+        collection_name = collection_name.replace("-", "_")
+        result = self.client.query(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            filter='id != ""',
+        )
+        return self._result_to_get_result([result])
+
+    def insert(self, collection_name: str, items: list[VectorItem]):
+        # Insert the items into the collection, if the collection does not exist, it will be created.
+        collection_name = collection_name.replace("-", "_")
+        if not self.client.has_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}"
+        ):
+            self._create_collection(
+                collection_name=collection_name, dimension=len(items[0]["vector"])
+            )
+
+        return self.client.insert(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            data=[
+                {
+                    "id": item["id"],
+                    "vector": item["vector"],
+                    "data": {"text": item["text"]},
+                    "metadata": item["metadata"],
+                }
+                for item in items
+            ],
+        )
+
+    def upsert(self, collection_name: str, items: list[VectorItem]):
+        # Update the items in the collection, if the items are not present, insert them. If the collection does not exist, it will be created.
+        collection_name = collection_name.replace("-", "_")
+        if not self.client.has_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}"
+        ):
+            self._create_collection(
+                collection_name=collection_name, dimension=len(items[0]["vector"])
+            )
+
+        return self.client.upsert(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            data=[
+                {
+                    "id": item["id"],
+                    "vector": item["vector"],
+                    "data": {"text": item["text"]},
+                    "metadata": item["metadata"],
+                }
+                for item in items
+            ],
+        )
+
+    def delete(
+        self,
+        collection_name: str,
+        ids: Optional[list[str]] = None,
+        filter: Optional[dict] = None,
+    ):
+        # Delete the items from the collection based on the ids.
+        collection_name = collection_name.replace("-", "_")
+        if ids:
+            return self.client.delete(
+                collection_name=f"{self.collection_prefix}_{collection_name}",
+                ids=ids,
+            )
+        elif filter:
+            # Convert the filter dictionary to a string using JSON_CONTAINS.
+            filter_string = " && ".join(
+                [
+                    f'metadata["{key}"] == {json.dumps(value)}'
+                    for key, value in filter.items()
+                ]
+            )
+
+            return self.client.delete(
+                collection_name=f"{self.collection_prefix}_{collection_name}",
+                filter=filter_string,
+            )
+
+    def reset(self):
+        # Resets the database. This will delete all collections and item entries.
+        collection_names = self.client.list_collections()
+        for collection_name in collection_names:
+            if collection_name.startswith(self.collection_prefix):
+                self.client.drop_collection(collection_name=collection_name)
diff --git a/backend/open_webui/apps/retrieval/vector/dbs/opensearch.py b/backend/open_webui/apps/retrieval/vector/dbs/opensearch.py
new file mode 100644
index 0000000000000000000000000000000000000000..6234b28378e0f987466065fe548b04d6bbce3338
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/dbs/opensearch.py
@@ -0,0 +1,178 @@
+from opensearchpy import OpenSearch
+from typing import Optional
+
+from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
+from open_webui.config import (
+    OPENSEARCH_URI,
+    OPENSEARCH_SSL,
+    OPENSEARCH_CERT_VERIFY,
+    OPENSEARCH_USERNAME,
+    OPENSEARCH_PASSWORD,
+)
+
+
+class OpenSearchClient:
+    def __init__(self):
+        self.index_prefix = "open_webui"
+        self.client = OpenSearch(
+            hosts=[OPENSEARCH_URI],
+            use_ssl=OPENSEARCH_SSL,
+            verify_certs=OPENSEARCH_CERT_VERIFY,
+            http_auth=(OPENSEARCH_USERNAME, OPENSEARCH_PASSWORD),
+        )
+
+    def _result_to_get_result(self, result) -> GetResult:
+        ids = []
+        documents = []
+        metadatas = []
+
+        for hit in result["hits"]["hits"]:
+            ids.append(hit["_id"])
+            documents.append(hit["_source"].get("text"))
+            metadatas.append(hit["_source"].get("metadata"))
+
+        return GetResult(ids=ids, documents=documents, metadatas=metadatas)
+
+    def _result_to_search_result(self, result) -> SearchResult:
+        ids = []
+        distances = []
+        documents = []
+        metadatas = []
+
+        for hit in result["hits"]["hits"]:
+            ids.append(hit["_id"])
+            distances.append(hit["_score"])
+            documents.append(hit["_source"].get("text"))
+            metadatas.append(hit["_source"].get("metadata"))
+
+        return SearchResult(
+            ids=ids, distances=distances, documents=documents, metadatas=metadatas
+        )
+
+    def _create_index(self, index_name: str, dimension: int):
+        body = {
+            "mappings": {
+                "properties": {
+                    "id": {"type": "keyword"},
+                    "vector": {
+                        "type": "dense_vector",
+                        "dims": dimension,  # Adjust based on your vector dimensions
+                        "index": true,
+                        "similarity": "faiss",
+                        "method": {
+                            "name": "hnsw",
+                            "space_type": "ip",  # Use inner product to approximate cosine similarity
+                            "engine": "faiss",
+                            "ef_construction": 128,
+                            "m": 16,
+                        },
+                    },
+                    "text": {"type": "text"},
+                    "metadata": {"type": "object"},
+                }
+            }
+        }
+        self.client.indices.create(index=f"{self.index_prefix}_{index_name}", body=body)
+
+    def _create_batches(self, items: list[VectorItem], batch_size=100):
+        for i in range(0, len(items), batch_size):
+            yield items[i : i + batch_size]
+
+    def has_collection(self, index_name: str) -> bool:
+        # has_collection here means has index.
+        # We are simply adapting to the norms of the other DBs.
+        return self.client.indices.exists(index=f"{self.index_prefix}_{index_name}")
+
+    def delete_colleciton(self, index_name: str):
+        # delete_collection here means delete index.
+        # We are simply adapting to the norms of the other DBs.
+        self.client.indices.delete(index=f"{self.index_prefix}_{index_name}")
+
+    def search(
+        self, index_name: str, vectors: list[list[float]], limit: int
+    ) -> Optional[SearchResult]:
+        query = {
+            "size": limit,
+            "_source": ["text", "metadata"],
+            "query": {
+                "script_score": {
+                    "query": {"match_all": {}},
+                    "script": {
+                        "source": "cosineSimilarity(params.vector, 'vector') + 1.0",
+                        "params": {
+                            "vector": vectors[0]
+                        },  # Assuming single query vector
+                    },
+                }
+            },
+        }
+
+        result = self.client.search(
+            index=f"{self.index_prefix}_{index_name}", body=query
+        )
+
+        return self._result_to_search_result(result)
+
+    def get_or_create_index(self, index_name: str, dimension: int):
+        if not self.has_index(index_name):
+            self._create_index(index_name, dimension)
+
+    def get(self, index_name: str) -> Optional[GetResult]:
+        query = {"query": {"match_all": {}}, "_source": ["text", "metadata"]}
+
+        result = self.client.search(
+            index=f"{self.index_prefix}_{index_name}", body=query
+        )
+        return self._result_to_get_result(result)
+
+    def insert(self, index_name: str, items: list[VectorItem]):
+        if not self.has_index(index_name):
+            self._create_index(index_name, dimension=len(items[0]["vector"]))
+
+        for batch in self._create_batches(items):
+            actions = [
+                {
+                    "index": {
+                        "_id": item["id"],
+                        "_source": {
+                            "vector": item["vector"],
+                            "text": item["text"],
+                            "metadata": item["metadata"],
+                        },
+                    }
+                }
+                for item in batch
+            ]
+            self.client.bulk(actions)
+
+    def upsert(self, index_name: str, items: list[VectorItem]):
+        if not self.has_index(index_name):
+            self._create_index(index_name, dimension=len(items[0]["vector"]))
+
+        for batch in self._create_batches(items):
+            actions = [
+                {
+                    "index": {
+                        "_id": item["id"],
+                        "_source": {
+                            "vector": item["vector"],
+                            "text": item["text"],
+                            "metadata": item["metadata"],
+                        },
+                    }
+                }
+                for item in batch
+            ]
+            self.client.bulk(actions)
+
+    def delete(self, index_name: str, ids: list[str]):
+        actions = [
+            {"delete": {"_index": f"{self.index_prefix}_{index_name}", "_id": id}}
+            for id in ids
+        ]
+        self.client.bulk(body=actions)
+
+    def reset(self):
+        indices = self.client.indices.get(index=f"{self.index_prefix}_*")
+        for index in indices:
+            self.client.indices.delete(index=index)
diff --git a/backend/open_webui/apps/retrieval/vector/dbs/pgvector.py b/backend/open_webui/apps/retrieval/vector/dbs/pgvector.py
new file mode 100644
index 0000000000000000000000000000000000000000..d537943a161379cec2e7859352443997b14ccf4b
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/dbs/pgvector.py
@@ -0,0 +1,354 @@
+from typing import Optional, List, Dict, Any
+from sqlalchemy import (
+    cast,
+    column,
+    create_engine,
+    Column,
+    Integer,
+    select,
+    text,
+    Text,
+    values,
+)
+from sqlalchemy.sql import true
+from sqlalchemy.pool import NullPool
+
+from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker
+from sqlalchemy.dialects.postgresql import JSONB, array
+from pgvector.sqlalchemy import Vector
+from sqlalchemy.ext.mutable import MutableDict
+
+from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
+from open_webui.config import PGVECTOR_DB_URL
+
+VECTOR_LENGTH = 1536
+Base = declarative_base()
+
+
+class DocumentChunk(Base):
+    __tablename__ = "document_chunk"
+
+    id = Column(Text, primary_key=True)
+    vector = Column(Vector(dim=VECTOR_LENGTH), nullable=True)
+    collection_name = Column(Text, nullable=False)
+    text = Column(Text, nullable=True)
+    vmetadata = Column(MutableDict.as_mutable(JSONB), nullable=True)
+
+
+class PgvectorClient:
+    def __init__(self) -> None:
+
+        # if no pgvector uri, use the existing database connection
+        if not PGVECTOR_DB_URL:
+            from open_webui.apps.webui.internal.db import Session
+
+            self.session = Session
+        else:
+            engine = create_engine(
+                PGVECTOR_DB_URL, pool_pre_ping=True, poolclass=NullPool
+            )
+            SessionLocal = sessionmaker(
+                autocommit=False, autoflush=False, bind=engine, expire_on_commit=False
+            )
+            self.session = scoped_session(SessionLocal)
+
+        try:
+            # Ensure the pgvector extension is available
+            self.session.execute(text("CREATE EXTENSION IF NOT EXISTS vector;"))
+
+            # Create the tables if they do not exist
+            # Base.metadata.create_all requires a bind (engine or connection)
+            # Get the connection from the session
+            connection = self.session.connection()
+            Base.metadata.create_all(bind=connection)
+
+            # Create an index on the vector column if it doesn't exist
+            self.session.execute(
+                text(
+                    "CREATE INDEX IF NOT EXISTS idx_document_chunk_vector "
+                    "ON document_chunk USING ivfflat (vector vector_cosine_ops) WITH (lists = 100);"
+                )
+            )
+            self.session.execute(
+                text(
+                    "CREATE INDEX IF NOT EXISTS idx_document_chunk_collection_name "
+                    "ON document_chunk (collection_name);"
+                )
+            )
+            self.session.commit()
+            print("Initialization complete.")
+        except Exception as e:
+            self.session.rollback()
+            print(f"Error during initialization: {e}")
+            raise
+
+    def adjust_vector_length(self, vector: List[float]) -> List[float]:
+        # Adjust vector to have length VECTOR_LENGTH
+        current_length = len(vector)
+        if current_length < VECTOR_LENGTH:
+            # Pad the vector with zeros
+            vector += [0.0] * (VECTOR_LENGTH - current_length)
+        elif current_length > VECTOR_LENGTH:
+            raise Exception(
+                f"Vector length {current_length} not supported. Max length must be <= {VECTOR_LENGTH}"
+            )
+        return vector
+
+    def insert(self, collection_name: str, items: List[VectorItem]) -> None:
+        try:
+            new_items = []
+            for item in items:
+                vector = self.adjust_vector_length(item["vector"])
+                new_chunk = DocumentChunk(
+                    id=item["id"],
+                    vector=vector,
+                    collection_name=collection_name,
+                    text=item["text"],
+                    vmetadata=item["metadata"],
+                )
+                new_items.append(new_chunk)
+            self.session.bulk_save_objects(new_items)
+            self.session.commit()
+            print(
+                f"Inserted {len(new_items)} items into collection '{collection_name}'."
+            )
+        except Exception as e:
+            self.session.rollback()
+            print(f"Error during insert: {e}")
+            raise
+
+    def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
+        try:
+            for item in items:
+                vector = self.adjust_vector_length(item["vector"])
+                existing = (
+                    self.session.query(DocumentChunk)
+                    .filter(DocumentChunk.id == item["id"])
+                    .first()
+                )
+                if existing:
+                    existing.vector = vector
+                    existing.text = item["text"]
+                    existing.vmetadata = item["metadata"]
+                    existing.collection_name = (
+                        collection_name  # Update collection_name if necessary
+                    )
+                else:
+                    new_chunk = DocumentChunk(
+                        id=item["id"],
+                        vector=vector,
+                        collection_name=collection_name,
+                        text=item["text"],
+                        vmetadata=item["metadata"],
+                    )
+                    self.session.add(new_chunk)
+            self.session.commit()
+            print(f"Upserted {len(items)} items into collection '{collection_name}'.")
+        except Exception as e:
+            self.session.rollback()
+            print(f"Error during upsert: {e}")
+            raise
+
+    def search(
+        self,
+        collection_name: str,
+        vectors: List[List[float]],
+        limit: Optional[int] = None,
+    ) -> Optional[SearchResult]:
+        try:
+            if not vectors:
+                return None
+
+            # Adjust query vectors to VECTOR_LENGTH
+            vectors = [self.adjust_vector_length(vector) for vector in vectors]
+            num_queries = len(vectors)
+
+            def vector_expr(vector):
+                return cast(array(vector), Vector(VECTOR_LENGTH))
+
+            # Create the values for query vectors
+            qid_col = column("qid", Integer)
+            q_vector_col = column("q_vector", Vector(VECTOR_LENGTH))
+            query_vectors = (
+                values(qid_col, q_vector_col)
+                .data(
+                    [(idx, vector_expr(vector)) for idx, vector in enumerate(vectors)]
+                )
+                .alias("query_vectors")
+            )
+
+            # Build the lateral subquery for each query vector
+            subq = (
+                select(
+                    DocumentChunk.id,
+                    DocumentChunk.text,
+                    DocumentChunk.vmetadata,
+                    (
+                        DocumentChunk.vector.cosine_distance(query_vectors.c.q_vector)
+                    ).label("distance"),
+                )
+                .where(DocumentChunk.collection_name == collection_name)
+                .order_by(
+                    (DocumentChunk.vector.cosine_distance(query_vectors.c.q_vector))
+                )
+            )
+            if limit is not None:
+                subq = subq.limit(limit)
+            subq = subq.lateral("result")
+
+            # Build the main query by joining query_vectors and the lateral subquery
+            stmt = (
+                select(
+                    query_vectors.c.qid,
+                    subq.c.id,
+                    subq.c.text,
+                    subq.c.vmetadata,
+                    subq.c.distance,
+                )
+                .select_from(query_vectors)
+                .join(subq, true())
+                .order_by(query_vectors.c.qid, subq.c.distance)
+            )
+
+            result_proxy = self.session.execute(stmt)
+            results = result_proxy.all()
+
+            ids = [[] for _ in range(num_queries)]
+            distances = [[] for _ in range(num_queries)]
+            documents = [[] for _ in range(num_queries)]
+            metadatas = [[] for _ in range(num_queries)]
+
+            if not results:
+                return SearchResult(
+                    ids=ids,
+                    distances=distances,
+                    documents=documents,
+                    metadatas=metadatas,
+                )
+
+            for row in results:
+                qid = int(row.qid)
+                ids[qid].append(row.id)
+                distances[qid].append(row.distance)
+                documents[qid].append(row.text)
+                metadatas[qid].append(row.vmetadata)
+
+            return SearchResult(
+                ids=ids, distances=distances, documents=documents, metadatas=metadatas
+            )
+        except Exception as e:
+            print(f"Error during search: {e}")
+            return None
+
+    def query(
+        self, collection_name: str, filter: Dict[str, Any], limit: Optional[int] = None
+    ) -> Optional[GetResult]:
+        try:
+            query = self.session.query(DocumentChunk).filter(
+                DocumentChunk.collection_name == collection_name
+            )
+
+            for key, value in filter.items():
+                query = query.filter(DocumentChunk.vmetadata[key].astext == str(value))
+
+            if limit is not None:
+                query = query.limit(limit)
+
+            results = query.all()
+
+            if not results:
+                return None
+
+            ids = [[result.id for result in results]]
+            documents = [[result.text for result in results]]
+            metadatas = [[result.vmetadata for result in results]]
+
+            return GetResult(
+                ids=ids,
+                documents=documents,
+                metadatas=metadatas,
+            )
+        except Exception as e:
+            print(f"Error during query: {e}")
+            return None
+
+    def get(
+        self, collection_name: str, limit: Optional[int] = None
+    ) -> Optional[GetResult]:
+        try:
+            query = self.session.query(DocumentChunk).filter(
+                DocumentChunk.collection_name == collection_name
+            )
+            if limit is not None:
+                query = query.limit(limit)
+
+            results = query.all()
+
+            if not results:
+                return None
+
+            ids = [[result.id for result in results]]
+            documents = [[result.text for result in results]]
+            metadatas = [[result.vmetadata for result in results]]
+
+            return GetResult(ids=ids, documents=documents, metadatas=metadatas)
+        except Exception as e:
+            print(f"Error during get: {e}")
+            return None
+
+    def delete(
+        self,
+        collection_name: str,
+        ids: Optional[List[str]] = None,
+        filter: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        try:
+            query = self.session.query(DocumentChunk).filter(
+                DocumentChunk.collection_name == collection_name
+            )
+            if ids:
+                query = query.filter(DocumentChunk.id.in_(ids))
+            if filter:
+                for key, value in filter.items():
+                    query = query.filter(
+                        DocumentChunk.vmetadata[key].astext == str(value)
+                    )
+            deleted = query.delete(synchronize_session=False)
+            self.session.commit()
+            print(f"Deleted {deleted} items from collection '{collection_name}'.")
+        except Exception as e:
+            self.session.rollback()
+            print(f"Error during delete: {e}")
+            raise
+
+    def reset(self) -> None:
+        try:
+            deleted = self.session.query(DocumentChunk).delete()
+            self.session.commit()
+            print(
+                f"Reset complete. Deleted {deleted} items from 'document_chunk' table."
+            )
+        except Exception as e:
+            self.session.rollback()
+            print(f"Error during reset: {e}")
+            raise
+
+    def close(self) -> None:
+        pass
+
+    def has_collection(self, collection_name: str) -> bool:
+        try:
+            exists = (
+                self.session.query(DocumentChunk)
+                .filter(DocumentChunk.collection_name == collection_name)
+                .first()
+                is not None
+            )
+            return exists
+        except Exception as e:
+            print(f"Error checking collection existence: {e}")
+            return False
+
+    def delete_collection(self, collection_name: str) -> None:
+        self.delete(collection_name)
+        print(f"Collection '{collection_name}' deleted.")
diff --git a/backend/open_webui/apps/retrieval/vector/dbs/qdrant.py b/backend/open_webui/apps/retrieval/vector/dbs/qdrant.py
new file mode 100644
index 0000000000000000000000000000000000000000..60c1c3d4d1fc38c670ae4fd43c73e034d22c1845
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/dbs/qdrant.py
@@ -0,0 +1,184 @@
+from typing import Optional
+
+from qdrant_client import QdrantClient as Qclient
+from qdrant_client.http.models import PointStruct
+from qdrant_client.models import models
+
+from open_webui.apps.retrieval.vector.main import VectorItem, SearchResult, GetResult
+from open_webui.config import QDRANT_URI, QDRANT_API_KEY
+
+NO_LIMIT = 999999999
+
+
+class QdrantClient:
+    def __init__(self):
+        self.collection_prefix = "open-webui"
+        self.QDRANT_URI = QDRANT_URI
+        self.QDRANT_API_KEY = QDRANT_API_KEY
+        self.client = (
+            Qclient(url=self.QDRANT_URI, api_key=self.QDRANT_API_KEY)
+            if self.QDRANT_URI
+            else None
+        )
+
+    def _result_to_get_result(self, points) -> GetResult:
+        ids = []
+        documents = []
+        metadatas = []
+
+        for point in points:
+            payload = point.payload
+            ids.append(point.id)
+            documents.append(payload["text"])
+            metadatas.append(payload["metadata"])
+
+        return GetResult(
+            **{
+                "ids": [ids],
+                "documents": [documents],
+                "metadatas": [metadatas],
+            }
+        )
+
+    def _create_collection(self, collection_name: str, dimension: int):
+        collection_name_with_prefix = f"{self.collection_prefix}_{collection_name}"
+        self.client.create_collection(
+            collection_name=collection_name_with_prefix,
+            vectors_config=models.VectorParams(
+                size=dimension, distance=models.Distance.COSINE
+            ),
+        )
+
+        print(f"collection {collection_name_with_prefix} successfully created!")
+
+    def _create_collection_if_not_exists(self, collection_name, dimension):
+        if not self.has_collection(collection_name=collection_name):
+            self._create_collection(
+                collection_name=collection_name, dimension=dimension
+            )
+
+    def _create_points(self, items: list[VectorItem]):
+        return [
+            PointStruct(
+                id=item["id"],
+                vector=item["vector"],
+                payload={"text": item["text"], "metadata": item["metadata"]},
+            )
+            for item in items
+        ]
+
+    def has_collection(self, collection_name: str) -> bool:
+        return self.client.collection_exists(
+            f"{self.collection_prefix}_{collection_name}"
+        )
+
+    def delete_collection(self, collection_name: str):
+        return self.client.delete_collection(
+            collection_name=f"{self.collection_prefix}_{collection_name}"
+        )
+
+    def search(
+        self, collection_name: str, vectors: list[list[float | int]], limit: int
+    ) -> Optional[SearchResult]:
+        # Search for the nearest neighbor items based on the vectors and return 'limit' number of results.
+        if limit is None:
+            limit = NO_LIMIT  # otherwise qdrant would set limit to 10!
+
+        query_response = self.client.query_points(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            query=vectors[0],
+            limit=limit,
+        )
+        get_result = self._result_to_get_result(query_response.points)
+        return SearchResult(
+            ids=get_result.ids,
+            documents=get_result.documents,
+            metadatas=get_result.metadatas,
+            distances=[[point.score for point in query_response.points]],
+        )
+
+    def query(self, collection_name: str, filter: dict, limit: Optional[int] = None):
+        # Construct the filter string for querying
+        if not self.has_collection(collection_name):
+            return None
+        try:
+            if limit is None:
+                limit = NO_LIMIT  # otherwise qdrant would set limit to 10!
+
+            field_conditions = []
+            for key, value in filter.items():
+                field_conditions.append(
+                    models.FieldCondition(
+                        key=f"metadata.{key}", match=models.MatchValue(value=value)
+                    )
+                )
+
+            points = self.client.query_points(
+                collection_name=f"{self.collection_prefix}_{collection_name}",
+                query_filter=models.Filter(should=field_conditions),
+                limit=limit,
+            )
+            return self._result_to_get_result(points.points)
+        except Exception as e:
+            print(e)
+            return None
+
+    def get(self, collection_name: str) -> Optional[GetResult]:
+        # Get all the items in the collection.
+        points = self.client.query_points(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            limit=NO_LIMIT,  # otherwise qdrant would set limit to 10!
+        )
+        return self._result_to_get_result(points.points)
+
+    def insert(self, collection_name: str, items: list[VectorItem]):
+        # Insert the items into the collection, if the collection does not exist, it will be created.
+        self._create_collection_if_not_exists(collection_name, len(items[0]["vector"]))
+        points = self._create_points(items)
+        self.client.upload_points(f"{self.collection_prefix}_{collection_name}", points)
+
+    def upsert(self, collection_name: str, items: list[VectorItem]):
+        # Update the items in the collection, if the items are not present, insert them. If the collection does not exist, it will be created.
+        self._create_collection_if_not_exists(collection_name, len(items[0]["vector"]))
+        points = self._create_points(items)
+        return self.client.upsert(f"{self.collection_prefix}_{collection_name}", points)
+
+    def delete(
+        self,
+        collection_name: str,
+        ids: Optional[list[str]] = None,
+        filter: Optional[dict] = None,
+    ):
+        # Delete the items from the collection based on the ids.
+        field_conditions = []
+
+        if ids:
+            for id_value in ids:
+                field_conditions.append(
+                    models.FieldCondition(
+                        key="metadata.id",
+                        match=models.MatchValue(value=id_value),
+                    ),
+                ),
+        elif filter:
+            for key, value in filter.items():
+                field_conditions.append(
+                    models.FieldCondition(
+                        key=f"metadata.{key}",
+                        match=models.MatchValue(value=value),
+                    ),
+                ),
+
+        return self.client.delete(
+            collection_name=f"{self.collection_prefix}_{collection_name}",
+            points_selector=models.FilterSelector(
+                filter=models.Filter(must=field_conditions)
+            ),
+        )
+
+    def reset(self):
+        # Resets the database. This will delete all collections and item entries.
+        collection_names = self.client.get_collections().collections
+        for collection_name in collection_names:
+            if collection_name.name.startswith(self.collection_prefix):
+                self.client.delete_collection(collection_name=collection_name.name)
diff --git a/backend/open_webui/apps/retrieval/vector/main.py b/backend/open_webui/apps/retrieval/vector/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..f0cf0c03876c1f803ddc0d6e3f0903026df50a12
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/vector/main.py
@@ -0,0 +1,19 @@
+from pydantic import BaseModel
+from typing import Optional, List, Any
+
+
+class VectorItem(BaseModel):
+    id: str
+    text: str
+    vector: List[float | int]
+    metadata: Any
+
+
+class GetResult(BaseModel):
+    ids: Optional[List[List[str]]]
+    documents: Optional[List[List[str]]]
+    metadatas: Optional[List[List[Any]]]
+
+
+class SearchResult(GetResult):
+    distances: Optional[List[List[float | int]]]
diff --git a/backend/open_webui/apps/retrieval/web/bing.py b/backend/open_webui/apps/retrieval/web/bing.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5f889c54ac8674dc91bc6b644b318106977f1ea
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/bing.py
@@ -0,0 +1,73 @@
+import logging
+import os
+from pprint import pprint
+from typing import Optional
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+import argparse
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+"""
+Documentation: https://docs.microsoft.com/en-us/bing/search-apis/bing-web-search/overview
+"""
+
+
+def search_bing(
+    subscription_key: str,
+    endpoint: str,
+    locale: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+) -> list[SearchResult]:
+    mkt = locale
+    params = {"q": query, "mkt": mkt, "answerCount": count}
+    headers = {"Ocp-Apim-Subscription-Key": subscription_key}
+
+    try:
+        response = requests.get(endpoint, headers=headers, params=params)
+        response.raise_for_status()
+        json_response = response.json()
+        results = json_response.get("webPages", {}).get("value", [])
+        if filter_list:
+            results = get_filtered_results(results, filter_list)
+        return [
+            SearchResult(
+                link=result["url"],
+                title=result.get("name"),
+                snippet=result.get("snippet"),
+            )
+            for result in results
+        ]
+    except Exception as ex:
+        log.error(f"Error: {ex}")
+        raise ex
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Search Bing from the command line.")
+    parser.add_argument(
+        "query",
+        type=str,
+        default="Top 10 international news today",
+        help="The search query.",
+    )
+    parser.add_argument(
+        "--count", type=int, default=10, help="Number of search results to return."
+    )
+    parser.add_argument(
+        "--filter", nargs="*", help="List of filters to apply to the search results."
+    )
+    parser.add_argument(
+        "--locale",
+        type=str,
+        default="en-US",
+        help="The locale to use for the search, maps to market in api",
+    )
+
+    args = parser.parse_args()
+
+    results = search_bing(args.locale, args.query, args.count, args.filter)
+    pprint(results)
diff --git a/backend/open_webui/apps/retrieval/web/brave.py b/backend/open_webui/apps/retrieval/web/brave.py
new file mode 100644
index 0000000000000000000000000000000000000000..f988b3b08ef6f432dd2205263cbbf662a117fcfc
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/brave.py
@@ -0,0 +1,42 @@
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_brave(
+    api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None
+) -> list[SearchResult]:
+    """Search using Brave's Search API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A Brave Search API key
+        query (str): The query to search for
+    """
+    url = "https://api.search.brave.com/res/v1/web/search"
+    headers = {
+        "Accept": "application/json",
+        "Accept-Encoding": "gzip",
+        "X-Subscription-Token": api_key,
+    }
+    params = {"q": query, "count": count}
+
+    response = requests.get(url, headers=headers, params=params)
+    response.raise_for_status()
+
+    json_response = response.json()
+    results = json_response.get("web", {}).get("results", [])
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+
+    return [
+        SearchResult(
+            link=result["url"], title=result.get("title"), snippet=result.get("snippet")
+        )
+        for result in results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/duckduckgo.py b/backend/open_webui/apps/retrieval/web/duckduckgo.py
new file mode 100644
index 0000000000000000000000000000000000000000..11e5122964761b37f992e51325aaf793e951377b
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/duckduckgo.py
@@ -0,0 +1,50 @@
+import logging
+from typing import Optional
+
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from duckduckgo_search import DDGS
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_duckduckgo(
+    query: str, count: int, filter_list: Optional[list[str]] = None
+) -> list[SearchResult]:
+    """
+    Search using DuckDuckGo's Search API and return the results as a list of SearchResult objects.
+    Args:
+        query (str): The query to search for
+        count (int): The number of results to return
+
+    Returns:
+        list[SearchResult]: A list of search results
+    """
+    # Use the DDGS context manager to create a DDGS object
+    with DDGS() as ddgs:
+        # Use the ddgs.text() method to perform the search
+        ddgs_gen = ddgs.text(
+            query, safesearch="moderate", max_results=count, backend="api"
+        )
+        # Check if there are search results
+        if ddgs_gen:
+            # Convert the search results into a list
+            search_results = [r for r in ddgs_gen]
+
+    # Create an empty list to store the SearchResult objects
+    results = []
+    # Iterate over each search result
+    for result in search_results:
+        # Create a SearchResult object and append it to the results list
+        results.append(
+            SearchResult(
+                link=result["href"],
+                title=result.get("title"),
+                snippet=result.get("body"),
+            )
+        )
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    # Return the list of search results
+    return results
diff --git a/backend/open_webui/apps/retrieval/web/google_pse.py b/backend/open_webui/apps/retrieval/web/google_pse.py
new file mode 100644
index 0000000000000000000000000000000000000000..61b919583ceba95eb02fddd4db41feeef6e5b2ea
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/google_pse.py
@@ -0,0 +1,50 @@
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_google_pse(
+    api_key: str,
+    search_engine_id: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+) -> list[SearchResult]:
+    """Search using Google's Programmable Search Engine API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A Programmable Search Engine API key
+        search_engine_id (str): A Programmable Search Engine ID
+        query (str): The query to search for
+    """
+    url = "https://www.googleapis.com/customsearch/v1"
+
+    headers = {"Content-Type": "application/json"}
+    params = {
+        "cx": search_engine_id,
+        "q": query,
+        "key": api_key,
+        "num": count,
+    }
+
+    response = requests.request("GET", url, headers=headers, params=params)
+    response.raise_for_status()
+
+    json_response = response.json()
+    results = json_response.get("items", [])
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    return [
+        SearchResult(
+            link=result["link"],
+            title=result.get("title"),
+            snippet=result.get("snippet"),
+        )
+        for result in results
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/jina_search.py b/backend/open_webui/apps/retrieval/web/jina_search.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5e2febbe132be4d9a54ee3e7d4557605621204b
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/jina_search.py
@@ -0,0 +1,39 @@
+import logging
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult
+from open_webui.env import SRC_LOG_LEVELS
+from yarl import URL
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_jina(api_key: str, query: str, count: int) -> list[SearchResult]:
+    """
+    Search using Jina's Search API and return the results as a list of SearchResult objects.
+    Args:
+        query (str): The query to search for
+        count (int): The number of results to return
+
+    Returns:
+        list[SearchResult]: A list of search results
+    """
+    jina_search_endpoint = "https://s.jina.ai/"
+    headers = {"Accept": "application/json", "Authorization": f"Bearer {api_key}"}
+    url = str(URL(jina_search_endpoint + query))
+    response = requests.get(url, headers=headers)
+    response.raise_for_status()
+    data = response.json()
+
+    results = []
+    for result in data["data"][:count]:
+        results.append(
+            SearchResult(
+                link=result["url"],
+                title=result.get("title"),
+                snippet=result.get("content"),
+            )
+        )
+
+    return results
diff --git a/backend/open_webui/apps/retrieval/web/main.py b/backend/open_webui/apps/retrieval/web/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1af8a70aa15fa97ef5d5bff02ef33b5cc86c42b0
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/main.py
@@ -0,0 +1,22 @@
+from typing import Optional
+from urllib.parse import urlparse
+
+from pydantic import BaseModel
+
+
+def get_filtered_results(results, filter_list):
+    if not filter_list:
+        return results
+    filtered_results = []
+    for result in results:
+        url = result.get("url") or result.get("link", "")
+        domain = urlparse(url).netloc
+        if any(domain.endswith(filtered_domain) for filtered_domain in filter_list):
+            filtered_results.append(result)
+    return filtered_results
+
+
+class SearchResult(BaseModel):
+    link: str
+    title: Optional[str]
+    snippet: Optional[str]
diff --git a/backend/open_webui/apps/retrieval/web/mojeek.py b/backend/open_webui/apps/retrieval/web/mojeek.py
new file mode 100644
index 0000000000000000000000000000000000000000..f257c92aafe262f8dff9c6e03287c464768eacd5
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/mojeek.py
@@ -0,0 +1,40 @@
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_mojeek(
+    api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None
+) -> list[SearchResult]:
+    """Search using Mojeek's Search API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A Mojeek Search API key
+        query (str): The query to search for
+    """
+    url = "https://api.mojeek.com/search"
+    headers = {
+        "Accept": "application/json",
+    }
+    params = {"q": query, "api_key": api_key, "fmt": "json", "t": count}
+
+    response = requests.get(url, headers=headers, params=params)
+    response.raise_for_status()
+    json_response = response.json()
+    results = json_response.get("response", {}).get("results", [])
+    print(results)
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+
+    return [
+        SearchResult(
+            link=result["url"], title=result.get("title"), snippet=result.get("desc")
+        )
+        for result in results
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/searchapi.py b/backend/open_webui/apps/retrieval/web/searchapi.py
new file mode 100644
index 0000000000000000000000000000000000000000..412dc6b6955c691e0e8050b1cdb71269cc27bce6
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/searchapi.py
@@ -0,0 +1,48 @@
+import logging
+from typing import Optional
+from urllib.parse import urlencode
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_searchapi(
+    api_key: str,
+    engine: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+) -> list[SearchResult]:
+    """Search using searchapi.io's API and return the results as a list of SearchResult objects.
+
+    Args:
+      api_key (str): A searchapi.io API key
+      query (str): The query to search for
+    """
+    url = "https://www.searchapi.io/api/v1/search"
+
+    engine = engine or "google"
+
+    payload = {"engine": engine, "q": query, "api_key": api_key}
+
+    url = f"{url}?{urlencode(payload)}"
+    response = requests.request("GET", url)
+
+    json_response = response.json()
+    log.info(f"results from searchapi search: {json_response}")
+
+    results = sorted(
+        json_response.get("organic_results", []), key=lambda x: x.get("position", 0)
+    )
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    return [
+        SearchResult(
+            link=result["link"], title=result["title"], snippet=result["snippet"]
+        )
+        for result in results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/searxng.py b/backend/open_webui/apps/retrieval/web/searxng.py
new file mode 100644
index 0000000000000000000000000000000000000000..cb1eaf91d03f88299e70b8e304b1a97a09aeff38
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/searxng.py
@@ -0,0 +1,91 @@
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_searxng(
+    query_url: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+    **kwargs,
+) -> list[SearchResult]:
+    """
+    Search a SearXNG instance for a given query and return the results as a list of SearchResult objects.
+
+    The function allows passing additional parameters such as language or time_range to tailor the search result.
+
+    Args:
+        query_url (str): The base URL of the SearXNG server.
+        query (str): The search term or question to find in the SearXNG database.
+        count (int): The maximum number of results to retrieve from the search.
+
+    Keyword Args:
+        language (str): Language filter for the search results; e.g., "en-US". Defaults to an empty string.
+        safesearch (int): Safe search filter for safer web results; 0 = off, 1 = moderate, 2 = strict. Defaults to 1 (moderate).
+        time_range (str): Time range for filtering results by date; e.g., "2023-04-05..today" or "all-time". Defaults to ''.
+        categories: (Optional[list[str]]): Specific categories within which the search should be performed, defaulting to an empty string if not provided.
+
+    Returns:
+        list[SearchResult]: A list of SearchResults sorted by relevance score in descending order.
+
+    Raise:
+        requests.exceptions.RequestException: If a request error occurs during the search process.
+    """
+
+    # Default values for optional parameters are provided as empty strings or None when not specified.
+    language = kwargs.get("language", "en-US")
+    safesearch = kwargs.get("safesearch", "1")
+    time_range = kwargs.get("time_range", "")
+    categories = "".join(kwargs.get("categories", []))
+
+    params = {
+        "q": query,
+        "format": "json",
+        "pageno": 1,
+        "safesearch": safesearch,
+        "language": language,
+        "time_range": time_range,
+        "categories": categories,
+        "theme": "simple",
+        "image_proxy": 0,
+    }
+
+    # Legacy query format
+    if "<query>" in query_url:
+        # Strip all query parameters from the URL
+        query_url = query_url.split("?")[0]
+
+    log.debug(f"searching {query_url}")
+
+    response = requests.get(
+        query_url,
+        headers={
+            "User-Agent": "Open WebUI (https://github.com/open-webui/open-webui) RAG Bot",
+            "Accept": "text/html",
+            "Accept-Encoding": "gzip, deflate",
+            "Accept-Language": "en-US,en;q=0.5",
+            "Connection": "keep-alive",
+        },
+        params=params,
+    )
+
+    response.raise_for_status()  # Raise an exception for HTTP errors.
+
+    json_response = response.json()
+    results = json_response.get("results", [])
+    sorted_results = sorted(results, key=lambda x: x.get("score", 0), reverse=True)
+    if filter_list:
+        sorted_results = get_filtered_results(sorted_results, filter_list)
+    return [
+        SearchResult(
+            link=result["url"], title=result.get("title"), snippet=result.get("content")
+        )
+        for result in sorted_results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/serper.py b/backend/open_webui/apps/retrieval/web/serper.py
new file mode 100644
index 0000000000000000000000000000000000000000..436fa167e989ad5aba782f57e2cf36ba1e8bb619
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/serper.py
@@ -0,0 +1,43 @@
+import json
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_serper(
+    api_key: str, query: str, count: int, filter_list: Optional[list[str]] = None
+) -> list[SearchResult]:
+    """Search using serper.dev's API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A serper.dev API key
+        query (str): The query to search for
+    """
+    url = "https://google.serper.dev/search"
+
+    payload = json.dumps({"q": query})
+    headers = {"X-API-KEY": api_key, "Content-Type": "application/json"}
+
+    response = requests.request("POST", url, headers=headers, data=payload)
+    response.raise_for_status()
+
+    json_response = response.json()
+    results = sorted(
+        json_response.get("organic", []), key=lambda x: x.get("position", 0)
+    )
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    return [
+        SearchResult(
+            link=result["link"],
+            title=result.get("title"),
+            snippet=result.get("description"),
+        )
+        for result in results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/serply.py b/backend/open_webui/apps/retrieval/web/serply.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c2521c47ab894fe1a8d8eca73af8a272af2c96e
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/serply.py
@@ -0,0 +1,69 @@
+import logging
+from typing import Optional
+from urllib.parse import urlencode
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_serply(
+    api_key: str,
+    query: str,
+    count: int,
+    hl: str = "us",
+    limit: int = 10,
+    device_type: str = "desktop",
+    proxy_location: str = "US",
+    filter_list: Optional[list[str]] = None,
+) -> list[SearchResult]:
+    """Search using serper.dev's API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A serply.io API key
+        query (str): The query to search for
+        hl (str): Host Language code to display results in (reference https://developers.google.com/custom-search/docs/xml_results?hl=en#wsInterfaceLanguages)
+        limit (int): The maximum number of results to return [10-100, defaults to 10]
+    """
+    log.info("Searching with Serply")
+
+    url = "https://api.serply.io/v1/search/"
+
+    query_payload = {
+        "q": query,
+        "language": "en",
+        "num": limit,
+        "gl": proxy_location.upper(),
+        "hl": hl.lower(),
+    }
+
+    url = f"{url}{urlencode(query_payload)}"
+    headers = {
+        "X-API-KEY": api_key,
+        "X-User-Agent": device_type,
+        "User-Agent": "open-webui",
+        "X-Proxy-Location": proxy_location,
+    }
+
+    response = requests.request("GET", url, headers=headers)
+    response.raise_for_status()
+
+    json_response = response.json()
+    log.info(f"results from serply search: {json_response}")
+
+    results = sorted(
+        json_response.get("results", []), key=lambda x: x.get("realPosition", 0)
+    )
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    return [
+        SearchResult(
+            link=result["link"],
+            title=result.get("title"),
+            snippet=result.get("description"),
+        )
+        for result in results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/serpstack.py b/backend/open_webui/apps/retrieval/web/serpstack.py
new file mode 100644
index 0000000000000000000000000000000000000000..b655934de5a692faa01e4d28ebb7c41ecda85df3
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/serpstack.py
@@ -0,0 +1,48 @@
+import logging
+from typing import Optional
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult, get_filtered_results
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_serpstack(
+    api_key: str,
+    query: str,
+    count: int,
+    filter_list: Optional[list[str]] = None,
+    https_enabled: bool = True,
+) -> list[SearchResult]:
+    """Search using serpstack.com's and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A serpstack.com API key
+        query (str): The query to search for
+        https_enabled (bool): Whether to use HTTPS or HTTP for the API request
+    """
+    url = f"{'https' if https_enabled else 'http'}://api.serpstack.com/search"
+
+    headers = {"Content-Type": "application/json"}
+    params = {
+        "access_key": api_key,
+        "query": query,
+    }
+
+    response = requests.request("POST", url, headers=headers, params=params)
+    response.raise_for_status()
+
+    json_response = response.json()
+    results = sorted(
+        json_response.get("organic_results", []), key=lambda x: x.get("position", 0)
+    )
+    if filter_list:
+        results = get_filtered_results(results, filter_list)
+    return [
+        SearchResult(
+            link=result["url"], title=result.get("title"), snippet=result.get("snippet")
+        )
+        for result in results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/tavily.py b/backend/open_webui/apps/retrieval/web/tavily.py
new file mode 100644
index 0000000000000000000000000000000000000000..03b0be75ac027053a5d7dd3d2f6a2fc05db52af0
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/tavily.py
@@ -0,0 +1,38 @@
+import logging
+
+import requests
+from open_webui.apps.retrieval.web.main import SearchResult
+from open_webui.env import SRC_LOG_LEVELS
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def search_tavily(api_key: str, query: str, count: int) -> list[SearchResult]:
+    """Search using Tavily's Search API and return the results as a list of SearchResult objects.
+
+    Args:
+        api_key (str): A Tavily Search API key
+        query (str): The query to search for
+
+    Returns:
+        list[SearchResult]: A list of search results
+    """
+    url = "https://api.tavily.com/search"
+    data = {"query": query, "api_key": api_key}
+
+    response = requests.post(url, json=data)
+    response.raise_for_status()
+
+    json_response = response.json()
+
+    raw_search_results = json_response.get("results", [])
+
+    return [
+        SearchResult(
+            link=result["url"],
+            title=result.get("title", ""),
+            snippet=result.get("content"),
+        )
+        for result in raw_search_results[:count]
+    ]
diff --git a/backend/open_webui/apps/retrieval/web/testdata/bing.json b/backend/open_webui/apps/retrieval/web/testdata/bing.json
new file mode 100644
index 0000000000000000000000000000000000000000..80324f3b40fa5f8504d027ca028ab103b21f45fd
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/bing.json
@@ -0,0 +1,58 @@
+{
+	"_type": "SearchResponse",
+	"queryContext": {
+		"originalQuery": "Top 10 international results"
+	},
+	"webPages": {
+		"webSearchUrl": "https://www.bing.com/search?q=Top+10+international+results",
+		"totalEstimatedMatches": 687,
+		"value": [
+			{
+				"id": "https://api.bing.microsoft.com/api/v7/#WebPages.0",
+				"name": "2024 Mexican Grand Prix - F1 results and latest standings ... - PlanetF1",
+				"url": "https://www.planetf1.com/news/f1-results-2024-mexican-grand-prix-race-standings",
+				"datePublished": "2024-10-27T00:00:00.0000000",
+				"datePublishedFreshnessText": "1 day ago",
+				"isFamilyFriendly": true,
+				"displayUrl": "https://www.planetf1.com/news/f1-results-2024-mexican-grand-prix-race-standings",
+				"snippet": "Nico Hulkenberg and Pierre Gasly completed the top 10. A full report of the Mexican Grand Prix is available at the bottom of this article. F1 results – 2024 Mexican Grand Prix",
+				"dateLastCrawled": "2024-10-28T07:15:00.0000000Z",
+				"cachedPageUrl": "https://cc.bingj.com/cache.aspx?q=Top+10+international+results&d=916492551782&mkt=en-US&setlang=en-US&w=zBsfaAPyF2tUrHFHr_vFFdUm8sng4g34",
+				"language": "en",
+				"isNavigational": false,
+				"noCache": false
+			},
+			{
+				"id": "https://api.bing.microsoft.com/api/v7/#WebPages.1",
+				"name": "F1 Results Today: HUGE Verstappen penalties cause major title change",
+				"url": "https://www.gpfans.com/en/f1-news/1033512/f1-results-today-mexican-grand-prix-huge-max-verstappen-penalties-cause-major-title-change/",
+				"datePublished": "2024-10-27T00:00:00.0000000",
+				"datePublishedFreshnessText": "1 day ago",
+				"isFamilyFriendly": true,
+				"displayUrl": "https://www.gpfans.com/en/f1-news/1033512/f1-results-today-mexican-grand-prix-huge-max...",
+				"snippet": "Elsewhere, Mercedes duo Lewis Hamilton and George Russell came home in P4 and P5 respectively. Meanwhile, the surprise package of the day were Haas, with both Kevin Magnussen and Nico Hulkenberg finishing inside the points.. READ MORE: RB star issues apology after red flag CRASH at Mexican GP Mexican Grand Prix 2024 results. 1. Carlos Sainz [Ferrari] 2. Lando Norris [McLaren] - +4.705",
+				"dateLastCrawled": "2024-10-28T06:06:00.0000000Z",
+				"cachedPageUrl": "https://cc.bingj.com/cache.aspx?q=Top+10+international+results&d=2840656522642&mkt=en-US&setlang=en-US&w=-Tbkwxnq52jZCvG7l3CtgcwT1vwAjIUD",
+				"language": "en",
+				"isNavigational": false,
+				"noCache": false
+			},
+			{
+				"id": "https://api.bing.microsoft.com/api/v7/#WebPages.2",
+				"name": "International Power Rankings: England flying, Kangaroos cruising, Fiji rise",
+				"url": "https://www.loverugbyleague.com/post/international-power-rankings-england-flying-kangaroos-cruising-fiji-rise",
+				"datePublished": "2024-10-28T00:00:00.0000000",
+				"datePublishedFreshnessText": "7 hours ago",
+				"isFamilyFriendly": true,
+				"displayUrl": "https://www.loverugbyleague.com/post/international-power-rankings-england-flying...",
+				"snippet": "LRL RECOMMENDS: England player ratings from first Test against Samoa as omnificent George Williams scores perfect 10. 2. Australia (Men) – SAME. The Kangaroos remain 2nd in our Power Rankings after their 22-10 win against New Zealand in Christchurch on Sunday. As was the case in their win against Tonga last week, Mal Meninga’s side weren ...",
+				"dateLastCrawled": "2024-10-28T07:09:00.0000000Z",
+				"cachedPageUrl": "https://cc.bingj.com/cache.aspx?q=Top+10+international+results&d=1535008462672&mkt=en-US&setlang=en-US&w=82ujhH4Kp0iuhCS7wh1xLUFYUeetaVVm",
+				"language": "en",
+				"isNavigational": false,
+				"noCache": false
+			}
+		],
+		"someResultsRemoved": true
+	}
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/brave.json b/backend/open_webui/apps/retrieval/web/testdata/brave.json
new file mode 100644
index 0000000000000000000000000000000000000000..38487390d9067236e8d5bd6afcedfd08ae9e8368
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/brave.json
@@ -0,0 +1,998 @@
+{
+	"query": {
+		"original": "python",
+		"show_strict_warning": false,
+		"is_navigational": true,
+		"is_news_breaking": false,
+		"spellcheck_off": true,
+		"country": "us",
+		"bad_results": false,
+		"should_fallback": false,
+		"postal_code": "",
+		"city": "",
+		"header_country": "",
+		"more_results_available": true,
+		"state": ""
+	},
+	"mixed": {
+		"type": "mixed",
+		"main": [
+			{
+				"type": "web",
+				"index": 0,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 1,
+				"all": false
+			},
+			{
+				"type": "news",
+				"all": true
+			},
+			{
+				"type": "web",
+				"index": 2,
+				"all": false
+			},
+			{
+				"type": "videos",
+				"all": true
+			},
+			{
+				"type": "web",
+				"index": 3,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 4,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 5,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 6,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 7,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 8,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 9,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 10,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 11,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 12,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 13,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 14,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 15,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 16,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 17,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 18,
+				"all": false
+			},
+			{
+				"type": "web",
+				"index": 19,
+				"all": false
+			}
+		],
+		"top": [],
+		"side": []
+	},
+	"news": {
+		"type": "news",
+		"results": [
+			{
+				"title": "Google lays off staff from Flutter, Dart and Python teams weeks before its developer conference | TechCrunch",
+				"url": "https://techcrunch.com/2024/05/01/google-lays-off-staff-from-flutter-dart-python-weeks-before-its-developer-conference/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Google told TechCrunch that Flutter will have new updates to share at I/O this year.",
+				"page_age": "2024-05-02T17:40:05",
+				"family_friendly": true,
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "techcrunch.com",
+					"hostname": "techcrunch.com",
+					"favicon": "https://imgs.search.brave.com/N6VSEVahheQOb7lqfb47dhUOB4XD-6sfQOP94sCe3Oo/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvZGI5Njk0Yzlk/YWM3ZWMwZjg1MTM1/NmIyMWEyNzBjZDZj/ZDQyNmFlNGU0NDRi/MDgyYjQwOGU0Y2Qy/ZWMwNWQ2ZC90ZWNo/Y3J1bmNoLmNvbS8",
+					"path": "› 2024  › 05  › 01  › google-lays-off-staff-from-flutter-dart-python-weeks-before-its-developer-conference"
+				},
+				"breaking": false,
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/gCI5UG8muOEOZDAx9vpu6L6r6R00mD7jOF08-biFoyQ/rs:fit:200:200:1/g:ce/aHR0cHM6Ly90ZWNo/Y3J1bmNoLmNvbS93/cC1jb250ZW50L3Vw/bG9hZHMvMjAxOC8x/MS9HZXR0eUltYWdl/cy0xMDAyNDg0NzQ2/LmpwZz9yZXNpemU9/MTIwMCw4MDA"
+				},
+				"age": "3 days ago",
+				"extra_snippets": [
+					"Ahead of Google’s annual I/O developer conference in May, the tech giant has laid off staff across key teams like Flutter, Dart, Python and others, according to reports from affected employees shared on social media. Google confirmed the layoffs to TechCrunch, but not the specific teams, roles or how many people were let go.",
+					"In a separate post on Reddit, another commenter noted the Python team affected by the layoffs were those who managed the internal Python runtimes and toolchains and worked with OSS Python. Included in this group were “multiple current and former core devs and steering council members,” they said.",
+					"Meanwhile, others shared on Y Combinator’s Hacker News, where a Python team member detailed their specific duties on the technical front and noted that, for years, much of the work was done with fewer than 10 people. Another Hacker News commenter said their early years on the Python team were spent paying down internal technical debt accumulated from not having a strong Python strategy.",
+					"CNBC reports that a total of 200 people were let go across Google’s “Core” teams, which included those working on Python, app platforms, and other engineering roles. Some jobs were being shifted to India and Mexico, it said, citing internal documents."
+				]
+			}
+		],
+		"mutated_by_goggles": false
+	},
+	"type": "search",
+	"videos": {
+		"type": "videos",
+		"results": [
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=b093aqAZiPU",
+				"title": "👩‍💻 Python for Beginners Tutorial - YouTube",
+				"description": "In this step-by-step Python for beginner's tutorial, learn how you can get started programming in Python. In this video, I assume that you are completely new...",
+				"age": "March 25, 2021",
+				"page_age": "2021-03-25T10:00:08",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/tZI4Do4_EYcTCsD_MvE3Jx8FzjIXwIJ5ZuKhwiWTyZs/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9i/MDkzYXFBWmlQVS9t/YXhyZXNkZWZhdWx0/LmpwZw"
+				}
+			},
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=rfscVS0vtbw",
+				"title": "Learn Python - Full Course for Beginners [Tutorial] - YouTube",
+				"description": "This course will give you a full introduction into all of the core concepts in python. Follow along with the videos and you'll be a python programmer in no t...",
+				"age": "July 11, 2018",
+				"page_age": "2018-07-11T18:00:42",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/65zkx_kPU_zJb-4nmvvY-q5-ZZwzceChz-N00V8cqvk/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9y/ZnNjVlMwdnRidy9t/YXhyZXNkZWZhdWx0/LmpwZw"
+				}
+			},
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=_uQrJ0TkZlc",
+				"title": "Python Tutorial - Python Full Course for Beginners - YouTube",
+				"description": "Become a Python pro! 🚀 This comprehensive tutorial takes you from beginner to hero, covering the basics, machine learning, and web development projects.🚀 W...",
+				"age": "February 18, 2019",
+				"page_age": "2019-02-18T15:00:08",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/Djiv1pXLq1ClqBSE_86jQnEYR8bW8UJP6Cs7LrgyQzQ/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9f/dVFySjBUa1psYy9t/YXhyZXNkZWZhdWx0/LmpwZw"
+				}
+			},
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=wRKgzC-MhIc",
+				"title": "[] and {} vs list() and dict(), which is better?",
+				"description": "Enjoy the videos and music you love, upload original content, and share it all with friends, family, and the world on YouTube.",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/Hw9ep2Pio13X1VZjRw_h9R2VH_XvZFOuGlQJVnVkeq0/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS93/UktnekMtTWhJYy9o/cWRlZmF1bHQuanBn"
+				}
+			},
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=LWdsF79H1Pg",
+				"title": "print() vs. return in Python Functions - YouTube",
+				"description": "In this video, you will learn the differences between the return statement and the print function when they are used inside Python functions. We will see an ...",
+				"age": "June 11, 2022",
+				"page_age": "2022-06-11T21:33:26",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/ebglnr5_jwHHpvon3WU-5hzt0eHdTZSVGg3Ts6R38xY/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9M/V2RzRjc5SDFQZy9t/YXhyZXNkZWZhdWx0/LmpwZw"
+				}
+			},
+			{
+				"type": "video_result",
+				"url": "https://www.youtube.com/watch?v=AovxLr8jUH4",
+				"title": "Python Tutorial for Beginners 5 - Python print() and input() Function ...",
+				"description": "In this Video I am going to show How to use print() Function and input() Function in Python. In python The print() function is used to print the specified ...",
+				"age": "August 28, 2018",
+				"page_age": "2018-08-28T20:11:09",
+				"video": {},
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "youtube.com",
+					"hostname": "www.youtube.com",
+					"favicon": "https://imgs.search.brave.com/Ux4Hee4evZhvjuTKwtapBycOGjGDci2Gvn2pbSzvbC0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTkyZTZiMWU3/YzU3Nzc5YjExYzUy/N2VhZTIxOWNlYjM5/ZGVjN2MyZDY4Nzdh/ZDYzMTYxNmI5N2Rk/Y2Q3N2FkNy93d3cu/eW91dHViZS5jb20v",
+					"path": "› watch"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/nCoLEcWkKtiecprWbS6nufwGCaSbPH7o0-sMeIkFmjI/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLnl0/aW1nLmNvbS92aS9B/b3Z4THI4alVINC9o/cWRlZmF1bHQuanBn"
+				}
+			}
+		],
+		"mutated_by_goggles": false
+	},
+	"web": {
+		"type": "search",
+		"results": [
+			{
+				"title": "Welcome to Python.org",
+				"url": "https://www.python.org",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "The official home of the <strong>Python</strong> Programming Language",
+				"page_age": "2023-09-09T15:55:05",
+				"profile": {
+					"name": "Python",
+					"url": "https://www.python.org",
+					"long_name": "python.org",
+					"img": "https://imgs.search.brave.com/vBaRH-v6oPS4csO4cdvuKhZ7-xDVvydin3oe3zXYxAI/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNTJjMzZjNDBj/MmIzODgwMGUyOTRj/Y2E5MjM3YjRkYTZj/YWI1Yzk1NTlmYTgw/ZDBjNzM0MGMxZjQz/YWFjNTczYy93d3cu/cHl0aG9uLm9yZy8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "python.org",
+					"hostname": "www.python.org",
+					"favicon": "https://imgs.search.brave.com/vBaRH-v6oPS4csO4cdvuKhZ7-xDVvydin3oe3zXYxAI/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNTJjMzZjNDBj/MmIzODgwMGUyOTRj/Y2E5MjM3YjRkYTZj/YWI1Yzk1NTlmYTgw/ZDBjNzM0MGMxZjQz/YWFjNTczYy93d3cu/cHl0aG9uLm9yZy8",
+					"path": ""
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/GGfNfe5rxJ8QWEoxXniSLc0-POLU3qPyTIpuqPdbmXk/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/cHl0aG9uLm9yZy9z/dGF0aWMvb3Blbmdy/YXBoLWljb24tMjAw/eDIwMC5wbmc",
+					"original": "https://www.python.org/static/opengraph-icon-200x200.png",
+					"logo": false
+				},
+				"age": "September 9, 2023",
+				"cluster_type": "generic",
+				"cluster": [
+					{
+						"title": "Downloads",
+						"url": "https://www.python.org/downloads/",
+						"is_source_local": false,
+						"is_source_both": false,
+						"description": "The official home of the <strong>Python</strong> Programming Language",
+						"family_friendly": true
+					},
+					{
+						"title": "Macos",
+						"url": "https://www.python.org/downloads/macos/",
+						"is_source_local": false,
+						"is_source_both": false,
+						"description": "The official home of the <strong>Python</strong> Programming Language",
+						"family_friendly": true
+					},
+					{
+						"title": "Windows",
+						"url": "https://www.python.org/downloads/windows/",
+						"is_source_local": false,
+						"is_source_both": false,
+						"description": "The official home of the <strong>Python</strong> Programming Language",
+						"family_friendly": true
+					},
+					{
+						"title": "Getting Started",
+						"url": "https://www.python.org/about/gettingstarted/",
+						"is_source_local": false,
+						"is_source_both": false,
+						"description": "The official home of the <strong>Python</strong> Programming Language",
+						"family_friendly": true
+					}
+				],
+				"extra_snippets": [
+					"Calculations are simple with Python, and expression syntax is straightforward: the operators +, -, * and / work as expected; parentheses () can be used for grouping. More about simple math functions in Python 3.",
+					"The core of extensible programming is defining functions. Python allows mandatory and optional arguments, keyword arguments, and even arbitrary argument lists. More about defining functions in Python 3",
+					"Lists (known as arrays in other languages) are one of the compound data types that Python understands. Lists can be indexed, sliced and manipulated with other built-in functions. More about lists in Python 3",
+					"# Python 3: Simple output (with Unicode) >>> print(\"Hello, I'm Python!\") Hello, I'm Python! # Input, assignment >>> name = input('What is your name?\\n') >>> print('Hi, %s.' % name) What is your name? Python Hi, Python."
+				]
+			},
+			{
+				"title": "Python (programming language) - Wikipedia",
+				"url": "https://en.wikipedia.org/wiki/Python_(programming_language)",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "<strong>Python</strong> is a high-level, general-purpose programming language. Its design philosophy emphasizes code readability with the use of significant indentation. <strong>Python</strong> is dynamically typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly procedural), ...",
+				"page_age": "2024-05-01T12:54:03",
+				"profile": {
+					"name": "Wikipedia",
+					"url": "https://en.wikipedia.org/wiki/Python_(programming_language)",
+					"long_name": "en.wikipedia.org",
+					"img": "https://imgs.search.brave.com/0kxnVOiqv-faZvOJc7zpym4Zin1CTs1f1svfNZSzmfU/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNjQwNGZhZWY0/ZTQ1YWUzYzQ3MDUw/MmMzMGY3NTQ0ZjNj/NDUwMDk5ZTI3MWRk/NWYyNTM4N2UwOTE0/NTI3ZDQzNy9lbi53/aWtpcGVkaWEub3Jn/Lw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "en.wikipedia.org",
+					"hostname": "en.wikipedia.org",
+					"favicon": "https://imgs.search.brave.com/0kxnVOiqv-faZvOJc7zpym4Zin1CTs1f1svfNZSzmfU/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNjQwNGZhZWY0/ZTQ1YWUzYzQ3MDUw/MmMzMGY3NTQ0ZjNj/NDUwMDk5ZTI3MWRk/NWYyNTM4N2UwOTE0/NTI3ZDQzNy9lbi53/aWtpcGVkaWEub3Jn/Lw",
+					"path": "› wiki  › Python_(programming_language)"
+				},
+				"age": "4 days ago",
+				"extra_snippets": [
+					"Python is dynamically typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly procedural), object-oriented and functional programming. It is often described as a \"batteries included\" language due to its comprehensive standard library.",
+					"Guido van Rossum began working on Python in the late 1980s as a successor to the ABC programming language and first released it in 1991 as Python 0.9.0. Python 2.0 was released in 2000. Python 3.0, released in 2008, was a major revision not completely backward-compatible with earlier versions. Python 2.7.18, released in 2020, was the last release of Python 2.",
+					"Python was invented in the late 1980s by Guido van Rossum at Centrum Wiskunde & Informatica (CWI) in the Netherlands as a successor to the ABC programming language, which was inspired by SETL, capable of exception handling and interfacing with the Amoeba operating system.",
+					"Python consistently ranks as one of the most popular programming languages, and has gained widespread use in the machine learning community."
+				]
+			},
+			{
+				"title": "Python Tutorial",
+				"url": "https://www.w3schools.com/python/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, <strong>Python</strong>, SQL, Java, and many, many more.",
+				"page_age": "2017-12-07T00:00:00",
+				"profile": {
+					"name": "W3Schools",
+					"url": "https://www.w3schools.com/python/",
+					"long_name": "w3schools.com",
+					"img": "https://imgs.search.brave.com/JwO5r7z3HTBkU29vgNH_4rrSWLf2M4-8FMWNvbxrKX8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjVlMGVkZDVj/ZGMyZWRmMzAwODRi/ZDAwZGE4NWI3NmU4/MjRhNjEzOGFhZWY3/ZGViMjY1OWY2ZDYw/YTZiOGUyZS93d3cu/dzNzY2hvb2xzLmNv/bS8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "w3schools.com",
+					"hostname": "www.w3schools.com",
+					"favicon": "https://imgs.search.brave.com/JwO5r7z3HTBkU29vgNH_4rrSWLf2M4-8FMWNvbxrKX8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjVlMGVkZDVj/ZGMyZWRmMzAwODRi/ZDAwZGE4NWI3NmU4/MjRhNjEzOGFhZWY3/ZGViMjY1OWY2ZDYw/YTZiOGUyZS93d3cu/dzNzY2hvb2xzLmNv/bS8",
+					"path": "› python"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/EMfp8dodbJehmj0yCJh8317RHuaumsddnHI4bujvFcg/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/dzNzY2hvb2xzLmNv/bS9pbWFnZXMvdzNz/Y2hvb2xzX2xvZ29f/NDM2XzIucG5n",
+					"original": "https://www.w3schools.com/images/w3schools_logo_436_2.png",
+					"logo": true
+				},
+				"age": "December 7, 2017",
+				"extra_snippets": [
+					"Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, Python, PHP, Bootstrap, Java, XML and more.",
+					"HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE",
+					"Python Variables Variable Names Assign Multiple Values Output Variables Global Variables Variable Exercises Python Data Types Python Numbers Python Casting Python Strings",
+					"Python Strings Slicing Strings Modify Strings Concatenate Strings Format Strings Escape Characters String Methods String Exercises Python Booleans Python Operators Python Lists"
+				]
+			},
+			{
+				"title": "Online Python - IDE, Editor, Compiler, Interpreter",
+				"url": "https://www.online-python.com/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Build and Run your <strong>Python</strong> code instantly. Online-<strong>Python</strong> is a quick and easy tool that helps you to build, compile, test your <strong>python</strong> programs.",
+				"profile": {
+					"name": "Online-python",
+					"url": "https://www.online-python.com/",
+					"long_name": "online-python.com",
+					"img": "https://imgs.search.brave.com/kfaEvapwHxSsRObO52-I-otYFPHpG1h7UXJyUqDM2Ec/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvZGYxODdjNWQ0/NjZjZTNiMjk5NDY1/MWI5MTgyYjU3Y2Q3/MTI3NGM5MjUzY2Fi/OGQ3MTQ4MmIxMTQx/ZTcxNWFhMC93d3cu/b25saW5lLXB5dGhv/bi5jb20v"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "online-python.com",
+					"hostname": "www.online-python.com",
+					"favicon": "https://imgs.search.brave.com/kfaEvapwHxSsRObO52-I-otYFPHpG1h7UXJyUqDM2Ec/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvZGYxODdjNWQ0/NjZjZTNiMjk5NDY1/MWI5MTgyYjU3Y2Q3/MTI3NGM5MjUzY2Fi/OGQ3MTQ4MmIxMTQx/ZTcxNWFhMC93d3cu/b25saW5lLXB5dGhv/bi5jb20v",
+					"path": ""
+				},
+				"extra_snippets": [
+					"Build, run, and share Python code online for free with the help of online-integrated python's development environment (IDE). It is one of the most efficient, dependable, and potent online compilers for the Python programming language. It is not necessary for you to bother about establishing a Python environment in your local.",
+					"It is one of the most efficient, dependable, and potent online compilers for the Python programming language. It is not necessary for you to bother about establishing a Python environment in your local. Now You can immediately execute the Python code in the web browser of your choice.",
+					"It is not necessary for you to bother about establishing a Python environment in your local. Now You can immediately execute the Python code in the web browser of your choice. Using this Python editor is simple and quick to get up and running with. Simply type in the programme, and then press the RUN button!",
+					"Now You can immediately execute the Python code in the web browser of your choice. Using this Python editor is simple and quick to get up and running with. Simply type in the programme, and then press the RUN button! The code can be saved online by choosing the SHARE option, which also gives you the ability to access your code from any location providing you have internet access."
+				]
+			},
+			{
+				"title": "Python · GitHub",
+				"url": "https://github.com/python",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Repositories related to the <strong>Python</strong> Programming language - <strong>Python</strong>",
+				"page_age": "2023-03-06T00:00:00",
+				"profile": {
+					"name": "GitHub",
+					"url": "https://github.com/python",
+					"long_name": "github.com",
+					"img": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "github.com",
+					"hostname": "github.com",
+					"favicon": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw",
+					"path": "› python"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/POoaRfu_7gfp-D_O3qMNJrwDqJNbiDu1HuBpNJ_MpVQ/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9hdmF0/YXJzLmdpdGh1YnVz/ZXJjb250ZW50LmNv/bS91LzE1MjU5ODE_/cz0yMDAmYW1wO3Y9/NA",
+					"original": "https://avatars.githubusercontent.com/u/1525981?s=200&amp;v=4",
+					"logo": false
+				},
+				"age": "March 6, 2023",
+				"extra_snippets": ["Configuration for Python planets (e.g. http://planetpython.org)"]
+			},
+			{
+				"title": "Online Python Compiler (Interpreter)",
+				"url": "https://www.programiz.com/python-programming/online-compiler/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Write and run <strong>Python</strong> code using our online compiler (interpreter). You can use <strong>Python</strong> Shell like IDLE, and take inputs from the user in our <strong>Python</strong> compiler.",
+				"page_age": "2020-06-02T00:00:00",
+				"profile": {
+					"name": "Programiz",
+					"url": "https://www.programiz.com/python-programming/online-compiler/",
+					"long_name": "programiz.com",
+					"img": "https://imgs.search.brave.com/ozj4JFayZ3Fs5c9eTp7M5g12azQ_Hblgu4dpTuHRz6U/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMGJlN2U1YjVi/Y2M3ZDU5OGMwMWNi/M2Q3YjhjOTM1ZTFk/Y2NkZjE4NGQwOGIx/MTQ4NjI2YmNhODVj/MzFkMmJhYy93d3cu/cHJvZ3JhbWl6LmNv/bS8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "programiz.com",
+					"hostname": "www.programiz.com",
+					"favicon": "https://imgs.search.brave.com/ozj4JFayZ3Fs5c9eTp7M5g12azQ_Hblgu4dpTuHRz6U/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMGJlN2U1YjVi/Y2M3ZDU5OGMwMWNi/M2Q3YjhjOTM1ZTFk/Y2NkZjE4NGQwOGIx/MTQ4NjI2YmNhODVj/MzFkMmJhYy93d3cu/cHJvZ3JhbWl6LmNv/bS8",
+					"path": "› python-programming  › online-compiler"
+				},
+				"age": "June 2, 2020",
+				"extra_snippets": [
+					"Python Online Compiler Online R Compiler SQL Online Editor Online HTML/CSS Editor Online Java Compiler C Online Compiler C++ Online Compiler C# Online Compiler JavaScript Online Compiler Online GoLang Compiler Online PHP Compiler Online Swift Compiler Online Rust Compiler",
+					"# Online Python compiler (interpreter) to run Python online. # Write Python 3 code in this online editor and run it. print(\"Try programiz.pro\")"
+				]
+			},
+			{
+				"title": "Python Developer",
+				"url": "https://twitter.com/Python_Dv/status/1786763460992544791",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "<strong>Python</strong> Developer",
+				"page_age": "2024-05-04T14:30:03",
+				"profile": {
+					"name": "X",
+					"url": "https://twitter.com/Python_Dv/status/1786763460992544791",
+					"long_name": "twitter.com",
+					"img": "https://imgs.search.brave.com/Zq483bGX0GnSgym-1P7iyOyEDX3PkDZSNT8m56F862A/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvN2MxOTUxNzhj/OTY1ZTQ3N2I0MjJk/MTY5NGM0MTRlYWVi/MjU1YWE2NDUwYmQ2/YTA2MDFhMDlkZDEx/NTAzZGNiNi90d2l0/dGVyLmNvbS8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "twitter.com",
+					"hostname": "twitter.com",
+					"favicon": "https://imgs.search.brave.com/Zq483bGX0GnSgym-1P7iyOyEDX3PkDZSNT8m56F862A/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvN2MxOTUxNzhj/OTY1ZTQ3N2I0MjJk/MTY5NGM0MTRlYWVi/MjU1YWE2NDUwYmQ2/YTA2MDFhMDlkZDEx/NTAzZGNiNi90d2l0/dGVyLmNvbS8",
+					"path": "› Python_Dv  › status  › 1786763460992544791"
+				},
+				"age": "20 hours ago"
+			},
+			{
+				"title": "input table name? - python script - KNIME Extensions - KNIME Community Forum",
+				"url": "https://forum.knime.com/t/input-table-name-python-script/78978",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Hi, when running a <strong>python</strong> script node, I get the error seen on the screenshot Same happens with this code too: The script input is output from the csv reader node. How can I get the right name for that table? Best wishes, Dario",
+				"page_age": "2024-05-04T09:20:44",
+				"profile": {
+					"name": "Knime",
+					"url": "https://forum.knime.com/t/input-table-name-python-script/78978",
+					"long_name": "forum.knime.com",
+					"img": "https://imgs.search.brave.com/WQoOhAD5i6uEhJ-qXvlWMJwbGA52f2Ycc_ns36EK698/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTAxNzMxNjFl/MzJjNzU5NzRkOTMz/Mjg4NDU2OWUxM2Rj/YzVkOGM3MzIwNzI2/YTY1NzYxNzA1MDE5/NzQzOWU3NC9mb3J1/bS5rbmltZS5jb20v"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "article",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "forum.knime.com",
+					"hostname": "forum.knime.com",
+					"favicon": "https://imgs.search.brave.com/WQoOhAD5i6uEhJ-qXvlWMJwbGA52f2Ycc_ns36EK698/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTAxNzMxNjFl/MzJjNzU5NzRkOTMz/Mjg4NDU2OWUxM2Rj/YzVkOGM3MzIwNzI2/YTY1NzYxNzA1MDE5/NzQzOWU3NC9mb3J1/bS5rbmltZS5jb20v",
+					"path": "  › knime extensions"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/DtEl38dcvuM1kGfhN0T5HfOrsMJcztWNyriLvtDJmKI/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9mb3J1/bS1jZG4ua25pbWUu/Y29tL3VwbG9hZHMv/ZGVmYXVsdC9vcmln/aW5hbC8zWC9lLzYv/ZTY0M2M2NzFlNzAz/MDg2MjkwMWY2YzJh/OWFjOWI5ZmEwM2M3/ZjMwZi5wbmc",
+					"original": "https://forum-cdn.knime.com/uploads/default/original/3X/e/6/e643c671e7030862901f6c2a9ac9b9fa03c7f30f.png",
+					"logo": false
+				},
+				"age": "1 day ago",
+				"extra_snippets": [
+					"Hi, when running a python script node, I get the error seen on the screenshot Same happens with this code too: The script input is output from the csv reader node. How can I get the right name for that table? …"
+				]
+			},
+			{
+				"title": "What does the Double Star operator mean in Python? - GeeksforGeeks",
+				"url": "https://www.geeksforgeeks.org/what-does-the-double-star-operator-mean-in-python/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.",
+				"page_age": "2023-03-14T17:15:04",
+				"profile": {
+					"name": "GeeksforGeeks",
+					"url": "https://www.geeksforgeeks.org/what-does-the-double-star-operator-mean-in-python/",
+					"long_name": "geeksforgeeks.org",
+					"img": "https://imgs.search.brave.com/fhzcfv5xltx6-YBvJI9RZgS7xZo0dPNaASsrB8YOsCs/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjBhOGQ3MmNi/ZWE5N2EwMmZjYzA1/ZTI0ZTFhMGUyMTE0/MGM0ZTBmMWZlM2Y2/Yzk2ODMxZTRhYTBi/NDdjYTE0OS93d3cu/Z2Vla3Nmb3JnZWVr/cy5vcmcv"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "article",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "geeksforgeeks.org",
+					"hostname": "www.geeksforgeeks.org",
+					"favicon": "https://imgs.search.brave.com/fhzcfv5xltx6-YBvJI9RZgS7xZo0dPNaASsrB8YOsCs/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjBhOGQ3MmNi/ZWE5N2EwMmZjYzA1/ZTI0ZTFhMGUyMTE0/MGM0ZTBmMWZlM2Y2/Yzk2ODMxZTRhYTBi/NDdjYTE0OS93d3cu/Z2Vla3Nmb3JnZWVr/cy5vcmcv",
+					"path": "› what-does-the-double-star-operator-mean-in-python"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/GcR-j_dLbyHkbHEI3ffLMi6xpXGhF_2Z8POIoqtokhM/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9tZWRp/YS5nZWVrc2Zvcmdl/ZWtzLm9yZy93cC1j/b250ZW50L3VwbG9h/ZHMvZ2ZnXzIwMFgy/MDAtMTAweDEwMC5w/bmc",
+					"original": "https://media.geeksforgeeks.org/wp-content/uploads/gfg_200X200-100x100.png",
+					"logo": false
+				},
+				"age": "March 14, 2023",
+				"extra_snippets": [
+					"Difference between / vs. // operator in Python",
+					"Double Star or (**) is one of the Arithmetic Operator (Like +, -, *, **, /, //, %) in Python Language. It is also known as Power Operator.",
+					"The time complexity of the given Python program is O(n), where n is the number of key-value pairs in the input dictionary.",
+					"Inplace Operators in Python | Set 2 (ixor(), iand(), ipow(),…)"
+				]
+			},
+			{
+				"title": "r/Python",
+				"url": "https://www.reddit.com/r/Python/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "The official <strong>Python</strong> community for Reddit! Stay up to date with the latest news, packages, and meta information relating to the <strong>Python</strong> programming language. --- If you have questions or are new to <strong>Python</strong> use r/LearnPython",
+				"page_age": "2022-12-30T16:25:02",
+				"profile": {
+					"name": "Reddit",
+					"url": "https://www.reddit.com/r/Python/",
+					"long_name": "reddit.com",
+					"img": "https://imgs.search.brave.com/mAZYEK9Wi13WLDUge7XZ8YuDTwm6DP6gBjvz1GdYZVY/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvN2ZiNTU0M2Nj/MTFhZjRiYWViZDlk/MjJiMjBjMzFjMDRk/Y2IzYWI0MGI0MjVk/OGY5NzQzOGQ5NzQ5/NWJhMWI0NC93d3cu/cmVkZGl0LmNvbS8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "reddit.com",
+					"hostname": "www.reddit.com",
+					"favicon": "https://imgs.search.brave.com/mAZYEK9Wi13WLDUge7XZ8YuDTwm6DP6gBjvz1GdYZVY/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvN2ZiNTU0M2Nj/MTFhZjRiYWViZDlk/MjJiMjBjMzFjMDRk/Y2IzYWI0MGI0MjVk/OGY5NzQzOGQ5NzQ5/NWJhMWI0NC93d3cu/cmVkZGl0LmNvbS8",
+					"path": "› r  › Python"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/zWd10t3zg34ciHiAB-K5WWK3h_H4LedeDot9BVX7Ydo/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9zdHls/ZXMucmVkZGl0bWVk/aWEuY29tL3Q1XzJx/aDB5L3N0eWxlcy9j/b21tdW5pdHlJY29u/X2NpZmVobDR4dDdu/YzEucG5n",
+					"original": "https://styles.redditmedia.com/t5_2qh0y/styles/communityIcon_cifehl4xt7nc1.png",
+					"logo": false
+				},
+				"age": "December 30, 2022",
+				"extra_snippets": [
+					"r/Python: The official Python community for Reddit! Stay up to date with the latest news, packages, and meta information relating to the Python…",
+					"By default, Python allows you to import and use anything, anywhere. Over time, this results in modules that were intended to be separate getting tightly coupled together, and domain boundaries breaking down. We experienced this first-hand at a unicorn startup, where the eng team paused development for over a year in an attempt to split up packages into independent services.",
+					"Hello r/Python! It's time to share what you've been working on! Whether it's a work-in-progress, a completed masterpiece, or just a rough idea, let us know what you're up to!",
+					"Whether it's your job, your hobby, or your passion project, all Python-related work is welcome here."
+				]
+			},
+			{
+				"title": "GitHub - python/cpython: The Python programming language",
+				"url": "https://github.com/python/cpython",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "The <strong>Python</strong> programming language. Contribute to <strong>python</strong>/cpython development by creating an account on GitHub.",
+				"page_age": "2022-10-29T00:00:00",
+				"profile": {
+					"name": "GitHub",
+					"url": "https://github.com/python/cpython",
+					"long_name": "github.com",
+					"img": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "software",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "github.com",
+					"hostname": "github.com",
+					"favicon": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw",
+					"path": "› python  › cpython"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/BJbWFRUqgP-tKIyGK9ByXjuYjHO2mtYigUOEFNz_gXk/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9vcGVu/Z3JhcGguZ2l0aHVi/YXNzZXRzLmNvbS82/MTY5YmJkNTQ0YzAy/NDg0MGU4NDdjYTU1/YTU3ZGZmMDA2ZDAw/YWQ1NDIzOTFmYTQ3/YmJjODg3OWM0NWYw/MTZhL3B5dGhvbi9j/cHl0aG9u",
+					"original": "https://opengraph.githubassets.com/6169bbd544c024840e847ca55a57dff006d00ad542391fa47bbc8879c45f016a/python/cpython",
+					"logo": false
+				},
+				"age": "October 29, 2022",
+				"extra_snippets": [
+					"You can pass many options to the configure script; run ./configure --help to find out more. On macOS case-insensitive file systems and on Cygwin, the executable is called python.exe; elsewhere it's just python.",
+					"Building a complete Python installation requires the use of various additional third-party libraries, depending on your build platform and configure options. Not all standard library modules are buildable or useable on all platforms. Refer to the Install dependencies section of the Developer Guide for current detailed information on dependencies for various Linux distributions and macOS.",
+					"To get an optimized build of Python, configure --enable-optimizations before you run make. This sets the default make targets up to enable Profile Guided Optimization (PGO) and may be used to auto-enable Link Time Optimization (LTO) on some platforms. For more details, see the sections below.",
+					"Copyright © 2001-2024 Python Software Foundation. All rights reserved."
+				]
+			},
+			{
+				"title": "5. Data Structures — Python 3.12.3 documentation",
+				"url": "https://docs.python.org/3/tutorial/datastructures.html",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "This chapter describes some things you’ve learned about already in more detail, and adds some new things as well. More on Lists: The list data type has some more methods. Here are all of the method...",
+				"page_age": "2023-07-04T00:00:00",
+				"profile": {
+					"name": "Python documentation",
+					"url": "https://docs.python.org/3/tutorial/datastructures.html",
+					"long_name": "docs.python.org",
+					"img": "https://imgs.search.brave.com/F5Ym7eSElhGdGUFKLRxDj9Z_tc180ldpeMvQ2Q6ARbA/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMTUzOTFjOGVi/YTcyOTVmODA3ODIy/YjE2NzFjY2ViMjhl/NzRlY2JhYTc5YjNm/ZjhmODAyZWI2OGUw/ZjU4NDVlNy9kb2Nz/LnB5dGhvbi5vcmcv"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "docs.python.org",
+					"hostname": "docs.python.org",
+					"favicon": "https://imgs.search.brave.com/F5Ym7eSElhGdGUFKLRxDj9Z_tc180ldpeMvQ2Q6ARbA/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMTUzOTFjOGVi/YTcyOTVmODA3ODIy/YjE2NzFjY2ViMjhl/NzRlY2JhYTc5YjNm/ZjhmODAyZWI2OGUw/ZjU4NDVlNy9kb2Nz/LnB5dGhvbi5vcmcv",
+					"path": "› 3  › tutorial  › datastructures.html"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/Y7GrMRF8WorDIMLuOl97XC8ltYpoOCqNwWF2pQIIKls/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9kb2Nz/LnB5dGhvbi5vcmcv/My9fc3RhdGljL29n/LWltYWdlLnBuZw",
+					"original": "https://docs.python.org/3/_static/og-image.png",
+					"logo": false
+				},
+				"age": "July 4, 2023",
+				"extra_snippets": [
+					"You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed – they return the default None. [1] This is a design principle for all mutable data structures in Python.",
+					"We saw that lists and strings have many common properties, such as indexing and slicing operations. They are two examples of sequence data types (see Sequence Types — list, tuple, range). Since Python is an evolving language, other sequence data types may be added. There is also another standard sequence data type: the tuple.",
+					"Python also includes a data type for sets. A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.",
+					"Another useful data type built into Python is the dictionary (see Mapping Types — dict). Dictionaries are sometimes found in other languages as “associative memories” or “associative arrays”. Unlike sequences, which are indexed by a range of numbers, dictionaries are indexed by keys, which can be any immutable type; strings and numbers can always be keys."
+				]
+			},
+			{
+				"title": "Something wrong with python packages / AUR Issues, Discussion & PKGBUILD Requests / Arch Linux Forums",
+				"url": "https://bbs.archlinux.org/viewtopic.php?id=295466",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Big <strong>Python</strong> updates require <strong>Python</strong> packages to be rebuild. For some reason they didn&#x27;t think a bump that made it necessary to rebuild half the official repo was a news post.",
+				"page_age": "2024-05-04T08:30:02",
+				"profile": {
+					"name": "Archlinux",
+					"url": "https://bbs.archlinux.org/viewtopic.php?id=295466",
+					"long_name": "bbs.archlinux.org",
+					"img": "https://imgs.search.brave.com/3au9oqkzSri_aLEec3jo-0bFgLuICkydrWfjFcC8lkI/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNWNkODM1MWJl/ZmJhMzkzNzYzMDkz/NmEyMWMxNjI5MjNk/NGJmZjFhNTBlZDNl/Mzk5MzJjOGZkYjZl/MjNmY2IzNS9iYnMu/YXJjaGxpbnV4Lm9y/Zy8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "bbs.archlinux.org",
+					"hostname": "bbs.archlinux.org",
+					"favicon": "https://imgs.search.brave.com/3au9oqkzSri_aLEec3jo-0bFgLuICkydrWfjFcC8lkI/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNWNkODM1MWJl/ZmJhMzkzNzYzMDkz/NmEyMWMxNjI5MjNk/NGJmZjFhNTBlZDNl/Mzk5MzJjOGZkYjZl/MjNmY2IzNS9iYnMu/YXJjaGxpbnV4Lm9y/Zy8",
+					"path": "› viewtopic.php"
+				},
+				"age": "1 day ago",
+				"extra_snippets": [
+					"Traceback (most recent call last): File \"/usr/lib/python3.12/importlib/metadata/__init__.py\", line 397, in from_name return next(cls.discover(name=name)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ StopIteration During handling of the above exception, another exception occurred: Traceback (most recent call last): File \"/usr/bin/informant\", line 33, in <module> sys.exit(load_entry_point('informant==0.5.0', 'console_scripts', 'informant')()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File \"/usr/bin/informant\", line 22, in importlib_load_entry_point for entry_point in distribution(dis"
+				]
+			},
+			{
+				"title": "Introduction to Python",
+				"url": "https://www.w3schools.com/python/python_intro.asp",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, <strong>Python</strong>, SQL, Java, and many, many more.",
+				"profile": {
+					"name": "W3Schools",
+					"url": "https://www.w3schools.com/python/python_intro.asp",
+					"long_name": "w3schools.com",
+					"img": "https://imgs.search.brave.com/JwO5r7z3HTBkU29vgNH_4rrSWLf2M4-8FMWNvbxrKX8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjVlMGVkZDVj/ZGMyZWRmMzAwODRi/ZDAwZGE4NWI3NmU4/MjRhNjEzOGFhZWY3/ZGViMjY1OWY2ZDYw/YTZiOGUyZS93d3cu/dzNzY2hvb2xzLmNv/bS8"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "w3schools.com",
+					"hostname": "www.w3schools.com",
+					"favicon": "https://imgs.search.brave.com/JwO5r7z3HTBkU29vgNH_4rrSWLf2M4-8FMWNvbxrKX8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjVlMGVkZDVj/ZGMyZWRmMzAwODRi/ZDAwZGE4NWI3NmU4/MjRhNjEzOGFhZWY3/ZGViMjY1OWY2ZDYw/YTZiOGUyZS93d3cu/dzNzY2hvb2xzLmNv/bS8",
+					"path": "› python  › python_intro.asp"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/EMfp8dodbJehmj0yCJh8317RHuaumsddnHI4bujvFcg/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/dzNzY2hvb2xzLmNv/bS9pbWFnZXMvdzNz/Y2hvb2xzX2xvZ29f/NDM2XzIucG5n",
+					"original": "https://www.w3schools.com/images/w3schools_logo_436_2.png",
+					"logo": true
+				},
+				"extra_snippets": [
+					"Well organized and easy to understand Web building tutorials with lots of examples of how to use HTML, CSS, JavaScript, SQL, Python, PHP, Bootstrap, Java, XML and more.",
+					"HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE",
+					"Python Variables Variable Names Assign Multiple Values Output Variables Global Variables Variable Exercises Python Data Types Python Numbers Python Casting Python Strings",
+					"Python Strings Slicing Strings Modify Strings Concatenate Strings Format Strings Escape Characters String Methods String Exercises Python Booleans Python Operators Python Lists"
+				]
+			},
+			{
+				"title": "bug: AUR package wants to use python but does not find any preset version · Issue #1740 · asdf-vm/asdf",
+				"url": "https://github.com/asdf-vm/asdf/issues/1740",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Describe the Bug I am not sure why this is happening, I am trying to install tlpui from AUR and it fails, here are some logs to help: ==&gt; Making package: tlpui 2:1.6.5-1 (Mi 10 apr 2024 23:19:15 +0...",
+				"page_age": "2024-05-04T06:45:04",
+				"profile": {
+					"name": "GitHub",
+					"url": "https://github.com/asdf-vm/asdf/issues/1740",
+					"long_name": "github.com",
+					"img": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "software",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "github.com",
+					"hostname": "github.com",
+					"favicon": "https://imgs.search.brave.com/v8685zI4XInM0zxlNI2s7oE_2Sb-EL7lAy81WXbkQD8/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYWQyNWM1NjA5/ZjZmZjNlYzI2MDNk/N2VkNmJhYjE2MzZl/MDY5ZTMxMDUzZmY1/NmU3NWIzNWVmMjk0/NTBjMjJjZi9naXRo/dWIuY29tLw",
+					"path": "› asdf-vm  › asdf  › issues  › 1740"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/KrLW5s_2n4jyP8XLbc3ZPVBaLD963tQgWzG9EWPZlQs/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9vcGVu/Z3JhcGguZ2l0aHVi/YXNzZXRzLmNvbS81/MTE0ZTdkOGIwODM2/YmQ2MTY3NzQ1ZGI4/MmZjMGE3OGUyMjcw/MGFlY2ZjMWZkODBl/MDYzZTNiN2ZjOWNj/NzYyL2FzZGYtdm0v/YXNkZi9pc3N1ZXMv/MTc0MA",
+					"original": "https://opengraph.githubassets.com/5114e7d8b0836bd6167745db82fc0a78e22700aecfc1fd80e063e3b7fc9cc762/asdf-vm/asdf/issues/1740",
+					"logo": false
+				},
+				"age": "1 day ago",
+				"extra_snippets": [
+					"==> Starting build()... No preset version installed for command python Please install a version by running one of the following: asdf install python 3.8 or add one of the following versions in your config file at /home/ferret/.tool-versions python 3.11.0 python 3.12.1 python 3.12.3 ==> ERROR: A failure occurred in build(). Aborting...",
+					"-> error making: tlpui-exit status 4 -> Failed to install the following packages. Manual intervention is required: tlpui - exit status 4 ferret@FX505DT in ~ $ cat /home/ferret/.tool-versions nodejs 21.6.0 python 3.12.3 ferret@FX505DT in ~ $ python -V Python 3.12.3 ferret@FX505DT in ~ $ which python /home/ferret/.asdf/shims/python",
+					"Describe the Bug I am not sure why this is happening, I am trying to install tlpui from AUR and it fails, here are some logs to help: ==> Making package: tlpui 2:1.6.5-1 (Mi 10 apr 2024 23:19:15 +0300) ==> Retrieving sources... -> Found ..."
+				]
+			},
+			{
+				"title": "What are python.exe and python3.exe, and why do they appear to point to App Installer? | Windows 11 Forum",
+				"url": "https://www.elevenforum.com/t/what-are-python-exe-and-python3-exe-and-why-do-they-appear-to-point-to-app-installer.24886/",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "I was looking at App execution aliases (Settings &gt; Apps &gt; Advanced app settings &gt; App execution aliases) on my new computer -- my first Windows 11 computer. Why are <strong>python</strong>.exe and python3.exe listed as App Installer? I assume that App Installer refers to installation of Microsoft Store / UWP...",
+				"page_age": "2024-05-03T17:30:04",
+				"profile": {
+					"name": "Windows 11 Forum",
+					"url": "https://www.elevenforum.com/t/what-are-python-exe-and-python3-exe-and-why-do-they-appear-to-point-to-app-installer.24886/",
+					"long_name": "elevenforum.com",
+					"img": "https://imgs.search.brave.com/XVRAYMEj6Im8i7jV5RxeTwpiRPtY9IWg4wRIuh-WhEw/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvZjk5MDZkMDIw/M2U1OWIwNjM5Y2U1/M2U2NzNiNzVkNTA5/NzA5OTI1ZTFmOTc4/MzU3OTlhYzU5OTVi/ZGNjNTY4MS93d3cu/ZWxldmVuZm9ydW0u/Y29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "elevenforum.com",
+					"hostname": "www.elevenforum.com",
+					"favicon": "https://imgs.search.brave.com/XVRAYMEj6Im8i7jV5RxeTwpiRPtY9IWg4wRIuh-WhEw/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvZjk5MDZkMDIw/M2U1OWIwNjM5Y2U1/M2U2NzNiNzVkNTA5/NzA5OTI1ZTFmOTc4/MzU3OTlhYzU5OTVi/ZGNjNTY4MS93d3cu/ZWxldmVuZm9ydW0u/Y29tLw",
+					"path": "  › windows support forums  › apps and software"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/DVoFcE6d_-lx3BVGNS-RZK_lZzxQ8VhwZVf3AVqEJFA/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/ZWxldmVuZm9ydW0u/Y29tL2RhdGEvYXNz/ZXRzL2xvZ28vbWV0/YTEtMjAxLnBuZw",
+					"original": "https://www.elevenforum.com/data/assets/logo/meta1-201.png",
+					"logo": true
+				},
+				"age": "2 days ago",
+				"extra_snippets": [
+					"Why are python.exe and python3.exe listed as App Installer? I assume that App Installer refers to installation of Microsoft Store / UWP apps, but if that's the case, then why are they called python.exe and python3.exe? Or are python.exe and python3.exe simply serving as aliases / pointers pointing to App Installer, which is itself a Microsoft Store App?",
+					"Or are python.exe and python3.exe simply serving as aliases / pointers pointing to App Installer, which is itself a Microsoft Store App? I wish to soon install Python, along with an integrated development editor (IDE), on my machine, so that I can code in Python.",
+					"I wish to soon install Python, along with an integrated development editor (IDE), on my machine, so that I can code in Python. But is a Python interpreter already on my computer as suggested, if obliquely, by the presence of python.exe and python3.exe? I kind of doubt it."
+				]
+			},
+			{
+				"title": "How to Watermark Your Images Using Python OpenCV in ...",
+				"url": "https://medium.com/@daily_data_prep/how-to-watermark-your-images-using-python-opencv-in-bulk-e472085389a1",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Medium is an open platform where readers find dynamic thinking, and where expert and undiscovered voices can share their writing on any topic.",
+				"page_age": "2024-05-03T14:05:06",
+				"profile": {
+					"name": "Medium",
+					"url": "https://medium.com/@daily_data_prep/how-to-watermark-your-images-using-python-opencv-in-bulk-e472085389a1",
+					"long_name": "medium.com",
+					"img": "https://imgs.search.brave.com/qvE2kIQCiAsnPv2C6P9xM5J2VVWdm55g-A-2Q_yIJ0g/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTZhYmQ1N2Q4/NDg4ZDcyODIyMDZi/MzFmOWNhNjE3Y2E4/Y2YzMThjNjljNDIx/ZjllZmNhYTcwODhl/YTcwNDEzYy9tZWRp/dW0uY29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "medium.com",
+					"hostname": "medium.com",
+					"favicon": "https://imgs.search.brave.com/qvE2kIQCiAsnPv2C6P9xM5J2VVWdm55g-A-2Q_yIJ0g/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvOTZhYmQ1N2Q4/NDg4ZDcyODIyMDZi/MzFmOWNhNjE3Y2E4/Y2YzMThjNjljNDIx/ZjllZmNhYTcwODhl/YTcwNDEzYy9tZWRp/dW0uY29tLw",
+					"path": "› @daily_data_prep  › how-to-watermark-your-images-using-python-opencv-in-bulk-e472085389a1"
+				},
+				"age": "2 days ago"
+			},
+			{
+				"title": "Increment and Decrement Operators in Python?",
+				"url": "https://www.tutorialspoint.com/increment-and-decrement-operators-in-python",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Increment and Decrement Operators in <strong>Python</strong> - <strong>Python</strong> does not have unary increment/decrement operator (++/--). Instead to increment a value, usea += 1to decrement a value, use −a -= 1Example&gt;&gt;&gt; a = 0 &gt;&gt;&gt; &gt;&gt;&gt; #Increment &gt;&gt;&gt; a +=1 &gt;&gt;&gt; &gt;&gt;&gt; #Decrement &gt;&gt;&gt; a -= 1 &gt;&gt;&gt; &gt;&gt;&gt; #value of a &gt;&gt;&gt; a 0Python ...",
+				"page_age": "2023-08-23T00:00:00",
+				"profile": {
+					"name": "Tutorialspoint",
+					"url": "https://www.tutorialspoint.com/increment-and-decrement-operators-in-python",
+					"long_name": "tutorialspoint.com",
+					"img": "https://imgs.search.brave.com/Wt8BSkivPlFwcU5yBtf7YzuvTuRExyd_502cdABCS5c/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjcyYjAzYmVl/ODU4MzZiMjJiYTFh/MjJhZDNmNWE4YzA5/MDgyYTZhMDg3NTYw/M2NiY2NiZTUxN2I5/MjU1MWFmMS93d3cu/dHV0b3JpYWxzcG9p/bnQuY29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "tutorialspoint.com",
+					"hostname": "www.tutorialspoint.com",
+					"favicon": "https://imgs.search.brave.com/Wt8BSkivPlFwcU5yBtf7YzuvTuRExyd_502cdABCS5c/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYjcyYjAzYmVl/ODU4MzZiMjJiYTFh/MjJhZDNmNWE4YzA5/MDgyYTZhMDg3NTYw/M2NiY2NiZTUxN2I5/MjU1MWFmMS93d3cu/dHV0b3JpYWxzcG9p/bnQuY29tLw",
+					"path": "› increment-and-decrement-operators-in-python"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/ddG5vyZGLVudvecEbQJPeG8tGuaZ7g3Xz6Gyjdl5WA8/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/dHV0b3JpYWxzcG9p/bnQuY29tL2ltYWdl/cy90cF9sb2dvXzQz/Ni5wbmc",
+					"original": "https://www.tutorialspoint.com/images/tp_logo_436.png",
+					"logo": true
+				},
+				"age": "August 23, 2023",
+				"extra_snippets": [
+					"Increment and Decrement Operators in Python - Python does not have unary increment/decrement operator (++/--). Instead to increment a value, usea += 1to decrement a value, use −a -= 1Example>>> a = 0 >>> >>> #Increment >>> a +=1 >>> >>> #Decrement >>> a -= 1 >>> >>> #value of a >>> a 0Python does not provide multiple ways to do the same thing",
+					"So what above statement means in python is: create an object of type int having value 1 and give the name a to it. The object is an instance of int having value 1 and the name a refers to it. The assigned name a and the object to which it refers are distinct.",
+					"Python does not provide multiple ways to do the same thing .",
+					"However, be careful if you are coming from a language like C, Python doesn’t have \"variables\" in the sense that C does, instead python uses names and objects and in python integers (int’s) are immutable."
+				]
+			},
+			{
+				"title": "Gumroad – How not to suck at Python / SideFX Houdini | CG Persia",
+				"url": "https://cgpersia.com/2024/05/gumroad-how-not-to-suck-at-python-sidefx-houdini-195370.html",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "Info: This course is made for artists or TD (technical director) willing to learn <strong>Python</strong> to improve their workflows inside SideFX Houdini, get faster in production and develop all the tools you always wished you had.",
+				"page_age": "2024-05-03T08:35:03",
+				"profile": {
+					"name": "Cgpersia",
+					"url": "https://cgpersia.com/2024/05/gumroad-how-not-to-suck-at-python-sidefx-houdini-195370.html",
+					"long_name": "cgpersia.com",
+					"img": "https://imgs.search.brave.com/VjyaopAm-M9sWvM7n-KnGZ3T5swIOwwE80iF5QVqQPg/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYmE0MzQ4NmI2/NjFhMTA1ZDBiN2Iw/ZWNiNDUxNjUwYjdh/MGE5ZjQ0ZjIxNzll/NmVkZDE2YzYyMDBh/NDNiMDgwMy9jZ3Bl/cnNpYS5jb20v"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "cgpersia.com",
+					"hostname": "cgpersia.com",
+					"favicon": "https://imgs.search.brave.com/VjyaopAm-M9sWvM7n-KnGZ3T5swIOwwE80iF5QVqQPg/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYmE0MzQ4NmI2/NjFhMTA1ZDBiN2Iw/ZWNiNDUxNjUwYjdh/MGE5ZjQ0ZjIxNzll/NmVkZDE2YzYyMDBh/NDNiMDgwMy9jZ3Bl/cnNpYS5jb20v",
+					"path": "› 2024  › 05  › gumroad-how-not-to-suck-at-python-sidefx-houdini-195370.html"
+				},
+				"age": "2 days ago",
+				"extra_snippets": [
+					"Posted in: 2D, CG Releases, Downloads, Learning, Tutorials, Videos. Tagged: Gumroad, Python, Sidefx. Leave a Comment",
+					"01 – Python – Fundamentals Get the Fundamentals of python before starting the fun stuff ! 02 – Python Construction Part02 digging further into python concepts 03 – Houdini – Python Basics Applying some basic python in Houdini and starting to make tools !",
+					"02 – Python Construction Part02 digging further into python concepts 03 – Houdini – Python Basics Applying some basic python in Houdini and starting to make tools ! 04 – Houdini – Python Intermediate Applying some more advanced python in Houdini to make tools ! 05 – Houdini – Python Expert Using QtDesigner in combinaison with Houdini Python/Pyside to create advanced tools."
+				]
+			},
+			{
+				"title": "How to install Python: The complete Python programmer’s guide",
+				"url": "https://www.pluralsight.com/resources/blog/software-development/python-installation-guide",
+				"is_source_local": false,
+				"is_source_both": false,
+				"description": "An easy guide on how set up your operating system so you can program in <strong>Python</strong>, and how to update or uninstall it. For Linux, Windows, and macOS.",
+				"page_age": "2024-05-02T07:30:02",
+				"profile": {
+					"name": "Pluralsight",
+					"url": "https://www.pluralsight.com/resources/blog/software-development/python-installation-guide",
+					"long_name": "pluralsight.com",
+					"img": "https://imgs.search.brave.com/zvwQNSVu9-jR2CRlNcsTzxjaXKPlXNuh-Jo9-0yA1OE/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMTNkNWQyNjk3/M2Q0NzYyMmUyNDc3/ZjYwMWFlZDI5YTI4/ODhmYzc2MDkzMjAy/MjNkMWY1MDE3NTQw/MzI5NWVkZS93d3cu/cGx1cmFsc2lnaHQu/Y29tLw"
+				},
+				"language": "en",
+				"family_friendly": true,
+				"type": "search_result",
+				"subtype": "generic",
+				"meta_url": {
+					"scheme": "https",
+					"netloc": "pluralsight.com",
+					"hostname": "www.pluralsight.com",
+					"favicon": "https://imgs.search.brave.com/zvwQNSVu9-jR2CRlNcsTzxjaXKPlXNuh-Jo9-0yA1OE/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMTNkNWQyNjk3/M2Q0NzYyMmUyNDc3/ZjYwMWFlZDI5YTI4/ODhmYzc2MDkzMjAy/MjNkMWY1MDE3NTQw/MzI5NWVkZS93d3cu/cGx1cmFsc2lnaHQu/Y29tLw",
+					"path": "  › blog  › blog"
+				},
+				"thumbnail": {
+					"src": "https://imgs.search.brave.com/xrv5PHH2Bzmq2rcIYzk__8h5RqCj6kS3I6SGCNw5dZM/rs:fit:200:200:1/g:ce/aHR0cHM6Ly93d3cu/cGx1cmFsc2lnaHQu/Y29tL2NvbnRlbnQv/ZGFtL3BzL2ltYWdl/cy9yZXNvdXJjZS1j/ZW50ZXIvYmxvZy9o/ZWFkZXItaGVyby1p/bWFnZXMvUHl0aG9u/LndlYnA",
+					"original": "https://www.pluralsight.com/content/dam/ps/images/resource-center/blog/header-hero-images/Python.webp",
+					"logo": false
+				},
+				"age": "3 days ago",
+				"extra_snippets": [
+					"Whether it’s your first time programming or you’re a seasoned programmer, you’ll have to install or update Python every now and then --- or if necessary, uninstall it. In this article, you'll learn how to do just that.",
+					"Some systems come with Python, so to start off, we’ll first check to see if it’s installed on your system before we proceed. To do that, we’ll need to open a terminal. Since you might be new to programming, let’s go over how to open a terminal for Linux, Windows, and macOS.",
+					"Before we dive into setting up your system so you can program in Python, let’s talk terminal basics and benefits.",
+					"However, let’s focus on why we need it for working with Python. We use a terminal, or command line, to:"
+				]
+			}
+		],
+		"family_friendly": true
+	}
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/google_pse.json b/backend/open_webui/apps/retrieval/web/testdata/google_pse.json
new file mode 100644
index 0000000000000000000000000000000000000000..15da9729cde30eee86d0302f662c5ca37d71f65b
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/google_pse.json
@@ -0,0 +1,442 @@
+{
+	"kind": "customsearch#search",
+	"url": {
+		"type": "application/json",
+		"template": "https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json"
+	},
+	"queries": {
+		"request": [
+			{
+				"title": "Google Custom Search - lectures",
+				"totalResults": "2450000000",
+				"searchTerms": "lectures",
+				"count": 10,
+				"startIndex": 1,
+				"inputEncoding": "utf8",
+				"outputEncoding": "utf8",
+				"safe": "off",
+				"cx": "0473ef98502d44e18"
+			}
+		],
+		"nextPage": [
+			{
+				"title": "Google Custom Search - lectures",
+				"totalResults": "2450000000",
+				"searchTerms": "lectures",
+				"count": 10,
+				"startIndex": 11,
+				"inputEncoding": "utf8",
+				"outputEncoding": "utf8",
+				"safe": "off",
+				"cx": "0473ef98502d44e18"
+			}
+		]
+	},
+	"context": {
+		"title": "LLM Search"
+	},
+	"searchInformation": {
+		"searchTime": 0.445959,
+		"formattedSearchTime": "0.45",
+		"totalResults": "2450000000",
+		"formattedTotalResults": "2,450,000,000"
+	},
+	"items": [
+		{
+			"kind": "customsearch#result",
+			"title": "The Feynman Lectures on Physics",
+			"htmlTitle": "The Feynman \u003cb\u003eLectures\u003c/b\u003e on Physics",
+			"link": "https://www.feynmanlectures.caltech.edu/",
+			"displayLink": "www.feynmanlectures.caltech.edu",
+			"snippet": "This edition has been designed for ease of reading on devices of any size or shape; text, figures and equations can all be zoomed without degradation.",
+			"htmlSnippet": "This edition has been designed for ease of reading on devices of any size or shape; text, figures and equations can all be zoomed without degradation.",
+			"cacheId": "CyXMWYWs9UEJ",
+			"formattedUrl": "https://www.feynmanlectures.caltech.edu/",
+			"htmlFormattedUrl": "https://www.feynman\u003cb\u003electures\u003c/b\u003e.caltech.edu/",
+			"pagemap": {
+				"metatags": [
+					{
+						"viewport": "width=device-width, initial-scale=1.0"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Video Lectures",
+			"htmlTitle": "Video \u003cb\u003eLectures\u003c/b\u003e",
+			"link": "https://www.reddit.com/r/lectures/",
+			"displayLink": "www.reddit.com",
+			"snippet": "r/lectures: This subreddit is all about video lectures, talks and interesting public speeches. The topics include mathematics, physics, computer…",
+			"htmlSnippet": "r/\u003cb\u003electures\u003c/b\u003e: This subreddit is all about video \u003cb\u003electures\u003c/b\u003e, talks and interesting public speeches. The topics include mathematics, physics, computer…",
+			"formattedUrl": "https://www.reddit.com/r/lectures/",
+			"htmlFormattedUrl": "https://www.reddit.com/r/\u003cb\u003electures\u003c/b\u003e/",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTZtOjhfkgUKQbL3DZxe5F6OVsgeDNffleObjJ7n9RllKQTSsimax7VIaY&s",
+						"width": "192",
+						"height": "192"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://www.redditstatic.com/shreddit/assets/favicon/192x192.png",
+						"theme-color": "#000000",
+						"og:image:width": "256",
+						"og:type": "website",
+						"twitter:card": "summary",
+						"twitter:title": "r/lectures",
+						"og:site_name": "Reddit",
+						"og:title": "r/lectures",
+						"og:image:height": "256",
+						"bingbot": "noarchive",
+						"msapplication-navbutton-color": "#000000",
+						"og:description": "This subreddit is all about video lectures, talks and interesting public speeches.\n\nThe topics include mathematics, physics, computer science, programming, engineering, biology, medicine, economics, politics, social sciences, and any other subjects!",
+						"twitter:image": "https://www.redditstatic.com/shreddit/assets/favicon/192x192.png",
+						"apple-mobile-web-app-status-bar-style": "black",
+						"twitter:site": "@reddit",
+						"viewport": "width=device-width, initial-scale=1, viewport-fit=cover",
+						"apple-mobile-web-app-capable": "yes",
+						"og:ttl": "600",
+						"og:url": "https://www.reddit.com/r/lectures/"
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://www.redditstatic.com/shreddit/assets/favicon/192x192.png"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Lectures & Discussions | Flint Institute of Arts",
+			"htmlTitle": "\u003cb\u003eLectures\u003c/b\u003e &amp; Discussions | Flint Institute of Arts",
+			"link": "https://flintarts.org/events/lectures",
+			"displayLink": "flintarts.org",
+			"snippet": "It will trace the intricate relationship between jewelry, attire, and the expression of personal identity, social hierarchy, and spiritual belief systems that ...",
+			"htmlSnippet": "It will trace the intricate relationship between jewelry, attire, and the expression of personal identity, social hierarchy, and spiritual belief systems that&nbsp;...",
+			"cacheId": "jvpb9DxrfxoJ",
+			"formattedUrl": "https://flintarts.org/events/lectures",
+			"htmlFormattedUrl": "https://flintarts.org/events/\u003cb\u003electures\u003c/b\u003e",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS23tMtAeNhJbOWdGxShYsmnyzFdzOC9Hb7lRykA9Pw72z1IlKTkjTdZw&s",
+						"width": "447",
+						"height": "113"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://flintarts.org/uploads/images/page-headers/_headerImage/nightshot.jpg",
+						"og:type": "website",
+						"viewport": "width=device-width, initial-scale=1",
+						"og:title": "Lectures & Discussions | Flint Institute of Arts",
+						"og:description": "The Flint Institute of Arts is the second largest art museum in Michigan and one of the largest museum art schools in the nation."
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://flintarts.org/uploads/images/page-headers/_headerImage/nightshot.jpg"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Mandel Lectures | Mandel Center for the Humanities ... - Waltham",
+			"htmlTitle": "Mandel \u003cb\u003eLectures\u003c/b\u003e | Mandel Center for the Humanities ... - Waltham",
+			"link": "https://www.brandeis.edu/mandel-center-humanities/mandel-lectures.html",
+			"displayLink": "www.brandeis.edu",
+			"snippet": "Past Lectures · Lecture 1: \"Invisible Music: The Sonic Idea of Black Revolution From Captivity to Reconstruction\" · Lecture 2: \"Solidarity in Sound: Grassroots ...",
+			"htmlSnippet": "Past \u003cb\u003eLectures\u003c/b\u003e &middot; \u003cb\u003eLecture\u003c/b\u003e 1: &quot;Invisible Music: The Sonic Idea of Black Revolution From Captivity to Reconstruction&quot; &middot; \u003cb\u003eLecture\u003c/b\u003e 2: &quot;Solidarity in Sound: Grassroots&nbsp;...",
+			"cacheId": "cQLOZr0kgEEJ",
+			"formattedUrl": "https://www.brandeis.edu/mandel-center-humanities/mandel-lectures.html",
+			"htmlFormattedUrl": "https://www.brandeis.edu/mandel-center-humanities/mandel-\u003cb\u003electures\u003c/b\u003e.html",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQWlU7bcJ5pIHk7RBCk2QKE-48ejF7hyPV0pr-20_cBt2BGdfKtiYXBuyw&s",
+						"width": "275",
+						"height": "183"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://www.brandeis.edu/mandel-center-humanities/events/events-images/mlhzumba",
+						"twitter:card": "summary_large_image",
+						"viewport": "width=device-width,initial-scale=1,minimum-scale=1",
+						"og:title": "Mandel Lectures in the Humanities",
+						"og:url": "https://www.brandeis.edu/mandel-center-humanities/mandel-lectures.html",
+						"og:description": "Annual Lecture Series",
+						"twitter:image": "https://www.brandeis.edu/mandel-center-humanities/events/events-images/mlhzumba"
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://www.brandeis.edu/mandel-center-humanities/events/events-images/mlhzumba"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Brian Douglas - YouTube",
+			"htmlTitle": "Brian Douglas - YouTube",
+			"link": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+			"displayLink": "www.youtube.com",
+			"snippet": "Welcome to Control Systems Lectures! This collection of videos is intended to supplement a first year controls class, not replace it.",
+			"htmlSnippet": "Welcome to Control Systems \u003cb\u003eLectures\u003c/b\u003e! This collection of videos is intended to supplement a first year controls class, not replace it.",
+			"cacheId": "NEROyBHolL0J",
+			"formattedUrl": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+			"htmlFormattedUrl": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+			"pagemap": {
+				"hcard": [
+					{
+						"fn": "Brian Douglas",
+						"url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg"
+					}
+				],
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR7G0CeCBz_wVTZgjnhEr2QbiKP7f3uYzKitZYn74Mi32cDmVxvsegJoLI&s",
+						"width": "225",
+						"height": "225"
+					}
+				],
+				"imageobject": [
+					{
+						"width": "900",
+						"url": "https://yt3.googleusercontent.com/ytc/AIdro_nLo68wetImbwGUYP3stve_iKmAEccjhqB-q4o79xdInN4=s900-c-k-c0x00ffffff-no-rj",
+						"height": "900"
+					}
+				],
+				"person": [
+					{
+						"name": "Brian Douglas",
+						"url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg"
+					}
+				],
+				"metatags": [
+					{
+						"apple-itunes-app": "app-id=544007664, app-argument=https://m.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg?referring_app=com.apple.mobilesafari-smartbanner, affiliate-data=ct=smart_app_banner_polymer&pt=9008",
+						"og:image": "https://yt3.googleusercontent.com/ytc/AIdro_nLo68wetImbwGUYP3stve_iKmAEccjhqB-q4o79xdInN4=s900-c-k-c0x00ffffff-no-rj",
+						"twitter:app:url:iphone": "vnd.youtube://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"twitter:app:id:googleplay": "com.google.android.youtube",
+						"theme-color": "rgb(255, 255, 255)",
+						"og:image:width": "900",
+						"twitter:card": "summary",
+						"og:site_name": "YouTube",
+						"twitter:url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"twitter:app:url:ipad": "vnd.youtube://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"al:android:package": "com.google.android.youtube",
+						"twitter:app:name:googleplay": "YouTube",
+						"al:ios:url": "vnd.youtube://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"twitter:app:id:iphone": "544007664",
+						"og:description": "Welcome to Control Systems Lectures!  This collection of videos is intended to supplement a first year controls class, not replace it.  My goal is to take specific concepts in controls and expand on them in order to provide an intuitive understanding which will ultimately make you a better controls engineer.  \n\nI'm glad you made it to my channel and I hope you find it useful.\n\nShoot me a message at controlsystemlectures@gmail.com, leave a comment or question and I'll get back to you if I can. Don't forget to subscribe!\n \nTwitter: @BrianBDouglas for engineering tweets and announcement of new videos.\nWebpage: http://engineeringmedia.com\n\nHere is the hardware/software I use: http://www.youtube.com/watch?v=m-M5_mIyHe4\n\nHere's a list of my favorite references: http://bit.ly/2skvmWd\n\n--Brian",
+						"al:ios:app_store_id": "544007664",
+						"twitter:image": "https://yt3.googleusercontent.com/ytc/AIdro_nLo68wetImbwGUYP3stve_iKmAEccjhqB-q4o79xdInN4=s900-c-k-c0x00ffffff-no-rj",
+						"twitter:site": "@youtube",
+						"og:type": "profile",
+						"twitter:title": "Brian Douglas",
+						"al:ios:app_name": "YouTube",
+						"og:title": "Brian Douglas",
+						"og:image:height": "900",
+						"twitter:app:id:ipad": "544007664",
+						"al:web:url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg?feature=applinks",
+						"al:android:url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg?feature=applinks",
+						"fb:app_id": "87741124305",
+						"twitter:app:url:googleplay": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"twitter:app:name:ipad": "YouTube",
+						"viewport": "width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no,",
+						"twitter:description": "Welcome to Control Systems Lectures!  This collection of videos is intended to supplement a first year controls class, not replace it.  My goal is to take specific concepts in controls and expand on them in order to provide an intuitive understanding which will ultimately make you a better controls engineer.  \n\nI'm glad you made it to my channel and I hope you find it useful.\n\nShoot me a message at controlsystemlectures@gmail.com, leave a comment or question and I'll get back to you if I can. Don't forget to subscribe!\n \nTwitter: @BrianBDouglas for engineering tweets and announcement of new videos.\nWebpage: http://engineeringmedia.com\n\nHere is the hardware/software I use: http://www.youtube.com/watch?v=m-M5_mIyHe4\n\nHere's a list of my favorite references: http://bit.ly/2skvmWd\n\n--Brian",
+						"og:url": "https://www.youtube.com/channel/UCq0imsn84ShAe9PBOFnoIrg",
+						"al:android:app_name": "YouTube",
+						"twitter:app:name:iphone": "YouTube"
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://yt3.googleusercontent.com/ytc/AIdro_nLo68wetImbwGUYP3stve_iKmAEccjhqB-q4o79xdInN4=s900-c-k-c0x00ffffff-no-rj"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Lecture - Wikipedia",
+			"htmlTitle": "\u003cb\u003eLecture\u003c/b\u003e - Wikipedia",
+			"link": "https://en.wikipedia.org/wiki/Lecture",
+			"displayLink": "en.wikipedia.org",
+			"snippet": "Lecture ... For the academic rank, see Lecturer. A lecture (from Latin: lēctūra 'reading') is an oral presentation intended to present information or teach people ...",
+			"htmlSnippet": "\u003cb\u003eLecture\u003c/b\u003e ... For the academic rank, see \u003cb\u003eLecturer\u003c/b\u003e. A \u003cb\u003electure\u003c/b\u003e (from Latin: lēctūra &#39;reading&#39;) is an oral presentation intended to present information or teach people&nbsp;...",
+			"cacheId": "d9Pjta02fmgJ",
+			"formattedUrl": "https://en.wikipedia.org/wiki/Lecture",
+			"htmlFormattedUrl": "https://en.wikipedia.org/wiki/Lecture",
+			"pagemap": {
+				"metatags": [
+					{
+						"referrer": "origin",
+						"og:image": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/26/ADFA_Lecture_Theatres.jpg/1200px-ADFA_Lecture_Theatres.jpg",
+						"theme-color": "#eaecf0",
+						"og:image:width": "1200",
+						"og:type": "website",
+						"viewport": "width=device-width, initial-scale=1.0, user-scalable=yes, minimum-scale=0.25, maximum-scale=5.0",
+						"og:title": "Lecture - Wikipedia",
+						"og:image:height": "799",
+						"format-detection": "telephone=no"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Mount Wilson Observatory | Lectures",
+			"htmlTitle": "Mount Wilson Observatory | \u003cb\u003eLectures\u003c/b\u003e",
+			"link": "https://www.mtwilson.edu/lectures/",
+			"displayLink": "www.mtwilson.edu",
+			"snippet": "Talks & Telescopes: August 24, 2024 – Panel: The Triumph of Hubble ... Compelling talks followed by picnicking and convivial stargazing through both the big ...",
+			"htmlSnippet": "Talks &amp; Telescopes: August 24, 2024 – Panel: The Triumph of Hubble ... Compelling talks followed by picnicking and convivial stargazing through both the big&nbsp;...",
+			"cacheId": "wdXI0azqx5UJ",
+			"formattedUrl": "https://www.mtwilson.edu/lectures/",
+			"htmlFormattedUrl": "https://www.mtwilson.edu/\u003cb\u003electures\u003c/b\u003e/",
+			"pagemap": {
+				"metatags": [
+					{
+						"viewport": "width=device-width,initial-scale=1,user-scalable=no"
+					}
+				],
+				"webpage": [
+					{
+						"image": "http://www.mtwilson.edu/wp-content/uploads/2016/09/Logo.jpg",
+						"url": "https://www.facebook.com/WilsonObs"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Lectures | NBER",
+			"htmlTitle": "\u003cb\u003eLectures\u003c/b\u003e | NBER",
+			"link": "https://www.nber.org/research/lectures",
+			"displayLink": "www.nber.org",
+			"snippet": "Results 1 - 50 of 354 ... Among featured events at the NBER Summer Institute are the Martin Feldstein Lecture, which examines a current issue involving economic ...",
+			"htmlSnippet": "Results 1 - 50 of 354 \u003cb\u003e...\u003c/b\u003e Among featured events at the NBER Summer Institute are the Martin Feldstein \u003cb\u003eLecture\u003c/b\u003e, which examines a current issue involving economic&nbsp;...",
+			"cacheId": "CvvP3U3nb44J",
+			"formattedUrl": "https://www.nber.org/research/lectures",
+			"htmlFormattedUrl": "https://www.nber.org/research/\u003cb\u003electures\u003c/b\u003e",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTmeViEZyV1YmFEFLhcA6WdgAG3v3RV6tB93ncyxSJ5JPst_p2aWrL7D1k&s",
+						"width": "310",
+						"height": "163"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://www.nber.org/sites/default/files/2022-06/NBER-FB-Share-Tile-1200.jpg",
+						"og:site_name": "NBER",
+						"handheldfriendly": "true",
+						"viewport": "width=device-width, initial-scale=1.0",
+						"og:title": "Lectures",
+						"mobileoptimized": "width",
+						"og:url": "https://www.nber.org/research/lectures"
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://www.nber.org/sites/default/files/2022-06/NBER-FB-Share-Tile-1200.jpg"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "STUDENTS CANNOT ACCESS RECORDED LECTURES ... - Solved",
+			"htmlTitle": "STUDENTS CANNOT ACCESS RECORDED LECTURES ... - Solved",
+			"link": "https://community.canvaslms.com/t5/Canvas-Question-Forum/STUDENTS-CANNOT-ACCESS-RECORDED-LECTURES/td-p/190358",
+			"displayLink": "community.canvaslms.com",
+			"snippet": "Mar 19, 2020 ... I believe the issue is that students were not invited. Are you trying to capture your screen? If not, there is an option to just record your web ...",
+			"htmlSnippet": "Mar 19, 2020 \u003cb\u003e...\u003c/b\u003e I believe the issue is that students were not invited. Are you trying to capture your screen? If not, there is an option to just record your web&nbsp;...",
+			"cacheId": "wqrynQXX61sJ",
+			"formattedUrl": "https://community.canvaslms.com/t5/Canvas...LECTURES/td-p/190358",
+			"htmlFormattedUrl": "https://community.canvaslms.com/t5/Canvas...\u003cb\u003eLECTURES\u003c/b\u003e/td-p/190358",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRUqXau3N8LfKgSD7OJOvV7xzGarLKRU-ckWXy1ZQ1p4CLPsedvLKmLMhk&s",
+						"width": "310",
+						"height": "163"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://community.canvaslms.com/html/@6A1FDD4D5FF35E4BBB4083A1022FA0DB/assets/CommunityPreview23.png",
+						"og:type": "article",
+						"article:section": "Canvas Question Forum",
+						"article:published_time": "2020-03-19T15:50:03.409Z",
+						"og:site_name": "Instructure Community",
+						"article:modified_time": "2020-03-19T13:55:53-07:00",
+						"viewport": "width=device-width, initial-scale=1.0, user-scalable=yes",
+						"og:title": "STUDENTS CANNOT ACCESS RECORDED LECTURES",
+						"og:url": "https://community.canvaslms.com/t5/Canvas-Question-Forum/STUDENTS-CANNOT-ACCESS-RECORDED-LECTURES/m-p/190358#M93667",
+						"og:description": "I can access and see my recorded lectures but my students can't. They have an error message when they try to open the recorded presentation or notes.",
+						"article:author": "https://community.canvaslms.com/t5/user/viewprofilepage/user-id/794287",
+						"twitter:image": "https://community.canvaslms.com/html/@6A1FDD4D5FF35E4BBB4083A1022FA0DB/assets/CommunityPreview23.png"
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://community.canvaslms.com/html/@6A1FDD4D5FF35E4BBB4083A1022FA0DB/assets/CommunityPreview23.png"
+					}
+				]
+			}
+		},
+		{
+			"kind": "customsearch#result",
+			"title": "Public Lecture Series - Sam Fox School of Design & Visual Arts",
+			"htmlTitle": "Public \u003cb\u003eLecture\u003c/b\u003e Series - Sam Fox School of Design &amp; Visual Arts",
+			"link": "https://samfoxschool.wustl.edu/calendar/series/2-public-lecture-series",
+			"displayLink": "samfoxschool.wustl.edu",
+			"snippet": "The Sam Fox School's Spring 2024 Public Lecture Series highlights design and art as catalysts for change. Renowned speakers will delve into themes like ...",
+			"htmlSnippet": "The Sam Fox School&#39;s Spring 2024 Public \u003cb\u003eLecture\u003c/b\u003e Series highlights design and art as catalysts for change. Renowned speakers will delve into themes like&nbsp;...",
+			"cacheId": "B-cgQG0j6tUJ",
+			"formattedUrl": "https://samfoxschool.wustl.edu/calendar/series/2-public-lecture-series",
+			"htmlFormattedUrl": "https://samfoxschool.wustl.edu/calendar/series/2-public-lecture-series",
+			"pagemap": {
+				"cse_thumbnail": [
+					{
+						"src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQSmHaGianm-64m-qauYjkPK_Q0JKWe-7yom4m1ogFYTmpWArA7k6dmk0sR&s",
+						"width": "307",
+						"height": "164"
+					}
+				],
+				"website": [
+					{
+						"name": "Public Lecture Series - Sam Fox School of Design & Visual Arts — Washington University in St. Louis"
+					}
+				],
+				"metatags": [
+					{
+						"og:image": "https://dvsp0hlm0xrn3.cloudfront.net/assets/default_og_image-44e73dee4b9d1e2c6a6295901371270c8ec5899eaed48ee8167a9b12f1b0f8b3.jpg",
+						"og:type": "website",
+						"og:site_name": "Sam Fox School of Design & Visual Arts — Washington University in St. Louis",
+						"viewport": "width=device-width, initial-scale=1.0",
+						"og:title": "Public Lecture Series - Sam Fox School of Design & Visual Arts — Washington University in St. Louis",
+						"csrf-token": "jBQsfZGY3RH8NVs0-KVDBYB-2N2kib4UYZHYdrShfTdLkvzfSvGeOaMrRKTRdYBPRKzdcGIuP7zwm9etqX_uvg",
+						"csrf-param": "authenticity_token",
+						"og:description": "The Sam Fox School's Spring 2024 Public Lecture Series highlights design and art as catalysts for change. Renowned speakers will delve into themes like social equity, resilient cities, and the impact of emerging technologies on contemporary life. Speakers include artists, architects, designers, and critics of the highest caliber, widely recognized for their research-based practices and multidisciplinary approaches to their fields."
+					}
+				],
+				"cse_image": [
+					{
+						"src": "https://dvsp0hlm0xrn3.cloudfront.net/assets/default_og_image-44e73dee4b9d1e2c6a6295901371270c8ec5899eaed48ee8167a9b12f1b0f8b3.jpg"
+					}
+				]
+			}
+		}
+	]
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/searchapi.json b/backend/open_webui/apps/retrieval/web/testdata/searchapi.json
new file mode 100644
index 0000000000000000000000000000000000000000..fa3d1c3d74097aaef3d975ff6af845b6c1370b17
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/searchapi.json
@@ -0,0 +1,357 @@
+{
+	"search_metadata": {
+		"id": "search_VW19X7MebbAtdMwoQe68NbDz",
+		"status": "Success",
+		"created_at": "2024-08-27T13:43:20Z",
+		"request_time_taken": 0.6,
+		"parsing_time_taken": 0.72,
+		"total_time_taken": 1.32,
+		"request_url": "https://www.google.com/search?q=chatgpt&oq=chatgpt&gl=us&hl=en&ie=UTF-8",
+		"html_url": "https://www.searchapi.io/api/v1/searches/search_VW19X7MebbAtdMwoQe68NbDz.html",
+		"json_url": "https://www.searchapi.io/api/v1/searches/search_VW19X7MebbAtdMwoQe68NbDz"
+	},
+	"search_parameters": {
+		"engine": "google",
+		"q": "chatgpt",
+		"device": "desktop",
+		"google_domain": "google.com",
+		"hl": "en",
+		"gl": "us"
+	},
+	"search_information": {
+		"query_displayed": "chatgpt",
+		"total_results": 1010000000,
+		"time_taken_displayed": 0.37,
+		"detected_location": "United States"
+	},
+	"knowledge_graph": {
+		"kgmid": "/g/11khcfz0y2",
+		"knowledge_graph_type": "Kp3 verticals",
+		"title": "ChatGPT",
+		"type": "Software",
+		"description": "ChatGPT is a chatbot and virtual assistant developed by OpenAI and launched on November 30, 2022. Based on large language models, it enables users to refine and steer a conversation towards a desired length, format, style, level of detail, and language.",
+		"source": {
+			"name": "Wikipedia",
+			"link": "https://en.wikipedia.org/wiki/ChatGPT"
+		},
+		"developer": "OpenAI, Microsoft",
+		"developer_links": [
+			{
+				"text": "OpenAI",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=OpenAI&si=ACC90nwLLwns5sISZcdzuISy7t-NHozt8Cbt6G3WNQfC9ekAgItjbuK5dmA2L3ta2Ero3Ypd_sib6W4Pr5sCi7O_W3yzdqxwyrjzsYeYOtNg2ogL1xVq9TKwgD48tL7rygfkRfNyy4k-R5yQgywoFukoCUths6NdRX69gl50cvd6dpZcMzVelCxT7mxXlRchl6XkueG326znDiZL-ODNOysdnCc4XoeAQUFtbaVjja6Vc7WkQF4X8rUdbDKPVU9WyLOV765d8Y777kMI7-nXGGyD7xXJX5E3HA%3D%3D&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAHoECD0QAg"
+			},
+			{
+				"text": "Microsoft",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=Microsoft&si=ACC90nyvvWro6QmnyY1IfSdgk5wwjB1r8BGd_IWRjXqmKPQqm-SdjhIP74XAMBYys4zy1Z9yzXEom04F9Qy-tMOt2d-L6jIC5cXse6I528G870-4sF-DZYAPj0F1HoGTUOqpWuP7jbEPm3w_-mCH0wVgBHBGCgxRrCaUn8_k2-aga9V9JD6hkq2kM8zVmERCqCM8rqo3bNfbPdJ-baTq4w8Pkxdan3K--CfOtXX--lTjJtO6BnfG2RdpY_jBfy3uZZ7DeAE4-P4rvKuty6UL6le4dqqDt-kLQA%3D%3D&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAXoECD0QAw"
+			}
+		],
+		"initial_release_date": "November 30, 2022",
+		"programming_language": "Python",
+		"programming_language_links": [
+			{
+				"text": "Python",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=Python&si=ACC90nyvvWro6QmnyY1IfSdgk5wwjB1r8BGd_IWRjXqmKPQqmwbtPHPEcZi5JOYKaqe_iu1m4TVPotntrDVKbuXCkoFhx-K-Dp6PbewOILPFWjhDofHha-WRuSQCgY7LnBkzXtVH7pxiRdHONv3wpVsflGBg_EdTHCxOnyWt1nDgBmCjsfchXU7DKtJq159-V0-seE_cp7VV&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAHoECDYQAg"
+			}
+		],
+		"engine": "GPT-4; GPT-4o; GPT-4o mini",
+		"engine_links": [
+			{
+				"text": "GPT-4",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=GPT-4&stick=H4sIAAAAAAAAAONgVuLVT9c3NMy2TI_PNUtOX8TK6h4QomsCAKiBOxkZAAAA&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAHoECDUQAg"
+			},
+			{
+				"text": "GPT-4o",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=GPT-4o&stick=H4sIAAAAAAAAAONgVuLVT9c3NCyryEg3rMooWMTK5h4QomuSDwC3NAfvGgAAAA&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAXoECDUQAw"
+			},
+			{
+				"text": "GPT-4o",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=GPT-4o&stick=H4sIAAAAAAAAAONgVuLVT9c3NCyryEg3rMooWMTK5h4QomuSDwC3NAfvGgAAAA&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAnoECDUQBA"
+			}
+		],
+		"license": "Proprietary",
+		"platform": "Cloud computing platforms",
+		"platform_links": [
+			{
+				"text": "Cloud computing",
+				"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=Cloud+computing&stick=H4sIAAAAAAAAAONgVuLSz9U3MKqMt8w1XsTK75yTX5qikJyfW1BakpmXDgB-4JvxIAAAAA&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQmxMoAHoECDcQAg"
+			}
+		],
+		"stable_release": "July 18, 2024; 40 days ago",
+		"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALoAAAC6CAMAAAAu0KfDAAAAaVBMVEX///8AAAD7+/uysrJ5eXmoqKiYmJhZWVn4+PiVlZXw8PDj4+P09PTr6+vNzc2vr6/V1dWMjIxmZmY7OzvExMSCgoIgICBMTEygoKBUVFQUFBQxMTG4uLhDQ0O+vr7b29snJydubm4LCwtts+PWAAAPGElEQVR4nM1d6YKiMAxWBKlcIocoooDv/5CrjjpNmrTlcNz82x2Oz5KmX46mi8VMIoI42ZbZ+VRfl9e6891108aRM9fjPyVOmHhZvVSl2hVp8G10Gom3uyMB+yldf0i+jZCRcOdfeeAPOfUr8W2YqoTZ3oD7IfXJ+zZSKE7Y2+B+SvMfKX24oWYmL8fL/2JvPM3cpKV2w2+DvkvgmiYnJefLt3EvFsl5BPC77L6tNNuRwG/Sf1VpxFqHrd53WoN5TL+HPNgxoPb9ZnspiiRJiqLx2CU2/9rqGpUkoNMhDQN50RRxeMlo7MV/hHxfxszlTUUY/y5ZOEIEaXJZeQfvkrRRJD4/fR0CuX/QUZTEPamfyCsr+D9VuUrC6KPQ1RlaHUxvTGgVUxWp9JLP8bQVft11Z2HsxKWioBJy6g8fMp6J8u0LOx0NXEvsd/SfAB/76DVH+3sLa+y3ydPOPmk38A373YB720HUoZx53Urg4+uD/diEh2EEedkdOHM7SpCia00iFM92mkriz7hwHeCj7bWlPY0hyMvrYS7kYQce7Frf14/B/ZBqJqWB60pmqS3hwcrvZqSbZbamwEKcW6ubxErnBea+65al2x9VpvDGPodbBTS9totNJBmr5P4hScM4iKIoiOMwLdbMb9xPxx4CG5HZ3OL0DPBrVqjBSEc0NPpmKvQLGAqL6RN4NO668thpEmwoIzrRSArgNJjtIjeEy2ylXceiA+YaN5nmVQXgWUZenTChjvxi/F7hocN3WdoEUkTRy49aGy4PSsZkrK04oULyltk4++6EG0ScrnoAYsvQlcyazB7wrSOiN6L1VMK3061GIs1p4Mchsy3BDxlqZsRlp+jdTXSWNmV8ObMXCKXFSjMsTHxxyS9f8ZMm2jFDbqfksmCFt6ZMNwl9hnyU7C0rxpuo4hFOTwCnem2tMoKLcN1oOn2Hw0VQzyOXlBg+z7ezMk7BfPibnOgFomWU/LweHZ5I4bhbsfdooyGqZ+rXxx41n+/e64TVZNEAGGeL+aIuCbJQQYAtt+on0zx7+CW3xutTveuukkbnzKxBxeRQFvyEpnFoDK67Qr1C+rpuMxX3TVLwSIOR2Zr8MUxgBKle+1Kv5CIpisTCZoCg2Vl7qRJTvEsuT3XsH12o35oZDGJR3u6qs8aoUS14um6iFoS25KtQ/u14sihU6WY/Ddnd+MUs697kNjtgfdH4CQrrufu1An62FbpHjVrvDHxDDoRdS8PcAwFaPsSphgXz9f3JzgDoe8OqJwq8Aug/EXg3sxzeVqIeI3db9XY99MzgwCdEsNq/6FTek/0tbkXFtGX/CipaQz+t9EMebshFt3Y13mcsKzFD/VL0wNN7AG2hl4almnBbXu/SzA/Z+NKenoOMnEQZ7KD7BrpC2ABJui2nNXJQhE65IuZ3lp5kBd3XmgonNGZkzikNPpYvohZUlCU6yl9mOvRwbRM03dFWXr6EoGARHJQKPGQy9ANill1PY89JlZdnyFp9C0xSofjqROgpZjl+G4XMjM0JjZBVuVS88wgm85H5nARdyQzsf/jNhQkznRWtkXlVr3wWmODq0V+10F9ZPRp6jIOm53fuLMKhqffIIlMla8RRsY7gk17xLxsNXQ2abmRYbUlHJrstANhKf8rx0gHsjwJutMIkuFg2Q2bZKRhf0pdVXvZlThg6sOmVok7jRl30yCB2BFcRDY29Pv5i1EF3ALNQF6wx0APM4zsuPlzSwYRl+RpCHXTgjqpzeAR0pRTj5PI0oaUDhMvTU+Vl6FjXZWRXRdNHQFcKYPQRMMEUWi2zh8rL5g9lUkHcRbU+g6EHO8Vkn3Z6Nhxvaa2p7x9Lng4Z1IlCfhPFiIdB90i6YkpXxkzgr96BehAXrqbyhNpTDGgAdKEW+7wfbSiOCo+0ytfyyrUBxgAEdU/UQ+2hc5mB55AZUnErbcTwIZA5xvIcwRxgEPRgg7yJHP3bqPIen3L/EcgL25z90xDomRo03cQxqltaVoaYZ6gvzkMhAUC9SBfRCnqHE83VwwSHyn8b/MBWVxDhQ7MOqDr5NCvoGOGbhTQYPFuJ+hRNNQeyf3JBdzUT9Fyu3IoPSOX3nh58wGXTsCMh20Y6TzYYOnYyUxzi8Q1WXrnhR5Cqg4AkHRMfCL1ScxlOgrTm6usDpXTmGMUchS6MOwI6lyVUUg6uQeWJSD9iMKKX/ka/dgD0nM/mC6zBV6PKK8QGUhgAnaCNg6AzkZSnqCqvDZTejJ/CKEF9wowKczSVcIsE2T1TbkBZ0sDrAXR64ttBr7cWFQwRrjjpDMn/Ffa8JZVx5B9GB4GtoB8tSy8ivOBc9dsicS5UJoiyXSfZlyX9skOOAic/t2oTrDjXIvm4cnApnwDdMjHtUMxWO71TuCacfq+dicNMgr7M1xp9Q+5L+f5GgDnSWYPPQ79nLPl74Fyt33QA6BJp2D8DPeuRsTnz2xxgru3toAIviax4/Qz0w6JBH+DKlqA4cG16aTvgX93fQV8TdPjMqTysoXgbQjmOXFOeL4COVXIKdMKh85nEK4wDvtYlMIHJxU1+Ps7TT4N+3w2BwNOVnAGw7i9yHsgqV1E3AiZxhgvIVOg3dotTBCQdATa8e/1vaboPjQzYNDQd+iJeo5GnWCCwJvUrhglKSciEMNqQfFr/XjQDdCX0pCQA7gJSO2+yBSg9GZRVUkLvgZkF+kIUQGuoWDwIi/gvSwRcADWr8bgRs/6unRP6AuUYqTvJ8hJYWM9UbihB8J+GBrNBl5WStHNAr99TsgeYmAILsUamIL/vDZ0NuqyTtP8h6/XbB4YFJRmHot0gn+W4fa8VfwAdxHVfhgKVCLA7GhwcQqyzF5I/gC57Fr/7NxLA4qiE0ku4BPkfQJfTu9c3YxHQsOq6FERY5f8MOigv+bXhqOK60nlcLRUR/AvoMs+UaCAq+tOWXFMJ8sFu9QjovXSJbMJRlEy/j0k0OD4yOJgxArqs1XJCDK5LN+Ohmat3wRHBoSGkEdDlS0BZqrLfb6MPZ6Y4QT4scDcCumwdXRB9VFJQuAgEI1G4wZBw6UToPRimQAms6kon7xLjEGLObxXDQeo5R/2GRN3uuTdkC2Ncv2idGhgBXWZoJRojKneWM6WTL2l9BMomITMOuuxoohwEk+A3dYHwsJU3psFGQpe/sCaZJwtdOvkrMa7G0CcfR0OXlQIpMp/lPhqMvFITokn5jocu3w790FBXGXE0JcgtE+3H10o2HHokPwdVC+jrIkwd6mzKG26+ycsCD4cu76rP4UgyhXtv6UzZQlNRSb4JpjBHeZaimIu5/ZtvUHl9Kc/DUk2ALisFtI2OzHuZuVUbVF5TQHX6YTjjoYPtVdA2Br30p/Vix2zGMyXI6bK196o8Hrr8RU/QwAAK09xLJ+lS//3WlCAnigXfC8No6LFsBXy40IAdwQ/bw5ZO6hPkBXZeS8mSjYa+krUAhcdC2R78GMJ4S6uuLkEe4sZwR/BDx0IH6ow5Xii/8mXDudLJPbexfY3LKVAHgbHQQaKxQwSPhM6XTi6JnUTigoDvXXzRSOgBMHl43BjoCyXT9hIfd4lL0NyoCTdrJHS45mA7AXatgEWfVXmwraLF+zd9aivsOOhwX4MSOAE+EhouMmp0k/PmpcnOQUkJ0anvUdB78GTlW4I5rKz43Eb35y7DAq8BXH/AUdChI5Gpm5NkekPsBW6Y6sNzGCmNRfiCujHQYS8EakuGzFCpYk2FGb5HGNmVo6ZJxAjo6IO7RKgKzGLyvUzpJJS9vqXKYOgx9APIXcqAr9PeqDD0dri/NtRv3xwKPUBvJNdCQFg5Zu6smM04P2LqH/w7Kyyh442Ge/Kx5vKShxDM8CVnQ62uXM1gB11JojD+gsxXzpr4RULvJOo2eu81Xsk4bKBHa/wizgDI5rPWRezEheAGpq0YiEJbQE975R0c24blJdrZFmAfvDO0VIlxbyUjdLFRFPPIf1bZDLGtEp7ilNLHrA3tfoju4QboolF9tFwTQQQv2JgSW+mr8Kwz7GEIGmJi4x8LtgCEK2L163SDCUqs9sY+Rk5zV999aWqpQvmJSn9M0AaCsmGGrpTgt/Idvt4S31vU6HNIbUl650rzU9NC3Zma8oCr52gOTbfJIJ5t6JHQmdqHCWBH66m4BdOJuj6rX0q/i01jW14C03hDmk6r4mCH742DYhnaE0ZseutCyjCpX2u7o0NQdNSVa4rwuINo2kAIdEjy0d3ShOLwPYXpTYVjCZJUls1LUa+148jzdpQI2FPOXAM2ZkPcTewbjqMsYTXi7AKHyGL+AGeD3IIxMB2xTZ0X9JDhPU9D5rSTfMcPA+3CdO6wjooxut++XepDxIEZcm0Ok8qonDaWJxn8Cm65pq3rwVIwRk6fOY5UffEbvadIiqp3hjzMryiVnE+p2VZHP9Ki6ysvGNdPUe3o2FspPKfkp9I010A82Z3SBjJV8xKG7N1N4gsdprma3Cc8vaadf0Zwj8zTBylW3Kpv7h8IjVpvsegPxH71efDCo07ouYtnMYYpKCSbfLIDnf/NCketGIkKrrv9tbfSWvC9LDiiETvTqivzkjaMgyASURCHbeIxinLvbm9HPiBtmuOcQh0h8nu3LMs+04XxjpYnPcJt1KY+A3aSakN0Btnbnh6EdnFOcxHeEms76mnF/uQgqG/dXEfHOLtxR6ic7L86WrlnPJGTa2+lk8r+/bhtN73hdaTESsTSIPWAI7IE/qozH5FH9RvlxVRoJYuDI8amAxgGi5MOOQ/U3hOPlO13sx7v9JSwN/XL+RXXjj4JpROzLho6Cfwa5+lYqWyOj01VNZx8WgwrUeGVloa+NE23eK0+abZDqWj0YbJ10eJflUTzpdzVffwIdwS9y0zLqE4cEUVtUjTbbXNJQiGEs0gIVaorjryGZGGW2trzb4Q5VqEvwhgU9wdhwkTztEekfFTY/sHHndc8Tmu9f6cNe2qVJkLzcUm1J93tO/0huQMOA/yAcGEMGzGf3fBZUXepWMqgCNWHpBl1mPW3TAuUdvgR4gMI8mdFXIYd3F4bqgr+VJQEvE76EQHRj8q6s1Kb/cDg95+I2Bopcu3bHF/8FUnWuDeyLP6u+YRTMZfE6aWkjGXXr5L/dcB/RURxu1q7WV5fl9f6VPW7bRIHs/Gsf2zY1viSH96vAAAAAElFTkSuQmCC"
+	},
+	"organic_results": [
+		{
+			"position": 1,
+			"title": "ChatGPT | OpenAI",
+			"link": "https://openai.com/chatgpt/",
+			"source": "OpenAI",
+			"domain": "openai.com",
+			"displayed_link": "https://openai.com › chatgpt",
+			"snippet": "ChatGPT helps you get answers, find inspiration and be more productive. It is free to use and easy to try. Just ask and ChatGPT can help with writing, ...",
+			"snippet_highlighted_words": ["ChatGPT", "ChatGPT"],
+			"sitelinks": {
+				"expanded": [
+					{
+						"title": "Introducing ChatGPT",
+						"link": "https://openai.com/index/chatgpt/",
+						"snippet": "We've trained a model called ChatGPT which interacts in a ..."
+					},
+					{
+						"title": "Download ChatGPT",
+						"link": "https://openai.com/chatgpt/download/",
+						"snippet": "Download ChatGPT Use ChatGPT your way. Talk to type or have a ..."
+					},
+					{
+						"title": "Pricing",
+						"link": "https://openai.com/chatgpt/pricing/",
+						"snippet": "Pricing · $25per user / month billed annually · $30per user / month ..."
+					},
+					{
+						"title": "“What is ChatGPT?” article",
+						"link": "https://help.openai.com/en/articles/6783457-what-is-chatgpt",
+						"snippet": "How does ChatGPT work? ChatGPT is fine-tuned from ..."
+					},
+					{
+						"title": "For Teams",
+						"link": "https://openai.com/chatgpt/team/",
+						"snippet": "ChatGPT simplifies lead qualification and forecasting ..."
+					}
+				]
+			},
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAABQElEQVR4Ac3PIYyDMBiG4VefPDtxEj0xM39qZl40mcPhMzONOjWNrqxA4UgmqklweBQKVfFdGhbSZZvfY5qmb35++DAbO4XQF7xjpN42s1oyXtlr2gN4SRpynnTaANtesy1tkOOR8aoAJ12J6ngmGkknCqn5gv0y8Jv03eYy+PEAu07jCQ66sDqqpohBCVb2PMtvSbeoxRJcLlIFVFKVBuOwBDdNxkzjEbKbVDwHvgZw8j+Qq2fVhhjkxB2g7JwqKJMRhUqo5Lol8OTxMbSsehXw45e9ao+J92EkGaFbBscxLqnbPRhYOVXr/53L+wTVaUDmNZ+tLNyDWgdWl3gxo7otHMYY5DYdwLc6gB18tVLBSVJD6qr6fsoBVt7wyCm4PxfiRyBTx5N8kCQP8DtrzysZrebG9ZLhnaILYbIbPss/4c/row+G/FAAAAAASUVORK5CYII="
+		},
+		{
+			"position": 2,
+			"title": "ChatGPT",
+			"link": "https://chatgpt.com/",
+			"source": "ChatGPT",
+			"domain": "chatgpt.com",
+			"displayed_link": "https://chatgpt.com",
+			"snippet": "ChatGPT helps you get answers, find inspiration and be more productive. It is free to use and easy to try. Just ask and ChatGPT can help with writing, learning,",
+			"snippet_highlighted_words": ["ChatGPT", "ChatGPT"],
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAAAAABXZoBIAAABGElEQVR4Aa3SIWzCUBSF4d8rDA6LnMfiMPjU18xiJjHPCxzBVePqaqsrK6sqK5qgnmjybzShzQKb4tjv3mvuwX/yHhya9i8cDgCXlziwKm99TnIM5RN+rlQvkO5Z97+wP1FpAbkadwwzWgAOW4L2rcppxoZLjc2i1xMEzZYzblMrbBILzpaQV0wYqUfcbNNk3+kZPibsaEek1oqjxj3DA6W8Y5uobs7kuggTphvNOKWq6/HQlQl70sF4oNaS2NNaMzxQ4Krt9rBPliMW82akubKqDFSuR9x9TiiF8QsybfnBLtDNePhQm3ifSOyAyhlvpKoZy0pzsuiM2kKSwlWNhKd/FiHsFsXtVrB5XbAAEHyN2jTv7+1TvgE1rn+XcUk3JAAAAABJRU5ErkJggg=="
+		},
+		{
+			"position": 3,
+			"title": "OpenAI",
+			"link": "https://openai.com/",
+			"source": "OpenAI",
+			"domain": "openai.com",
+			"displayed_link": "https://openai.com",
+			"snippet": "ChatGPT on your desktop. Chat about email, screenshots, files, and anything on your screen. Chat about email, screenshots, files ...",
+			"snippet_highlighted_words": ["ChatGPT"],
+			"sitelinks": {
+				"inline": [
+					{
+						"title": "ChatGPT",
+						"link": "https://openai.com/chatgpt/"
+					},
+					{
+						"title": "Introducing ChatGPT",
+						"link": "https://openai.com/index/chatgpt/"
+					},
+					{
+						"title": "Download ChatGPT",
+						"link": "https://openai.com/chatgpt/download/"
+					},
+					{
+						"title": "ChatGPT for teams",
+						"link": "https://openai.com/chatgpt/team/"
+					}
+				]
+			},
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAABQElEQVR4Ac3PIYyDMBiG4VefPDtxEj0xM39qZl40mcPhMzONOjWNrqxA4UgmqklweBQKVfFdGhbSZZvfY5qmb35++DAbO4XQF7xjpN42s1oyXtlr2gN4SRpynnTaANtesy1tkOOR8aoAJ12J6ngmGkknCqn5gv0y8Jv03eYy+PEAu07jCQ66sDqqpohBCVb2PMtvSbeoxRJcLlIFVFKVBuOwBDdNxkzjEbKbVDwHvgZw8j+Qq2fVhhjkxB2g7JwqKJMRhUqo5Lol8OTxMbSsehXw45e9ao+J92EkGaFbBscxLqnbPRhYOVXr/53L+wTVaUDmNZ+tLNyDWgdWl3gxo7otHMYY5DYdwLc6gB18tVLBSVJD6qr6fsoBVt7wyCm4PxfiRyBTx5N8kCQP8DtrzysZrebG9ZLhnaILYbIbPss/4c/row+G/FAAAAAASUVORK5CYII="
+		},
+		{
+			"position": 4,
+			"title": "ChatGPT - Apps on Google Play",
+			"link": "https://play.google.com/store/apps/details?id=com.openai.chatgpt&hl=en_US",
+			"source": "Google Play",
+			"domain": "play.google.com",
+			"displayed_link": "https://play.google.com › store › apps › details › id=com...",
+			"snippet": "With the official ChatGPT app, get instant answers and inspiration wherever you are. This app is free and brings you the newest model improvements from ...",
+			"snippet_highlighted_words": ["ChatGPT"],
+			"rich_snippet": {
+				"detected_extensions": {
+					"rating": 4.8,
+					"reviews": 3113820
+				},
+				"extensions": ["Rating: 4.8", "3,113,820 votes", "Free", "Android", "Business/Productivity"]
+			},
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADNklEQVR4AcXUA4wdURiG4VPbtu32mmvUtm3bts2gnNq2bbtBbSzn3n79M8nZrvdcZZO8yUTzPUMGIFmLOlBJTXhtqcPUUaoDxRwp16aObORuHcNpxmwnUjM5/uICam0PyKza2miJSmoGOlH0HlIGjwPUm9tOqrXdD6qt9RANEb39VEGPAXym/5YMK9ehxu5ahKgbH4I3m0rjdoDfBEj+EwDDyrUiiO9UR7ffAd8pMvzHAyJ3Ivr74R7AtD8SIRATURO1ttWBakuCCN4BqrDLAApRiAmAcflm1NilJUSwCAJqqcm8nBs7pRu4y+gCIAoRqdwJ07LtdCfUSSLUlEZqjBQbevwctVvX1QUA7zd8pkYIIfh41o2d0Xh7ICJOpAFOsic0ZHYBIIZQKynjaLQtCPLJVKCrRyQhaAjUYaqIMCBxxA5CqAgRRIjmUMapzBu7oHG0cZmPx2welU4AkARi6T7U3KWHanuAgshCV952hy/sJ1MmNs77Q5mcAHBEuIKwLD6Mmrv1yCS1QMftfsApJjLO+0A1cxjAEb5TwxRE/uX30PmkFrjAgJPRn7lQklMAXwL4TAtB8UVA927PIA8sBNxikC+mhHzMgwA+7j0tFEWXAN1GXkGksSzkMpXxdWBZ/L3LYLvMRBHfqLbCAD7uNT0MJRYDfYedQ4S5FOymonjvbcD7Slb86Fsa9psM9itJIuxUsPhLGG282BJg8ODjgL4QbKbieO+nxyc/NT55a/CughXfu5dLCrGGyir2GcYzPoTGYSiESFNJGtfhk69aSUH4qPG+YoKIc1Q58R+R+Hj8iJ5lYbuUArazKd/QkL9Tv2Lfqb9hpfGiSYzHh3hXxjv05/Di/e23GB8TB/Bxy4xw5YUbMfAwjRdJajw6Yun7KpaM37uWY/YbBDjpIICP023HxH47AV0ehJtLi4wfp0pR7H01M6OvwnGA/9Rf0v/xHTSeF2GWMviQ+PhzykoxJVcA1eZBKh5jvGxi47+oHnzULYCacyFN7is0Po9KRzG3Am7XbzopxFoeoZZyCY0foIrwIbcDZFPxzN+9qy2JZ/whZeADngJEP0k76gB1kOooOuwKIFn7B3LHHIJtp64TAAAAAElFTkSuQmCC",
+			"thumbnail": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFwAXAMBIgACEQEDEQH/xAAbAAABBQEBAAAAAAAAAAAAAAAAAgMEBQYBB//EADQQAAIBAwMDAgQEBAcAAAAAAAECAwAEEQUSIQYTMSJBFCMyUTNhgZFCUnHSFTRTVGJysf/EABkBAQADAQEAAAAAAAAAAAAAAAABAgMEBf/EACURAAICAAQFBQAAAAAAAAAAAAABAhEDEhMhIjFRUoFBYcHh8P/aAAwDAQACEQMRAD8A8sooorc8s0+l6ppbaRp1tqF3qNlNplxNNH8CvqnEgHh8jY4K43c8Grew6u0sx2UGoid4LWGwEfyEcxyRKwlbJ859PPuB7cVzo3pe21jpW9ke2jkvLh3W2nPcIhCBM5K8KctkL5cAiq/o/TkudMubmDT4b++F5FC0c8DzLbwMGJk7aEE+oBc+35VxT03m9vk64uaSLxuutNhuFltxMwWe0nkjW3C/EMgdJDksSDyjAknOwA481BseoOn9JtoILY3F40XxO6SSBkE3ciZQHXuHLZI3MNvHjxVje6Z05odvIur21msTXt1GQI5ZZWARCixSAjbgt5aqS+Syvun9FJttF0qW9inkmue1IvMcu0KuC2Mjzke3tVIxw5ck6+izc16ogw9RiebUZb+GKIS6K2m20VtGQkY3KVHJJxw3OTUrovqXT+m7aZ5rWe4uLi4jEoRwii3UHI5B3ZJOV4zgc1anpHSptJ0w28q3Dqly872MyyTXrJGj9uMcgEEsPGcAZGTWS6m0yPR9Xks4XkZBHG+2bHcjLIG2PjjcM4Nax0sS4L9RlJzjxMuX6pt7GwsbDR4I2WGK4tnuriI94QvMzAL6scoVzkHnPNUfUl7DqXUGo39tu7NzcvKm8YOCcjIqtoreOFGLtGcsSUlTCiiitDMKKKKAUruoIVmAJBIBxzXUZw2ULA4wSpxxXFXJp1Fz44pQboTtJ+piR/Wu7RgDJOPHNPrH9h+ppez/AJAUKOZFAIIKswKnKkHwfypDK2SSc5OSfvUwxk/Y006UolTsi0U4y5596boWCiiigCiilL5oBaj2qRGv7UzH71IHipM5sft0E1xFE0ixK7qpkbwgJxk/kK37QaN0wz20+LcveSJI17Zx3Us8KKgwFyBGjlnIb7Ac159BKYZ4pgquY3D7WGQ2DnB/Kt4uqaFrqqZjGiw3Bl7er3mwW0bnLrAEX5gGOFY/YBearKzTArfqUvV2i2mlJA9tHcW0jyvG1vcSrIXUYKyoygZRgft5BrNMM/1rQ9U6/barEttYxz9lby4ue5ORnMjcKij6UAA48kk5rO5qY3Rni1n4RiQc5phxzmpUnJqO3ihaLG6KKKFgpS+aTXV80A8nvV/0qmktqZm16VFsIYyzRszZlYkKFAX1H6t3A/h54rPKcGtBp+mG9jYW6WG+NYsLcSyK8pePecYYDjn9BRiMW5WWT2WiW+rW9vHcWVyqafKQ7XHyZrlWkCdxgRtBAU+QPHIzUqXTem7oRRteWlnc7i0ht70GH8SFSuXBPh3YHwNp+oc1XHprUBjNnpWSSAPjGOcDP+pjxSX6fu07bG303Y8ywlhJL6WL7PBbPk1XyaqL7S5TR+lBG8IvY3f6e82pQowy0ByM+n0q0wzyDtYecYhDSOmorSV/8UW5lNs5hBuo4w8mzcDjymGyu1/q8ik3fTEkd0sNqtjL3ACm8TKSTkY4c8cYz4JKjyRTMfTV60gR4NJjJGctcv8AoOH8nn9qjyS4vtMw58VHPipd2UKwSJGI+5HuKqSQDuYcZJPsPeoh8VcwSoRRRRQkKKKKAUDUxb3KqJbaCRlULuYNkgDA8EewA/SoVGaAnC6j/wBlbfs/91KF2i+LO3Hvxv8A7qghjXd1BbLCbUWnk7txDFLJ/PI0jN+5amjdRY/yVr+z/wB1Qy1czmlC2PXE5nZSURFVdqqg4AyT/wCkn9aYJozXKAKKKKAKKK7QFpHoVydCk1iXK2wC9rau7eS5Q5/lA2nk/dceeIkGn39yiPbWN3Mj52NFAzBsecEDnHvWv6mJXobTFHhfg88fVut3fn+ngY9sZzgYXply9j0dp93B+NbyzXClmYhnVJtmRnwp5AGBkknOTmmbY6NNZqMX8FeB4ozaXIeb8Jey2ZP+ox6v0rq2N42zbZ3LdyQxJiFjuceVHHLDB4816DDqD3g0SSeJGkkgGXDOCO8q274O70+hARjGGyacj6gur97dZooQLzUZ7GXZvHywJgNvq9LfPf1DngfnlmZKwY9TzcW1wySOtvMUjYI7CMkIxOACccHPsa7LZ3cIlM1pcRiFgkpeJl7bHwGyOCfsa9CXqK5srW+1OC3txcfFx3JBDFC8sy7sjdz+CmPcc85NNa/rVxFpms2sccOyxmis4S4MmUKxElwxIc/KXlgfJ+9TmZGjGuZidG05tV1KKxVzG82VVthbDAEjIHOOOT7eccUzf2c+n3b2l2mydApdc5xuUMB+xFWel302rdaWd/cduOa81OJn7Uaqql5BnC4x7++c+TkkmpnX57mq2U2MGWxRio8D5ki8Z5/hzySck1N7lHFZbMxRRRUmR//Z"
+		},
+		{
+			"position": 5,
+			"title": "ChatGPT on the App Store - Apple",
+			"link": "https://apps.apple.com/us/app/chatgpt/id6448311069",
+			"source": "Apple",
+			"domain": "apps.apple.com",
+			"displayed_link": "https://apps.apple.com › app › chatgpt",
+			"snippet": "This official app is free, syncs your history across devices, and brings you the newest model improvements from OpenAI. With ChatGPT in your pocket ...",
+			"snippet_highlighted_words": ["ChatGPT"],
+			"rich_snippet": {
+				"detected_extensions": {
+					"rating": 4.9,
+					"reviews": 1026513
+				},
+				"extensions": ["Rating: 4.9", "1,026,513 reviews", "Free", "iOS", "Business/Productivity"]
+			},
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAC5UlEQVR4Aa1XQ3hkQRjc+ynX2OZtbfu+tm3b1nlt27a9O4qNS5xxbdd+cTKvXydT31fJoPuvmvf6/ejw86dBlX6CwwQXCq6t5cLaz/xV4+ld6F8r9NdgsCAjIwf5+UUoLCwBydf8jN+JNQbBddzjDQM+gocErRSyWm2QgWu4lntq9/q01UAfwYKCgmK43W6ognu4lzEE+6oamCboLC0tR3vBGIwlOF2vgZm5uQWoqamBXrhcLpw5cxZ79uxFKxCxrGBMxpYZ6Eu33KAXNDp+/AQEBgbzv8Y6Kxi7+e1ofuAKVS/7zp27KE7i6dNnem5HAbVaM3CYh0YF/PWRkdEUpxHoQe3BPNTcQJCgTc9pT0tLh8VigdPpBLFv3368evVKBC7A16/fkJmZKX06qCXo39jAej67Wnjx4iVGjBiJ0NBwBAeHYsCAgTh48BCuXLmCKVOmIioqBrwS4eGRGDduPMxmMzyBWtRsbMCglWSePXuOkJAwCuhmnz79YLVaPSUrGjDWGQhgCvWEyspKdOrURUk8JiYO799/0Exg1KQ2DQxjHveEO3fuKomTPBcyUJPaNLCQxcQTNm3arGzAYDBABmoK7UU0sE7rAC5dukxJPCgoRPy6DMhATWpLDWzbtl35Cty//0DBgOQW3LhxU9nAsGEj4HA4dN0CySHkwvy6bKfECRMmISsrS34IZY8hMXnyFAZV5rFjx6WPoa5E9PnzZ2XxpKQUlJaWaiUik1IqXrBgkZKB06fPwBOKiv4fwA3Ni5FdK3NVVFSgd+++usRnzJilXIzII7JynJOTAxaa7t17Yt68+bh37z6+fPmKCxcuYvToMejVqzdWrVrNMi0rx4cVGxIFKDQkCi2ZAhRaMklTavWqeF6epCltxuneasvLyurb8lmqg0lfLw4m/dozmh0RtBUV6R/NuJZ7avf6eGs4ZeIwMoVmZrYcTvkZv+MarlUZTlUZIDi8diRfX8uFtZ8FqMb7Bx+2VJbBTrlcAAAAAElFTkSuQmCC"
+		},
+		{
+			"position": 6,
+			"title": "What is ChatGPT and why does it matter? Here's what you ...",
+			"link": "https://www.zdnet.com/article/what-is-chatgpt-and-why-does-it-matter-heres-everything-you-need-to-know/",
+			"source": "ZDNET",
+			"domain": "www.zdnet.com",
+			"displayed_link": "https://www.zdnet.com › ... › Artificial Intelligence",
+			"snippet": "ChatGPT is an AI chatbot with natural language processing (NLP) that allows you to have human-like conversations to complete various tasks. The ...",
+			"snippet_highlighted_words": ["ChatGPT"],
+			"date": "Jun 17, 2024",
+			"favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAMAAABF0y+mAAAAMFBMVEXQ/0rQ/0vM+0oDAxDV/0yixjxlfCpTZiV/nDK75USTtTg7SR/F8Uiu1UAfJhhyjC65DF56AAAAAXRSTlP3Yz+/2QAAANRJREFUKJHdkkGSxCAIRaOAAore/7YDJNPVSVfPAeYtdPEK5FMex1/U8pV/JutMatwVH9JaouTH1ok3SWsEjWGNBXve5JQ5qS+XJLJB8V3iZK8AZhBEAb5JBVjNEFPaU65tIibR1saiZ2XgbzpDH1H2ZtWttB316SSpZ5TeWymNtSfK501X2wFWo23mVR7DE49f2VYrkdPONVaEXmq9JHVIGUQSl/ia1jxFyOYT2YeUletTIyJ5SEIf4WoLfJNCE73EhJKoJHv9hHjFyQNzeefp8jvHD3ZbC4DWezICAAAAAElFTkSuQmCC"
+		}
+	],
+	"inline_images": {
+		"images": [
+			{
+				"title": "upload.wikimedia.org/wikipedia/commons/e/ef/ChatGP...",
+				"source": {
+					"name": "en.wikipedia.org",
+					"link": "https://en.wikipedia.org/wiki/ChatGPT"
+				},
+				"original": {
+					"link": "https://upload.wikimedia.org/wikipedia/commons/e/ef/ChatGPT-Logo.svg",
+					"height": 800,
+					"width": 800,
+					"size": "1KB"
+				},
+				"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALoAAAC6CAMAAAAu0KfDAAAAaVBMVEX///8AAAD7+/uysrJ5eXmoqKiYmJhZWVn4+PiVlZXw8PDj4+P09PTr6+vNzc2vr6/V1dWMjIxmZmY7OzvExMSCgoIgICBMTEygoKBUVFQUFBQxMTG4uLhDQ0O+vr7b29snJydubm4LCwtts+PWAAAPGElEQVR4nM1d6YKiMAxWBKlcIocoooDv/5CrjjpNmrTlcNz82x2Oz5KmX46mi8VMIoI42ZbZ+VRfl9e6891108aRM9fjPyVOmHhZvVSl2hVp8G10Gom3uyMB+yldf0i+jZCRcOdfeeAPOfUr8W2YqoTZ3oD7IfXJ+zZSKE7Y2+B+SvMfKX24oWYmL8fL/2JvPM3cpKV2w2+DvkvgmiYnJefLt3EvFsl5BPC77L6tNNuRwG/Sf1VpxFqHrd53WoN5TL+HPNgxoPb9ZnspiiRJiqLx2CU2/9rqGpUkoNMhDQN50RRxeMlo7MV/hHxfxszlTUUY/y5ZOEIEaXJZeQfvkrRRJD4/fR0CuX/QUZTEPamfyCsr+D9VuUrC6KPQ1RlaHUxvTGgVUxWp9JLP8bQVft11Z2HsxKWioBJy6g8fMp6J8u0LOx0NXEvsd/SfAB/76DVH+3sLa+y3ydPOPmk38A373YB720HUoZx53Urg4+uD/diEh2EEedkdOHM7SpCia00iFM92mkriz7hwHeCj7bWlPY0hyMvrYS7kYQce7Frf14/B/ZBqJqWB60pmqS3hwcrvZqSbZbamwEKcW6ubxErnBea+65al2x9VpvDGPodbBTS9totNJBmr5P4hScM4iKIoiOMwLdbMb9xPxx4CG5HZ3OL0DPBrVqjBSEc0NPpmKvQLGAqL6RN4NO668thpEmwoIzrRSArgNJjtIjeEy2ylXceiA+YaN5nmVQXgWUZenTChjvxi/F7hocN3WdoEUkTRy49aGy4PSsZkrK04oULyltk4++6EG0ScrnoAYsvQlcyazB7wrSOiN6L1VMK3061GIs1p4Mchsy3BDxlqZsRlp+jdTXSWNmV8ObMXCKXFSjMsTHxxyS9f8ZMm2jFDbqfksmCFt6ZMNwl9hnyU7C0rxpuo4hFOTwCnem2tMoKLcN1oOn2Hw0VQzyOXlBg+z7ezMk7BfPibnOgFomWU/LweHZ5I4bhbsfdooyGqZ+rXxx41n+/e64TVZNEAGGeL+aIuCbJQQYAtt+on0zx7+CW3xutTveuukkbnzKxBxeRQFvyEpnFoDK67Qr1C+rpuMxX3TVLwSIOR2Zr8MUxgBKle+1Kv5CIpisTCZoCg2Vl7qRJTvEsuT3XsH12o35oZDGJR3u6qs8aoUS14um6iFoS25KtQ/u14sihU6WY/Ddnd+MUs697kNjtgfdH4CQrrufu1An62FbpHjVrvDHxDDoRdS8PcAwFaPsSphgXz9f3JzgDoe8OqJwq8Aug/EXg3sxzeVqIeI3db9XY99MzgwCdEsNq/6FTek/0tbkXFtGX/CipaQz+t9EMebshFt3Y13mcsKzFD/VL0wNN7AG2hl4almnBbXu/SzA/Z+NKenoOMnEQZ7KD7BrpC2ABJui2nNXJQhE65IuZ3lp5kBd3XmgonNGZkzikNPpYvohZUlCU6yl9mOvRwbRM03dFWXr6EoGARHJQKPGQy9ANill1PY89JlZdnyFp9C0xSofjqROgpZjl+G4XMjM0JjZBVuVS88wgm85H5nARdyQzsf/jNhQkznRWtkXlVr3wWmODq0V+10F9ZPRp6jIOm53fuLMKhqffIIlMla8RRsY7gk17xLxsNXQ2abmRYbUlHJrstANhKf8rx0gHsjwJutMIkuFg2Q2bZKRhf0pdVXvZlThg6sOmVok7jRl30yCB2BFcRDY29Pv5i1EF3ALNQF6wx0APM4zsuPlzSwYRl+RpCHXTgjqpzeAR0pRTj5PI0oaUDhMvTU+Vl6FjXZWRXRdNHQFcKYPQRMMEUWi2zh8rL5g9lUkHcRbU+g6EHO8Vkn3Z6Nhxvaa2p7x9Lng4Z1IlCfhPFiIdB90i6YkpXxkzgr96BehAXrqbyhNpTDGgAdKEW+7wfbSiOCo+0ytfyyrUBxgAEdU/UQ+2hc5mB55AZUnErbcTwIZA5xvIcwRxgEPRgg7yJHP3bqPIen3L/EcgL25z90xDomRo03cQxqltaVoaYZ6gvzkMhAUC9SBfRCnqHE83VwwSHyn8b/MBWVxDhQ7MOqDr5NCvoGOGbhTQYPFuJ+hRNNQeyf3JBdzUT9Fyu3IoPSOX3nh58wGXTsCMh20Y6TzYYOnYyUxzi8Q1WXrnhR5Cqg4AkHRMfCL1ScxlOgrTm6usDpXTmGMUchS6MOwI6lyVUUg6uQeWJSD9iMKKX/ka/dgD0nM/mC6zBV6PKK8QGUhgAnaCNg6AzkZSnqCqvDZTejJ/CKEF9wowKczSVcIsE2T1TbkBZ0sDrAXR64ttBr7cWFQwRrjjpDMn/Ffa8JZVx5B9GB4GtoB8tSy8ivOBc9dsicS5UJoiyXSfZlyX9skOOAic/t2oTrDjXIvm4cnApnwDdMjHtUMxWO71TuCacfq+dicNMgr7M1xp9Q+5L+f5GgDnSWYPPQ79nLPl74Fyt33QA6BJp2D8DPeuRsTnz2xxgru3toAIviax4/Qz0w6JBH+DKlqA4cG16aTvgX93fQV8TdPjMqTysoXgbQjmOXFOeL4COVXIKdMKh85nEK4wDvtYlMIHJxU1+Ps7TT4N+3w2BwNOVnAGw7i9yHsgqV1E3AiZxhgvIVOg3dotTBCQdATa8e/1vaboPjQzYNDQd+iJeo5GnWCCwJvUrhglKSciEMNqQfFr/XjQDdCX0pCQA7gJSO2+yBSg9GZRVUkLvgZkF+kIUQGuoWDwIi/gvSwRcADWr8bgRs/6unRP6AuUYqTvJ8hJYWM9UbihB8J+GBrNBl5WStHNAr99TsgeYmAILsUamIL/vDZ0NuqyTtP8h6/XbB4YFJRmHot0gn+W4fa8VfwAdxHVfhgKVCLA7GhwcQqyzF5I/gC57Fr/7NxLA4qiE0ku4BPkfQJfTu9c3YxHQsOq6FERY5f8MOigv+bXhqOK60nlcLRUR/AvoMs+UaCAq+tOWXFMJ8sFu9QjovXSJbMJRlEy/j0k0OD4yOJgxArqs1XJCDK5LN+Ohmat3wRHBoSGkEdDlS0BZqrLfb6MPZ6Y4QT4scDcCumwdXRB9VFJQuAgEI1G4wZBw6UToPRimQAms6kon7xLjEGLObxXDQeo5R/2GRN3uuTdkC2Ncv2idGhgBXWZoJRojKneWM6WTL2l9BMomITMOuuxoohwEk+A3dYHwsJU3psFGQpe/sCaZJwtdOvkrMa7G0CcfR0OXlQIpMp/lPhqMvFITokn5jocu3w790FBXGXE0JcgtE+3H10o2HHokPwdVC+jrIkwd6mzKG26+ycsCD4cu76rP4UgyhXtv6UzZQlNRSb4JpjBHeZaimIu5/ZtvUHl9Kc/DUk2ALisFtI2OzHuZuVUbVF5TQHX6YTjjoYPtVdA2Br30p/Vix2zGMyXI6bK196o8Hrr8RU/QwAAK09xLJ+lS//3WlCAnigXfC8No6LFsBXy40IAdwQ/bw5ZO6hPkBXZeS8mSjYa+krUAhcdC2R78GMJ4S6uuLkEe4sZwR/BDx0IH6ow5Xii/8mXDudLJPbexfY3LKVAHgbHQQaKxQwSPhM6XTi6JnUTigoDvXXzRSOgBMHl43BjoCyXT9hIfd4lL0NyoCTdrJHS45mA7AXatgEWfVXmwraLF+zd9aivsOOhwX4MSOAE+EhouMmp0k/PmpcnOQUkJ0anvUdB78GTlW4I5rKz43Eb35y7DAq8BXH/AUdChI5Gpm5NkekPsBW6Y6sNzGCmNRfiCujHQYS8EakuGzFCpYk2FGb5HGNmVo6ZJxAjo6IO7RKgKzGLyvUzpJJS9vqXKYOgx9APIXcqAr9PeqDD0dri/NtRv3xwKPUBvJNdCQFg5Zu6smM04P2LqH/w7Kyyh442Ge/Kx5vKShxDM8CVnQ62uXM1gB11JojD+gsxXzpr4RULvJOo2eu81Xsk4bKBHa/wizgDI5rPWRezEheAGpq0YiEJbQE975R0c24blJdrZFmAfvDO0VIlxbyUjdLFRFPPIf1bZDLGtEp7ilNLHrA3tfoju4QboolF9tFwTQQQv2JgSW+mr8Kwz7GEIGmJi4x8LtgCEK2L163SDCUqs9sY+Rk5zV999aWqpQvmJSn9M0AaCsmGGrpTgt/Idvt4S31vU6HNIbUl650rzU9NC3Zma8oCr52gOTbfJIJ5t6JHQmdqHCWBH66m4BdOJuj6rX0q/i01jW14C03hDmk6r4mCH742DYhnaE0ZseutCyjCpX2u7o0NQdNSVa4rwuINo2kAIdEjy0d3ShOLwPYXpTYVjCZJUls1LUa+148jzdpQI2FPOXAM2ZkPcTewbjqMsYTXi7AKHyGL+AGeD3IIxMB2xTZ0X9JDhPU9D5rSTfMcPA+3CdO6wjooxut++XepDxIEZcm0Ok8qonDaWJxn8Cm65pq3rwVIwRk6fOY5UffEbvadIiqp3hjzMryiVnE+p2VZHP9Ki6ysvGNdPUe3o2FspPKfkp9I010A82Z3SBjJV8xKG7N1N4gsdprma3Cc8vaadf0Zwj8zTBylW3Kpv7h8IjVpvsegPxH71efDCo07ouYtnMYYpKCSbfLIDnf/NCketGIkKrrv9tbfSWvC9LDiiETvTqivzkjaMgyASURCHbeIxinLvbm9HPiBtmuOcQh0h8nu3LMs+04XxjpYnPcJt1KY+A3aSakN0Btnbnh6EdnFOcxHeEms76mnF/uQgqG/dXEfHOLtxR6ic7L86WrlnPJGTa2+lk8r+/bhtN73hdaTESsTSIPWAI7IE/qozH5FH9RvlxVRoJYuDI8amAxgGi5MOOQ/U3hOPlO13sx7v9JSwN/XL+RXXjj4JpROzLho6Cfwa5+lYqWyOj01VNZx8WgwrUeGVloa+NE23eK0+abZDqWj0YbJ10eJflUTzpdzVffwIdwS9y0zLqE4cEUVtUjTbbXNJQiGEs0gIVaorjryGZGGW2trzb4Q5VqEvwhgU9wdhwkTztEekfFTY/sHHndc8Tmu9f6cNe2qVJkLzcUm1J93tO/0huQMOA/yAcGEMGzGf3fBZUXepWMqgCNWHpBl1mPW3TAuUdvgR4gMI8mdFXIYd3F4bqgr+VJQEvE76EQHRj8q6s1Kb/cDg95+I2Bopcu3bHF/8FUnWuDeyLP6u+YRTMZfE6aWkjGXXr5L/dcB/RURxu1q7WV5fl9f6VPW7bRIHs/Gsf2zY1viSH96vAAAAAElFTkSuQmCC"
+			},
+			{
+				"title": "Introducing ChatGPT | OpenAI",
+				"source": {
+					"name": "OpenAI",
+					"link": "https://openai.com/index/chatgpt/"
+				},
+				"original": {
+					"link": "https://images.ctfassets.net/kftzwdyauwt9/40in10B8KtAGrQvwRv5cop/8241bb17c283dced48ea034a41d7464a/chatgpt_diagram_light.png?w=3840&q=90&fm=webp",
+					"height": 1153,
+					"width": 1940,
+					"size": "93KB"
+				},
+				"thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALgAAABtCAMAAAAlHltpAAAAwFBMVEX////P6t38/Pz4+Pj09PTe8Ofr6+vw8PDJyczExMTk5OTY2NjMzMz1+vjM6dvV1dW7u7ve3t61tbWtra2lpaXg4OQAAACfn5/W7eGNjY3p9O+VlZV3d3fI4dSCgoJiYmKpvLO1yb9ra2tXV1dJSUmisqrB2MzR0ddBQUFrbXU0NDSPkJaIlo9venSQn5fb9+klKCZJUExZYl5+i4N3eIJQUVs5PEdARlOUlKWioa9YW2esrLk3PU7BwMkiKT6BgomhiaRPAAAOkElEQVR4nO1ci3bbNhKlAAQwgKnxjBmWsmI5kRPXjpPdJn2k7eb//2oHpB58SSalxGnPKWxRMs3BXAKDwZ0BqCw7upDjRZP0aeJZZhzWIfgxqtOBGU4IcdPlK+DEcIrazRHaM+mtzbk6QrICriAEa/R0+Qo4lTL6QO0R2jNGmVBHdXslQxiljgg6XZqstXNO2BHaTyjf2cZTc/MKRqsqQte176+//o8mGSftu6Cbt/XJwQattXFeq9peQsXmE6tU7O+MYCxEb3xU0ctgJUQ8eluClYHm1ke7T7bSrKWWBUhbcCm9jNLiG75kJDH4YIU0uSn3AmcSFcgQAS/lEquQNkgwylqw3gMOv0LKPeMH0fqwyBG+LfAYvQ0IOs8Ru8HbyUPhDgFXKJ8HlFIIFj/KcuFCIUtpZF7YuUv/C3uBU8Sdm9xDLhcuiduYW2lhEfJFrsocdJSFHlZPGSOEEsFwoKBrwherXuk3EzT9+xDwTJAkj1fhOCNECTzS9Gkti5XRA6bChMMLKFaQFLokzBQjwooEAq+hGT0wGBxHM2u54mqgp1slbq/DqCrEG85afVn7ZEX5I66iPTj1+sgSiiRJJR4keGP3NHi2yMuFl3M/L6O6dvMcXubF3JcF/ixMXOSxiMUwhkqzmdt5EXUBi6K4xv4tc/kyLopwvdhnYU3gIl4XKDMPZSzLIs7RWENRxqIIMA9zWdp5sPmeCiwoabhUAQSP3IJZGKmdwrFpJFgADSYOD4+6ry0Pkimv8DojrTPAU4UgwR32d7WNo2JrrDFGC5A4zgwi8EblORqLVQb7b2+LH1++sx8/QfVp0hVX0RqcidppZRwoUMbjm8EfCdxQMKCctsFzeMTwpqk+TbqycftygePJXnv/amHn87iwMF+8Qn+6mIcFvI45vtky5otwDA3cp/o06ZrqVIVWvxldf6LVKbDoqiOvT5JDTnGy6tOk/9E2/n3Kv8CfuvwL/BsU8myw/Fj/81HxH4bFn40EflD74fLsfFNmWLZ/nP84Dviw+Gz2wzjgbfFNDbPzHx4H/mK2Kcv7+3fvt381gG9ZL2neTA3r2UbjbLV6uNpKn++At3ln52Z22mezq8uO9pHA3z/M3i9nbdFac+QepMSAK3CwVgPyQgzdgLaBP6DqIeDIQjBe00YmKRvanKQB/N3Fh/+8u2yIpyBCMMGV4FoIxgTlXDFOQQjFG6IvVsvZuyHgyEu91kYjZIUvBA/GOoBOiz+8mN1f9oFToxXeKVIq5FY6ANdmAPjl/X8v3r172xLHYBmM93i/MSAVxpv23uRBY6xqDW3c8+X91WoI+MGyA37182onfj5k4wO1NbRjd93ctIFjjMeZIgwwWhSCc8YdtjgVPMWcjc56/35nZA3gQjptkH1qriwwAIZ/boncDvj72Yud+A44QUIrMfhQGMhoJ+Ue4Jezhu4N8IqVZSlIJpwwTgSe4DvRm9XN1dXyCg/4drPEw3LZMhVXJSSQNYNHQ5Ua7X1rp89m56vlcrVKgisUT9KryyZwa71Kxm25dLAH+Orn1f1qdXX/sFoPkxq4tXjLvspjRK6jt1HGuBO9vFo+oNIK/VV1WN60TaXipSkaFylSrzhoo8WvUCWCrwSX6cNVE3iG7YV9nnJCFasdBH5+ieUGf2bLZos7Zwg4rpmyDvs5FaX4VvQGJZaXeLy8XKYKzm9uWsCpUk6hoQgzkA14NkPpVC5vZrV4qqU5OEFzivEOViFMLyRfA8e7Xl5drlLzNYEfLC/Qfz9gk2OjLx+wux8esOOvmsCVjyCDDcEMhCjPZlcP99hPxQ2qXd3cr36+XN0vG8BZSnQZrCKitYiu+Bp46unKVpfL8cDPN6OjktgOj0aLcxzJGj2qGshhPJs1xTdvTVMRhGMNPDkF1rvzjfbmRLR1DYfLj7PB8qI1S04Xnzzlt8qLx3EjTRostc4R9G5YvOrqMezwgPiE0ksQPCWtJc+P1/SmV9koMXGX7y6Ej7CVnga8p318ORL43ZdPZ5vP4hf761Z6EnD62/TFmnV5/ntXdJxm+fl2m3qkX7582UpPa/Gzx6/ZU+gfvcrGqdQN82Swo+/ThsjRDY793IP0uAwDA+m3YrnGSKPwc+2v/+bBMpEAWtfsnDLGEqGpG28ScB0lHJs87K/hjtLMEbYaunK9sqyqg6jXPZCpDZsEd0i2J4DdFWWVkKYNYBRw6pAO7AWeYbgUeB68dmmtLpee24GWTSs99KgWFzILBVPQOvk1JiDFnMXQhUjuBHJ5ACEHAF4jzw4vj9HiM35x4TNodeRIr3LWCCP5jgLWplItADfTBUOVMgd5bhuraGRs6wudkeu3DunzdOD+9vOWLNDb4rYN3EifHE0qPh1UAOhVkdaiDEZXWzh//IYG5bhyGmN8JJeaO8dxHGjHjeIN98dMVrye/4TebTrwePvnVor9mn9qAafSSLRukB40hm5Oc4Duci55ZWlW5hjmvd2eo9lZivKD9D4EBxjFYQyo8dZDem8I2+yni4sLLltYxwGnoRFJqnLrm3bukGy2DJBsZziNYjzg/XB8a5w8S2EyVywNfSZSdJ+oPVOJ2TcuEyDLwqv2hphRflxxjO1qk7RoCqnHPdkCJ05wlwI3jBZBpBjQDKxDaxWsDG3tZ2gqhDBFGWcUQ/yMoYnwPiQusebORp5RLa6Ucaq2Oup0Arm2wdpUMDYHnFykDGjcIG3keR84KT2aRXsrw1mGguByn1JBEidlGaT2Q76e96aRccBBm7X/r+JqBM52wDPBFE6n1aQqhGCEiqF13YBmq9u2f5YpnCJU6k7hNMXOwhGihqYvknd3Tk3340zKLa4TucpZ2g5DKxSPrGBb2Tv1aO2H9l2cDNzakNKrEg6vmRN/ROj2XNFNyVLyZ13eVJt/pgHvTjlnGWicZtHf6H2bBihnhCUrzFjKU4mGG3tc3fNNefNm+/H5zquMLr2Yc0RgATHyKK2x6ACQEsF25E7SrLpxyMTQ7Y/O5aMiIsZ1Jgh6TEFFo88maX5+GnDy+1Tgypq0ZpBWDLhPmdkdl5ikuZen+tamosApQDqD9EUgl9GA/HOTpp2iuQ/lVK9ygupJV/dyBPUERI+pK3s64GnfUttYauDohTUDB84oCsoZZAUWu/fxCicA75H38cCJZcJk7ZmiBq65jBwplHWel947nFOQfTTmusau3Za+CcCHM1nIJQW6+LQAQwiStEyIvvUanr3FAEo2zWVzFU4NaSM3slONU3i1+7FJEHXi2lrinSHrbmy9PBm4WKiQI5v3XuTRh7RPNcTugho6og8XAZliFzhRCp0sQztRe6IxhZGNcCmuwU9IkTdVjwfO/hpkh4xXbBXjpkwZZPFaCNUlDxjxqTKGjDX/UQOPUoE3Hm934m6x8cD5b90z7RupMyLDdg9kjgGUM81k2LrFGRGMK41BzDTHcrKpKGlyJKvYcviGQ8ta6G/NdDr/8OEn14r8vrcf50aakFbwmUFLxAlKgulD0tY5097PPRF49+rRwIngvUl7imLobgP/9uywLiaYbvKw0uwkkgEwJuzJA7OIKlhAv0LrTxuNJ65IjAbOXC8fWbtDjN1T+G4HM5tZ9uXuT27uPv7PubvbX7S7u/u0dh9TVySOobVZSpfiYTDpSVp/9Ur55ROn+cdPCg+f0+Hl2uKeqMXDK070RTu3P0ozk3p9oNtDLf00g9PwPC9Je5L4zrR2M3Fsk1+kXyVBcgfKgG8/jnCS5knAlbUd13CWyWikMukxGB64SFujVOh64vmCZPAa5S+ayL8CcAI7PPXzV2zwyR/R86ZnGU4c0qUERRA27XXBEK2XQqEgXdXiLelpbqHLRTYrEr7gZR5sDBaQq4WY99PMQysSI22cafAxZ9OTntuyBzin3JGUSNNccZpSdK63updlL5FFxPaKxFkWNZIfT3FKVmnbibPaM4eEvu21/bVj5qcjvMoWeJcybmy8P3H19rgLBXm07X0lZ5n31rOAh6CiZU5JHAd4B52HEqocLxyxeLWtoEsuaxuXENLAx6jHCul8/cRZ916shPTIS8uAJ5As1evs8bLYan91pSvgSMp0WsDFl5K8WlLRHddAX1uSlSXi/9A4e5aeqGQCOTFnzIgqw68EGyAeIu919hTgvWc2189IjKjH2fWKRBMA2nhexjJYi28LXuQL8D7PfV88HpGtPVTqPVlpg2jQWhgm08NLw5dqHpMltxoumQrvjYahJ3nFcYn9daGqm8paL15p4OiBkwV7iaR+WLoIxoWideqp8iouhk6Prb0Ky+pHCGnG+xsPNyWluF3bCkYB1zj008vJ0PTkf/8UnBZgNUbjou2SJmiWAb3SonXqKYAbinF4WsNVvOkSJ2j2Od5zeUrM2Z1Px+XHTXrY0YDAaaCpOh0o3yLYj0RpiCZv72F4qphzqLJ0EOCNoR7DeK6dSHtzcQrpOBBIO9oDcbYZmD7FisSeUre4Q0qMPMFXcae1QSL4DsErIs/ky4zBdcPNfusViUOV7SCkvddkvShP+7wJjJUMef9Q0nOkrq9uKmkPtWNKpLVdJbhSUpi0H6OdxAQZooU2m//OmSyVvgIAXSVyI29zGzTGIdaa3LYoWV5gCPWqBbUmWQGUokpxgfesFU1v477742TgBFkZzURyLmklPn2oHrBgbd+XjKTD5itxDAJSBBRjETxEnfuy9AMhUK+4p1sD6klPEm8/K+72fa3DSNVjLjK3dzj1poO7vSPZ83SopSuSVX/fhko7TdKowLE9vMC97+H8o8oo4Lf+kzEf45/g5vkncB/D7brtalobvbGLvCx8ASEvdRl9HgZshah9xPGbAfefP2fEfv5M8XDLMnl7KxrAM8qIMBjK0LT4w4jBscEGV1V68dwJZZyRVptfdge+AdAMlln6IoTThsyU8hUGJ5U++rRKGOIRX0N0tOrTpOtgWRlhPCOcdAnONyzfNZA4SfVp0v8Cn676NOl/KvBTy/8BsqneOZjJbOsAAAAASUVORK5CYII="
+			},
+			{
+				"title": "What Is ChatGPT? Everything You Need to Know | TechTarget",
+				"source": {
+					"name": "TechTarget",
+					"link": "https://www.techtarget.com/whatis/definition/ChatGPT"
+				},
+				"original": {
+					"link": "https://cdn.ttgtmedia.com/rms/onlineimages/chatgpt_screenshot-f_mobile.jpg",
+					"height": 252,
+					"width": 559,
+					"size": "12KB"
+				},
+				"thumbnail": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFwAzAMBIgACEQEDEQH/xAAcAAEAAgMBAQEAAAAAAAAAAAAAAwQBAgcIBgX/xABBEAABAgQCBAoGBwkBAAAAAAABAAIDBBESITETFEFRBQciNmF0gaGy0SMkMnGR8AZSVGKSscEVQkNEU3KCouEz/8QAFwEBAQEBAAAAAAAAAAAAAAAAAAEDAv/EABsRAQACAgMAAAAAAAAAAAAAAAABEQJBEyFR/9oADAMBAAIRAxEAPwDmMJzGua6JB0gDiXC4tuFMq7FvEfBdDc2HLOa40tcYpNOynzVTwp2CxkNroLXFuZNRd0Lcz8scpUD/ACd5rpH51rli11cu5fp/tCU+xj8bvNYbPy7X3GXBF1aVNKUySh3LiN5hQutx/EugLn3Es+76DQ3NwBm4/jX3d7t6ipkUN7t6Xu3qD8v6RjhayD+x7rqu0ltnRT2u1X6TdkPRmCHWi68HPsVbhThaHwaIZjMivMS6mjAOWeZG9WNchAML4rWXtDmhxpgrpljEcmUxPfjJbPZNdLUpmWuz+PuWSJ63B0tXH913ZtWNbhUrp2U31CxrsE5TEM7PaCjVmk9X2pbH7rsO9bUm/rQMsqHP5oo9egCtZhmGeIWwnINK6wyn9wQbMbOXN0joFv7wa0+asqmJ2C6lJhhrliMUM7BH8xD/ABBBcRUxOQjSkdhqaDEJrsEgETDCD94ILiKprcM5RmYGhxQTkEkDTsqcsRigtooDEo4NL8TlgjYhcKgmnSEE6KG929L3b0EyKG929L3b0HkDaibUVQQ5IhyQegeJHmFB61H8a+9XwXEjzCg9aj+NfeooiIgrzkjKTwaJyXhxg2tt4rSua0miIdGMjNggMwrCuoAraqzbnAgMix2YZQoV1e4olRdohFLhc2ba4bfQHE9HetdNgQZlhycPVzln8VtpH3O9YmwTiGmBliMsMUue1xrMTZG7QfraisOmDUgTbWkF2BgHGmzsWNMQL9ba3AAky5zph+q2cYrWD1maJBpXQAn4UWXOeORrMy0jC7QZmvu6UGrYjza0zUK53s+rkCtUEyCQTNtpQV9XOa2D3gubrM0SRWur5dyGI4EERpugOWr16fq9iDR8UtFHzjeSaOpLkrbWKHGbZS3CkE49KzV5qNYmwbqD0GXdj71qYrqYzM20DGplqfm1AEY0I1xl2dTLmtKblu1zosQMhTLCa1oYOGGeKwHuMeG3WJoH6pg0DqZ40VxgLWAOcXEZk7UBgcGARHBzt4FFsiICIiAiIg8g7UTaiIIckRB6C4kGk/QGFT7XH8a++scvg+IzmDC63H8a6CoqKxyWOUqIIrHJY5SogiscmjO4KVEEVjkscpUQRWO6Esd0KVEEVjk0blKiCKx3QljlKiCKxyWOUqIIrHJY5SogiscljlKiDx1tRDnmE7QqgidoTtCD0JxGcwYXW4/jXQVz7iN5hQutx/GugqKIiICIiAiIgIiICIiAiIgIiICIiAiIgIiIPIkKNNNhQmQ4QcxsVzmcgGrqCvvw2LcxZkEuMlDx3yopl7lFCZGOjdDjNYXOIb6W20gZ9HvVh8KfcHF06x1uJ9caTls5W7BVEUzGjhphR5aFCNP6AaezBWODvo5wzwnK61wfwfEjy5cWh7XsFSMwAXAn4L858R8SmkiOfTAXEmitS3CvCUnA0EpwhNQIOJ0cOK5ranPAKTelh3jiM5gwqfao/jXQVz7iM5gwutx/GugoCIiAiIgIiICItHutpgTiBgg3RVROA/wY46NEcFnWxWmijYiv/mUFlFrDde0OoRXY4UK2QEREBERAREQEREHkBjZctbpIr2uLzfRlQ1v640UhgyVR6287/Qf9ViRlIMcyjYjT6WK8OIOwB1PyCscOcGS0jLh8APuMcs5Tq4Cvkqj8mO2CxwECI6I2mJcy0hRoiD0JxGcwYXW4/jXQVz7iM5gwutx/GugqKIiINIhOFBXL81m1qpPjRNO5t2AJwoFpp4to5QrX6oQfoWtS1qpse90MOL8SDsHksh78eX3DyQW7Wpa35KqGI8QwQ7Gh2BHOeGe33DyQW7W/JS1vyVUc54pyzj0DyS9+PL7h5ILdrfkpa1VL309vZuHklz6e3/qPJBbtalrVWufaTecxsHktQ593tn4DyQWYnJY4saHOAwBNKrZip3vuIv2DYPJSQXvMQAuqN1AgtIiICIiD/9k="
+			},
+			{
+				"title": "ChatGPT Tutorial - A Crash Course on Chat GPT for Beginners",
+				"source": {
+					"name": "YouTube",
+					"link": "https://m.youtube.com/watch?v=JTxsNm9IdYU"
+				},
+				"original": {
+					"link": "https://i.ytimg.com/vi/JTxsNm9IdYU/maxresdefault.jpg",
+					"height": 720,
+					"width": 1280,
+					"size": "134KB"
+				},
+				"thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIDD6PSH-o5_a4uY4vMZypbGD47mIWLL6VsTXNuADpOw&s"
+			},
+			{
+				"title": "Introducing ChatGPT and Whisper APIs | OpenAI",
+				"source": {
+					"name": "OpenAI",
+					"link": "https://openai.com/index/introducing-chatgpt-and-whisper-apis/"
+				},
+				"original": {
+					"link": "https://images.ctfassets.net/kftzwdyauwt9/44fefabe-41f8-4dbf-d80656c1f876/8dec20d14a894ae52ae07449452a89c5/introducing-chatgpt-and-whisper-apis.jpg?w=3840&q=90&fm=webp",
+					"height": 2048,
+					"width": 2048,
+					"size": "93KB"
+				},
+				"thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ_IQLO0924Gl1jYnj0yWaeKwSWj8tbTbk0Jc6cAvQv6A&s"
+			}
+		]
+	},
+	"inline_videos": [
+		{
+			"position": 1,
+			"title": "2 MINUTES AGO: OpenAI Just Released the Most Powerful ...",
+			"link": "https://www.youtube.com/watch?v=7idowVzHZ9g",
+			"source": "YouTube",
+			"channel": "AI Uncovered",
+			"date": "1 day ago",
+			"image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFMAlAMBIgACEQEDEQH/xAAbAAABBQEBAAAAAAAAAAAAAAAAAgMEBQYHAf/EADgQAAEDAgQEAwYFBAIDAAAAAAECAwQAEQUGEiETMUFRFCKRBzJSYXGBFiNCobEVwdHwYsIzcpL/xAAZAQADAQEBAAAAAAAAAAAAAAAAAQIDBAX/xAAkEQACAQMEAgIDAAAAAAAAAAAAAQIDERIEFCExQVEyUhMi8P/aAAwDAQACEQMRAD8A4hatfh0cQsneIt+ZNdUb90pNgPpe9ZK1b7GUpZyhggQk6lRUkC3cmrghNmGWfOSefWrPBITsyU220nU4s2HyqHIirYcSh4WVbUodq6N7PILDbfiVutF5z9OoXSKmbxRcVdmsy9lmPEZYWHEeLadQ5ckcwb2+lbDEcNYxXDXI0+CH2lo82koIHzBJqHChx1XdWVKIOrTq2va3L6Vo4jbQQOGAFWt9rk/3NTTlKxFVPLo4U1HeyvjsjBJQUWffjqXbds/4rF5qiJjYw7oACHPMK7D7ZMMUIcHFkjzRpAbWQP0L2/kiuVZqPHahyD7xBSa18CTuZu1FhS7V5ppFCKLUu1FqAG6LVYYThMrFpRYiBsFKdS3HXAhDabgXJPzIHck1rMTyFHjQnHhNfiFh7hKemMlTT3TiflhRaRflr5gg/KkBgrUGpEyK7ClvxJKdD7DimnE3vZSSQR6imKAE2ry1LtXhFIBs0V6edFADlq6zliO3jDGFYVOUlbsRJWCAf/Gi10knqL27WFcq03BHeuhZYmON4iiY2gFC4irrH6VFCwR63/f7VdpoaipQk/KMnmV0KxaQ5YFOvl/anYmHScQjeJRhxaQlN0rYQbk/QG/3t0NTsHjxpmKOonpCm1kix610nL2VsEhJ48dpSjzCFLNqzqSSfJpTi2uCg9mmMTfGjCZTjq9Q1ILir+Ujoe24NXftBxjEIuIogwn5qefkiAhSu+43pgltjPMF5ASHF3SQkAWFdDxHL0PGwhcgLStN9LrStKhfmL9qxU/CNZU18mcMxjHYy8NmwJQnmWWtSHVylqBIIO4OxFUmJL4mFM9w8f4NdazrkXDYOXcZmcZ+TKEVS0LeXq0afNYdq488rVhbXzdP8VvTfZhUK/TRal2pIUg7BSfWtDMTavLU62W9aC4RwyoX35jrWjcdyih2MpuM+pPHCXkOKc0qbsvzAhQOq5QLf8b9TQBGyXLjRcQlNzC1wpEUoSh5zhtqcSpK0BS/07p2PK9r7XrqOJ4uP6a/ILr8JbkNLwmLUXYsY2IToUkFJUoqAIbvq0m4AukczjO5W8JEEqOpuVpWX1qLpa1EHSLBVykG3Ig8ue9lrdym9FRHaW83dbxSTxVcG5c0KUNVjtwvdHRV6kZTZilR5+PYhMhpUGH5C3EahYm5uTbpc3Nul7VWkVqVOZRD7auG4I916kK4vFH5t03OrSRw9tt786J7uTlQJpgNSG5BQkx+KpZso2JSPNbbcEnnsR1FO4jKkV5anPKQSFCw570m6SbBQ9aAGiN6KcKd6KAJATWkyxP4ALDly2pKuXMG19vrVAE1Mgu8BwLtyJP7H/NUibnSM25djYXlmHLw0hxyyH+MOuqx/g03lXGy80QsgHqO1Ly8XsdyO0yErddgFUc6VAHQmykEi4vsSLC/u1jIji4GIutFZa1AlJIvY1nWjdXNKErNlw5GzJiGb+PhkY2bXqQ4SAlKQN73+9dxwkYgzhpVibjKpJUb8H3bdK4DgmISXpC+Ni05p5J8vBZuL9q6rluVOEUlzEVSo36eOyULH71y85HdJJU73K/2sY0uLlp+I0fzpygzf/gd1ftcfeuNSUcOJGa+qj+1bPPuKJxbHUx2llTUYlPy19fSw/esfiBCpFh7qEgCu5RsjznK/JZZAjMSs74IxKbS4yuWnUhYuFWBIB+4FTcbzJmnEyYWMKeELxSboVBS0kEL2GoIB/eq1nCXWsD/AKwh9xp9EkJabQhSV7AHWFdLX5j1vXuIZmx7EovhcQxiZJY1JVw3XCRcG4P2NSuehvjs6NIw/BEe0DNL7GLLdxAwZeuCYJSlv8oXs5qsbbdOtVfs3gt4LlV7Hp0Rh9jEZAiv8ZaUlqELh1xIO58xFwOib1lGW8VfQ7jjeJOmTKSUSFg/mELcDVib7hW//wA2pUjBZk6W1hkueVIgsjhiQkaWWiTewCiAAdI576vlV/ikYvU0le76EJexfImap8GBK4TzTvh1rLSF8RrUFJPmBG40m471Z+1nGsRmZpxLCZD6VQIUq8dkNIToOgdQLn3jzNUU9udMxxqLPmLdl3aYLrhuUGwGn56SbfanJECTPdenYrPUh9xpLzjklPmUpWqyTdQN7I6A/TuKDY3WgvJq8oSX8KyTBk4fIdgmXizzc2XHgJlOBKWroTpIO17epNTnGcUi5rxGevNjjHDwVmW7OXhDalllShZHCuACL3vz6VlMKGOYP4ZvBsYmRfGrQHEtakJBKAvVa5CrJ5nY7WqLMexFx/GXsQxqUuRwgh9RBX4loqSEgkq2SSUm1qX45Aq9N9P+6LjN2bWnxgb+F4orEMXw9x1xeJrw5EfZVtKOHuDbfnUjPWa8bfy5gEdcxJaxTCuJMSGGxxVcQi99O2wHK1Y7FMLXhojFxSjx29YBQBbl2Ub8/lUaRKkSW47ch9x1EZvhspUbhtF76R2FzSxsXGakrogqTvRTpG9FMZKCaUBagKR8Q9a91I+JPrTuSarDJMqB7PsTlwnVtOR57B1oNj5goH/p6VSz8YRinEXKCI8xl0J8SE+RwG+6kjkduY2+laRDkKFk6RgUh1BkyQZMgJWDoUQNCfqEpST8ya5vIbdYWWyUqSuytuRHT6VMmOJ1PJ2cWMJU23MYRu4ga0eZCwVDe49auM953bdirZwIfmqA1vkW0BVx5e58tcnixvCJZlNJIeT5hrKVJ325W++/7VdsxC/lSRjC30lbs9KeFsNKUoI1fcqT6VMUkVKWXJDgWSpxw7htBJJ6kmoqSOMlaxcagVD7082tKYjoCk3UpI59N6Z1J+JPrWhJuH5MdmGHw07LugLLigG0aCCT1KieXasJptT/AImVYNeJVwdGyL7W5elN3T3HrUQp4dl1KmdhPmta5ta1r16tS1qKlrUokWJUq5tXupPcetF09x61pczEWN73N73vQsrWSVrUonmVG96Xt3HrXm3cUXAQVLUEhS1kJFkgqJsOwpNj3PID7U5t3FFh8qLgIVqUEpUtRCRZIJuEj5Ugop2vDSGMFO9FOkb0UcAWwy5F7D0pwZdhgXUAB8xVO5jcsYopDDyXGS6EoSEixF+htepuaMQiKirhNuqL4WNQCdtuhP8AjtXbudPZ2gLF+ycMuQ+iU+lLGXInwj0qtyliDq1Ox5LwLTbadF7DTbaw/wB6VF/EU2JPkFRQ+2XCEpJ8oSD+m3y60bqhZPAMX7L38NxPgHpXv4aiXvoTf6UziuML/orUiEoMyHSk6SQVJTvew/3aoUDNS3ElmWhCDwyEvpvsq3Mj69qb1ND6CxZPcyvFUbpVoPyFM/h0Mq8yEOt9wLEVFy5mFDDLzeJuurULrStR1bW9369qaxnMAnwGjFU5FdS9uhLm5FtjcVD1FFq6hyPFlzDyjPkKKDhz4cYKhJQU2KEb6fryGwvXv4bifCn0qEvM6WUwklhtwuNpU6pKvcv2+fWokrHkRsdXLZU5IZU0EBsnSBy5fz9zRHUU49xuLFlwctRPhT6V5+GYnwj0qnnY6nEXIrjJei8F7zDXcKTtvtbfblTsPNYTPkeMKlRN+EEI8wsdvUd6vc0b/AMWWYyzE+FPpQcsRLck+lZ5OY5cfEX3m3i8yteyHL2032sOhtT+OZi8ayz4B2RH0qPERfSTysbj70t3Qt8B4P2XH4YifCn0rw5Yi/Cn0qtjZpkCE+X20KeSkFoi4B3A3/mohzDNkty9UgMktAtpTtZQKb2PzGo091Q+gsZF0csR+gTSDlljsKoVZhnKVHUXCCz71iQHf/YU/NzC/KhAIcLD6XQfyyRqTY/36UbnTc/oPGXsthldk9vWiq05slAJDbSOQvqvuaKe40n1DGRnlc6DubkkmiivKND0gWrznz3oooAdC1kC61GwsLnpSLUUVohBbY0miipGKVva9JtRRSYhaORpCqKKb6GAFOJSNNeUU4gB3NulJIsdq9opMBFza19qORoopAKtfnRRRQB//9k="
+		},
+		{
+			"position": 2,
+			"title": "OpenAI Secretly Released a NEW ChatGPT Model and It’s ...",
+			"link": "https://www.youtube.com/watch?v=uh4baKXL6K4",
+			"source": "YouTube",
+			"channel": "Unveiling AI News",
+			"date": "21 hours ago",
+			"image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFMAlAMBIgACEQEDEQH/xAAbAAABBQEBAAAAAAAAAAAAAAAGAAIDBAUBB//EADYQAAIBAwMCAwYFAwQDAAAAAAECAwAEEQUSIQYxE0FRFCIyYXGBQpGhscEjYnIHFlLwFTOC/8QAGgEAAgMBAQAAAAAAAAAAAAAAAgQAAQMFBv/EACkRAAICAQQBAQgDAAAAAAAAAAABAhEDBBIhMUFRBRMiMnGh0fAzYZH/2gAMAwEAAhEDEQA/APJJDU0EWxdxHvH9KjRd8yqe2c1eVUz75GcE4IOPvVisSuFmnbbCNi+cjfwPOro0SAKpuJp5GYZBDADFMaW4RhIjbSoGCF5+lQzXFyyLJk7QCBxwKzcmNRh6l9NLsYoxzOAT73vZ2/PtWXr+lXFmFmWRJrRvgdDyP8h5VH7dMMNubag4GfOprXVpFlXeVCtw4PIP1qk5BNR8GDSrYu1t0nYQhRGeVB8h6VWZUP4VrQAogkHI71Nv3L+9OdUHkBUR2jtjmoQaeK7mmkj1pZqFDs13NNzXQahKHU4VHkeop4PlUBZIlWI6rqyjuQPvViLnGKsykWVHFKur2pVZhZy0GZ2YjIRSTWtpVq093vlGQVzgeX3qvp8aRl1fksvPzq1FO8NyFQ4JA3DyrOY1iXJr2FkuqTMkUAeNfiHY961LHpcSPiVO3ljgfLFaX+nyNEJGdC2Rgue3/eaML24trRN8rxx57FiBSk2zpQS9Dzu+6QgEp8BAuTnFYOodMojf+sJx5DvXo8+rWk91EltLFK/Jbw2zxig3U+o7GS9KmRs84jA+GghKW7g1lGFcgBrVsbbw0f4kZ0J+QwR+9ZdG3VEcLWceoRHKOwV/kw4/Yj8qGJdqjLDj6U+ujnNU6NnoLwvatT8bZj2Ndu/Hf2iHt9s0Ua9eWqWfU+t6PLFa3630cMscWAVkWRx4ifJ1OT/cG9RQZ07pdvrV7NbyTeAqW7Sh9owCCAM/LmteDo6EK/tbXIljtopHijMakOzOpGWIGBt9aJRbFsmqxY3UnyEUmuy/7n1prvULgW9rokbwtAVLRuy2xYpnjcTnP3pt/NPd2hu+mZJJNYuLG1ZJiEW7kh3TiUjb+PcIgdvO0D50N2PSdpfoZIbmdI1M0bB9pKyqyhF4453Dt9qoDp6NuoItJFwy7YBJcuVzsOzewA88dqm1lLVYm2r67DZm1Q6fqH/iGH+4Rb2IvjbFQxk3T7s44zs8Pd8+/NZ/UGnNrWnXVlosFvc6hFfwyXcdoUCh2tkWRlxxs8UPkjgH5UO2mhadqonOk3VwxS2aURXCqhVwwABb4cEHPB4q1a9IJJqiWlzcPHELOKWWQbW2ySEAKMcEZPf0FTayS1eKN26oKdRmvbq7E3Rc1v4LancnUZEZPDP9TCGX1h2dvw/F51WsrrTLTQ5XvIra4tJNOWKYQKB7rahcAtGDyCBhl+goXTRNOOmNLLJereJdCyaPamwTEHz77cj61Yl6as/EaO3mvGaC8jtZ5niXw2ZmCttI5GNw796m1lPVY7oLbu0a0jjtun7nxtSigsUkn0/aZntNjbmhyfNthODn4c8ZoU6zt1t+oHxKsjSwxSyMqovvlBuyEJXdnvg4zmp7jpbTba7tYLiW+j9puTbojpHuzkAP/gc/WsS/itbe9lhsjMY42KkygAlgSD28qumgI6iGX5RL2pUl7UqgA57hormEICxLDgdznjFalxYXFjOBcJtZuQQQQR9RxWJLKYNQgm4/pujjPyOf4r2a70uG7hezigVLe3gz4oH485GP1/KsMsttHR0+NStmjpaBNJg8GP4YwQvqaFtZ0bVtWc3F47JHvwkCjOU+fzPpRhp06qiKQFAUDAq9K6CMkNxilV6j9eAV6K6Xj0yaW7kQqWO1UfnA+lDvVfS9rFrk0iRYE+JUOcDPmKM11SUQzzRWs08aDEaQ4y7feg3qXqK4lu7eG5szA4ALLkNtPOeRwfKri3douSXTMq/0uReltTjUDaoWYDPYq2Tj7bqCISJYSjeXH2r0m5vFktJYsZSSJlI9cggivOLeMRQ734JH6U1ibcbEsySlwR2t1PYtcCEgGaFoJMjPunGf2rVHU+pOreP7NcBoo4XE8AcMqElcg+eSeay7lOzj71CnB+tapsWljhLmSNW01nUhIbayESe0XUcqwxRhV8RSNoA7AZAqZoeoINUutXMEi3UFxiZ8AgO/G3b55zjAz3rP02ZLXU7O5lzshnSRsDJwGBOKMZurrIxs1tBK0rSxTOGUAOyuvz/4oPvV8eWL5VOM0sWO77/foZl6nU2Utn0+KFLlGtY4YERUGTuYAA+6xxnn0pA9WIY7CGJ4ZGhTa0JVS0cQ2j3wcYGeefOren6tp2mXLPbNdzpc3guJTJGAYwA3A55OW703TtZ03TYI7FDPJbLHPumkt1b3pNoA2ZwVGOcnmpuj6gSxZ1H+L7P9/Jlay+ueDctqEKxo10k8rJt4lKkL2PmM01+qNSkBz7MHdkeSRYFDSshDKWI7nIq9Lf6JPZXdlLczRpNNHKrwWKoAVUgjYDjzHNDLhRIwjJZATtJGCRUYeOEZqpw6/r6FuK+nTUhqAI9oE3jZxxuznt6ZrrStNK8r/E7Fmx6mqi1OlUaOKRaU8V2o1PFKrMqHX8W9NwHbg/SvQen+v9Pj6e8HU5ZE1CKPw8bCRNgYU5H65oGicSrg43eYqrPZ85i7elBKKkuRnHlcHwe0Wd0Lq0hni4EqBgD5ZqxNI7QHc+0fib0FB/RWpSSaH4UwO6zbw2/w/C37j/5oklmM0e2Mggjn6UjOO10dCGTdGyOPVLmS0C2emS+Go90uVTI9QM80Ma1c3nhqj6csabi3vSKzE/nRncQXE1qqRssYxgMPKgnWNN8CUFr1pWPbK4/mih2G5raZssjHTpnlG0tGw257ZGP5oRun3NsXsO/1rf1y4MVuIA2Xc+XoKH1QscDvTkVSOdOVsfF78RB8uKrlMHB8qvbQiY8hVZhuJNEDuGqM4rd6esDeXCxRwmWZyFjT1JrGjWiroy6Wy1a2uJDiNJV8QkZ9wnDfoTS+p+Q7PsRKWouraTr/AAK4Ok7DwzHMZHm8MHxrYo6FyiuFVce8u10y7Oi5YAH1G+qNBGmxLlTtkTfGzxGJxg4Ksp7EEHzI7EE16GIfBgilC3YggYiMowZZv6aoqnbu8b3UXbhV4zuweSLdfT2/hCyhlWV7Zn4TO2BTt/pD1wwY8cDdgUq4qNNHoIZZ5d0Zcpp3x1S+3g8wlXaxFMAqece+ajxXQj0eJytKbo4KlSmAU5aIyZODxXKaDxXagFFgqVOVODU0MhdirDnHepZI6jhikaZfCRnb0UVZjGYWf6fLMdUulWMtAYMSt5Kcjbn68/rRLc2E0Mu62baP+PlTdBiGn9OaaYlCrPPmdh+JyWXB+mBW867/AC7UrlXxD+GXwgdf6xqULm3KhPQt/FDmpXM+4vPNub5Uaa1bvcTLGF4HnjtWVL02kRFxqHNuoyEB5kPkPp61UOXQU5cGHf29vP0/psdxhblzLIr+ag7cZ+WAPzoc9me3ciZcN5fT5Vt6tO1xfAD4IxgAeX/eKsxFvZ9pOR5ZFN7aQlKdvgF5Pe4HambKJ1MUhxcW0cnzK811tL0+Ye4skJ9VbI/I1e0H3gMhMCrNrO8DZU/WtWTQZMZt545f7T7pP8frWdNZzQNtmiZD/cKCcFJUzbBrJ4JqcHTLttrl5a7/AGWeSHeMP4Tsu4ehweapXN7JLHs4C/Ko/CrnhVktPBeDo5PbuqyRcXLvsqMuTmm7KuGKmGOtqOX7y3ZV20sVYMdMK1CbhlKnYpVC7N5UV5kVhkFgDW94McERSJAi/KlSrSBzW+At6ekaPpU7Dx4xGCM8cetasBy7JxtC9sV2lWWXyO4W6RRviVKbSRuYg4rI1QYgcc/F60qVTH0iZHywQMaeOfdHxH96vxRJ4ZG3ilSrfwJ2yMxpub3R3rqoo8qVKrBseow3HFTFiwKNyvoe1KlVA2Dd2ipcyoi4UOQBUJA9KVKszdDSBTGA9KVKqDRGwFRMBSpVDSJGaVKlUCP/2Q=="
+		},
+		{
+			"position": 3,
+			"title": "OpenAI's ChatGPT Does Research… And Breaks Itself!",
+			"link": "https://www.youtube.com/watch?v=iC-wRBsAhEs",
+			"source": "YouTube",
+			"channel": "Two Minute Papers",
+			"date": "2 days ago",
+			"image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAFMAlAMBIgACEQEDEQH/xAAcAAACAgMBAQAAAAAAAAAAAAAEBQMGAAECBwj/xAA+EAACAQMCBAMEBwYEBwAAAAABAgMABBEFEgYTITEiQVFhcZGhBxSBkrHB0RUjMjNCYhZScoNzgpOiwuHw/8QAGwEAAgMBAQEAAAAAAAAAAAAAAgQBAwUGAAf/xAAsEQACAgEDAgUDBAMAAAAAAAABAgADEQQhMRJBBRMiUZEUMkJxgcHRFVJh/9oADAMBAAIRAxEAPwCsoGA7CpEVicEYqYReA49KVcLcNrrPCfEGry313HNpUIeKKMjbIdpOGz18vKsjT1+bnBjdjdMtUMMkSqGBHTvW74MY+/lVd/w8q/R6eJWu7s3AvBbC3HVSCwGfXPWrA3AGkW93ZaJrHEl1bcQ3kQaOKGLfDGTnCsfPqCO65x5dK16mIG8TdczvQI2+uwHPXePxr2b93PbmOTBBGCDXz7BwVcpofEk8t3cftfQ7pYjbKw5cqHb4s9+oLEe4e2rZN9HPI4n03Sm1O75F1Zyzyy5GVaMqGA8sZdPnRs+YKpiWXVtOOnz+XKY+FvyoWMBuxBrx+4eKaRooruZld2EZzk7euCfsq2fQ8qniG7gvGaWPl7cFjjIJpivXZPSRFtR4KyoLQdj+/wA+0vG01mKf6lovKHNtQzIe69ytJmjwadSxXGRMW2l6mw0hxmtFaLtrdp5UjUdWOBTC80KWFC8TCVQOuO4+yvNYqnBM8tLupZRsIjK1rFTvESpA6Ejv6VHBbmONUZy5H9R7mizA6RjOd5xtruAYnjPtphbaXc3C7ooyV9ewqW60e4swkj7WXIyVPagNicZlqUWfdjaJp9Eiutd+v3CgrGPAD6+tb4k1KTTNNkngTc4GAPStcR65HprxQIMzSMAB6VDxbHv0RjjJIpGbQE8gvbu6vbl7iZ2Z2PU5rKbQaHcSJvOEyegNZVfmCNDT2EcRqQRG3THTzrX0Za/HofB3FTRahb2uptEjWSSOm+RwrY2q38XXHTBqG6iaUjaxBrjTuFrSTxSRgBcE4JGBkD19SKxabtPRX1AnJ5EZdLXbBG0eanxZLq/0Y51TUrafWY9RR1gYokhVXBB2Ljp7cUz1OXhzX+KtJ4vbiXT7O2gWKSezuJAs4eMlgoXueuB09Omc1ltwTojACeBnX/iMCPnUXEXAeiW1gs1skmSem5yfzprS6qu84QyqyspzBuCOL9MvvpE4ludQnhtdM1eIFGupBGDyiqoDnzKljin/APjmwuOHOIbuW7t11K2kvINPRpVDyxvgoUHcjO0f8tU3ROEdOur6GKRDtZgDgn9avMf0b8PyZ2hmwcEiRunzp0iUg5nhVpLy4Z9jpGyrtGe/2Vc/onVjq0rLnwqCce+rDrv0aWGm2s10h3JuyAScgfGj/o90e00m4Nyi/wA4FCMk9B1qAMHMse4sip2E9UiYMoHspRqWlGW6VoEAEn8XoDTOGZWAx06VKTt3OWJU+XpRq7IciL2VrauGgtjp0VtGAUVpAc78daG1KeWyYcoArID38jRSXqOTtIwDjrQOsTLM0caEHbknFWVgs+WlVxWuohNojZMmu7eyluZOXCwVz2LdhT2z0pOWHuM5P9PpRjCG1DTOyJEo6ZAG2rrNSMECK0aJshmk6BYkVFwAowBVY4u1vkW4SDDAyBCR65pPf65Nc67Fy5GWFiVCA9MVH9VF1ZOs7MMTluvc9aVTB3mlapT0mL+INJm1HVLV41GEALOewqXiC4/dxwA5XHWmd3eRW9vzLqQQxAeZ6mkvEKtfaUt1YMAAuV6dxUvkjaeoKq4LcRI80aNhnUH0JrKFsNKjltw902+UnqSa3VPkmOHXL7SRIx6L8abaNbm6uWtBtDTxMiEt/WPEv/copWq0fpzPFdRSRna6OGU+hHauRFgVgTxHCpIwJcIbO7EEcvIZkKg7o/EPlQPFFyg0+ODcOZ32nvTqRJGiGqaWziKYlpokPWN/6unpnv8AHsaovFcg1K/2uxklzgsp6gYrV0CJTewGcYz+o/5FrA1qiTcMZXVLYv0BbOTR1ldS2XE9xcLeMbOSQl0HUA1XdOE1ruVnZtgO0kYrvS5S9vnPdjWh4heaVDVyjTVdZw0vGp6iNZ0e4hiULMpyFJ/iANc8LoW0tjJECgYjcO6mkulIHSUOzYC5yKN0DUZYbF4IlyGDsW9Kq0Ore/qDdpOopFeMRsutpHNyUJO04z61ZBzJbH9ywLMK8hvtTa2k3Rgc1mPX0x7Ptoix4y1G16R3BA8wUU/lW/VpLLUDjAmLdrK6XKEEz0G4tZosGQEZ86YaZZhBzpMEnsPSqHHx9dSJsnWCQepUg01suNkmIh2pGzdh5H7aK3T3hNx8Sqi/TeZnJ/eXOWQL2NI+KZkfTo0H8bSqo9maHXVZZbWWdXQFThapWq8Wm6cxiIPErZVm7k+uB+FZ1hRB6pvaSi3UP6BxC9VENvqtkkJBKthqMvbyU21x9RTmXCHaAe2aqh1pWkDmJd4OQdgPWi4OIShYgJ4jlspjPwxVH1AAOOZoWeE3MROuKbeXUbG3iJHPQeMZ86MtbiKLRYrORxzQgXHqaBF8L7fJtCndggUs1SV0MLIcESDrSFPiFxvFbgRW/RisEdxGdlp11HEw8IBckVlF20VzLCr81uvsrdboVscTJNiA4zFSrR1iv75ffXSabJ5zQfeo20shHKpe5gAz18VfPnbI2nRgiHaNJe6Jqtzewy8+yuSGmsyMMGAxuU9s4H2/CncuicM8Vs95akC5U4kltn2SI396+vvFA7LVQCL2A59DVTvOFbqbWZtR0/VY7WRmyssUpRx7Mg9q1PD9a6Dy7/tHErKKW6kbBno0PDEMdtyJZ+euMBpIxux7SK6veE9Lu4QDFyJtoBmgwhJ9SOxqoWtxxdZLg8T2Nwo7C5twxH2rg06i4zGnaWv7Xlhu9SZ2CR2cbKrjyJznHfv8q2k1Wnt2BidiWA5JkUnD11o9vcyB4bqLYcu3gZR7c9PnVNs9WSxRWMmN2cAdc05u3uOIWMuuXciw947OBSEX0z6n2n5UHJw/ZyQLAMyRjtvGCPdij0aV/UM2DgxfUs/lYU7yr3V8PrsV2kUdyEYOYpBhX8WSp99LxdiSWRnEULu7PyUbpGCchR7BU+pwJZX8tpHnbEdoyaWT2MU8xlfuRg104BrQPWM7cTAHTY5S04yeYwa4WJeY4LIvVgPTzoyO8trnUTLpqTJaGQ8kTEb9oHnjp3zSK1sFguOaHOAMBc9Kc6ZY3uoTtHpygyxpv6vtwMgd/tosu6l29OxGIJWut+hfVvnP8S5QXnL0udGbxLE7Y+w1Q4tReKOa1a0jdZihW5Y9YsZyB78j/wC7WzUdOudL0UXWo9biX9zhD4PFkDr7B1qiXEIubZomOM4wfSuauyjYadr4anmVFlz2Px2jBXBqVtTZbb9nJYRs8kyyC7OdyoO6+mO/x92EA019uFxn/V0pnYQ/VLZYt24jJJ9tL4VN+Zq5t1GFYFcb5jvT54khZGciRjkD5U5fTrazjWfVG3OOqQg/jQGi6aZPq93G8Ql7KJUYqDuPXtim1zw5f3UjSS39m7HvmQ/pTWi0ulqIv1DAE8DM5rxvVW23NVplO3J/qJ7nWbhpTym5aDoFXyrKYHg6+JyLmz/6v/qsrc/yWk7WCcv9Bqf9DIFiizjmZPoJP0FbFuFPikY+zdj8qiAkJG4x7fRutTJjPdcegFYf0tA/AfE6HzXP5TsRR46Nj/cP6VJEFVsB8/7proSZx5++o3uYkYBpcN5KDk/CpXT0k7IPieZ3A5hgAVclEHtbLH8K6DIXGAGI7ELj8qEWbf8A1Ig9XcE/AfrU8EqIMfWQfbuA/CmlRV4EoLE8mGb2QFXTDAdtrAn4ipYH65xj4n8qCNzbxRl5LhFUf31pBcXy5PMt7U+WcSSe/wDyj5+6jGYBlf46hW/vbc2c9tzVBSYlwpU9MFie/u7+yj14e4alRQuozxuAASkwIJ9eqmpLqwh3AJDGqr2AUYFaSIKOiL92rQ7gYBlfQh3Ilf4s0y20W3gmsL+S65jlWVkHhGM56fpV14M02Cx0sTrMr3N3GjSElSE89o6+3vVe1eFXtJGZBlFJU47HFPOHbmOK2RRNGqFQR46lrLCvSTBFVYOQI/nEF5by2tzGGjOC8UoDKfQjyPXzrznjPSorLUrZ7KLlQTsBJyznDZ9M4GR28ulegyXcIjyLqLPoZBVT150uG8XLnRTnYJOo/wBPX5UtZX1jGI7pb2pfqBixdA0/cR+15VHkeUrf+QpNJbNFxCNNacywlwFmRcFlI/iAP2/A07i0+3u4edbeNB0JBOVPoR5VCdHCSiQSOHHY7u1UNQvtNGrxG8cvmW+wuY7GC3t4lVoIlIwRhjn1JyO/WiP2hFv6xgIR1zhjn7CtVmOJSuHLFvNg5U/EVJh0OUmyP8snX5j881eACMETMbqzkneWNmil8aSIinyMTfqfxrKrpuCOhRyf7WUj5kfhWVHlV+0jqb3gcSgkEjJ9tGRAZrVZQjeGdpxIeZLy36p3xUn1eA4zDGfeorVZRn7oH4yaO3gHaGP7ooqG3hJ6wx/dFarKsEAwJYo59YZZY1IhGY8Ljb8KblfD/E/3zWVlT3g9oDcIM92++aFaMerfeNZWUcAxTrWVMIVnAOcgMetXDSUURphQOg8qysqPeePaNJQNnYUjvANx6DvWVlRCEWrBENVs5OWpYzJnIyG6+Y7H7adcb6bZWuqMtvbRxqUDEKOmTWVlVNzGElZjgi3fy0+FF/V4cfyY/uitVleWFZODbw5/kx/dFZWVlFKp/9k="
+		}
+	],
+	"inline_videos_more_link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&tbm=vid&q=chatgpt&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ8ccDegQIIhAH",
+	"related_searches": [
+		{
+			"query": "ChatGPT login",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+login&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhUEAE"
+		},
+		{
+			"query": "ChatGPT free",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+free&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhXEAE"
+		},
+		{
+			"query": "ChatGPT 4",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+4&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhREAE"
+		},
+		{
+			"query": "ChatGPT app",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+app&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhQEAE"
+		},
+		{
+			"query": "ChatGPT download",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+download&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhPEAE"
+		},
+		{
+			"query": "ChatGPT OpenAI",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+OpenAI&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhOEAE"
+		},
+		{
+			"query": "ChatGPT website",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+website&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhVEAE"
+		},
+		{
+			"query": "ChatGPT free online",
+			"link": "https://www.google.com/search?sca_esv=acb05f42373aaad6&gl=us&hl=en&q=ChatGPT+free+online&sa=X&ved=2ahUKEwi17_rnppWIAxX2rokEHfAoEzYQ1QJ6BAhWEAE"
+		}
+	],
+	"pagination": {
+		"current": 1,
+		"next": "https://www.google.com/search?q=chatgpt&oq=chatgpt&gl=us&hl=en&start=10&ie=UTF-8"
+	}
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/searxng.json b/backend/open_webui/apps/retrieval/web/testdata/searxng.json
new file mode 100644
index 0000000000000000000000000000000000000000..0e6952baa807842cf130bd0232eab6fe55f1ffba
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/searxng.json
@@ -0,0 +1,476 @@
+{
+	"query": "python",
+	"number_of_results": 116000000,
+	"results": [
+		{
+			"url": "https://www.python.org/",
+			"title": "Welcome to Python.org",
+			"content": "Python is a versatile and powerful language that lets you work quickly and integrate systems more effectively. Learn how to get started, download the latest version, access documentation, find jobs, and join the Python community.",
+			"engine": "bing",
+			"parsed_url": ["https", "www.python.org", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [1, 1, 1],
+			"score": 9.0,
+			"category": "general"
+		},
+		{
+			"url": "https://wiki.nerdvpn.de/wiki/Python_(programming_language)",
+			"title": "Python (programming language) - Wikipedia",
+			"content": "Python is a high-level, general-purpose programming language. Its design philosophy emphasizes code readability with the use of significant indentation. Python is dynamically typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly procedural), object-oriented and functional programming.",
+			"engine": "bing",
+			"parsed_url": ["https", "wiki.nerdvpn.de", "/wiki/Python_(programming_language)", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [4, 3, 2],
+			"score": 3.25,
+			"category": "general"
+		},
+		{
+			"url": "https://docs.python.org/3/tutorial/index.html",
+			"title": "The Python Tutorial \u2014 Python 3.12.3 documentation",
+			"content": "3 days ago \u00b7 Python is an easy to learn, powerful programming language. It has efficient high-level data structures and a simple but effective approach to object-oriented programming. Python\u2019s elegant syntax and dynamic typing, together with its interpreted nature, make it an ideal language for scripting and rapid application development in many \u2026",
+			"engine": "bing",
+			"parsed_url": ["https", "docs.python.org", "/3/tutorial/index.html", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [5, 5, 3],
+			"score": 2.2,
+			"category": "general"
+		},
+		{
+			"url": "https://www.python.org/downloads/",
+			"title": "Download Python | Python.org",
+			"content": "Python is a popular programming language for various purposes. Find the latest version of Python for different operating systems, download release notes, and learn about the development process.",
+			"engine": "bing",
+			"parsed_url": ["https", "www.python.org", "/downloads/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "duckduckgo"],
+			"positions": [2, 2],
+			"score": 2.0,
+			"category": "general"
+		},
+		{
+			"url": "https://www.python.org/about/gettingstarted/",
+			"title": "Python For Beginners | Python.org",
+			"content": "Learn the basics of Python, a popular and easy-to-use programming language, from installing it to using it for various purposes. Find out how to access online documentation, tutorials, books, code samples, and more resources to help you get started with Python.",
+			"engine": "bing",
+			"parsed_url": ["https", "www.python.org", "/about/gettingstarted/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [9, 4, 4],
+			"score": 1.8333333333333333,
+			"category": "general"
+		},
+		{
+			"url": "https://www.python.org/shell/",
+			"title": "Welcome to Python.org",
+			"content": "Python is a versatile and easy-to-use programming language that lets you work quickly. Learn more about Python, download the latest version, access documentation, find jobs, and join the community.",
+			"engine": "bing",
+			"parsed_url": ["https", "www.python.org", "/shell/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [3, 10, 8],
+			"score": 1.675,
+			"category": "general"
+		},
+		{
+			"url": "https://realpython.com/",
+			"title": "Python Tutorials \u2013 Real Python",
+			"content": "Real Python offers comprehensive and up-to-date tutorials, books, and courses for Python developers of all skill levels. Whether you want to learn Python basics, web development, data science, machine learning, or more, you can find clear and practical guides and code examples here.",
+			"engine": "bing",
+			"parsed_url": ["https", "realpython.com", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "qwant", "duckduckgo"],
+			"positions": [6, 6, 5],
+			"score": 1.6,
+			"category": "general"
+		},
+		{
+			"url": "https://wiki.nerdvpn.de/wiki/Python",
+			"title": "Python",
+			"content": "Topics referred to by the same term",
+			"engine": "wikipedia",
+			"parsed_url": ["https", "wiki.nerdvpn.de", "/wiki/Python", "", "", ""],
+			"template": "default.html",
+			"engines": ["wikipedia"],
+			"positions": [1],
+			"score": 1.0,
+			"category": "general"
+		},
+		{
+			"title": "Online Python - IDE, Editor, Compiler, Interpreter",
+			"content": "Online Python IDE is a free online tool that lets you write, execute, and share Python code in the web browser. Learn about Python, its features, and its popularity as a general-purpose programming language for web development, data science, and more.",
+			"url": "https://www.online-python.com/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "www.online-python.com", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["qwant", "duckduckgo"],
+			"positions": [8, 6],
+			"score": 0.5833333333333333,
+			"category": "general"
+		},
+		{
+			"url": "https://micropython.org/",
+			"title": "MicroPython - Python for microcontrollers",
+			"content": "MicroPython is a full Python compiler and runtime that runs on the bare-metal. You get an interactive prompt (the REPL) to execute commands immediately, along ...",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "micropython.org", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [1],
+			"score": 1.0,
+			"category": "general"
+		},
+		{
+			"url": "https://dictionary.cambridge.org/uk/dictionary/english/python",
+			"title": "PYTHON | \u0417\u043d\u0430\u0447\u0435\u043d\u043d\u044f \u0432 \u0430\u043d\u0433\u043b\u0456\u0439\u0441\u044c\u043a\u0456\u0439 \u043c\u043e\u0432\u0456 - Cambridge Dictionary",
+			"content": "Apr 17, 2024 \u2014 \u0412\u0438\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044f PYTHON: 1. a very large snake that kills animals for food by wrapping itself around them and crushing them\u2026. \u0414\u0456\u0437\u043d\u0430\u0439\u0442\u0435\u0441\u044f \u0431\u0456\u043b\u044c\u0448\u0435.",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": [
+				"https",
+				"dictionary.cambridge.org",
+				"/uk/dictionary/english/python",
+				"",
+				"",
+				""
+			],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [2],
+			"score": 0.5,
+			"category": "general"
+		},
+		{
+			"url": "https://www.codetoday.co.uk/code",
+			"title": "Web-based Python Editor (with Turtle graphics)",
+			"content": "Quick way of starting to write Python code, including drawing with Turtle, provided by CodeToday using Trinket.io Ideal for young children to start ...",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "www.codetoday.co.uk", "/code", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [3],
+			"score": 0.3333333333333333,
+			"category": "general"
+		},
+		{
+			"url": "https://snapcraft.io/docs/python-plugin",
+			"title": "The python plugin | Snapcraft documentation",
+			"content": "The python plugin can be used by either Python 2 or Python 3 based parts using a setup.py script for building the project, or using a package published to ...",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "snapcraft.io", "/docs/python-plugin", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [4],
+			"score": 0.25,
+			"category": "general"
+		},
+		{
+			"url": "https://www.developer-tech.com/categories/developer-languages/developer-languages-python/",
+			"title": "Latest Python Developer News",
+			"content": "Python's status as the primary language for AI and machine learning projects, from its extensive data-handling capabilities to its flexibility and ...",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": [
+				"https",
+				"www.developer-tech.com",
+				"/categories/developer-languages/developer-languages-python/",
+				"",
+				"",
+				""
+			],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [5],
+			"score": 0.2,
+			"category": "general"
+		},
+		{
+			"url": "https://subjectguides.york.ac.uk/coding/python",
+			"title": "Coding: a Practical Guide - Python - Subject Guides",
+			"content": "Python is a coding language used for a wide range of things, including working with data, building systems and software, and even creating games.",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "subjectguides.york.ac.uk", "/coding/python", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [6],
+			"score": 0.16666666666666666,
+			"category": "general"
+		},
+		{
+			"url": "https://hub.salford.ac.uk/psytech/python/getting-started-python/",
+			"title": "Getting Started - Python - Salford PsyTech Home - The Hub",
+			"content": "Python in itself is a very friendly programming language, when we get to grips with writing code, once you grasp the logic, it will become very intuitive.",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": [
+				"https",
+				"hub.salford.ac.uk",
+				"/psytech/python/getting-started-python/",
+				"",
+				"",
+				""
+			],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [7],
+			"score": 0.14285714285714285,
+			"category": "general"
+		},
+		{
+			"url": "https://snapcraft.io/docs/python-apps",
+			"title": "Python apps | Snapcraft documentation",
+			"content": "Snapcraft can be used to package and distribute Python applications in a way that enables convenient installation by users. The process of creating a snap ...",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "snapcraft.io", "/docs/python-apps", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [8],
+			"score": 0.125,
+			"category": "general"
+		},
+		{
+			"url": "https://anvil.works/",
+			"title": "Anvil | Build Web Apps with Nothing but Python",
+			"content": "Anvil is a free Python-based drag-and-drop web app builder.\u200eSign Up \u00b7 \u200eSign in \u00b7 \u200ePricing \u00b7 \u200eForum",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": ["https", "anvil.works", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [9],
+			"score": 0.1111111111111111,
+			"category": "general"
+		},
+		{
+			"url": "https://docs.python.org/",
+			"title": "Python 3.12.3 documentation",
+			"content": "3 days ago \u00b7 This is the official documentation for Python 3.12.3. Documentation sections: What's new in Python 3.12? Or all \"What's new\" documents since Python 2.0. Tutorial. Start here: a tour of Python's syntax and features. Library reference. Standard library and builtins. Language reference.",
+			"engine": "bing",
+			"parsed_url": ["https", "docs.python.org", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing", "duckduckgo"],
+			"positions": [7, 13],
+			"score": 0.43956043956043955,
+			"category": "general"
+		},
+		{
+			"title": "How to Use Python: Your First Steps - Real Python",
+			"content": "Learn the basics of Python syntax, installation, error handling, and more in this tutorial. You'll also code your first Python program and test your knowledge with a quiz.",
+			"url": "https://realpython.com/python-first-steps/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "realpython.com", "/python-first-steps/", "", "", ""],
+			"template": "default.html",
+			"engines": ["qwant", "duckduckgo"],
+			"positions": [14, 7],
+			"score": 0.42857142857142855,
+			"category": "general"
+		},
+		{
+			"title": "The Python Tutorial \u2014 Python 3.11.8 documentation",
+			"content": "This tutorial introduces the reader informally to the basic concepts and features of the Python language and system. It helps to have a Python interpreter handy for hands-on experience, but all examples are self-contained, so the tutorial can be read off-line as well. For a description of standard objects and modules, see The Python Standard ...",
+			"url": "https://docs.python.org/3.11/tutorial/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "docs.python.org", "/3.11/tutorial/", "", "", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [7],
+			"score": 0.14285714285714285,
+			"category": "general"
+		},
+		{
+			"url": "https://realpython.com/python-introduction/",
+			"title": "Introduction to Python 3 \u2013 Real Python",
+			"content": "Python programming language, including a brief history of the development of Python and reasons why you might select Python as your language of choice.",
+			"engine": "bing",
+			"parsed_url": ["https", "realpython.com", "/python-introduction/", "", "", ""],
+			"template": "default.html",
+			"engines": ["bing"],
+			"positions": [8],
+			"score": 0.125,
+			"category": "general"
+		},
+		{
+			"title": "Our Documentation | Python.org",
+			"content": "Find online or download Python's documentation, tutorials, and guides for beginners and advanced users. Learn how to port from Python 2 to Python 3, contribute to Python, and access Python videos and books.",
+			"url": "https://www.python.org/doc/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "www.python.org", "/doc/", "", "", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [9],
+			"score": 0.1111111111111111,
+			"category": "general"
+		},
+		{
+			"title": "Welcome to Python.org",
+			"url": "http://www.get-python.org/shell/",
+			"content": "The mission of the Python Software Foundation is to promote, protect, and advance the Python programming language, and to support and facilitate the growth of a diverse and international community of Python programmers. Learn more. Become a Member Donate to the PSF.",
+			"engine": "qwant",
+			"parsed_url": ["http", "www.get-python.org", "/shell/", "", "", ""],
+			"template": "default.html",
+			"engines": ["qwant"],
+			"positions": [9],
+			"score": 0.1111111111111111,
+			"category": "general"
+		},
+		{
+			"title": "About Python\u2122 | Python.org",
+			"content": "Python is a powerful, fast, and versatile programming language that runs on various platforms and is easy to learn. Learn how to get started, explore the applications, and join the community of Python programmers and users.",
+			"url": "https://www.python.org/about/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "www.python.org", "/about/", "", "", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [11],
+			"score": 0.09090909090909091,
+			"category": "general"
+		},
+		{
+			"title": "Online Python Compiler (Interpreter) - Programiz",
+			"content": "Write and run Python code using this online tool. You can use Python Shell like IDLE, and take inputs from the user in our Python compiler.",
+			"url": "https://www.programiz.com/python-programming/online-compiler/",
+			"engine": "duckduckgo",
+			"parsed_url": [
+				"https",
+				"www.programiz.com",
+				"/python-programming/online-compiler/",
+				"",
+				"",
+				""
+			],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [12],
+			"score": 0.08333333333333333,
+			"category": "general"
+		},
+		{
+			"title": "Welcome to Python.org",
+			"content": "Python is a versatile and powerful language that lets you work quickly and integrate systems more effectively. Download the latest version, read the documentation, find jobs, events, success stories, and more on Python.org.",
+			"url": "https://www.python.org/?downloads",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "www.python.org", "/", "", "downloads", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [15],
+			"score": 0.06666666666666667,
+			"category": "general"
+		},
+		{
+			"url": "https://www.matillion.com/blog/the-importance-of-python-and-its-growing-influence-on-data-productivty-a-matillion-perspective",
+			"title": "The Importance of Python and its Growing Influence on ...",
+			"content": "Jan 30, 2024 \u2014 The synergy of low-code functionality with Python's versatility empowers data professionals to orchestrate complex transformations seamlessly.",
+			"img_src": null,
+			"engine": "google",
+			"parsed_url": [
+				"https",
+				"www.matillion.com",
+				"/blog/the-importance-of-python-and-its-growing-influence-on-data-productivty-a-matillion-perspective",
+				"",
+				"",
+				""
+			],
+			"template": "default.html",
+			"engines": ["google"],
+			"positions": [10],
+			"score": 0.1,
+			"category": "general"
+		},
+		{
+			"title": "BeginnersGuide - Python Wiki",
+			"content": "This is the program that reads Python programs and carries out their instructions; you need it before you can do any Python programming. Mac and Linux distributions may include an outdated version of Python (Python 2), but you should install an updated one (Python 3). See BeginnersGuide/Download for instructions to download the correct version ...",
+			"url": "https://wiki.python.org/moin/BeginnersGuide",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "wiki.python.org", "/moin/BeginnersGuide", "", "", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [16],
+			"score": 0.0625,
+			"category": "general"
+		},
+		{
+			"title": "Learn Python - Free Interactive Python Tutorial",
+			"content": "Learn Python from scratch or improve your skills with this website that offers tutorials, exercises, tests and certification. Explore topics such as basics, data science, advanced features and more with DataCamp.",
+			"url": "https://www.learnpython.org/",
+			"engine": "duckduckgo",
+			"parsed_url": ["https", "www.learnpython.org", "/", "", "", ""],
+			"template": "default.html",
+			"engines": ["duckduckgo"],
+			"positions": [17],
+			"score": 0.058823529411764705,
+			"category": "general"
+		}
+	],
+	"answers": [],
+	"corrections": [],
+	"infoboxes": [
+		{
+			"infobox": "Python",
+			"id": "https://en.wikipedia.org/wiki/Python_(programming_language)",
+			"content": "general-purpose programming language",
+			"img_src": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/.PY_file_recreation.png/500px-.PY_file_recreation.png",
+			"urls": [
+				{
+					"title": "Official website",
+					"url": "https://www.python.org/",
+					"official": true
+				},
+				{
+					"title": "Wikipedia (en)",
+					"url": "https://en.wikipedia.org/wiki/Python_(programming_language)"
+				},
+				{
+					"title": "Wikidata",
+					"url": "http://www.wikidata.org/entity/Q28865"
+				}
+			],
+			"attributes": [
+				{
+					"label": "Inception",
+					"value": "Wednesday, February 20, 1991",
+					"entity": "P571"
+				},
+				{
+					"label": "Developer",
+					"value": "Python Software Foundation, Guido van Rossum",
+					"entity": "P178"
+				},
+				{
+					"label": "Copyright license",
+					"value": "Python Software Foundation License",
+					"entity": "P275"
+				},
+				{
+					"label": "Programmed in",
+					"value": "C, Python",
+					"entity": "P277"
+				},
+				{
+					"label": "Software version identifier",
+					"value": "3.12.3, 3.13.0a6",
+					"entity": "P348"
+				}
+			],
+			"engine": "wikidata",
+			"engines": ["wikidata"]
+		}
+	],
+	"suggestions": [
+		"python turtle",
+		"micro python tutorial",
+		"python docs",
+		"python compiler",
+		"snapcraft python",
+		"micropython vs python",
+		"python online",
+		"python download"
+	],
+	"unresponsive_engines": []
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/serper.json b/backend/open_webui/apps/retrieval/web/testdata/serper.json
new file mode 100644
index 0000000000000000000000000000000000000000..b269eaf5b34fe64234ba6e7ffb27fd3fbbaa3fe0
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/serper.json
@@ -0,0 +1,190 @@
+{
+	"searchParameters": {
+		"q": "apple inc",
+		"gl": "us",
+		"hl": "en",
+		"autocorrect": true,
+		"page": 1,
+		"type": "search"
+	},
+	"knowledgeGraph": {
+		"title": "Apple",
+		"type": "Technology company",
+		"website": "http://www.apple.com/",
+		"imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQwGQRv5TjjkycpctY66mOg_e2-npacrmjAb6_jAWhzlzkFE3OTjxyzbA&s=0",
+		"description": "Apple Inc. is an American multinational technology company specializing in consumer electronics, software and online services headquartered in Cupertino, California, United States.",
+		"descriptionSource": "Wikipedia",
+		"descriptionLink": "https://en.wikipedia.org/wiki/Apple_Inc.",
+		"attributes": {
+			"Headquarters": "Cupertino, CA",
+			"CEO": "Tim Cook (Aug 24, 2011–)",
+			"Founded": "April 1, 1976, Los Altos, CA",
+			"Sales": "1 (800) 692-7753",
+			"Products": "iPhone, Apple Watch, iPad, and more",
+			"Founders": "Steve Jobs, Steve Wozniak, and Ronald Wayne",
+			"Subsidiaries": "Apple Store, Beats Electronics, Beddit, and more"
+		}
+	},
+	"organic": [
+		{
+			"title": "Apple",
+			"link": "https://www.apple.com/",
+			"snippet": "Discover the innovative world of Apple and shop everything iPhone, iPad, Apple Watch, Mac, and Apple TV, plus explore accessories, entertainment, ...",
+			"sitelinks": [
+				{
+					"title": "Support",
+					"link": "https://support.apple.com/"
+				},
+				{
+					"title": "iPhone",
+					"link": "https://www.apple.com/iphone/"
+				},
+				{
+					"title": "Apple makes business better.",
+					"link": "https://www.apple.com/business/"
+				},
+				{
+					"title": "Mac",
+					"link": "https://www.apple.com/mac/"
+				}
+			],
+			"position": 1
+		},
+		{
+			"title": "Apple Inc. - Wikipedia",
+			"link": "https://en.wikipedia.org/wiki/Apple_Inc.",
+			"snippet": "Apple Inc. is an American multinational technology company specializing in consumer electronics, software and online services headquartered in Cupertino, ...",
+			"attributes": {
+				"Products": "AirPods; Apple Watch; iPad; iPhone; Mac",
+				"Founders": "Steve Jobs; Steve Wozniak; Ronald Wayne",
+				"Founded": "April 1, 1976; 46 years ago in Los Altos, California, U.S",
+				"Industry": "Consumer electronics; Software services; Online services"
+			},
+			"sitelinks": [
+				{
+					"title": "History",
+					"link": "https://en.wikipedia.org/wiki/History_of_Apple_Inc."
+				},
+				{
+					"title": "Timeline of Apple Inc. products",
+					"link": "https://en.wikipedia.org/wiki/Timeline_of_Apple_Inc._products"
+				},
+				{
+					"title": "List of software by Apple Inc.",
+					"link": "https://en.wikipedia.org/wiki/List_of_software_by_Apple_Inc."
+				},
+				{
+					"title": "Apple Store",
+					"link": "https://en.wikipedia.org/wiki/Apple_Store"
+				}
+			],
+			"position": 2
+		},
+		{
+			"title": "Apple Inc. | History, Products, Headquarters, & Facts | Britannica",
+			"link": "https://www.britannica.com/topic/Apple-Inc",
+			"snippet": "Apple Inc., formerly Apple Computer, Inc., American manufacturer of personal computers, smartphones, tablet computers, computer peripherals, ...",
+			"date": "Aug 31, 2022",
+			"attributes": {
+				"Related People": "Steve Jobs Steve Wozniak Jony Ive Tim Cook Angela Ahrendts",
+				"Date": "1976 - present",
+				"Areas Of Involvement": "peripheral device"
+			},
+			"position": 3
+		},
+		{
+			"title": "AAPL: Apple Inc Stock Price Quote - NASDAQ GS - Bloomberg.com",
+			"link": "https://www.bloomberg.com/quote/AAPL:US",
+			"snippet": "Stock analysis for Apple Inc (AAPL:NASDAQ GS) including stock price, stock chart, company news, key statistics, fundamentals and company profile.",
+			"position": 4
+		},
+		{
+			"title": "Apple Inc. (AAPL) Company Profile & Facts - Yahoo Finance",
+			"link": "https://finance.yahoo.com/quote/AAPL/profile/",
+			"snippet": "Apple Inc. designs, manufactures, and markets smartphones, personal computers, tablets, wearables, and accessories worldwide. It also sells various related ...",
+			"position": 5
+		},
+		{
+			"title": "AAPL | Apple Inc. Stock Price & News - WSJ",
+			"link": "https://www.wsj.com/market-data/quotes/AAPL",
+			"snippet": "Apple, Inc. engages in the design, manufacture, and sale of smartphones, personal computers, tablets, wearables and accessories, and other varieties of ...",
+			"position": 6
+		},
+		{
+			"title": "Apple Inc Company Profile - Apple Inc Overview - GlobalData",
+			"link": "https://www.globaldata.com/company-profile/apple-inc/",
+			"snippet": "Apple Inc (Apple) designs, manufactures, and markets smartphones, tablets, personal computers (PCs), portable and wearable devices. The company also offers ...",
+			"position": 7
+		},
+		{
+			"title": "Apple Inc (AAPL) Stock Price & News - Google Finance",
+			"link": "https://www.google.com/finance/quote/AAPL:NASDAQ?hl=en",
+			"snippet": "Get the latest Apple Inc (AAPL) real-time quote, historical performance, charts, and other financial information to help you make more informed trading and ...",
+			"position": 8
+		}
+	],
+	"peopleAlsoAsk": [
+		{
+			"question": "What does Apple Inc mean?",
+			"snippet": "Apple Inc., formerly Apple Computer, Inc., American manufacturer of personal\ncomputers, smartphones, tablet computers, computer peripherals, and computer\nsoftware. It was the first successful personal computer company and the\npopularizer of the graphical user interface.\nAug 31, 2022",
+			"title": "Apple Inc. | History, Products, Headquarters, & Facts | Britannica",
+			"link": "https://www.britannica.com/topic/Apple-Inc"
+		},
+		{
+			"question": "Is Apple and Apple Inc same?",
+			"snippet": "Apple was founded as Apple Computer Company on April 1, 1976, by Steve Jobs,\nSteve Wozniak and Ronald Wayne to develop and sell Wozniak's Apple I personal\ncomputer. It was incorporated by Jobs and Wozniak as Apple Computer, Inc.",
+			"title": "Apple Inc. - Wikipedia",
+			"link": "https://en.wikipedia.org/wiki/Apple_Inc."
+		},
+		{
+			"question": "Who owns Apple Inc?",
+			"snippet": "Apple Inc. is owned by two main institutional investors (Vanguard Group and\nBlackRock, Inc). While its major individual shareholders comprise people like\nArt Levinson, Tim Cook, Bruce Sewell, Al Gore, Johny Sroujli, and others.",
+			"title": "Who Owns Apple In 2022? - FourWeekMBA",
+			"link": "https://fourweekmba.com/who-owns-apple/"
+		},
+		{
+			"question": "What products does Apple Inc offer?",
+			"snippet": "APPLE FOOTER\nStore.\nMac.\niPad.\niPhone.\nWatch.\nAirPods.\nTV & Home.\nAirTag.",
+			"title": "More items...",
+			"link": "https://www.apple.com/business/"
+		}
+	],
+	"relatedSearches": [
+		{
+			"query": "Who invented the iPhone"
+		},
+		{
+			"query": "Apple Inc competitors"
+		},
+		{
+			"query": "Apple iPad"
+		},
+		{
+			"query": "iPhones"
+		},
+		{
+			"query": "Apple Inc us"
+		},
+		{
+			"query": "Apple company history"
+		},
+		{
+			"query": "Apple Store"
+		},
+		{
+			"query": "Apple customer service"
+		},
+		{
+			"query": "Apple Watch"
+		},
+		{
+			"query": "Apple Inc Industry"
+		},
+		{
+			"query": "Apple Inc registered address"
+		},
+		{
+			"query": "Apple Inc Bloomberg"
+		}
+	]
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/serply.json b/backend/open_webui/apps/retrieval/web/testdata/serply.json
new file mode 100644
index 0000000000000000000000000000000000000000..0fc2a31e4d63cefba8aa96cad147208d596060c4
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/serply.json
@@ -0,0 +1,206 @@
+{
+	"ads": [],
+	"ads_count": 0,
+	"answers": [],
+	"results": [
+		{
+			"title": "Apple",
+			"link": "https://www.apple.com/",
+			"description": "Discover the innovative world of Apple and shop everything iPhone, iPad, Apple Watch, Mac, and Apple TV, plus explore accessories, entertainment, ...",
+			"additional_links": [
+				{
+					"text": "AppleApplehttps://www.apple.com",
+					"href": "https://www.apple.com/"
+				}
+			],
+			"cite": {},
+			"subdomains": [
+				{
+					"title": "Support",
+					"link": "https://support.apple.com/",
+					"description": "SupportContact - iPhone Support - Billing and Subscriptions - Apple Repair"
+				},
+				{
+					"title": "Store",
+					"link": "https://www.apple.com/store",
+					"description": "StoreShop iPhone - Shop iPad - App Store - Shop Mac - ..."
+				},
+				{
+					"title": "Mac",
+					"link": "https://www.apple.com/mac/",
+					"description": "MacMacBook Air - MacBook Pro - iMac - Compare Mac models - Mac mini"
+				},
+				{
+					"title": "iPad",
+					"link": "https://www.apple.com/ipad/",
+					"description": "iPadShop iPad - iPad Pro - iPad Air - Compare iPad models - ..."
+				},
+				{
+					"title": "Watch",
+					"link": "https://www.apple.com/watch/",
+					"description": "WatchShop Apple Watch - Series 9 - SE - Ultra 2 - Nike - Hermès - ..."
+				}
+			],
+			"realPosition": 1
+		},
+		{
+			"title": "Apple",
+			"link": "https://www.apple.com/",
+			"description": "Discover the innovative world of Apple and shop everything iPhone, iPad, Apple Watch, Mac, and Apple TV, plus explore accessories, entertainment, ...",
+			"additional_links": [
+				{
+					"text": "AppleApplehttps://www.apple.com",
+					"href": "https://www.apple.com/"
+				}
+			],
+			"cite": {},
+			"realPosition": 2
+		},
+		{
+			"title": "Apple Inc.",
+			"link": "https://en.wikipedia.org/wiki/Apple_Inc.",
+			"description": "Apple Inc. (formerly Apple Computer, Inc.) is an American multinational corporation and technology company headquartered in Cupertino, California, ...",
+			"additional_links": [
+				{
+					"text": "Apple Inc.Wikipediahttps://en.wikipedia.org › wiki › Apple_Inc",
+					"href": "https://en.wikipedia.org/wiki/Apple_Inc."
+				},
+				{
+					"text": "",
+					"href": "https://en.wikipedia.org/wiki/Apple_Inc."
+				},
+				{
+					"text": "History",
+					"href": "https://en.wikipedia.org/wiki/History_of_Apple_Inc."
+				},
+				{
+					"text": "List of Apple products",
+					"href": "https://en.wikipedia.org/wiki/List_of_Apple_products"
+				},
+				{
+					"text": "Litigation involving Apple Inc.",
+					"href": "https://en.wikipedia.org/wiki/Litigation_involving_Apple_Inc."
+				},
+				{
+					"text": "Apple Park",
+					"href": "https://en.wikipedia.org/wiki/Apple_Park"
+				}
+			],
+			"cite": {
+				"domain": "https://en.wikipedia.org › wiki › Apple_Inc",
+				"span": " › wiki › Apple_Inc"
+			},
+			"realPosition": 3
+		},
+		{
+			"title": "Apple Inc. (AAPL) Company Profile & Facts",
+			"link": "https://finance.yahoo.com/quote/AAPL/profile/",
+			"description": "Apple Inc. designs, manufactures, and markets smartphones, personal computers, tablets, wearables, and accessories worldwide. The company offers iPhone, a line ...",
+			"additional_links": [
+				{
+					"text": "Apple Inc. (AAPL) Company Profile & FactsYahoo Financehttps://finance.yahoo.com › quote › AAPL › profile",
+					"href": "https://finance.yahoo.com/quote/AAPL/profile/"
+				}
+			],
+			"cite": {
+				"domain": "https://finance.yahoo.com › quote › AAPL › profile",
+				"span": " › quote › AAPL › profile"
+			},
+			"realPosition": 4
+		},
+		{
+			"title": "Apple Inc - Company Profile and News",
+			"link": "https://www.bloomberg.com/profile/company/AAPL:US",
+			"description": "Apple Inc. Apple Inc. designs, manufactures, and markets smartphones, personal computers, tablets, wearables and accessories, and sells a variety of related ...",
+			"additional_links": [
+				{
+					"text": "Apple Inc - Company Profile and NewsBloomberghttps://www.bloomberg.com › company › AAPL:US",
+					"href": "https://www.bloomberg.com/profile/company/AAPL:US"
+				},
+				{
+					"text": "",
+					"href": "https://www.bloomberg.com/profile/company/AAPL:US"
+				}
+			],
+			"cite": {
+				"domain": "https://www.bloomberg.com › company › AAPL:US",
+				"span": " › company › AAPL:US"
+			},
+			"realPosition": 5
+		},
+		{
+			"title": "Apple Inc. | History, Products, Headquarters, & Facts",
+			"link": "https://www.britannica.com/money/Apple-Inc",
+			"description": "May 22, 2024 — Apple Inc. is an American multinational technology company that revolutionized the technology sector through its innovation of computer ...",
+			"additional_links": [
+				{
+					"text": "Apple Inc. | History, Products, Headquarters, & FactsBritannicahttps://www.britannica.com › money › Apple-Inc",
+					"href": "https://www.britannica.com/money/Apple-Inc"
+				},
+				{
+					"text": "",
+					"href": "https://www.britannica.com/money/Apple-Inc"
+				}
+			],
+			"cite": {
+				"domain": "https://www.britannica.com › money › Apple-Inc",
+				"span": " › money › Apple-Inc"
+			},
+			"realPosition": 6
+		}
+	],
+	"shopping_ads": [],
+	"places": [
+		{
+			"title": "Apple Inc."
+		},
+		{
+			"title": "Apple Inc"
+		},
+		{
+			"title": "Apple Inc"
+		}
+	],
+	"related_searches": {
+		"images": [],
+		"text": [
+			{
+				"title": "apple inc full form",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+Inc+full+form&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhPEAE"
+			},
+			{
+				"title": "apple company history",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+company+history&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhOEAE"
+			},
+			{
+				"title": "apple store",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+Store&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhQEAE"
+			},
+			{
+				"title": "apple id",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+id&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhSEAE"
+			},
+			{
+				"title": "apple inc industry",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+Inc+industry&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhREAE"
+			},
+			{
+				"title": "apple login",
+				"link": "https://www.google.com/search?sca_esv=6b6df170a5c9891b&sca_upv=1&q=Apple+login&sa=X&ved=2ahUKEwjLxuSJwM-GAxUHODQIHYuJBhgQ1QJ6BAhTEAE"
+			}
+		]
+	},
+	"image_results": [],
+	"carousel": [],
+	"total": 2450000000,
+	"knowledge_graph": "",
+	"related_questions": [
+		"What does the Apple Inc do?",
+		"Why did Apple change to Apple Inc?",
+		"Who owns Apple Inc.?",
+		"What is Apple Inc best known for?"
+	],
+	"carousel_count": 0,
+	"ts": 2.491065263748169,
+	"device_type": null
+}
diff --git a/backend/open_webui/apps/retrieval/web/testdata/serpstack.json b/backend/open_webui/apps/retrieval/web/testdata/serpstack.json
new file mode 100644
index 0000000000000000000000000000000000000000..a82f689d8b2293586d6b94974e018f74e49d1013
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/testdata/serpstack.json
@@ -0,0 +1,276 @@
+{
+	"request": {
+		"success": true,
+		"total_time_taken": 3.4,
+		"processed_timestamp": 1714968442,
+		"search_url": "http://www.google.com/search?q=mcdonalds\u0026gl=us\u0026hl=en\u0026safe=0\u0026num=10"
+	},
+	"search_parameters": {
+		"engine": "google",
+		"type": "web",
+		"device": "desktop",
+		"auto_location": "1",
+		"google_domain": "google.com",
+		"gl": "us",
+		"hl": "en",
+		"safe": "0",
+		"news_type": "all",
+		"exclude_autocorrected_results": "0",
+		"images_color": "any",
+		"page": "1",
+		"num": "10",
+		"output": "json",
+		"csv_fields": "search_parameters.query,organic_results.position,organic_results.title,organic_results.url,organic_results.domain",
+		"query": "mcdonalds",
+		"action": "search",
+		"access_key": "aac48e007e15c532bb94ffb34532a4b2",
+		"error": {}
+	},
+	"search_information": {
+		"total_results": 1170000000,
+		"time_taken_displayed": 0.49,
+		"detected_location": {},
+		"did_you_mean": {},
+		"no_results_for_original_query": false,
+		"showing_results_for": {}
+	},
+	"organic_results": [
+		{
+			"position": 1,
+			"title": "Our Full McDonald\u0027s Food Menu",
+			"snippet": "",
+			"prerender": false,
+			"cached_page_url": {},
+			"related_pages_url": {},
+			"url": "https://www.mcdonalds.com/us/en-us/full-menu.html",
+			"domain": "www.mcdonalds.com",
+			"displayed_url": "https://www.mcdonalds.com \u203a en-us \u203a full-menu"
+		},
+		{
+			"position": 2,
+			"title": "McDonald\u0027s",
+			"snippet": "McDonald\u0027s is the world\u0027s largest fast food restaurant chain, serving over 69 million customers daily in over 100 countries in more than 40,000 outlets as of\u00a0...",
+			"prerender": false,
+			"cached_page_url": {},
+			"related_pages_url": {},
+			"url": "https://en.wikipedia.org/wiki/McDonald%27s",
+			"domain": "en.wikipedia.org",
+			"displayed_url": "https://en.wikipedia.org \u203a wiki \u203a McDonald\u0027s"
+		},
+		{
+			"position": 3,
+			"title": "Restaurants Near Me: Nearby McDonald\u0027s Locations",
+			"snippet": "",
+			"prerender": false,
+			"cached_page_url": {},
+			"related_pages_url": {},
+			"url": "https://www.mcdonalds.com/us/en-us/restaurant-locator.html",
+			"domain": "www.mcdonalds.com",
+			"displayed_url": "https://www.mcdonalds.com \u203a en-us \u203a restaurant-locator"
+		},
+		{
+			"position": 4,
+			"title": "Download the McDonald\u0027s App: Deals, Promotions \u0026 ...",
+			"snippet": "Download the McDonald\u0027s app for Mobile Order \u0026 Pay, exclusive deals and coupons, menu information and special promotions.",
+			"prerender": false,
+			"cached_page_url": {},
+			"related_pages_url": {},
+			"url": "https://www.mcdonalds.com/us/en-us/download-app.html",
+			"domain": "www.mcdonalds.com",
+			"displayed_url": "https://www.mcdonalds.com \u203a en-us \u203a download-app"
+		},
+		{
+			"position": 5,
+			"title": "McDonald\u0027s Restaurant Careers in the US",
+			"snippet": "McDonald\u0027s restaurant jobs are one-of-a-kind \u2013 just like you. Restaurants are hiring across all levels, from Crew team to Management. Apply today!",
+			"prerender": false,
+			"cached_page_url": {},
+			"related_pages_url": {},
+			"url": "https://jobs.mchire.com/",
+			"domain": "jobs.mchire.com",
+			"displayed_url": "https://jobs.mchire.com"
+		}
+	],
+	"inline_images": [
+		{
+			"image_url": "https://serpstack-assets.apilayer.net/2418910010831954152.png",
+			"title": ""
+		}
+	],
+	"local_results": [
+		{
+			"position": 1,
+			"title": "McDonald\u0027s",
+			"coordinates": {
+				"latitude": 0,
+				"longitude": 0
+			},
+			"address": "",
+			"rating": 0,
+			"reviews": 0,
+			"type": "",
+			"price": {},
+			"url": 0
+		},
+		{
+			"position": 2,
+			"title": "McDonald\u0027s",
+			"coordinates": {
+				"latitude": 0,
+				"longitude": 0
+			},
+			"address": "",
+			"rating": 0,
+			"reviews": 0,
+			"type": "",
+			"price": {},
+			"url": 0
+		},
+		{
+			"position": 3,
+			"title": "McDonald\u0027s",
+			"coordinates": {
+				"latitude": 0,
+				"longitude": 0
+			},
+			"address": "",
+			"rating": 0,
+			"reviews": 0,
+			"type": "",
+			"price": {},
+			"url": 0
+		}
+	],
+	"top_stories": [
+		{
+			"block_position": 1,
+			"title": "Menu nutrition",
+			"url": "/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=mcdonald%27s+double+quarter+pounder+with+cheese\u0026stick=H4sIAAAAAAAAAONgFuLUz9U3ME-vLDBX4tVP1zc0TCsuNE0ytjTTUs5OttJPy89P0c9NzSuNLyjKL8tMSS2yAvNS80qKMlOLF7Hq5ian5Ocl5qSoFyuk5Jcm5aQqFJYmFpWkFikU5JfmATUolGeWZCgkZ6SmFqcCAM4ilJtxAAAA\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4Qri56BAh0EAM",
+			"source": "",
+			"uploaded": "",
+			"uploaded_utc": "2024-05-06T04:07:22.082Z"
+		},
+		{
+			"block_position": 2,
+			"title": "Profiles",
+			"url": "https://www.instagram.com/McDonalds",
+			"source": "",
+			"uploaded": "",
+			"uploaded_utc": "2024-05-06T04:07:22.082Z"
+		},
+		{
+			"block_position": 3,
+			"title": "People also search for",
+			"url": "/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026si=ACC90nzx_D3_zUKRnpAjmO0UBLNxnt7EyN4YYdru6U3bxLI-L5Wg8IL2sxPFxxcDEhVbocy-LJPZIvZySijw0ho2hfZ-KtV-sSEEJ9lw7JuEkXHDnRK5y4Dm8aqbiLwugbLbslwjG3hO_gpDTFZK2VoUGZPy2nrmOBCy0G3PoOfoiEtct2GSZlUz0uufG-xP8emtNzQKQpvjkAm5Zmi57iVZueiD62upz7-x2N3dAbwtm6FkInAPRw1yR91zuT7F3lEaPblTW3LaRwCDC0bvaRCh9x4N9zHgY1OOQa_rzts2jf5WpXcuw4Y%3D\u0026q=Burger+King\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4Qs9oBKAB6BAhzEAI",
+			"source": "",
+			"uploaded": "",
+			"uploaded_utc": "2024-05-06T04:07:22.082Z"
+		}
+	],
+	"related_questions": [
+		{
+			"question": "What\u0027s a number 7 at McDonald\u0027s?What\u0027s a number 7 at McDonald\u0027s?What\u0027s a number 7 at McDonald\u0027s?",
+			"answer": "",
+			"title": "",
+			"displayed_url": ""
+		},
+		{
+			"question": "Why is McDonald\u0027s changing their name?Why is McDonald\u0027s changing their name?Why is McDonald\u0027s changing their name?",
+			"answer": "",
+			"title": "",
+			"displayed_url": ""
+		},
+		{
+			"question": "What is the oldest still running Mcdonalds?What is the oldest still running Mcdonalds?What is the oldest still running Mcdonalds?",
+			"answer": "",
+			"title": "",
+			"displayed_url": ""
+		},
+		{
+			"question": "Why is McDonald\u0027s now WcDonald\u0027s?Why is McDonald\u0027s now WcDonald\u0027s?Why is McDonald\u0027s now WcDonald\u0027s?",
+			"answer": "",
+			"title": "",
+			"displayed_url": ""
+		}
+	],
+	"knowledge_graph": {
+		"title": "",
+		"type": "Fast-food restaurant company",
+		"image_urls": ["https://serpstack-assets.apilayer.net/2418910010831954152.png"],
+		"description": "McDonald\u0027s Corporation is an American multinational fast food chain, founded in 1940 as a restaurant operated by Richard and Maurice McDonald, in San Bernardino, California, United States.",
+		"source": {
+			"name": "Wikipedia",
+			"url": "https://en.wikipedia.org/wiki/McDonald\u0027s"
+		},
+		"people_also_search_for": [],
+		"known_attributes": [
+			{
+				"attribute": "kc:/business/business_operation:founder",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Ray+Kroc\u0026si=ACC90nzx_D3_zUKRnpAjmO0UBLNxnt7EyN4YYdru6U3bxLI-LxARWRdbk5SkoY2sDn5Qq7yOmqYGei6qZ7sfJhsjZXBPgjMlLbS7824rpJOm69GzqVWMdoNIZiFX2T4A2td14sZOn4a1BexZLtZXHU7NZdF6VsWbGMVuiSYtXdev7uaUjEJKumiwlqTAATTebOriYTEBuSzC\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECHgQAg",
+				"name": "Founder: ",
+				"value": "Ray Kroc"
+			},
+			{
+				"attribute": "kc:/organization/organization:ceo",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Chris+Kempczinski\u0026si=ACC90nwLLwns5sISZcdzuISy7t-NHozt8Cbt6G3WNQfC9ekAgKFbjdEFCDgxLbt57EDZGosYDGiZuq1AcBhA6IhTOSZxfVSySuGQ3VDwmmTA7Z93n3K3596jAuZH9VVv5h8PyvKJSuGuSsQWviJTl3eKj2UL1ZIWuDgkjyVMnC47rN7j0G9PlHRCCLdQF7VDQ1gubTiC4onXqLRBTbwAj6a--PD6Jv_NoA%3D%3D\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECHUQAg",
+				"name": "CEO: ",
+				"value": "Chris Kempczinski (Nov 1, 2019\u2013)"
+			},
+			{
+				"attribute": "kc:/business/employer:revenue",
+				"link": "",
+				"name": "Revenue: ",
+				"value": "25.49\u00a0billion USD (2023)"
+			},
+			{
+				"attribute": "kc:/organization/organization:founded",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Des+Plaines\u0026si=ACC90nyvvWro6QmnyY1IfSdgk5wwjB1r8BGd_IWRjXqmKPQqm_yqLtI_DBi5PXGOtg_Z3qrzzEP6mcih1nN7h5A7v6OefnEJiC7a8dBR-v9LxlRubfyR6vlMr3fZ3TmVKWwz9FRpvZb1eYNt-RM7KIDKQlwGEIgINvzhxjUrv6uxSmceduzxd8W7Pkz71XGwxF0F8OlSzHlx\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECG4QAg",
+				"name": "Founded: ",
+				"value": "April 15, 1955, Des Plaines, IL"
+			},
+			{
+				"attribute": "kc:/organization/organization:headquarters",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Chicago\u0026si=ACC90nyvvWro6QmnyY1IfSdgk5wwjB1r8BGd_IWRjXqmKPQqm-46AEJ_kJbUIEvsvEEZqteiYJvXVXs2ScRNDvFFpjfeAaW3dxtpTGCgcsf5RMdi6IdzOdtjJMN3ZaFwqZOmdi7tC6r0Mh1O9bnP3HrVDB9hH02m7aA6f70dCAfTdpOFnGxDU6wVMAI5MxWBE3wTugtUDOK-\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECHYQAg",
+				"name": "Headquarters: ",
+				"value": "Chicago, IL"
+			},
+			{
+				"attribute": "kc:/organization/organization:president",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Chris+Kempczinski\u0026si=ACC90nwLLwns5sISZcdzuISy7t-NHozt8Cbt6G3WNQfC9ekAgKFbjdEFCDgxLbt57EDZGosYDGiZuq1AcBhA6IhTOSZxfVSySuGQ3VDwmmTA7Z93n3K3596jAuZH9VVv5h8PyvKJSuGuSsQWviJTl3eKj2UL1ZIWuDgkjyVMnC47rN7j0G9PlHRCCLdQF7VDQ1gubTiC4onXqLRBTbwAj6a--PD6Jv_NoA%3D%3D\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECHEQAg",
+				"name": "President: ",
+				"value": "Chris Kempczinski"
+			}
+		],
+		"website": "https://www.mcdonalds.com/us/en-us.html",
+		"profiles": [
+			{
+				"name": "Instagram",
+				"url": "https://www.instagram.com/McDonalds"
+			},
+			{
+				"name": "X (Twitter)",
+				"url": "https://twitter.com/McDonalds"
+			},
+			{
+				"name": "Facebook",
+				"url": "https://www.facebook.com/McDonaldsUS"
+			},
+			{
+				"name": "YouTube",
+				"url": "https://www.youtube.com/user/McDonaldsUS"
+			},
+			{
+				"name": "Pinterest",
+				"url": "https://www.pinterest.com/mcdonalds"
+			}
+		],
+		"founded": "April 15, 1955, Des Plaines, IL",
+		"headquarters": "Chicago, IL",
+		"founders": [
+			{
+				"name": "Ray Kroc",
+				"link": "http://www.google.com/search?safe=0\u0026sca_esv=c9c7fd42856085e2\u0026sca_upv=1\u0026gl=us\u0026hl=en\u0026q=Ray+Kroc\u0026si=ACC90nzx_D3_zUKRnpAjmO0UBLNxnt7EyN4YYdru6U3bxLI-LxARWRdbk5SkoY2sDn5Qq7yOmqYGei6qZ7sfJhsjZXBPgjMlLbS7824rpJOm69GzqVWMdoNIZiFX2T4A2td14sZOn4a1BexZLtZXHU7NZdF6VsWbGMVuiSYtXdev7uaUjEJKumiwlqTAATTebOriYTEBuSzC\u0026sa=X\u0026ved=2ahUKEwjF55alk_iFAxXlamwGHbqgAs4QmxMoAHoECHgQAg"
+			}
+		]
+	}
+}
diff --git a/backend/open_webui/apps/retrieval/web/utils.py b/backend/open_webui/apps/retrieval/web/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..2df98b33c8564d39892a25cd29ecbc916fcc8618
--- /dev/null
+++ b/backend/open_webui/apps/retrieval/web/utils.py
@@ -0,0 +1,97 @@
+import socket
+import urllib.parse
+import validators
+from typing import Union, Sequence, Iterator
+
+from langchain_community.document_loaders import (
+    WebBaseLoader,
+)
+from langchain_core.documents import Document
+
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.config import ENABLE_RAG_LOCAL_WEB_FETCH
+from open_webui.env import SRC_LOG_LEVELS
+
+import logging
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def validate_url(url: Union[str, Sequence[str]]):
+    if isinstance(url, str):
+        if isinstance(validators.url(url), validators.ValidationError):
+            raise ValueError(ERROR_MESSAGES.INVALID_URL)
+        if not ENABLE_RAG_LOCAL_WEB_FETCH:
+            # Local web fetch is disabled, filter out any URLs that resolve to private IP addresses
+            parsed_url = urllib.parse.urlparse(url)
+            # Get IPv4 and IPv6 addresses
+            ipv4_addresses, ipv6_addresses = resolve_hostname(parsed_url.hostname)
+            # Check if any of the resolved addresses are private
+            # This is technically still vulnerable to DNS rebinding attacks, as we don't control WebBaseLoader
+            for ip in ipv4_addresses:
+                if validators.ipv4(ip, private=True):
+                    raise ValueError(ERROR_MESSAGES.INVALID_URL)
+            for ip in ipv6_addresses:
+                if validators.ipv6(ip, private=True):
+                    raise ValueError(ERROR_MESSAGES.INVALID_URL)
+        return True
+    elif isinstance(url, Sequence):
+        return all(validate_url(u) for u in url)
+    else:
+        return False
+
+
+def resolve_hostname(hostname):
+    # Get address information
+    addr_info = socket.getaddrinfo(hostname, None)
+
+    # Extract IP addresses from address information
+    ipv4_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET]
+    ipv6_addresses = [info[4][0] for info in addr_info if info[0] == socket.AF_INET6]
+
+    return ipv4_addresses, ipv6_addresses
+
+
+class SafeWebBaseLoader(WebBaseLoader):
+    """WebBaseLoader with enhanced error handling for URLs."""
+
+    def lazy_load(self) -> Iterator[Document]:
+        """Lazy load text from the url(s) in web_path with error handling."""
+        for path in self.web_paths:
+            try:
+                soup = self._scrape(path, bs_kwargs=self.bs_kwargs)
+                text = soup.get_text(**self.bs_get_text_kwargs)
+
+                # Build metadata
+                metadata = {"source": path}
+                if title := soup.find("title"):
+                    metadata["title"] = title.get_text()
+                if description := soup.find("meta", attrs={"name": "description"}):
+                    metadata["description"] = description.get(
+                        "content", "No description found."
+                    )
+                if html := soup.find("html"):
+                    metadata["language"] = html.get("lang", "No language found.")
+
+                yield Document(page_content=text, metadata=metadata)
+            except Exception as e:
+                # Log the error and continue with the next URL
+                log.error(f"Error loading {path}: {e}")
+
+
+def get_web_loader(
+    url: Union[str, Sequence[str]],
+    verify_ssl: bool = True,
+    requests_per_second: int = 2,
+):
+    # Check if the URL is valid
+    if not validate_url(url):
+        raise ValueError(ERROR_MESSAGES.INVALID_URL)
+    return SafeWebBaseLoader(
+        url,
+        verify_ssl=verify_ssl,
+        requests_per_second=requests_per_second,
+        continue_on_failure=True,
+    )
diff --git a/backend/open_webui/apps/socket/main.py b/backend/open_webui/apps/socket/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c284f18d7a3512fb1a84675e78b785121294edd
--- /dev/null
+++ b/backend/open_webui/apps/socket/main.py
@@ -0,0 +1,221 @@
+# TODO: move socket to webui app
+
+import asyncio
+import socketio
+import logging
+import sys
+import time
+
+from open_webui.apps.webui.models.users import Users
+from open_webui.env import (
+    ENABLE_WEBSOCKET_SUPPORT,
+    WEBSOCKET_MANAGER,
+    WEBSOCKET_REDIS_URL,
+)
+from open_webui.utils.utils import decode_token
+from open_webui.apps.socket.utils import RedisDict
+
+from open_webui.env import (
+    GLOBAL_LOG_LEVEL,
+    SRC_LOG_LEVELS,
+)
+
+
+logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["SOCKET"])
+
+
+if WEBSOCKET_MANAGER == "redis":
+    mgr = socketio.AsyncRedisManager(WEBSOCKET_REDIS_URL)
+    sio = socketio.AsyncServer(
+        cors_allowed_origins=[],
+        async_mode="asgi",
+        transports=(
+            ["polling", "websocket"] if ENABLE_WEBSOCKET_SUPPORT else ["polling"]
+        ),
+        allow_upgrades=ENABLE_WEBSOCKET_SUPPORT,
+        always_connect=True,
+        client_manager=mgr,
+    )
+else:
+    sio = socketio.AsyncServer(
+        cors_allowed_origins=[],
+        async_mode="asgi",
+        transports=(
+            ["polling", "websocket"] if ENABLE_WEBSOCKET_SUPPORT else ["polling"]
+        ),
+        allow_upgrades=ENABLE_WEBSOCKET_SUPPORT,
+        always_connect=True,
+    )
+
+
+# Dictionary to maintain the user pool
+
+if WEBSOCKET_MANAGER == "redis":
+    SESSION_POOL = RedisDict("open-webui:session_pool", redis_url=WEBSOCKET_REDIS_URL)
+    USER_POOL = RedisDict("open-webui:user_pool", redis_url=WEBSOCKET_REDIS_URL)
+    USAGE_POOL = RedisDict("open-webui:usage_pool", redis_url=WEBSOCKET_REDIS_URL)
+else:
+    SESSION_POOL = {}
+    USER_POOL = {}
+    USAGE_POOL = {}
+
+
+# Timeout duration in seconds
+TIMEOUT_DURATION = 3
+
+
+async def periodic_usage_pool_cleanup():
+    while True:
+        now = int(time.time())
+        for model_id, connections in list(USAGE_POOL.items()):
+            # Creating a list of sids to remove if they have timed out
+            expired_sids = [
+                sid
+                for sid, details in connections.items()
+                if now - details["updated_at"] > TIMEOUT_DURATION
+            ]
+
+            for sid in expired_sids:
+                del connections[sid]
+
+            if not connections:
+                log.debug(f"Cleaning up model {model_id} from usage pool")
+                del USAGE_POOL[model_id]
+            else:
+                USAGE_POOL[model_id] = connections
+
+            # Emit updated usage information after cleaning
+            await sio.emit("usage", {"models": get_models_in_use()})
+
+        await asyncio.sleep(TIMEOUT_DURATION)
+
+
+app = socketio.ASGIApp(
+    sio,
+    socketio_path="/ws/socket.io",
+)
+
+
+def get_models_in_use():
+    # List models that are currently in use
+    models_in_use = list(USAGE_POOL.keys())
+    return models_in_use
+
+
+@sio.on("usage")
+async def usage(sid, data):
+    model_id = data["model"]
+    # Record the timestamp for the last update
+    current_time = int(time.time())
+
+    # Store the new usage data and task
+    USAGE_POOL[model_id] = {
+        **(USAGE_POOL[model_id] if model_id in USAGE_POOL else {}),
+        sid: {"updated_at": current_time},
+    }
+
+    # Broadcast the usage data to all clients
+    await sio.emit("usage", {"models": get_models_in_use()})
+
+
+@sio.event
+async def connect(sid, environ, auth):
+    user = None
+    if auth and "token" in auth:
+        data = decode_token(auth["token"])
+
+        if data is not None and "id" in data:
+            user = Users.get_user_by_id(data["id"])
+
+        if user:
+            SESSION_POOL[sid] = user.id
+            if user.id in USER_POOL:
+                USER_POOL[user.id].append(sid)
+            else:
+                USER_POOL[user.id] = [sid]
+
+            # print(f"user {user.name}({user.id}) connected with session ID {sid}")
+            await sio.emit("user-count", {"count": len(USER_POOL.items())})
+            await sio.emit("usage", {"models": get_models_in_use()})
+
+
+@sio.on("user-join")
+async def user_join(sid, data):
+    # print("user-join", sid, data)
+
+    auth = data["auth"] if "auth" in data else None
+    if not auth or "token" not in auth:
+        return
+
+    data = decode_token(auth["token"])
+    if data is None or "id" not in data:
+        return
+
+    user = Users.get_user_by_id(data["id"])
+    if not user:
+        return
+
+    SESSION_POOL[sid] = user.id
+    if user.id in USER_POOL:
+        USER_POOL[user.id].append(sid)
+    else:
+        USER_POOL[user.id] = [sid]
+
+    # print(f"user {user.name}({user.id}) connected with session ID {sid}")
+
+    await sio.emit("user-count", {"count": len(USER_POOL.items())})
+
+
+@sio.on("user-count")
+async def user_count(sid):
+    await sio.emit("user-count", {"count": len(USER_POOL.items())})
+
+
+@sio.event
+async def disconnect(sid):
+    if sid in SESSION_POOL:
+        user_id = SESSION_POOL[sid]
+        del SESSION_POOL[sid]
+
+        USER_POOL[user_id] = [_sid for _sid in USER_POOL[user_id] if _sid != sid]
+
+        if len(USER_POOL[user_id]) == 0:
+            del USER_POOL[user_id]
+
+        await sio.emit("user-count", {"count": len(USER_POOL)})
+    else:
+        pass
+        # print(f"Unknown session ID {sid} disconnected")
+
+
+def get_event_emitter(request_info):
+    async def __event_emitter__(event_data):
+        await sio.emit(
+            "chat-events",
+            {
+                "chat_id": request_info["chat_id"],
+                "message_id": request_info["message_id"],
+                "data": event_data,
+            },
+            to=request_info["session_id"],
+        )
+
+    return __event_emitter__
+
+
+def get_event_call(request_info):
+    async def __event_call__(event_data):
+        response = await sio.call(
+            "chat-events",
+            {
+                "chat_id": request_info["chat_id"],
+                "message_id": request_info["message_id"],
+                "data": event_data,
+            },
+            to=request_info["session_id"],
+        )
+        return response
+
+    return __event_call__
diff --git a/backend/open_webui/apps/socket/utils.py b/backend/open_webui/apps/socket/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..1862ff439e8f8f6489ddc31a72f90abfa877ed72
--- /dev/null
+++ b/backend/open_webui/apps/socket/utils.py
@@ -0,0 +1,59 @@
+import json
+import redis
+
+
+class RedisDict:
+    def __init__(self, name, redis_url):
+        self.name = name
+        self.redis = redis.Redis.from_url(redis_url, decode_responses=True)
+
+    def __setitem__(self, key, value):
+        serialized_value = json.dumps(value)
+        self.redis.hset(self.name, key, serialized_value)
+
+    def __getitem__(self, key):
+        value = self.redis.hget(self.name, key)
+        if value is None:
+            raise KeyError(key)
+        return json.loads(value)
+
+    def __delitem__(self, key):
+        result = self.redis.hdel(self.name, key)
+        if result == 0:
+            raise KeyError(key)
+
+    def __contains__(self, key):
+        return self.redis.hexists(self.name, key)
+
+    def __len__(self):
+        return self.redis.hlen(self.name)
+
+    def keys(self):
+        return self.redis.hkeys(self.name)
+
+    def values(self):
+        return [json.loads(v) for v in self.redis.hvals(self.name)]
+
+    def items(self):
+        return [(k, json.loads(v)) for k, v in self.redis.hgetall(self.name).items()]
+
+    def get(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    def clear(self):
+        self.redis.delete(self.name)
+
+    def update(self, other=None, **kwargs):
+        if other is not None:
+            for k, v in other.items() if hasattr(other, "items") else other:
+                self[k] = v
+        for k, v in kwargs.items():
+            self[k] = v
+
+    def setdefault(self, key, default=None):
+        if key not in self:
+            self[key] = default
+        return self[key]
diff --git a/backend/open_webui/apps/webui/internal/db.py b/backend/open_webui/apps/webui/internal/db.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcf913e6fd6e474f59e9581bc944cec2b66415d9
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/db.py
@@ -0,0 +1,114 @@
+import json
+import logging
+from contextlib import contextmanager
+from typing import Any, Optional
+
+from open_webui.apps.webui.internal.wrappers import register_connection
+from open_webui.env import (
+    OPEN_WEBUI_DIR,
+    DATABASE_URL,
+    SRC_LOG_LEVELS,
+    DATABASE_POOL_MAX_OVERFLOW,
+    DATABASE_POOL_RECYCLE,
+    DATABASE_POOL_SIZE,
+    DATABASE_POOL_TIMEOUT,
+)
+from peewee_migrate import Router
+from sqlalchemy import Dialect, create_engine, types
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import scoped_session, sessionmaker
+from sqlalchemy.pool import QueuePool, NullPool
+from sqlalchemy.sql.type_api import _T
+from typing_extensions import Self
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["DB"])
+
+
+class JSONField(types.TypeDecorator):
+    impl = types.Text
+    cache_ok = True
+
+    def process_bind_param(self, value: Optional[_T], dialect: Dialect) -> Any:
+        return json.dumps(value)
+
+    def process_result_value(self, value: Optional[_T], dialect: Dialect) -> Any:
+        if value is not None:
+            return json.loads(value)
+
+    def copy(self, **kw: Any) -> Self:
+        return JSONField(self.impl.length)
+
+    def db_value(self, value):
+        return json.dumps(value)
+
+    def python_value(self, value):
+        if value is not None:
+            return json.loads(value)
+
+
+# Workaround to handle the peewee migration
+# This is required to ensure the peewee migration is handled before the alembic migration
+def handle_peewee_migration(DATABASE_URL):
+    # db = None
+    try:
+        # Replace the postgresql:// with postgres:// to handle the peewee migration
+        db = register_connection(DATABASE_URL.replace("postgresql://", "postgres://"))
+        migrate_dir = OPEN_WEBUI_DIR / "apps" / "webui" / "internal" / "migrations"
+        router = Router(db, logger=log, migrate_dir=migrate_dir)
+        router.run()
+        db.close()
+
+    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(DATABASE_URL)
+
+
+SQLALCHEMY_DATABASE_URL = DATABASE_URL
+if "sqlite" in SQLALCHEMY_DATABASE_URL:
+    engine = create_engine(
+        SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
+    )
+else:
+    if DATABASE_POOL_SIZE > 0:
+        engine = create_engine(
+            SQLALCHEMY_DATABASE_URL,
+            pool_size=DATABASE_POOL_SIZE,
+            max_overflow=DATABASE_POOL_MAX_OVERFLOW,
+            pool_timeout=DATABASE_POOL_TIMEOUT,
+            pool_recycle=DATABASE_POOL_RECYCLE,
+            pool_pre_ping=True,
+            poolclass=QueuePool,
+        )
+    else:
+        engine = create_engine(
+            SQLALCHEMY_DATABASE_URL, pool_pre_ping=True, poolclass=NullPool
+        )
+
+
+SessionLocal = sessionmaker(
+    autocommit=False, autoflush=False, bind=engine, expire_on_commit=False
+)
+Base = declarative_base()
+Session = scoped_session(SessionLocal)
+
+
+def get_session():
+    db = SessionLocal()
+    try:
+        yield db
+    finally:
+        db.close()
+
+
+get_db = contextmanager(get_session)
diff --git a/backend/open_webui/apps/webui/internal/migrations/001_initial_schema.py b/backend/open_webui/apps/webui/internal/migrations/001_initial_schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..93f278f15b842306c6d7e3367c696272c5e9da69
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/001_initial_schema.py
@@ -0,0 +1,254 @@
+"""Peewee migrations -- 001_initial_schema.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # We perform different migrations for SQLite and other databases
+    # This is because SQLite is very loose with enforcing its schema, and trying to migrate other databases like SQLite
+    # will require per-database SQL queries.
+    # Instead, we assume that because external DB support was added at a later date, it is safe to assume a newer base
+    # schema instead of trying to migrate from an older schema.
+    if isinstance(database, pw.SqliteDatabase):
+        migrate_sqlite(migrator, database, fake=fake)
+    else:
+        migrate_external(migrator, database, fake=fake)
+
+
+def migrate_sqlite(migrator: Migrator, database: pw.Database, *, fake=False):
+    @migrator.create_model
+    class Auth(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        email = pw.CharField(max_length=255)
+        password = pw.CharField(max_length=255)
+        active = pw.BooleanField()
+
+        class Meta:
+            table_name = "auth"
+
+    @migrator.create_model
+    class Chat(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        title = pw.CharField()
+        chat = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "chat"
+
+    @migrator.create_model
+    class ChatIdTag(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        tag_name = pw.CharField(max_length=255)
+        chat_id = pw.CharField(max_length=255)
+        user_id = pw.CharField(max_length=255)
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "chatidtag"
+
+    @migrator.create_model
+    class Document(pw.Model):
+        id = pw.AutoField()
+        collection_name = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255, unique=True)
+        title = pw.CharField()
+        filename = pw.CharField()
+        content = pw.TextField(null=True)
+        user_id = pw.CharField(max_length=255)
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "document"
+
+    @migrator.create_model
+    class Modelfile(pw.Model):
+        id = pw.AutoField()
+        tag_name = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        modelfile = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "modelfile"
+
+    @migrator.create_model
+    class Prompt(pw.Model):
+        id = pw.AutoField()
+        command = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        title = pw.CharField()
+        content = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "prompt"
+
+    @migrator.create_model
+    class Tag(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255)
+        user_id = pw.CharField(max_length=255)
+        data = pw.TextField(null=True)
+
+        class Meta:
+            table_name = "tag"
+
+    @migrator.create_model
+    class User(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255)
+        email = pw.CharField(max_length=255)
+        role = pw.CharField(max_length=255)
+        profile_image_url = pw.CharField(max_length=255)
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "user"
+
+
+def migrate_external(migrator: Migrator, database: pw.Database, *, fake=False):
+    @migrator.create_model
+    class Auth(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        email = pw.CharField(max_length=255)
+        password = pw.TextField()
+        active = pw.BooleanField()
+
+        class Meta:
+            table_name = "auth"
+
+    @migrator.create_model
+    class Chat(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        title = pw.TextField()
+        chat = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "chat"
+
+    @migrator.create_model
+    class ChatIdTag(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        tag_name = pw.CharField(max_length=255)
+        chat_id = pw.CharField(max_length=255)
+        user_id = pw.CharField(max_length=255)
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "chatidtag"
+
+    @migrator.create_model
+    class Document(pw.Model):
+        id = pw.AutoField()
+        collection_name = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255, unique=True)
+        title = pw.TextField()
+        filename = pw.TextField()
+        content = pw.TextField(null=True)
+        user_id = pw.CharField(max_length=255)
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "document"
+
+    @migrator.create_model
+    class Modelfile(pw.Model):
+        id = pw.AutoField()
+        tag_name = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        modelfile = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "modelfile"
+
+    @migrator.create_model
+    class Prompt(pw.Model):
+        id = pw.AutoField()
+        command = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        title = pw.TextField()
+        content = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "prompt"
+
+    @migrator.create_model
+    class Tag(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255)
+        user_id = pw.CharField(max_length=255)
+        data = pw.TextField(null=True)
+
+        class Meta:
+            table_name = "tag"
+
+    @migrator.create_model
+    class User(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        name = pw.CharField(max_length=255)
+        email = pw.CharField(max_length=255)
+        role = pw.CharField(max_length=255)
+        profile_image_url = pw.TextField()
+        timestamp = pw.BigIntegerField()
+
+        class Meta:
+            table_name = "user"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("user")
+
+    migrator.remove_model("tag")
+
+    migrator.remove_model("prompt")
+
+    migrator.remove_model("modelfile")
+
+    migrator.remove_model("document")
+
+    migrator.remove_model("chatidtag")
+
+    migrator.remove_model("chat")
+
+    migrator.remove_model("auth")
diff --git a/backend/open_webui/apps/webui/internal/migrations/002_add_local_sharing.py b/backend/open_webui/apps/webui/internal/migrations/002_add_local_sharing.py
new file mode 100644
index 0000000000000000000000000000000000000000..e93501aeec522fc102a3ce26112b2edd0e518455
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/002_add_local_sharing.py
@@ -0,0 +1,48 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields(
+        "chat", share_id=pw.CharField(max_length=255, null=True, unique=True)
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("chat", "share_id")
diff --git a/backend/open_webui/apps/webui/internal/migrations/003_add_auth_api_key.py b/backend/open_webui/apps/webui/internal/migrations/003_add_auth_api_key.py
new file mode 100644
index 0000000000000000000000000000000000000000..07144f3aca6688a960f7036bcd2c20470da0881c
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/003_add_auth_api_key.py
@@ -0,0 +1,48 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields(
+        "user", api_key=pw.CharField(max_length=255, null=True, unique=True)
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("user", "api_key")
diff --git a/backend/open_webui/apps/webui/internal/migrations/004_add_archived.py b/backend/open_webui/apps/webui/internal/migrations/004_add_archived.py
new file mode 100644
index 0000000000000000000000000000000000000000..d01c06b4e665a61c709a8c662387e0c0755efa9a
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/004_add_archived.py
@@ -0,0 +1,46 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields("chat", archived=pw.BooleanField(default=False))
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("chat", "archived")
diff --git a/backend/open_webui/apps/webui/internal/migrations/005_add_updated_at.py b/backend/open_webui/apps/webui/internal/migrations/005_add_updated_at.py
new file mode 100644
index 0000000000000000000000000000000000000000..950866ef024e80fa1b1af6e296d89feb50a5f5f9
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/005_add_updated_at.py
@@ -0,0 +1,130 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    if isinstance(database, pw.SqliteDatabase):
+        migrate_sqlite(migrator, database, fake=fake)
+    else:
+        migrate_external(migrator, database, fake=fake)
+
+
+def migrate_sqlite(migrator: Migrator, database: pw.Database, *, fake=False):
+    # Adding fields created_at and updated_at to the 'chat' table
+    migrator.add_fields(
+        "chat",
+        created_at=pw.DateTimeField(null=True),  # Allow null for transition
+        updated_at=pw.DateTimeField(null=True),  # Allow null for transition
+    )
+
+    # Populate the new fields from an existing 'timestamp' field
+    migrator.sql(
+        "UPDATE chat SET created_at = timestamp, updated_at = timestamp WHERE timestamp IS NOT NULL"
+    )
+
+    # Now that the data has been copied, remove the original 'timestamp' field
+    migrator.remove_fields("chat", "timestamp")
+
+    # Update the fields to be not null now that they are populated
+    migrator.change_fields(
+        "chat",
+        created_at=pw.DateTimeField(null=False),
+        updated_at=pw.DateTimeField(null=False),
+    )
+
+
+def migrate_external(migrator: Migrator, database: pw.Database, *, fake=False):
+    # Adding fields created_at and updated_at to the 'chat' table
+    migrator.add_fields(
+        "chat",
+        created_at=pw.BigIntegerField(null=True),  # Allow null for transition
+        updated_at=pw.BigIntegerField(null=True),  # Allow null for transition
+    )
+
+    # Populate the new fields from an existing 'timestamp' field
+    migrator.sql(
+        "UPDATE chat SET created_at = timestamp, updated_at = timestamp WHERE timestamp IS NOT NULL"
+    )
+
+    # Now that the data has been copied, remove the original 'timestamp' field
+    migrator.remove_fields("chat", "timestamp")
+
+    # Update the fields to be not null now that they are populated
+    migrator.change_fields(
+        "chat",
+        created_at=pw.BigIntegerField(null=False),
+        updated_at=pw.BigIntegerField(null=False),
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    if isinstance(database, pw.SqliteDatabase):
+        rollback_sqlite(migrator, database, fake=fake)
+    else:
+        rollback_external(migrator, database, fake=fake)
+
+
+def rollback_sqlite(migrator: Migrator, database: pw.Database, *, fake=False):
+    # Recreate the timestamp field initially allowing null values for safe transition
+    migrator.add_fields("chat", timestamp=pw.DateTimeField(null=True))
+
+    # Copy the earliest created_at date back into the new timestamp field
+    # This assumes created_at was originally a copy of timestamp
+    migrator.sql("UPDATE chat SET timestamp = created_at")
+
+    # Remove the created_at and updated_at fields
+    migrator.remove_fields("chat", "created_at", "updated_at")
+
+    # Finally, alter the timestamp field to not allow nulls if that was the original setting
+    migrator.change_fields("chat", timestamp=pw.DateTimeField(null=False))
+
+
+def rollback_external(migrator: Migrator, database: pw.Database, *, fake=False):
+    # Recreate the timestamp field initially allowing null values for safe transition
+    migrator.add_fields("chat", timestamp=pw.BigIntegerField(null=True))
+
+    # Copy the earliest created_at date back into the new timestamp field
+    # This assumes created_at was originally a copy of timestamp
+    migrator.sql("UPDATE chat SET timestamp = created_at")
+
+    # Remove the created_at and updated_at fields
+    migrator.remove_fields("chat", "created_at", "updated_at")
+
+    # Finally, alter the timestamp field to not allow nulls if that was the original setting
+    migrator.change_fields("chat", timestamp=pw.BigIntegerField(null=False))
diff --git a/backend/open_webui/apps/webui/internal/migrations/006_migrate_timestamps_and_charfields.py b/backend/open_webui/apps/webui/internal/migrations/006_migrate_timestamps_and_charfields.py
new file mode 100644
index 0000000000000000000000000000000000000000..caca14d323e1fad148e7e14bae207c7e1b8896a9
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/006_migrate_timestamps_and_charfields.py
@@ -0,0 +1,130 @@
+"""Peewee migrations -- 006_migrate_timestamps_and_charfields.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # Alter the tables with timestamps
+    migrator.change_fields(
+        "chatidtag",
+        timestamp=pw.BigIntegerField(),
+    )
+    migrator.change_fields(
+        "document",
+        timestamp=pw.BigIntegerField(),
+    )
+    migrator.change_fields(
+        "modelfile",
+        timestamp=pw.BigIntegerField(),
+    )
+    migrator.change_fields(
+        "prompt",
+        timestamp=pw.BigIntegerField(),
+    )
+    migrator.change_fields(
+        "user",
+        timestamp=pw.BigIntegerField(),
+    )
+    # Alter the tables with varchar to text where necessary
+    migrator.change_fields(
+        "auth",
+        password=pw.TextField(),
+    )
+    migrator.change_fields(
+        "chat",
+        title=pw.TextField(),
+    )
+    migrator.change_fields(
+        "document",
+        title=pw.TextField(),
+        filename=pw.TextField(),
+    )
+    migrator.change_fields(
+        "prompt",
+        title=pw.TextField(),
+    )
+    migrator.change_fields(
+        "user",
+        profile_image_url=pw.TextField(),
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    if isinstance(database, pw.SqliteDatabase):
+        # Alter the tables with timestamps
+        migrator.change_fields(
+            "chatidtag",
+            timestamp=pw.DateField(),
+        )
+        migrator.change_fields(
+            "document",
+            timestamp=pw.DateField(),
+        )
+        migrator.change_fields(
+            "modelfile",
+            timestamp=pw.DateField(),
+        )
+        migrator.change_fields(
+            "prompt",
+            timestamp=pw.DateField(),
+        )
+        migrator.change_fields(
+            "user",
+            timestamp=pw.DateField(),
+        )
+    migrator.change_fields(
+        "auth",
+        password=pw.CharField(max_length=255),
+    )
+    migrator.change_fields(
+        "chat",
+        title=pw.CharField(),
+    )
+    migrator.change_fields(
+        "document",
+        title=pw.CharField(),
+        filename=pw.CharField(),
+    )
+    migrator.change_fields(
+        "prompt",
+        title=pw.CharField(),
+    )
+    migrator.change_fields(
+        "user",
+        profile_image_url=pw.CharField(),
+    )
diff --git a/backend/open_webui/apps/webui/internal/migrations/007_add_user_last_active_at.py b/backend/open_webui/apps/webui/internal/migrations/007_add_user_last_active_at.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd176ba73e51b15f74f45b839d0eb8e72fc63ecf
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/007_add_user_last_active_at.py
@@ -0,0 +1,79 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # Adding fields created_at and updated_at to the 'user' table
+    migrator.add_fields(
+        "user",
+        created_at=pw.BigIntegerField(null=True),  # Allow null for transition
+        updated_at=pw.BigIntegerField(null=True),  # Allow null for transition
+        last_active_at=pw.BigIntegerField(null=True),  # Allow null for transition
+    )
+
+    # Populate the new fields from an existing 'timestamp' field
+    migrator.sql(
+        'UPDATE "user" SET created_at = timestamp, updated_at = timestamp, last_active_at = timestamp WHERE timestamp IS NOT NULL'
+    )
+
+    # Now that the data has been copied, remove the original 'timestamp' field
+    migrator.remove_fields("user", "timestamp")
+
+    # Update the fields to be not null now that they are populated
+    migrator.change_fields(
+        "user",
+        created_at=pw.BigIntegerField(null=False),
+        updated_at=pw.BigIntegerField(null=False),
+        last_active_at=pw.BigIntegerField(null=False),
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    # Recreate the timestamp field initially allowing null values for safe transition
+    migrator.add_fields("user", timestamp=pw.BigIntegerField(null=True))
+
+    # Copy the earliest created_at date back into the new timestamp field
+    # This assumes created_at was originally a copy of timestamp
+    migrator.sql('UPDATE "user" SET timestamp = created_at')
+
+    # Remove the created_at and updated_at fields
+    migrator.remove_fields("user", "created_at", "updated_at", "last_active_at")
+
+    # Finally, alter the timestamp field to not allow nulls if that was the original setting
+    migrator.change_fields("user", timestamp=pw.BigIntegerField(null=False))
diff --git a/backend/open_webui/apps/webui/internal/migrations/008_add_memory.py b/backend/open_webui/apps/webui/internal/migrations/008_add_memory.py
new file mode 100644
index 0000000000000000000000000000000000000000..9307aa4d5c933cf0ee98c0932ab0e48bc4cecbc6
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/008_add_memory.py
@@ -0,0 +1,53 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    @migrator.create_model
+    class Memory(pw.Model):
+        id = pw.CharField(max_length=255, unique=True)
+        user_id = pw.CharField(max_length=255)
+        content = pw.TextField(null=False)
+        updated_at = pw.BigIntegerField(null=False)
+        created_at = pw.BigIntegerField(null=False)
+
+        class Meta:
+            table_name = "memory"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("memory")
diff --git a/backend/open_webui/apps/webui/internal/migrations/009_add_models.py b/backend/open_webui/apps/webui/internal/migrations/009_add_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..548ec7cdcabbc620f8c2c79b65709a6b08d9a11c
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/009_add_models.py
@@ -0,0 +1,61 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    @migrator.create_model
+    class Model(pw.Model):
+        id = pw.TextField(unique=True)
+        user_id = pw.TextField()
+        base_model_id = pw.TextField(null=True)
+
+        name = pw.TextField()
+
+        meta = pw.TextField()
+        params = pw.TextField()
+
+        created_at = pw.BigIntegerField(null=False)
+        updated_at = pw.BigIntegerField(null=False)
+
+        class Meta:
+            table_name = "model"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("model")
diff --git a/backend/open_webui/apps/webui/internal/migrations/010_migrate_modelfiles_to_models.py b/backend/open_webui/apps/webui/internal/migrations/010_migrate_modelfiles_to_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..322ddd44ec9e810cdb351b903af5cbb8831e72e7
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/010_migrate_modelfiles_to_models.py
@@ -0,0 +1,130 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+import json
+
+from open_webui.utils.misc import parse_ollama_modelfile
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # Fetch data from 'modelfile' table and insert into 'model' table
+    migrate_modelfile_to_model(migrator, database)
+    # Drop the 'modelfile' table
+    migrator.remove_model("modelfile")
+
+
+def migrate_modelfile_to_model(migrator: Migrator, database: pw.Database):
+    ModelFile = migrator.orm["modelfile"]
+    Model = migrator.orm["model"]
+
+    modelfiles = ModelFile.select()
+
+    for modelfile in modelfiles:
+        # Extract and transform data in Python
+
+        modelfile.modelfile = json.loads(modelfile.modelfile)
+        meta = json.dumps(
+            {
+                "description": modelfile.modelfile.get("desc"),
+                "profile_image_url": modelfile.modelfile.get("imageUrl"),
+                "ollama": {"modelfile": modelfile.modelfile.get("content")},
+                "suggestion_prompts": modelfile.modelfile.get("suggestionPrompts"),
+                "categories": modelfile.modelfile.get("categories"),
+                "user": {**modelfile.modelfile.get("user", {}), "community": True},
+            }
+        )
+
+        info = parse_ollama_modelfile(modelfile.modelfile.get("content"))
+
+        # Insert the processed data into the 'model' table
+        Model.create(
+            id=f"ollama-{modelfile.tag_name}",
+            user_id=modelfile.user_id,
+            base_model_id=info.get("base_model_id"),
+            name=modelfile.modelfile.get("title"),
+            meta=meta,
+            params=json.dumps(info.get("params", {})),
+            created_at=modelfile.timestamp,
+            updated_at=modelfile.timestamp,
+        )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    recreate_modelfile_table(migrator, database)
+    move_data_back_to_modelfile(migrator, database)
+    migrator.remove_model("model")
+
+
+def recreate_modelfile_table(migrator: Migrator, database: pw.Database):
+    query = """
+    CREATE TABLE IF NOT EXISTS modelfile (
+        user_id TEXT,
+        tag_name TEXT,
+        modelfile JSON,
+        timestamp BIGINT
+    )
+    """
+    migrator.sql(query)
+
+
+def move_data_back_to_modelfile(migrator: Migrator, database: pw.Database):
+    Model = migrator.orm["model"]
+    Modelfile = migrator.orm["modelfile"]
+
+    models = Model.select()
+
+    for model in models:
+        # Extract and transform data in Python
+        meta = json.loads(model.meta)
+
+        modelfile_data = {
+            "title": model.name,
+            "desc": meta.get("description"),
+            "imageUrl": meta.get("profile_image_url"),
+            "content": meta.get("ollama", {}).get("modelfile"),
+            "suggestionPrompts": meta.get("suggestion_prompts"),
+            "categories": meta.get("categories"),
+            "user": {k: v for k, v in meta.get("user", {}).items() if k != "community"},
+        }
+
+        # Insert the processed data back into the 'modelfile' table
+        Modelfile.create(
+            user_id=model.user_id,
+            tag_name=model.id,
+            modelfile=modelfile_data,
+            timestamp=model.created_at,
+        )
diff --git a/backend/open_webui/apps/webui/internal/migrations/011_add_user_settings.py b/backend/open_webui/apps/webui/internal/migrations/011_add_user_settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..a1620dcadae41891922e324a9a6b152b81ea0ec4
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/011_add_user_settings.py
@@ -0,0 +1,48 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # Adding fields settings to the 'user' table
+    migrator.add_fields("user", settings=pw.TextField(null=True))
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    # Remove the settings field
+    migrator.remove_fields("user", "settings")
diff --git a/backend/open_webui/apps/webui/internal/migrations/012_add_tools.py b/backend/open_webui/apps/webui/internal/migrations/012_add_tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a68eea552e4fb9b5d5f64f55e7b7966f342435b
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/012_add_tools.py
@@ -0,0 +1,61 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    @migrator.create_model
+    class Tool(pw.Model):
+        id = pw.TextField(unique=True)
+        user_id = pw.TextField()
+
+        name = pw.TextField()
+        content = pw.TextField()
+        specs = pw.TextField()
+
+        meta = pw.TextField()
+
+        created_at = pw.BigIntegerField(null=False)
+        updated_at = pw.BigIntegerField(null=False)
+
+        class Meta:
+            table_name = "tool"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("tool")
diff --git a/backend/open_webui/apps/webui/internal/migrations/013_add_user_info.py b/backend/open_webui/apps/webui/internal/migrations/013_add_user_info.py
new file mode 100644
index 0000000000000000000000000000000000000000..0f68669cca869fcd5537d1a0600ed45155056437
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/013_add_user_info.py
@@ -0,0 +1,48 @@
+"""Peewee migrations -- 002_add_local_sharing.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    # Adding fields info to the 'user' table
+    migrator.add_fields("user", info=pw.TextField(null=True))
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    # Remove the settings field
+    migrator.remove_fields("user", "info")
diff --git a/backend/open_webui/apps/webui/internal/migrations/014_add_files.py b/backend/open_webui/apps/webui/internal/migrations/014_add_files.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e1acf0ad8b9510b8d090c6458f292a124dd73cd
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/014_add_files.py
@@ -0,0 +1,55 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    @migrator.create_model
+    class File(pw.Model):
+        id = pw.TextField(unique=True)
+        user_id = pw.TextField()
+        filename = pw.TextField()
+        meta = pw.TextField()
+        created_at = pw.BigIntegerField(null=False)
+
+        class Meta:
+            table_name = "file"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("file")
diff --git a/backend/open_webui/apps/webui/internal/migrations/015_add_functions.py b/backend/open_webui/apps/webui/internal/migrations/015_add_functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..8316a9333bad45eccf7d6708016ef5c45b208360
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/015_add_functions.py
@@ -0,0 +1,61 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    @migrator.create_model
+    class Function(pw.Model):
+        id = pw.TextField(unique=True)
+        user_id = pw.TextField()
+
+        name = pw.TextField()
+        type = pw.TextField()
+
+        content = pw.TextField()
+        meta = pw.TextField()
+
+        created_at = pw.BigIntegerField(null=False)
+        updated_at = pw.BigIntegerField(null=False)
+
+        class Meta:
+            table_name = "function"
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_model("function")
diff --git a/backend/open_webui/apps/webui/internal/migrations/016_add_valves_and_is_active.py b/backend/open_webui/apps/webui/internal/migrations/016_add_valves_and_is_active.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3af521b7e841ee01b226ad867ae509e42e5eb4f
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/016_add_valves_and_is_active.py
@@ -0,0 +1,50 @@
+"""Peewee migrations -- 009_add_models.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields("tool", valves=pw.TextField(null=True))
+    migrator.add_fields("function", valves=pw.TextField(null=True))
+    migrator.add_fields("function", is_active=pw.BooleanField(default=False))
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("tool", "valves")
+    migrator.remove_fields("function", "valves")
+    migrator.remove_fields("function", "is_active")
diff --git a/backend/open_webui/apps/webui/internal/migrations/017_add_user_oauth_sub.py b/backend/open_webui/apps/webui/internal/migrations/017_add_user_oauth_sub.py
new file mode 100644
index 0000000000000000000000000000000000000000..eaa3fa5fe54bd37691593ba6fc2840b8653d534d
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/017_add_user_oauth_sub.py
@@ -0,0 +1,45 @@
+"""Peewee migrations -- 017_add_user_oauth_sub.py.
+Some examples (model - class or model name)::
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields(
+        "user",
+        oauth_sub=pw.TextField(null=True, unique=True),
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("user", "oauth_sub")
diff --git a/backend/open_webui/apps/webui/internal/migrations/018_add_function_is_global.py b/backend/open_webui/apps/webui/internal/migrations/018_add_function_is_global.py
new file mode 100644
index 0000000000000000000000000000000000000000..04cdab705986a36227a7f73e800d6739f00033ce
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/migrations/018_add_function_is_global.py
@@ -0,0 +1,49 @@
+"""Peewee migrations -- 017_add_user_oauth_sub.py.
+
+Some examples (model - class or model name)::
+
+    > Model = migrator.orm['table_name']            # Return model in current state by name
+    > Model = migrator.ModelClass                   # Return model in current state by name
+
+    > migrator.sql(sql)                             # Run custom SQL
+    > migrator.run(func, *args, **kwargs)           # Run python function with the given args
+    > migrator.create_model(Model)                  # Create a model (could be used as decorator)
+    > migrator.remove_model(model, cascade=True)    # Remove a model
+    > migrator.add_fields(model, **fields)          # Add fields to a model
+    > migrator.change_fields(model, **fields)       # Change fields
+    > migrator.remove_fields(model, *field_names, cascade=True)
+    > migrator.rename_field(model, old_field_name, new_field_name)
+    > migrator.rename_table(model, new_table_name)
+    > migrator.add_index(model, *col_names, unique=False)
+    > migrator.add_not_null(model, *field_names)
+    > migrator.add_default(model, field_name, default)
+    > migrator.add_constraint(model, name, sql)
+    > migrator.drop_index(model, *col_names)
+    > migrator.drop_not_null(model, *field_names)
+    > migrator.drop_constraints(model, *constraints)
+
+"""
+
+from contextlib import suppress
+
+import peewee as pw
+from peewee_migrate import Migrator
+
+
+with suppress(ImportError):
+    import playhouse.postgres_ext as pw_pext
+
+
+def migrate(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your migrations here."""
+
+    migrator.add_fields(
+        "function",
+        is_global=pw.BooleanField(default=False),
+    )
+
+
+def rollback(migrator: Migrator, database: pw.Database, *, fake=False):
+    """Write your rollback migrations here."""
+
+    migrator.remove_fields("function", "is_global")
diff --git a/backend/open_webui/apps/webui/internal/wrappers.py b/backend/open_webui/apps/webui/internal/wrappers.py
new file mode 100644
index 0000000000000000000000000000000000000000..ccc62b9a5741c52f5a84ade064c6e935fb66dd8a
--- /dev/null
+++ b/backend/open_webui/apps/webui/internal/wrappers.py
@@ -0,0 +1,66 @@
+import logging
+from contextvars import ContextVar
+
+from open_webui.env import SRC_LOG_LEVELS
+from peewee import *
+from peewee import InterfaceError as PeeWeeInterfaceError
+from peewee import PostgresqlDatabase
+from playhouse.db_url import connect, parse
+from playhouse.shortcuts import ReconnectMixin
+
+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, unquote_password=True)
+    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, unquote_password=True)
+
+        # Use our custom database class that supports reconnection
+        db = ReconnectingPostgresqlDatabase(**connection)
+        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
diff --git a/backend/open_webui/apps/webui/main.py b/backend/open_webui/apps/webui/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..054c6280e7c4f73797cc43552c3797b3841d4f7b
--- /dev/null
+++ b/backend/open_webui/apps/webui/main.py
@@ -0,0 +1,506 @@
+import inspect
+import json
+import logging
+import time
+from typing import AsyncGenerator, Generator, Iterator
+
+from open_webui.apps.socket.main import get_event_call, get_event_emitter
+from open_webui.apps.webui.models.functions import Functions
+from open_webui.apps.webui.models.models import Models
+from open_webui.apps.webui.routers import (
+    auths,
+    chats,
+    folders,
+    configs,
+    groups,
+    files,
+    functions,
+    memories,
+    models,
+    knowledge,
+    prompts,
+    evaluations,
+    tools,
+    users,
+    utils,
+)
+from open_webui.apps.webui.utils import load_function_module_by_id
+from open_webui.config import (
+    ADMIN_EMAIL,
+    CORS_ALLOW_ORIGIN,
+    DEFAULT_MODELS,
+    DEFAULT_PROMPT_SUGGESTIONS,
+    DEFAULT_USER_ROLE,
+    MODEL_ORDER_LIST,
+    ENABLE_COMMUNITY_SHARING,
+    ENABLE_LOGIN_FORM,
+    ENABLE_MESSAGE_RATING,
+    ENABLE_SIGNUP,
+    ENABLE_API_KEY,
+    ENABLE_EVALUATION_ARENA_MODELS,
+    EVALUATION_ARENA_MODELS,
+    DEFAULT_ARENA_MODEL,
+    JWT_EXPIRES_IN,
+    ENABLE_OAUTH_ROLE_MANAGEMENT,
+    OAUTH_ROLES_CLAIM,
+    OAUTH_EMAIL_CLAIM,
+    OAUTH_PICTURE_CLAIM,
+    OAUTH_USERNAME_CLAIM,
+    OAUTH_ALLOWED_ROLES,
+    OAUTH_ADMIN_ROLES,
+    SHOW_ADMIN_DETAILS,
+    USER_PERMISSIONS,
+    WEBHOOK_URL,
+    WEBUI_AUTH,
+    WEBUI_BANNERS,
+    ENABLE_LDAP,
+    LDAP_SERVER_LABEL,
+    LDAP_SERVER_HOST,
+    LDAP_SERVER_PORT,
+    LDAP_ATTRIBUTE_FOR_USERNAME,
+    LDAP_SEARCH_FILTERS,
+    LDAP_SEARCH_BASE,
+    LDAP_APP_DN,
+    LDAP_APP_PASSWORD,
+    LDAP_USE_TLS,
+    LDAP_CA_CERT_FILE,
+    LDAP_CIPHERS,
+    AppConfig,
+)
+from open_webui.env import (
+    ENV,
+    SRC_LOG_LEVELS,
+    WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
+    WEBUI_AUTH_TRUSTED_NAME_HEADER,
+)
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import StreamingResponse
+from pydantic import BaseModel
+from open_webui.utils.misc import (
+    openai_chat_chunk_message_template,
+    openai_chat_completion_message_template,
+)
+from open_webui.utils.payload import (
+    apply_model_params_to_body_openai,
+    apply_model_system_prompt_to_body,
+)
+
+
+from open_webui.utils.tools import get_tools
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+)
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MAIN"])
+
+app.state.config = AppConfig()
+
+app.state.config.ENABLE_SIGNUP = ENABLE_SIGNUP
+app.state.config.ENABLE_LOGIN_FORM = ENABLE_LOGIN_FORM
+app.state.config.ENABLE_API_KEY = ENABLE_API_KEY
+
+app.state.config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
+app.state.AUTH_TRUSTED_EMAIL_HEADER = WEBUI_AUTH_TRUSTED_EMAIL_HEADER
+app.state.AUTH_TRUSTED_NAME_HEADER = WEBUI_AUTH_TRUSTED_NAME_HEADER
+
+
+app.state.config.SHOW_ADMIN_DETAILS = SHOW_ADMIN_DETAILS
+app.state.config.ADMIN_EMAIL = ADMIN_EMAIL
+
+
+app.state.config.DEFAULT_MODELS = DEFAULT_MODELS
+app.state.config.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
+app.state.config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
+
+
+app.state.config.USER_PERMISSIONS = USER_PERMISSIONS
+app.state.config.WEBHOOK_URL = WEBHOOK_URL
+app.state.config.BANNERS = WEBUI_BANNERS
+app.state.config.MODEL_ORDER_LIST = MODEL_ORDER_LIST
+
+app.state.config.ENABLE_COMMUNITY_SHARING = ENABLE_COMMUNITY_SHARING
+app.state.config.ENABLE_MESSAGE_RATING = ENABLE_MESSAGE_RATING
+
+app.state.config.ENABLE_EVALUATION_ARENA_MODELS = ENABLE_EVALUATION_ARENA_MODELS
+app.state.config.EVALUATION_ARENA_MODELS = EVALUATION_ARENA_MODELS
+
+app.state.config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
+app.state.config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
+app.state.config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
+
+app.state.config.ENABLE_OAUTH_ROLE_MANAGEMENT = ENABLE_OAUTH_ROLE_MANAGEMENT
+app.state.config.OAUTH_ROLES_CLAIM = OAUTH_ROLES_CLAIM
+app.state.config.OAUTH_ALLOWED_ROLES = OAUTH_ALLOWED_ROLES
+app.state.config.OAUTH_ADMIN_ROLES = OAUTH_ADMIN_ROLES
+
+app.state.config.ENABLE_LDAP = ENABLE_LDAP
+app.state.config.LDAP_SERVER_LABEL = LDAP_SERVER_LABEL
+app.state.config.LDAP_SERVER_HOST = LDAP_SERVER_HOST
+app.state.config.LDAP_SERVER_PORT = LDAP_SERVER_PORT
+app.state.config.LDAP_ATTRIBUTE_FOR_USERNAME = LDAP_ATTRIBUTE_FOR_USERNAME
+app.state.config.LDAP_APP_DN = LDAP_APP_DN
+app.state.config.LDAP_APP_PASSWORD = LDAP_APP_PASSWORD
+app.state.config.LDAP_SEARCH_BASE = LDAP_SEARCH_BASE
+app.state.config.LDAP_SEARCH_FILTERS = LDAP_SEARCH_FILTERS
+app.state.config.LDAP_USE_TLS = LDAP_USE_TLS
+app.state.config.LDAP_CA_CERT_FILE = LDAP_CA_CERT_FILE
+app.state.config.LDAP_CIPHERS = LDAP_CIPHERS
+
+app.state.TOOLS = {}
+app.state.FUNCTIONS = {}
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+
+app.include_router(configs.router, prefix="/configs", tags=["configs"])
+
+app.include_router(auths.router, prefix="/auths", tags=["auths"])
+app.include_router(users.router, prefix="/users", tags=["users"])
+
+app.include_router(chats.router, prefix="/chats", tags=["chats"])
+
+app.include_router(models.router, prefix="/models", tags=["models"])
+app.include_router(knowledge.router, prefix="/knowledge", tags=["knowledge"])
+app.include_router(prompts.router, prefix="/prompts", tags=["prompts"])
+app.include_router(tools.router, prefix="/tools", tags=["tools"])
+
+app.include_router(memories.router, prefix="/memories", tags=["memories"])
+app.include_router(folders.router, prefix="/folders", tags=["folders"])
+
+app.include_router(groups.router, prefix="/groups", tags=["groups"])
+app.include_router(files.router, prefix="/files", tags=["files"])
+app.include_router(functions.router, prefix="/functions", tags=["functions"])
+app.include_router(evaluations.router, prefix="/evaluations", tags=["evaluations"])
+
+
+app.include_router(utils.router, prefix="/utils", tags=["utils"])
+
+
+@app.get("/")
+async def get_status():
+    return {
+        "status": True,
+        "auth": WEBUI_AUTH,
+        "default_models": app.state.config.DEFAULT_MODELS,
+        "default_prompt_suggestions": app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
+    }
+
+
+async def get_all_models():
+    models = []
+    pipe_models = await get_pipe_models()
+    models = models + pipe_models
+
+    if app.state.config.ENABLE_EVALUATION_ARENA_MODELS:
+        arena_models = []
+        if len(app.state.config.EVALUATION_ARENA_MODELS) > 0:
+            arena_models = [
+                {
+                    "id": model["id"],
+                    "name": model["name"],
+                    "info": {
+                        "meta": model["meta"],
+                    },
+                    "object": "model",
+                    "created": int(time.time()),
+                    "owned_by": "arena",
+                    "arena": True,
+                }
+                for model in app.state.config.EVALUATION_ARENA_MODELS
+            ]
+        else:
+            # Add default arena model
+            arena_models = [
+                {
+                    "id": DEFAULT_ARENA_MODEL["id"],
+                    "name": DEFAULT_ARENA_MODEL["name"],
+                    "info": {
+                        "meta": DEFAULT_ARENA_MODEL["meta"],
+                    },
+                    "object": "model",
+                    "created": int(time.time()),
+                    "owned_by": "arena",
+                    "arena": True,
+                }
+            ]
+        models = models + arena_models
+    return models
+
+
+def get_function_module(pipe_id: str):
+    # Check if function is already loaded
+    if pipe_id not in app.state.FUNCTIONS:
+        function_module, _, _ = load_function_module_by_id(pipe_id)
+        app.state.FUNCTIONS[pipe_id] = function_module
+    else:
+        function_module = app.state.FUNCTIONS[pipe_id]
+
+    if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
+        valves = Functions.get_function_valves_by_id(pipe_id)
+        function_module.valves = function_module.Valves(**(valves if valves else {}))
+    return function_module
+
+
+async def get_pipe_models():
+    pipes = Functions.get_functions_by_type("pipe", active_only=True)
+    pipe_models = []
+
+    for pipe in pipes:
+        function_module = get_function_module(pipe.id)
+
+        # Check if function is a manifold
+        if hasattr(function_module, "pipes"):
+            sub_pipes = []
+
+            # Check if pipes is a function or a list
+
+            try:
+                if callable(function_module.pipes):
+                    sub_pipes = function_module.pipes()
+                else:
+                    sub_pipes = function_module.pipes
+            except Exception as e:
+                log.exception(e)
+                sub_pipes = []
+
+            log.debug(
+                f"get_pipe_models: function '{pipe.id}' is a manifold of {sub_pipes}"
+            )
+
+            for p in sub_pipes:
+                sub_pipe_id = f'{pipe.id}.{p["id"]}'
+                sub_pipe_name = p["name"]
+
+                if hasattr(function_module, "name"):
+                    sub_pipe_name = f"{function_module.name}{sub_pipe_name}"
+
+                pipe_flag = {"type": pipe.type}
+
+                pipe_models.append(
+                    {
+                        "id": sub_pipe_id,
+                        "name": sub_pipe_name,
+                        "object": "model",
+                        "created": pipe.created_at,
+                        "owned_by": "openai",
+                        "pipe": pipe_flag,
+                    }
+                )
+        else:
+            pipe_flag = {"type": "pipe"}
+
+            log.debug(
+                f"get_pipe_models: function '{pipe.id}' is a single pipe {{ 'id': {pipe.id}, 'name': {pipe.name} }}"
+            )
+
+            pipe_models.append(
+                {
+                    "id": pipe.id,
+                    "name": pipe.name,
+                    "object": "model",
+                    "created": pipe.created_at,
+                    "owned_by": "openai",
+                    "pipe": pipe_flag,
+                }
+            )
+
+    return pipe_models
+
+
+async def execute_pipe(pipe, params):
+    if inspect.iscoroutinefunction(pipe):
+        return await pipe(**params)
+    else:
+        return pipe(**params)
+
+
+async def get_message_content(res: str | Generator | AsyncGenerator) -> str:
+    if isinstance(res, str):
+        return res
+    if isinstance(res, Generator):
+        return "".join(map(str, res))
+    if isinstance(res, AsyncGenerator):
+        return "".join([str(stream) async for stream in res])
+
+
+def process_line(form_data: dict, line):
+    if isinstance(line, BaseModel):
+        line = line.model_dump_json()
+        line = f"data: {line}"
+    if isinstance(line, dict):
+        line = f"data: {json.dumps(line)}"
+
+    try:
+        line = line.decode("utf-8")
+    except Exception:
+        pass
+
+    if line.startswith("data:"):
+        return f"{line}\n\n"
+    else:
+        line = openai_chat_chunk_message_template(form_data["model"], line)
+        return f"data: {json.dumps(line)}\n\n"
+
+
+def get_pipe_id(form_data: dict) -> str:
+    pipe_id = form_data["model"]
+    if "." in pipe_id:
+        pipe_id, _ = pipe_id.split(".", 1)
+
+    return pipe_id
+
+
+def get_function_params(function_module, form_data, user, extra_params=None):
+    if extra_params is None:
+        extra_params = {}
+
+    pipe_id = get_pipe_id(form_data)
+
+    # Get the signature of the function
+    sig = inspect.signature(function_module.pipe)
+    params = {"body": form_data} | {
+        k: v for k, v in extra_params.items() if k in sig.parameters
+    }
+
+    if "__user__" in params and hasattr(function_module, "UserValves"):
+        user_valves = Functions.get_user_valves_by_id_and_user_id(pipe_id, user.id)
+        try:
+            params["__user__"]["valves"] = function_module.UserValves(**user_valves)
+        except Exception as e:
+            log.exception(e)
+            params["__user__"]["valves"] = function_module.UserValves()
+
+    return params
+
+
+async def generate_function_chat_completion(form_data, user, models: dict = {}):
+    model_id = form_data.get("model")
+    model_info = Models.get_model_by_id(model_id)
+
+    metadata = form_data.pop("metadata", {})
+
+    files = metadata.get("files", [])
+    tool_ids = metadata.get("tool_ids", [])
+    # Check if tool_ids is None
+    if tool_ids is None:
+        tool_ids = []
+
+    __event_emitter__ = None
+    __event_call__ = None
+    __task__ = None
+    __task_body__ = None
+
+    if metadata:
+        if all(k in metadata for k in ("session_id", "chat_id", "message_id")):
+            __event_emitter__ = get_event_emitter(metadata)
+            __event_call__ = get_event_call(metadata)
+        __task__ = metadata.get("task", None)
+        __task_body__ = metadata.get("task_body", None)
+
+    extra_params = {
+        "__event_emitter__": __event_emitter__,
+        "__event_call__": __event_call__,
+        "__task__": __task__,
+        "__task_body__": __task_body__,
+        "__files__": files,
+        "__user__": {
+            "id": user.id,
+            "email": user.email,
+            "name": user.name,
+            "role": user.role,
+        },
+        "__metadata__": metadata,
+    }
+    extra_params["__tools__"] = get_tools(
+        app,
+        tool_ids,
+        user,
+        {
+            **extra_params,
+            "__model__": models.get(form_data["model"], None),
+            "__messages__": form_data["messages"],
+            "__files__": files,
+        },
+    )
+
+    if model_info:
+        if model_info.base_model_id:
+            form_data["model"] = model_info.base_model_id
+
+        params = model_info.params.model_dump()
+        form_data = apply_model_params_to_body_openai(params, form_data)
+        form_data = apply_model_system_prompt_to_body(params, form_data, user)
+
+    pipe_id = get_pipe_id(form_data)
+    function_module = get_function_module(pipe_id)
+
+    pipe = function_module.pipe
+    params = get_function_params(function_module, form_data, user, extra_params)
+
+    if form_data.get("stream", False):
+
+        async def stream_content():
+            try:
+                res = await execute_pipe(pipe, params)
+
+                # Directly return if the response is a StreamingResponse
+                if isinstance(res, StreamingResponse):
+                    async for data in res.body_iterator:
+                        yield data
+                    return
+                if isinstance(res, dict):
+                    yield f"data: {json.dumps(res)}\n\n"
+                    return
+
+            except Exception as e:
+                log.error(f"Error: {e}")
+                yield f"data: {json.dumps({'error': {'detail':str(e)}})}\n\n"
+                return
+
+            if isinstance(res, str):
+                message = openai_chat_chunk_message_template(form_data["model"], res)
+                yield f"data: {json.dumps(message)}\n\n"
+
+            if isinstance(res, Iterator):
+                for line in res:
+                    yield process_line(form_data, line)
+
+            if isinstance(res, AsyncGenerator):
+                async for line in res:
+                    yield process_line(form_data, line)
+
+            if isinstance(res, str) or isinstance(res, Generator):
+                finish_message = openai_chat_chunk_message_template(
+                    form_data["model"], ""
+                )
+                finish_message["choices"][0]["finish_reason"] = "stop"
+                yield f"data: {json.dumps(finish_message)}\n\n"
+                yield "data: [DONE]"
+
+        return StreamingResponse(stream_content(), media_type="text/event-stream")
+    else:
+        try:
+            res = await execute_pipe(pipe, params)
+
+        except Exception as e:
+            log.error(f"Error: {e}")
+            return {"error": {"detail": str(e)}}
+
+        if isinstance(res, StreamingResponse) or isinstance(res, dict):
+            return res
+        if isinstance(res, BaseModel):
+            return res.model_dump()
+
+        message = await get_message_content(res)
+        return openai_chat_completion_message_template(form_data["model"], message)
diff --git a/backend/open_webui/apps/webui/models/auths.py b/backend/open_webui/apps/webui/models/auths.py
new file mode 100644
index 0000000000000000000000000000000000000000..ead897347d077214a6afabbab01f1e9c0b3c055b
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/auths.py
@@ -0,0 +1,206 @@
+import logging
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.apps.webui.models.users import UserModel, Users
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel
+from sqlalchemy import Boolean, Column, String, Text
+from open_webui.utils.utils import verify_password
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# DB MODEL
+####################
+
+
+class Auth(Base):
+    __tablename__ = "auth"
+
+    id = Column(String, primary_key=True)
+    email = Column(String)
+    password = Column(Text)
+    active = Column(Boolean)
+
+
+class AuthModel(BaseModel):
+    id: str
+    email: str
+    password: str
+    active: bool = True
+
+
+####################
+# Forms
+####################
+
+
+class Token(BaseModel):
+    token: str
+    token_type: str
+
+
+class ApiKey(BaseModel):
+    api_key: Optional[str] = None
+
+
+class UserResponse(BaseModel):
+    id: str
+    email: str
+    name: str
+    role: str
+    profile_image_url: str
+
+
+class SigninResponse(Token, UserResponse):
+    pass
+
+
+class SigninForm(BaseModel):
+    email: str
+    password: str
+
+
+class LdapForm(BaseModel):
+    user: str
+    password: str
+
+
+class ProfileImageUrlForm(BaseModel):
+    profile_image_url: str
+
+
+class UpdateProfileForm(BaseModel):
+    profile_image_url: str
+    name: str
+
+
+class UpdatePasswordForm(BaseModel):
+    password: str
+    new_password: str
+
+
+class SignupForm(BaseModel):
+    name: str
+    email: str
+    password: str
+    profile_image_url: Optional[str] = "/user.png"
+
+
+class AddUserForm(SignupForm):
+    role: Optional[str] = "pending"
+
+
+class AuthsTable:
+    def insert_new_auth(
+        self,
+        email: str,
+        password: str,
+        name: str,
+        profile_image_url: str = "/user.png",
+        role: str = "pending",
+        oauth_sub: Optional[str] = None,
+    ) -> Optional[UserModel]:
+        with get_db() as db:
+            log.info("insert_new_auth")
+
+            id = str(uuid.uuid4())
+
+            auth = AuthModel(
+                **{"id": id, "email": email, "password": password, "active": True}
+            )
+            result = Auth(**auth.model_dump())
+            db.add(result)
+
+            user = Users.insert_new_user(
+                id, name, email, profile_image_url, role, oauth_sub
+            )
+
+            db.commit()
+            db.refresh(result)
+
+            if result and user:
+                return user
+            else:
+                return None
+
+    def authenticate_user(self, email: str, password: str) -> Optional[UserModel]:
+        log.info(f"authenticate_user: {email}")
+        try:
+            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:
+                    return None
+        except Exception:
+            return None
+
+    def authenticate_user_by_api_key(self, api_key: str) -> Optional[UserModel]:
+        log.info(f"authenticate_user_by_api_key: {api_key}")
+        # if no api_key, return None
+        if not api_key:
+            return None
+
+        try:
+            user = Users.get_user_by_api_key(api_key)
+            return user if user else None
+        except Exception:
+            return False
+
+    def authenticate_user_by_trusted_header(self, email: str) -> Optional[UserModel]:
+        log.info(f"authenticate_user_by_trusted_header: {email}")
+        try:
+            with get_db() as db:
+                auth = db.query(Auth).filter_by(email=email, active=True).first()
+                if auth:
+                    user = Users.get_user_by_id(auth.id)
+                    return user
+        except Exception:
+            return None
+
+    def update_user_password_by_id(self, id: str, new_password: str) -> bool:
+        try:
+            with get_db() as db:
+                result = (
+                    db.query(Auth).filter_by(id=id).update({"password": new_password})
+                )
+                db.commit()
+                return True if result == 1 else False
+        except Exception:
+            return False
+
+    def update_email_by_id(self, id: str, email: str) -> bool:
+        try:
+            with get_db() as db:
+                result = db.query(Auth).filter_by(id=id).update({"email": email})
+                db.commit()
+                return True if result == 1 else False
+        except Exception:
+            return False
+
+    def delete_auth_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                # Delete User
+                result = Users.delete_user_by_id(id)
+
+                if result:
+                    db.query(Auth).filter_by(id=id).delete()
+                    db.commit()
+
+                    return True
+                else:
+                    return False
+        except Exception:
+            return False
+
+
+Auths = AuthsTable()
diff --git a/backend/open_webui/apps/webui/models/chats.py b/backend/open_webui/apps/webui/models/chats.py
new file mode 100644
index 0000000000000000000000000000000000000000..21250add826035badce06c12e91dc55ceb47c7c0
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/chats.py
@@ -0,0 +1,812 @@
+import json
+import time
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.apps.webui.models.tags import TagModel, Tag, Tags
+
+
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
+from sqlalchemy import or_, func, select, and_, text
+from sqlalchemy.sql import exists
+
+####################
+# Chat DB Schema
+####################
+
+
+class Chat(Base):
+    __tablename__ = "chat"
+
+    id = Column(String, primary_key=True)
+    user_id = Column(String)
+    title = Column(Text)
+    chat = Column(JSON)
+
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+    share_id = Column(Text, unique=True, nullable=True)
+    archived = Column(Boolean, default=False)
+    pinned = Column(Boolean, default=False, nullable=True)
+
+    meta = Column(JSON, server_default="{}")
+    folder_id = Column(Text, nullable=True)
+
+
+class ChatModel(BaseModel):
+    model_config = ConfigDict(from_attributes=True)
+
+    id: str
+    user_id: str
+    title: str
+    chat: dict
+
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+    share_id: Optional[str] = None
+    archived: bool = False
+    pinned: Optional[bool] = False
+
+    meta: dict = {}
+    folder_id: Optional[str] = None
+
+
+####################
+# Forms
+####################
+
+
+class ChatForm(BaseModel):
+    chat: dict
+
+
+class ChatImportForm(ChatForm):
+    meta: Optional[dict] = {}
+    pinned: Optional[bool] = False
+    folder_id: Optional[str] = None
+
+
+class ChatTitleMessagesForm(BaseModel):
+    title: str
+    messages: list[dict]
+
+
+class ChatTitleForm(BaseModel):
+    title: str
+
+
+class ChatResponse(BaseModel):
+    id: str
+    user_id: str
+    title: str
+    chat: dict
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+    share_id: Optional[str] = None  # id of the chat to be shared
+    archived: bool
+    pinned: Optional[bool] = False
+    meta: dict = {}
+    folder_id: Optional[str] = None
+
+
+class ChatTitleIdResponse(BaseModel):
+    id: str
+    title: str
+    updated_at: int
+    created_at: int
+
+
+class ChatTable:
+    def insert_new_chat(self, user_id: str, form_data: ChatForm) -> Optional[ChatModel]:
+        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": 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 import_chat(
+        self, user_id: str, form_data: ChatImportForm
+    ) -> Optional[ChatModel]:
+        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": form_data.chat,
+                    "meta": form_data.meta,
+                    "pinned": form_data.pinned,
+                    "folder_id": form_data.folder_id,
+                    "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]:
+        try:
+            with get_db() as db:
+                chat_item = db.get(Chat, id)
+                chat_item.chat = chat
+                chat_item.title = chat["title"] if "title" in chat else "New Chat"
+                chat_item.updated_at = int(time.time())
+                db.commit()
+                db.refresh(chat_item)
+
+                return ChatModel.model_validate(chat_item)
+        except Exception:
+            return None
+
+    def insert_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
+        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})
+            )
+            db.commit()
+            return shared_chat if (shared_result and result) else None
+
+    def update_shared_chat_by_chat_id(self, chat_id: str) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, chat_id)
+                shared_chat = (
+                    db.query(Chat).filter_by(user_id=f"shared-{chat_id}").first()
+                )
+
+                if shared_chat is None:
+                    return self.insert_shared_chat_by_chat_id(chat_id)
+
+                shared_chat.title = chat.title
+                shared_chat.chat = chat.chat
+
+                shared_chat.updated_at = int(time.time())
+                db.commit()
+                db.refresh(shared_chat)
+
+                return ChatModel.model_validate(shared_chat)
+        except Exception:
+            return None
+
+    def delete_shared_chat_by_chat_id(self, chat_id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(user_id=f"shared-{chat_id}").delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+    def update_chat_share_id_by_id(
+        self, id: str, share_id: Optional[str]
+    ) -> Optional[ChatModel]:
+        try:
+            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 Exception:
+            return None
+
+    def toggle_chat_pinned_by_id(self, id: str) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                chat.pinned = not chat.pinned
+                chat.updated_at = int(time.time())
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def toggle_chat_archive_by_id(self, id: str) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                chat.archived = not chat.archived
+                chat.updated_at = int(time.time())
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def archive_all_chats_by_user_id(self, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(user_id=user_id).update({"archived": True})
+                db.commit()
+                return True
+        except Exception:
+            return False
+
+    def get_archived_chat_list_by_user_id(
+        self, user_id: str, skip: int = 0, limit: int = 50
+    ) -> list[ChatModel]:
+        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(
+        self,
+        user_id: str,
+        include_archived: bool = False,
+        skip: int = 0,
+        limit: int = 50,
+    ) -> list[ChatModel]:
+        with get_db() as db:
+            query = db.query(Chat).filter_by(user_id=user_id).filter_by(folder_id=None)
+            if not include_archived:
+                query = query.filter_by(archived=False)
+
+            query = query.order_by(Chat.updated_at.desc())
+
+            if skip:
+                query = query.offset(skip)
+            if limit:
+                query = query.limit(limit)
+
+            all_chats = query.all()
+            return [ChatModel.model_validate(chat) for chat in all_chats]
+
+    def get_chat_title_id_list_by_user_id(
+        self,
+        user_id: str,
+        include_archived: bool = False,
+        skip: Optional[int] = None,
+        limit: Optional[int] = None,
+    ) -> list[ChatTitleIdResponse]:
+        with get_db() as db:
+            query = db.query(Chat).filter_by(user_id=user_id).filter_by(folder_id=None)
+            query = query.filter(or_(Chat.pinned == False, Chat.pinned == None))
+
+            if not include_archived:
+                query = query.filter_by(archived=False)
+
+            query = query.order_by(Chat.updated_at.desc()).with_entities(
+                Chat.id, Chat.title, Chat.updated_at, Chat.created_at
+            )
+
+            if skip:
+                query = query.offset(skip)
+            if limit:
+                query = query.limit(limit)
+
+            all_chats = query.all()
+
+            # result has to be destrctured from sqlalchemy `row` and mapped to a dict since the `ChatModel`is not the returned dataclass.
+            return [
+                ChatTitleIdResponse.model_validate(
+                    {
+                        "id": chat[0],
+                        "title": chat[1],
+                        "updated_at": chat[2],
+                        "created_at": chat[3],
+                    }
+                )
+                for chat in all_chats
+            ]
+
+    def get_chat_list_by_chat_ids(
+        self, chat_ids: list[str], skip: int = 0, limit: int = 50
+    ) -> list[ChatModel]:
+        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]:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def get_chat_by_share_id(self, id: str) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.query(Chat).filter_by(share_id=id).first()
+
+                if chat:
+                    return self.get_chat_by_id(id)
+                else:
+                    return None
+        except Exception:
+            return None
+
+    def get_chat_by_id_and_user_id(self, id: str, user_id: str) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.query(Chat).filter_by(id=id, user_id=user_id).first()
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def get_chats(self, skip: int = 0, limit: int = 50) -> list[ChatModel]:
+        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]:
+        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_pinned_chats_by_user_id(self, user_id: str) -> list[ChatModel]:
+        with get_db() as db:
+            all_chats = (
+                db.query(Chat)
+                .filter_by(user_id=user_id, pinned=True, archived=False)
+                .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]:
+        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 get_chats_by_user_id_and_search_text(
+        self,
+        user_id: str,
+        search_text: str,
+        include_archived: bool = False,
+        skip: int = 0,
+        limit: int = 60,
+    ) -> list[ChatModel]:
+        """
+        Filters chats based on a search query using Python, allowing pagination using skip and limit.
+        """
+        search_text = search_text.lower().strip()
+
+        if not search_text:
+            return self.get_chat_list_by_user_id(user_id, include_archived, skip, limit)
+
+        search_text_words = search_text.split(" ")
+
+        # search_text might contain 'tag:tag_name' format so we need to extract the tag_name, split the search_text and remove the tags
+        tag_ids = [
+            word.replace("tag:", "").replace(" ", "_").lower()
+            for word in search_text_words
+            if word.startswith("tag:")
+        ]
+
+        search_text_words = [
+            word for word in search_text_words if not word.startswith("tag:")
+        ]
+
+        search_text = " ".join(search_text_words)
+
+        with get_db() as db:
+            query = db.query(Chat).filter(Chat.user_id == user_id)
+
+            if not include_archived:
+                query = query.filter(Chat.archived == False)
+
+            query = query.order_by(Chat.updated_at.desc())
+
+            # Check if the database dialect is either 'sqlite' or 'postgresql'
+            dialect_name = db.bind.dialect.name
+            if dialect_name == "sqlite":
+                # SQLite case: using JSON1 extension for JSON searching
+                query = query.filter(
+                    (
+                        Chat.title.ilike(
+                            f"%{search_text}%"
+                        )  # Case-insensitive search in title
+                        | text(
+                            """
+                            EXISTS (
+                                SELECT 1 
+                                FROM json_each(Chat.chat, '$.messages') AS message 
+                                WHERE LOWER(message.value->>'content') LIKE '%' || :search_text || '%'
+                            )
+                            """
+                        )
+                    ).params(search_text=search_text)
+                )
+
+                # Check if there are any tags to filter, it should have all the tags
+                if "none" in tag_ids:
+                    query = query.filter(
+                        text(
+                            """
+                            NOT EXISTS (
+                                SELECT 1
+                                FROM json_each(Chat.meta, '$.tags') AS tag
+                            )
+                            """
+                        )
+                    )
+                elif tag_ids:
+                    query = query.filter(
+                        and_(
+                            *[
+                                text(
+                                    f"""
+                                    EXISTS (
+                                        SELECT 1
+                                        FROM json_each(Chat.meta, '$.tags') AS tag
+                                        WHERE tag.value = :tag_id_{tag_idx}
+                                    )
+                                    """
+                                ).params(**{f"tag_id_{tag_idx}": tag_id})
+                                for tag_idx, tag_id in enumerate(tag_ids)
+                            ]
+                        )
+                    )
+
+            elif dialect_name == "postgresql":
+                # PostgreSQL relies on proper JSON query for search
+                query = query.filter(
+                    (
+                        Chat.title.ilike(
+                            f"%{search_text}%"
+                        )  # Case-insensitive search in title
+                        | text(
+                            """
+                            EXISTS (
+                                SELECT 1
+                                FROM json_array_elements(Chat.chat->'messages') AS message
+                                WHERE LOWER(message->>'content') LIKE '%' || :search_text || '%'
+                            )
+                            """
+                        )
+                    ).params(search_text=search_text)
+                )
+
+                # Check if there are any tags to filter, it should have all the tags
+                if "none" in tag_ids:
+                    query = query.filter(
+                        text(
+                            """
+                            NOT EXISTS (
+                                SELECT 1
+                                FROM json_array_elements_text(Chat.meta->'tags') AS tag
+                            )
+                            """
+                        )
+                    )
+                elif tag_ids:
+                    query = query.filter(
+                        and_(
+                            *[
+                                text(
+                                    f"""
+                                    EXISTS (
+                                        SELECT 1
+                                        FROM json_array_elements_text(Chat.meta->'tags') AS tag
+                                        WHERE tag = :tag_id_{tag_idx}
+                                    )
+                                    """
+                                ).params(**{f"tag_id_{tag_idx}": tag_id})
+                                for tag_idx, tag_id in enumerate(tag_ids)
+                            ]
+                        )
+                    )
+            else:
+                raise NotImplementedError(
+                    f"Unsupported dialect: {db.bind.dialect.name}"
+                )
+
+            # Perform pagination at the SQL level
+            all_chats = query.offset(skip).limit(limit).all()
+
+            print(len(all_chats))
+
+            # Validate and return chats
+            return [ChatModel.model_validate(chat) for chat in all_chats]
+
+    def get_chats_by_folder_id_and_user_id(
+        self, folder_id: str, user_id: str
+    ) -> list[ChatModel]:
+        with get_db() as db:
+            query = db.query(Chat).filter_by(folder_id=folder_id, user_id=user_id)
+            query = query.filter(or_(Chat.pinned == False, Chat.pinned == None))
+            query = query.filter_by(archived=False)
+
+            query = query.order_by(Chat.updated_at.desc())
+
+            all_chats = query.all()
+            return [ChatModel.model_validate(chat) for chat in all_chats]
+
+    def get_chats_by_folder_ids_and_user_id(
+        self, folder_ids: list[str], user_id: str
+    ) -> list[ChatModel]:
+        with get_db() as db:
+            query = db.query(Chat).filter(
+                Chat.folder_id.in_(folder_ids), Chat.user_id == user_id
+            )
+            query = query.filter(or_(Chat.pinned == False, Chat.pinned == None))
+            query = query.filter_by(archived=False)
+
+            query = query.order_by(Chat.updated_at.desc())
+
+            all_chats = query.all()
+            return [ChatModel.model_validate(chat) for chat in all_chats]
+
+    def update_chat_folder_id_by_id_and_user_id(
+        self, id: str, user_id: str, folder_id: str
+    ) -> Optional[ChatModel]:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                chat.folder_id = folder_id
+                chat.updated_at = int(time.time())
+                chat.pinned = False
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def get_chat_tags_by_id_and_user_id(self, id: str, user_id: str) -> list[TagModel]:
+        with get_db() as db:
+            chat = db.get(Chat, id)
+            tags = chat.meta.get("tags", [])
+            return [Tags.get_tag_by_name_and_user_id(tag, user_id) for tag in tags]
+
+    def get_chat_list_by_user_id_and_tag_name(
+        self, user_id: str, tag_name: str, skip: int = 0, limit: int = 50
+    ) -> list[ChatModel]:
+        with get_db() as db:
+            query = db.query(Chat).filter_by(user_id=user_id)
+            tag_id = tag_name.replace(" ", "_").lower()
+
+            print(db.bind.dialect.name)
+            if db.bind.dialect.name == "sqlite":
+                # SQLite JSON1 querying for tags within the meta JSON field
+                query = query.filter(
+                    text(
+                        f"EXISTS (SELECT 1 FROM json_each(Chat.meta, '$.tags') WHERE json_each.value = :tag_id)"
+                    )
+                ).params(tag_id=tag_id)
+            elif db.bind.dialect.name == "postgresql":
+                # PostgreSQL JSON query for tags within the meta JSON field (for `json` type)
+                query = query.filter(
+                    text(
+                        "EXISTS (SELECT 1 FROM json_array_elements_text(Chat.meta->'tags') elem WHERE elem = :tag_id)"
+                    )
+                ).params(tag_id=tag_id)
+            else:
+                raise NotImplementedError(
+                    f"Unsupported dialect: {db.bind.dialect.name}"
+                )
+
+            all_chats = query.all()
+            print("all_chats", all_chats)
+            return [ChatModel.model_validate(chat) for chat in all_chats]
+
+    def add_chat_tag_by_id_and_user_id_and_tag_name(
+        self, id: str, user_id: str, tag_name: str
+    ) -> Optional[ChatModel]:
+        tag = Tags.get_tag_by_name_and_user_id(tag_name, user_id)
+        if tag is None:
+            tag = Tags.insert_new_tag(tag_name, user_id)
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+
+                tag_id = tag.id
+                if tag_id not in chat.meta.get("tags", []):
+                    chat.meta = {
+                        **chat.meta,
+                        "tags": list(set(chat.meta.get("tags", []) + [tag_id])),
+                    }
+
+                db.commit()
+                db.refresh(chat)
+                return ChatModel.model_validate(chat)
+        except Exception:
+            return None
+
+    def count_chats_by_tag_name_and_user_id(self, tag_name: str, user_id: str) -> int:
+        with get_db() as db:  # Assuming `get_db()` returns a session object
+            query = db.query(Chat).filter_by(user_id=user_id, archived=False)
+
+            # Normalize the tag_name for consistency
+            tag_id = tag_name.replace(" ", "_").lower()
+
+            if db.bind.dialect.name == "sqlite":
+                # SQLite JSON1 support for querying the tags inside the `meta` JSON field
+                query = query.filter(
+                    text(
+                        f"EXISTS (SELECT 1 FROM json_each(Chat.meta, '$.tags') WHERE json_each.value = :tag_id)"
+                    )
+                ).params(tag_id=tag_id)
+
+            elif db.bind.dialect.name == "postgresql":
+                # PostgreSQL JSONB support for querying the tags inside the `meta` JSON field
+                query = query.filter(
+                    text(
+                        "EXISTS (SELECT 1 FROM json_array_elements_text(Chat.meta->'tags') elem WHERE elem = :tag_id)"
+                    )
+                ).params(tag_id=tag_id)
+
+            else:
+                raise NotImplementedError(
+                    f"Unsupported dialect: {db.bind.dialect.name}"
+                )
+
+            # Get the count of matching records
+            count = query.count()
+
+            # Debugging output for inspection
+            print(f"Count of chats for tag '{tag_name}':", count)
+
+            return count
+
+    def delete_tag_by_id_and_user_id_and_tag_name(
+        self, id: str, user_id: str, tag_name: str
+    ) -> bool:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                tags = chat.meta.get("tags", [])
+                tag_id = tag_name.replace(" ", "_").lower()
+
+                tags = [tag for tag in tags if tag != tag_id]
+                chat.meta = {
+                    **chat.meta,
+                    "tags": list(set(tags)),
+                }
+                db.commit()
+                return True
+        except Exception:
+            return False
+
+    def delete_all_tags_by_id_and_user_id(self, id: str, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                chat = db.get(Chat, id)
+                chat.meta = {
+                    **chat.meta,
+                    "tags": [],
+                }
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+    def delete_chat_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(id=id).delete()
+                db.commit()
+
+                return True and self.delete_shared_chat_by_chat_id(id)
+        except Exception:
+            return False
+
+    def delete_chat_by_id_and_user_id(self, id: str, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(id=id, user_id=user_id).delete()
+                db.commit()
+
+                return True and self.delete_shared_chat_by_chat_id(id)
+        except Exception:
+            return False
+
+    def delete_chats_by_user_id(self, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                self.delete_shared_chats_by_user_id(user_id)
+
+                db.query(Chat).filter_by(user_id=user_id).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+    def delete_chats_by_user_id_and_folder_id(
+        self, user_id: str, folder_id: str
+    ) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Chat).filter_by(user_id=user_id, folder_id=folder_id).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+    def delete_shared_chats_by_user_id(self, user_id: str) -> bool:
+        try:
+            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()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+
+Chats = ChatTable()
diff --git a/backend/open_webui/apps/webui/models/feedbacks.py b/backend/open_webui/apps/webui/models/feedbacks.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2356dfd86051d1b5713f891c6ec03bde05d0328
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/feedbacks.py
@@ -0,0 +1,254 @@
+import logging
+import time
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.apps.webui.models.chats import Chats
+
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+####################
+# Feedback DB Schema
+####################
+
+
+class Feedback(Base):
+    __tablename__ = "feedback"
+    id = Column(Text, primary_key=True)
+    user_id = Column(Text)
+    version = Column(BigInteger, default=0)
+    type = Column(Text)
+    data = Column(JSON, nullable=True)
+    meta = Column(JSON, nullable=True)
+    snapshot = Column(JSON, nullable=True)
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+
+class FeedbackModel(BaseModel):
+    id: str
+    user_id: str
+    version: int
+    type: str
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+    snapshot: Optional[dict] = None
+    created_at: int
+    updated_at: int
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class FeedbackResponse(BaseModel):
+    id: str
+    user_id: str
+    version: int
+    type: str
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+    created_at: int
+    updated_at: int
+
+
+class RatingData(BaseModel):
+    rating: Optional[str | int] = None
+    model_id: Optional[str] = None
+    sibling_model_ids: Optional[list[str]] = None
+    reason: Optional[str] = None
+    comment: Optional[str] = None
+    model_config = ConfigDict(extra="allow", protected_namespaces=())
+
+
+class MetaData(BaseModel):
+    arena: Optional[bool] = None
+    chat_id: Optional[str] = None
+    message_id: Optional[str] = None
+    tags: Optional[list[str]] = None
+    model_config = ConfigDict(extra="allow")
+
+
+class SnapshotData(BaseModel):
+    chat: Optional[dict] = None
+    model_config = ConfigDict(extra="allow")
+
+
+class FeedbackForm(BaseModel):
+    type: str
+    data: Optional[RatingData] = None
+    meta: Optional[dict] = None
+    snapshot: Optional[SnapshotData] = None
+    model_config = ConfigDict(extra="allow")
+
+
+class FeedbackTable:
+    def insert_new_feedback(
+        self, user_id: str, form_data: FeedbackForm
+    ) -> Optional[FeedbackModel]:
+        with get_db() as db:
+            id = str(uuid.uuid4())
+            feedback = FeedbackModel(
+                **{
+                    "id": id,
+                    "user_id": user_id,
+                    "version": 0,
+                    **form_data.model_dump(),
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+            try:
+                result = Feedback(**feedback.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return FeedbackModel.model_validate(result)
+                else:
+                    return None
+            except Exception as e:
+                print(e)
+                return None
+
+    def get_feedback_by_id(self, id: str) -> Optional[FeedbackModel]:
+        try:
+            with get_db() as db:
+                feedback = db.query(Feedback).filter_by(id=id).first()
+                if not feedback:
+                    return None
+                return FeedbackModel.model_validate(feedback)
+        except Exception:
+            return None
+
+    def get_feedback_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[FeedbackModel]:
+        try:
+            with get_db() as db:
+                feedback = db.query(Feedback).filter_by(id=id, user_id=user_id).first()
+                if not feedback:
+                    return None
+                return FeedbackModel.model_validate(feedback)
+        except Exception:
+            return None
+
+    def get_all_feedbacks(self) -> list[FeedbackModel]:
+        with get_db() as db:
+            return [
+                FeedbackModel.model_validate(feedback)
+                for feedback in db.query(Feedback)
+                .order_by(Feedback.updated_at.desc())
+                .all()
+            ]
+
+    def get_feedbacks_by_type(self, type: str) -> list[FeedbackModel]:
+        with get_db() as db:
+            return [
+                FeedbackModel.model_validate(feedback)
+                for feedback in db.query(Feedback)
+                .filter_by(type=type)
+                .order_by(Feedback.updated_at.desc())
+                .all()
+            ]
+
+    def get_feedbacks_by_user_id(self, user_id: str) -> list[FeedbackModel]:
+        with get_db() as db:
+            return [
+                FeedbackModel.model_validate(feedback)
+                for feedback in db.query(Feedback)
+                .filter_by(user_id=user_id)
+                .order_by(Feedback.updated_at.desc())
+                .all()
+            ]
+
+    def update_feedback_by_id(
+        self, id: str, form_data: FeedbackForm
+    ) -> Optional[FeedbackModel]:
+        with get_db() as db:
+            feedback = db.query(Feedback).filter_by(id=id).first()
+            if not feedback:
+                return None
+
+            if form_data.data:
+                feedback.data = form_data.data.model_dump()
+            if form_data.meta:
+                feedback.meta = form_data.meta
+            if form_data.snapshot:
+                feedback.snapshot = form_data.snapshot.model_dump()
+
+            feedback.updated_at = int(time.time())
+
+            db.commit()
+            return FeedbackModel.model_validate(feedback)
+
+    def update_feedback_by_id_and_user_id(
+        self, id: str, user_id: str, form_data: FeedbackForm
+    ) -> Optional[FeedbackModel]:
+        with get_db() as db:
+            feedback = db.query(Feedback).filter_by(id=id, user_id=user_id).first()
+            if not feedback:
+                return None
+
+            if form_data.data:
+                feedback.data = form_data.data.model_dump()
+            if form_data.meta:
+                feedback.meta = form_data.meta
+            if form_data.snapshot:
+                feedback.snapshot = form_data.snapshot.model_dump()
+
+            feedback.updated_at = int(time.time())
+
+            db.commit()
+            return FeedbackModel.model_validate(feedback)
+
+    def delete_feedback_by_id(self, id: str) -> bool:
+        with get_db() as db:
+            feedback = db.query(Feedback).filter_by(id=id).first()
+            if not feedback:
+                return False
+            db.delete(feedback)
+            db.commit()
+            return True
+
+    def delete_feedback_by_id_and_user_id(self, id: str, user_id: str) -> bool:
+        with get_db() as db:
+            feedback = db.query(Feedback).filter_by(id=id, user_id=user_id).first()
+            if not feedback:
+                return False
+            db.delete(feedback)
+            db.commit()
+            return True
+
+    def delete_feedbacks_by_user_id(self, user_id: str) -> bool:
+        with get_db() as db:
+            feedbacks = db.query(Feedback).filter_by(user_id=user_id).all()
+            if not feedbacks:
+                return False
+            for feedback in feedbacks:
+                db.delete(feedback)
+            db.commit()
+            return True
+
+    def delete_all_feedbacks(self) -> bool:
+        with get_db() as db:
+            feedbacks = db.query(Feedback).all()
+            if not feedbacks:
+                return False
+            for feedback in feedbacks:
+                db.delete(feedback)
+            db.commit()
+            return True
+
+
+Feedbacks = FeedbackTable()
diff --git a/backend/open_webui/apps/webui/models/files.py b/backend/open_webui/apps/webui/models/files.py
new file mode 100644
index 0000000000000000000000000000000000000000..31c9164b60c1822732a6fae4261b4c7103c7082b
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/files.py
@@ -0,0 +1,230 @@
+import logging
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, JSONField, get_db
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text, JSON
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# Files DB Schema
+####################
+
+
+class File(Base):
+    __tablename__ = "file"
+    id = Column(String, primary_key=True)
+    user_id = Column(String)
+    hash = Column(Text, nullable=True)
+
+    filename = Column(Text)
+    path = Column(Text, nullable=True)
+
+    data = Column(JSON, nullable=True)
+    meta = Column(JSON, nullable=True)
+
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+
+class FileModel(BaseModel):
+    model_config = ConfigDict(from_attributes=True)
+
+    id: str
+    user_id: str
+    hash: Optional[str] = None
+
+    filename: str
+    path: Optional[str] = None
+
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+
+    created_at: Optional[int]  # timestamp in epoch
+    updated_at: Optional[int]  # timestamp in epoch
+
+
+####################
+# Forms
+####################
+
+
+class FileMeta(BaseModel):
+    name: Optional[str] = None
+    content_type: Optional[str] = None
+    size: Optional[int] = None
+
+    model_config = ConfigDict(extra="allow")
+
+
+class FileModelResponse(BaseModel):
+    id: str
+    user_id: str
+    hash: Optional[str] = None
+
+    filename: str
+    data: Optional[dict] = None
+    meta: FileMeta
+
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+    model_config = ConfigDict(extra="allow")
+
+
+class FileMetadataResponse(BaseModel):
+    id: str
+    meta: dict
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+
+class FileForm(BaseModel):
+    id: str
+    hash: Optional[str] = None
+    filename: str
+    path: str
+    data: dict = {}
+    meta: dict = {}
+
+
+class FilesTable:
+    def insert_new_file(self, user_id: str, form_data: FileForm) -> Optional[FileModel]:
+        with get_db() as db:
+            file = FileModel(
+                **{
+                    **form_data.model_dump(),
+                    "user_id": user_id,
+                    "created_at": int(time.time()),
+                    "updated_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
+
+    def get_file_by_id(self, id: str) -> Optional[FileModel]:
+        with get_db() as db:
+            try:
+                file = db.get(File, id)
+                return FileModel.model_validate(file)
+            except Exception:
+                return None
+
+    def get_file_metadata_by_id(self, id: str) -> Optional[FileMetadataResponse]:
+        with get_db() as db:
+            try:
+                file = db.get(File, id)
+                return FileMetadataResponse(
+                    id=file.id,
+                    meta=file.meta,
+                    created_at=file.created_at,
+                    updated_at=file.updated_at,
+                )
+            except Exception:
+                return None
+
+    def get_files(self) -> list[FileModel]:
+        with get_db() as db:
+            return [FileModel.model_validate(file) for file in db.query(File).all()]
+
+    def get_files_by_ids(self, ids: list[str]) -> list[FileModel]:
+        with get_db() as db:
+            return [
+                FileModel.model_validate(file)
+                for file in db.query(File)
+                .filter(File.id.in_(ids))
+                .order_by(File.updated_at.desc())
+                .all()
+            ]
+
+    def get_file_metadatas_by_ids(self, ids: list[str]) -> list[FileMetadataResponse]:
+        with get_db() as db:
+            return [
+                FileMetadataResponse(
+                    id=file.id,
+                    meta=file.meta,
+                    created_at=file.created_at,
+                    updated_at=file.updated_at,
+                )
+                for file in db.query(File)
+                .filter(File.id.in_(ids))
+                .order_by(File.updated_at.desc())
+                .all()
+            ]
+
+    def get_files_by_user_id(self, user_id: str) -> list[FileModel]:
+        with get_db() as db:
+            return [
+                FileModel.model_validate(file)
+                for file in db.query(File).filter_by(user_id=user_id).all()
+            ]
+
+    def update_file_hash_by_id(self, id: str, hash: str) -> Optional[FileModel]:
+        with get_db() as db:
+            try:
+                file = db.query(File).filter_by(id=id).first()
+                file.hash = hash
+                db.commit()
+
+                return FileModel.model_validate(file)
+            except Exception:
+                return None
+
+    def update_file_data_by_id(self, id: str, data: dict) -> Optional[FileModel]:
+        with get_db() as db:
+            try:
+                file = db.query(File).filter_by(id=id).first()
+                file.data = {**(file.data if file.data else {}), **data}
+                db.commit()
+                return FileModel.model_validate(file)
+            except Exception as e:
+
+                return None
+
+    def update_file_metadata_by_id(self, id: str, meta: dict) -> Optional[FileModel]:
+        with get_db() as db:
+            try:
+                file = db.query(File).filter_by(id=id).first()
+                file.meta = {**(file.meta if file.meta else {}), **meta}
+                db.commit()
+                return FileModel.model_validate(file)
+            except Exception:
+                return None
+
+    def delete_file_by_id(self, id: str) -> bool:
+        with get_db() as db:
+            try:
+                db.query(File).filter_by(id=id).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+    def delete_all_files(self) -> bool:
+        with get_db() as db:
+            try:
+                db.query(File).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+
+Files = FilesTable()
diff --git a/backend/open_webui/apps/webui/models/folders.py b/backend/open_webui/apps/webui/models/folders.py
new file mode 100644
index 0000000000000000000000000000000000000000..90e8880aaddf0b98710f3a4b9987b59408a9aa86
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/folders.py
@@ -0,0 +1,271 @@
+import logging
+import time
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.apps.webui.models.chats import Chats
+
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+####################
+# Folder DB Schema
+####################
+
+
+class Folder(Base):
+    __tablename__ = "folder"
+    id = Column(Text, primary_key=True)
+    parent_id = Column(Text, nullable=True)
+    user_id = Column(Text)
+    name = Column(Text)
+    items = Column(JSON, nullable=True)
+    meta = Column(JSON, nullable=True)
+    is_expanded = Column(Boolean, default=False)
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+
+class FolderModel(BaseModel):
+    id: str
+    parent_id: Optional[str] = None
+    user_id: str
+    name: str
+    items: Optional[dict] = None
+    meta: Optional[dict] = None
+    is_expanded: bool = False
+    created_at: int
+    updated_at: int
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class FolderForm(BaseModel):
+    name: str
+    model_config = ConfigDict(extra="allow")
+
+
+class FolderTable:
+    def insert_new_folder(
+        self, user_id: str, name: str, parent_id: Optional[str] = None
+    ) -> Optional[FolderModel]:
+        with get_db() as db:
+            id = str(uuid.uuid4())
+            folder = FolderModel(
+                **{
+                    "id": id,
+                    "user_id": user_id,
+                    "name": name,
+                    "parent_id": parent_id,
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+            try:
+                result = Folder(**folder.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return FolderModel.model_validate(result)
+                else:
+                    return None
+            except Exception as e:
+                print(e)
+                return None
+
+    def get_folder_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+
+                if not folder:
+                    return None
+
+                return FolderModel.model_validate(folder)
+        except Exception:
+            return None
+
+    def get_children_folders_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                folders = []
+
+                def get_children(folder):
+                    children = self.get_folders_by_parent_id_and_user_id(
+                        folder.id, user_id
+                    )
+                    for child in children:
+                        get_children(child)
+                        folders.append(child)
+
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+                if not folder:
+                    return None
+
+                get_children(folder)
+                return folders
+        except Exception:
+            return None
+
+    def get_folders_by_user_id(self, user_id: str) -> list[FolderModel]:
+        with get_db() as db:
+            return [
+                FolderModel.model_validate(folder)
+                for folder in db.query(Folder).filter_by(user_id=user_id).all()
+            ]
+
+    def get_folder_by_parent_id_and_user_id_and_name(
+        self, parent_id: Optional[str], user_id: str, name: str
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                # Check if folder exists
+                folder = (
+                    db.query(Folder)
+                    .filter_by(parent_id=parent_id, user_id=user_id)
+                    .filter(Folder.name.ilike(name))
+                    .first()
+                )
+
+                if not folder:
+                    return None
+
+                return FolderModel.model_validate(folder)
+        except Exception as e:
+            log.error(f"get_folder_by_parent_id_and_user_id_and_name: {e}")
+            return None
+
+    def get_folders_by_parent_id_and_user_id(
+        self, parent_id: Optional[str], user_id: str
+    ) -> list[FolderModel]:
+        with get_db() as db:
+            return [
+                FolderModel.model_validate(folder)
+                for folder in db.query(Folder)
+                .filter_by(parent_id=parent_id, user_id=user_id)
+                .all()
+            ]
+
+    def update_folder_parent_id_by_id_and_user_id(
+        self,
+        id: str,
+        user_id: str,
+        parent_id: str,
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+
+                if not folder:
+                    return None
+
+                folder.parent_id = parent_id
+                folder.updated_at = int(time.time())
+
+                db.commit()
+
+                return FolderModel.model_validate(folder)
+        except Exception as e:
+            log.error(f"update_folder: {e}")
+            return
+
+    def update_folder_name_by_id_and_user_id(
+        self, id: str, user_id: str, name: str
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+
+                if not folder:
+                    return None
+
+                existing_folder = (
+                    db.query(Folder)
+                    .filter_by(name=name, parent_id=folder.parent_id, user_id=user_id)
+                    .first()
+                )
+
+                if existing_folder:
+                    return None
+
+                folder.name = name
+                folder.updated_at = int(time.time())
+
+                db.commit()
+
+                return FolderModel.model_validate(folder)
+        except Exception as e:
+            log.error(f"update_folder: {e}")
+            return
+
+    def update_folder_is_expanded_by_id_and_user_id(
+        self, id: str, user_id: str, is_expanded: bool
+    ) -> Optional[FolderModel]:
+        try:
+            with get_db() as db:
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+
+                if not folder:
+                    return None
+
+                folder.is_expanded = is_expanded
+                folder.updated_at = int(time.time())
+
+                db.commit()
+
+                return FolderModel.model_validate(folder)
+        except Exception as e:
+            log.error(f"update_folder: {e}")
+            return
+
+    def delete_folder_by_id_and_user_id(self, id: str, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                folder = db.query(Folder).filter_by(id=id, user_id=user_id).first()
+                if not folder:
+                    return False
+
+                # Delete all chats in the folder
+                Chats.delete_chats_by_user_id_and_folder_id(user_id, folder.id)
+
+                # Delete all children folders
+                def delete_children(folder):
+                    folder_children = self.get_folders_by_parent_id_and_user_id(
+                        folder.id, user_id
+                    )
+                    for folder_child in folder_children:
+                        Chats.delete_chats_by_user_id_and_folder_id(
+                            user_id, folder_child.id
+                        )
+                        delete_children(folder_child)
+
+                        folder = db.query(Folder).filter_by(id=folder_child.id).first()
+                        db.delete(folder)
+                        db.commit()
+
+                delete_children(folder)
+                db.delete(folder)
+                db.commit()
+                return True
+        except Exception as e:
+            log.error(f"delete_folder: {e}")
+            return False
+
+
+Folders = FolderTable()
diff --git a/backend/open_webui/apps/webui/models/functions.py b/backend/open_webui/apps/webui/models/functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..fda15507506cdb984afcb05b7b14fa7b7016af6a
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/functions.py
@@ -0,0 +1,270 @@
+import logging
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, JSONField, get_db
+from open_webui.apps.webui.models.users import Users
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Boolean, Column, String, Text
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# Functions DB Schema
+####################
+
+
+class Function(Base):
+    __tablename__ = "function"
+
+    id = Column(String, primary_key=True)
+    user_id = Column(String)
+    name = Column(Text)
+    type = Column(Text)
+    content = Column(Text)
+    meta = Column(JSONField)
+    valves = Column(JSONField)
+    is_active = Column(Boolean)
+    is_global = Column(Boolean)
+    updated_at = Column(BigInteger)
+    created_at = Column(BigInteger)
+
+
+class FunctionMeta(BaseModel):
+    description: Optional[str] = None
+    manifest: Optional[dict] = {}
+
+
+class FunctionModel(BaseModel):
+    id: str
+    user_id: str
+    name: str
+    type: str
+    content: str
+    meta: FunctionMeta
+    is_active: bool = False
+    is_global: bool = False
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class FunctionResponse(BaseModel):
+    id: str
+    user_id: str
+    type: str
+    name: str
+    meta: FunctionMeta
+    is_active: bool
+    is_global: bool
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+
+class FunctionForm(BaseModel):
+    id: str
+    name: str
+    content: str
+    meta: FunctionMeta
+
+
+class FunctionValves(BaseModel):
+    valves: Optional[dict] = None
+
+
+class FunctionsTable:
+    def insert_new_function(
+        self, user_id: str, type: str, form_data: FunctionForm
+    ) -> Optional[FunctionModel]:
+        function = FunctionModel(
+            **{
+                **form_data.model_dump(),
+                "user_id": user_id,
+                "type": type,
+                "updated_at": int(time.time()),
+                "created_at": int(time.time()),
+            }
+        )
+
+        try:
+            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:
+            print(f"Error creating tool: {e}")
+            return None
+
+    def get_function_by_id(self, id: str) -> Optional[FunctionModel]:
+        try:
+            with get_db() as db:
+                function = db.get(Function, id)
+                return FunctionModel.model_validate(function)
+        except Exception:
+            return None
+
+    def get_functions(self, active_only=False) -> list[FunctionModel]:
+        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(
+        self, type: str, active_only=False
+    ) -> list[FunctionModel]:
+        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 [
+                FunctionModel.model_validate(function)
+                for function in db.query(Function)
+                .filter_by(type="filter", is_active=True, is_global=True)
+                .all()
+            ]
+
+    def get_global_action_functions(self) -> list[FunctionModel]:
+        with get_db() as db:
+            return [
+                FunctionModel.model_validate(function)
+                for function in db.query(Function)
+                .filter_by(type="action", is_active=True, is_global=True)
+                .all()
+            ]
+
+    def get_function_valves_by_id(self, id: str) -> Optional[dict]:
+        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(
+        self, id: str, valves: dict
+    ) -> Optional[FunctionValves]:
+        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 Exception:
+                return None
+
+    def get_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump() if user.settings else {}
+
+            # Check if user has "functions" and "valves" settings
+            if "functions" not in user_settings:
+                user_settings["functions"] = {}
+            if "valves" not in user_settings["functions"]:
+                user_settings["functions"]["valves"] = {}
+
+            return user_settings["functions"]["valves"].get(id, {})
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str, valves: dict
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump() if user.settings else {}
+
+            # Check if user has "functions" and "valves" settings
+            if "functions" not in user_settings:
+                user_settings["functions"] = {}
+            if "valves" not in user_settings["functions"]:
+                user_settings["functions"]["valves"] = {}
+
+            user_settings["functions"]["valves"][id] = valves
+
+            # Update the user settings in the database
+            Users.update_user_by_id(user_id, {"settings": user_settings})
+
+            return user_settings["functions"]["valves"][id]
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_function_by_id(self, id: str, updated: dict) -> Optional[FunctionModel]:
+        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 Exception:
+                return None
+
+    def deactivate_all_functions(self) -> Optional[bool]:
+        with get_db() as db:
+            try:
+                db.query(Function).update(
+                    {
+                        "is_active": False,
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return True
+            except Exception:
+                return None
+
+    def delete_function_by_id(self, id: str) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Function).filter_by(id=id).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+
+Functions = FunctionsTable()
diff --git a/backend/open_webui/apps/webui/models/groups.py b/backend/open_webui/apps/webui/models/groups.py
new file mode 100644
index 0000000000000000000000000000000000000000..e692198cd91a398820f852636b52798b2369f6d4
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/groups.py
@@ -0,0 +1,186 @@
+import json
+import logging
+import time
+from typing import Optional
+import uuid
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.env import SRC_LOG_LEVELS
+
+from open_webui.apps.webui.models.files import FileMetadataResponse
+
+
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text, JSON, func
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# UserGroup DB Schema
+####################
+
+
+class Group(Base):
+    __tablename__ = "group"
+
+    id = Column(Text, unique=True, primary_key=True)
+    user_id = Column(Text)
+
+    name = Column(Text)
+    description = Column(Text)
+
+    data = Column(JSON, nullable=True)
+    meta = Column(JSON, nullable=True)
+
+    permissions = Column(JSON, nullable=True)
+    user_ids = Column(JSON, nullable=True)
+
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+
+class GroupModel(BaseModel):
+    model_config = ConfigDict(from_attributes=True)
+    id: str
+    user_id: str
+
+    name: str
+    description: str
+
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+
+    permissions: Optional[dict] = None
+    user_ids: list[str] = []
+
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+
+####################
+# Forms
+####################
+
+
+class GroupResponse(BaseModel):
+    id: str
+    user_id: str
+    name: str
+    description: str
+    permissions: Optional[dict] = None
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+    user_ids: list[str] = []
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+
+class GroupForm(BaseModel):
+    name: str
+    description: str
+
+
+class GroupUpdateForm(GroupForm):
+    permissions: Optional[dict] = None
+    user_ids: Optional[list[str]] = None
+    admin_ids: Optional[list[str]] = None
+
+
+class GroupTable:
+    def insert_new_group(
+        self, user_id: str, form_data: GroupForm
+    ) -> Optional[GroupModel]:
+        with get_db() as db:
+            group = GroupModel(
+                **{
+                    **form_data.model_dump(),
+                    "id": str(uuid.uuid4()),
+                    "user_id": user_id,
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+
+            try:
+                result = Group(**group.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return GroupModel.model_validate(result)
+                else:
+                    return None
+
+            except Exception:
+                return None
+
+    def get_groups(self) -> list[GroupModel]:
+        with get_db() as db:
+            return [
+                GroupModel.model_validate(group)
+                for group in db.query(Group).order_by(Group.updated_at.desc()).all()
+            ]
+
+    def get_groups_by_member_id(self, user_id: str) -> list[GroupModel]:
+        with get_db() as db:
+            return [
+                GroupModel.model_validate(group)
+                for group in db.query(Group)
+                .filter(
+                    func.json_array_length(Group.user_ids) > 0
+                )  # Ensure array exists
+                .filter(
+                    Group.user_ids.cast(String).like(f'%"{user_id}"%')
+                )  # String-based check
+                .order_by(Group.updated_at.desc())
+                .all()
+            ]
+
+    def get_group_by_id(self, id: str) -> Optional[GroupModel]:
+        try:
+            with get_db() as db:
+                group = db.query(Group).filter_by(id=id).first()
+                return GroupModel.model_validate(group) if group else None
+        except Exception:
+            return None
+
+    def update_group_by_id(
+        self, id: str, form_data: GroupUpdateForm, overwrite: bool = False
+    ) -> Optional[GroupModel]:
+        try:
+            with get_db() as db:
+                db.query(Group).filter_by(id=id).update(
+                    {
+                        **form_data.model_dump(exclude_none=True),
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_group_by_id(id=id)
+        except Exception as e:
+            log.exception(e)
+            return None
+
+    def delete_group_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Group).filter_by(id=id).delete()
+                db.commit()
+                return True
+        except Exception:
+            return False
+
+    def delete_all_groups(self) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Group).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+
+Groups = GroupTable()
diff --git a/backend/open_webui/apps/webui/models/knowledge.py b/backend/open_webui/apps/webui/models/knowledge.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1a13b3fdd78150d2ef5fe02d0ae1cab424e54da
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/knowledge.py
@@ -0,0 +1,221 @@
+import json
+import logging
+import time
+from typing import Optional
+import uuid
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.env import SRC_LOG_LEVELS
+
+from open_webui.apps.webui.models.files import FileMetadataResponse
+from open_webui.apps.webui.models.users import Users, UserResponse
+
+
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text, JSON
+
+from open_webui.utils.access_control import has_access
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# Knowledge DB Schema
+####################
+
+
+class Knowledge(Base):
+    __tablename__ = "knowledge"
+
+    id = Column(Text, unique=True, primary_key=True)
+    user_id = Column(Text)
+
+    name = Column(Text)
+    description = Column(Text)
+
+    data = Column(JSON, nullable=True)
+    meta = Column(JSON, nullable=True)
+
+    access_control = Column(JSON, nullable=True)  # Controls data access levels.
+    # Defines access control rules for this entry.
+    # - `None`: Public access, available to all users with the "user" role.
+    # - `{}`: Private access, restricted exclusively to the owner.
+    # - Custom permissions: Specific access control for reading and writing;
+    #   Can specify group or user-level restrictions:
+    #   {
+    #      "read": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      },
+    #      "write": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      }
+    #   }
+
+    created_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+
+
+class KnowledgeModel(BaseModel):
+    model_config = ConfigDict(from_attributes=True)
+
+    id: str
+    user_id: str
+
+    name: str
+    description: str
+
+    data: Optional[dict] = None
+    meta: Optional[dict] = None
+
+    access_control: Optional[dict] = None
+
+    created_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+
+
+####################
+# Forms
+####################
+
+
+class KnowledgeUserModel(KnowledgeModel):
+    user: Optional[UserResponse] = None
+
+
+class KnowledgeResponse(KnowledgeModel):
+    files: Optional[list[FileMetadataResponse | dict]] = None
+
+
+class KnowledgeUserResponse(KnowledgeUserModel):
+    files: Optional[list[FileMetadataResponse | dict]] = None
+
+
+class KnowledgeForm(BaseModel):
+    name: str
+    description: str
+    data: Optional[dict] = None
+    access_control: Optional[dict] = None
+
+
+class KnowledgeTable:
+    def insert_new_knowledge(
+        self, user_id: str, form_data: KnowledgeForm
+    ) -> Optional[KnowledgeModel]:
+        with get_db() as db:
+            knowledge = KnowledgeModel(
+                **{
+                    **form_data.model_dump(),
+                    "id": str(uuid.uuid4()),
+                    "user_id": user_id,
+                    "created_at": int(time.time()),
+                    "updated_at": int(time.time()),
+                }
+            )
+
+            try:
+                result = Knowledge(**knowledge.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return KnowledgeModel.model_validate(result)
+                else:
+                    return None
+            except Exception:
+                return None
+
+    def get_knowledge_bases(self) -> list[KnowledgeUserModel]:
+        with get_db() as db:
+            knowledge_bases = []
+            for knowledge in (
+                db.query(Knowledge).order_by(Knowledge.updated_at.desc()).all()
+            ):
+                user = Users.get_user_by_id(knowledge.user_id)
+                knowledge_bases.append(
+                    KnowledgeUserModel.model_validate(
+                        {
+                            **KnowledgeModel.model_validate(knowledge).model_dump(),
+                            "user": user.model_dump() if user else None,
+                        }
+                    )
+                )
+            return knowledge_bases
+
+    def get_knowledge_bases_by_user_id(
+        self, user_id: str, permission: str = "write"
+    ) -> list[KnowledgeUserModel]:
+        knowledge_bases = self.get_knowledge_bases()
+        return [
+            knowledge_base
+            for knowledge_base in knowledge_bases
+            if knowledge_base.user_id == user_id
+            or has_access(user_id, permission, knowledge_base.access_control)
+        ]
+
+    def get_knowledge_by_id(self, id: str) -> Optional[KnowledgeModel]:
+        try:
+            with get_db() as db:
+                knowledge = db.query(Knowledge).filter_by(id=id).first()
+                return KnowledgeModel.model_validate(knowledge) if knowledge else None
+        except Exception:
+            return None
+
+    def update_knowledge_by_id(
+        self, id: str, form_data: KnowledgeForm, overwrite: bool = False
+    ) -> Optional[KnowledgeModel]:
+        try:
+            with get_db() as db:
+                knowledge = self.get_knowledge_by_id(id=id)
+                db.query(Knowledge).filter_by(id=id).update(
+                    {
+                        **form_data.model_dump(),
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_knowledge_by_id(id=id)
+        except Exception as e:
+            log.exception(e)
+            return None
+
+    def update_knowledge_data_by_id(
+        self, id: str, data: dict
+    ) -> Optional[KnowledgeModel]:
+        try:
+            with get_db() as db:
+                knowledge = self.get_knowledge_by_id(id=id)
+                db.query(Knowledge).filter_by(id=id).update(
+                    {
+                        "data": data,
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+                return self.get_knowledge_by_id(id=id)
+        except Exception as e:
+            log.exception(e)
+            return None
+
+    def delete_knowledge_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Knowledge).filter_by(id=id).delete()
+                db.commit()
+                return True
+        except Exception:
+            return False
+
+    def delete_all_knowledge(self) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Knowledge).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+
+Knowledges = KnowledgeTable()
diff --git a/backend/open_webui/apps/webui/models/memories.py b/backend/open_webui/apps/webui/models/memories.py
new file mode 100644
index 0000000000000000000000000000000000000000..6686058d368937e9c359964b7a42297346c891fe
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/memories.py
@@ -0,0 +1,137 @@
+import time
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text
+
+####################
+# Memory DB Schema
+####################
+
+
+class Memory(Base):
+    __tablename__ = "memory"
+
+    id = Column(String, primary_key=True)
+    user_id = Column(String)
+    content = Column(Text)
+    updated_at = Column(BigInteger)
+    created_at = Column(BigInteger)
+
+
+class MemoryModel(BaseModel):
+    id: str
+    user_id: str
+    content: str
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class MemoriesTable:
+    def insert_new_memory(
+        self,
+        user_id: str,
+        content: str,
+    ) -> Optional[MemoryModel]:
+        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(
+        self,
+        id: str,
+        content: str,
+    ) -> Optional[MemoryModel]:
+        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 Exception:
+                return None
+
+    def get_memories(self) -> list[MemoryModel]:
+        with get_db() as db:
+            try:
+                memories = db.query(Memory).all()
+                return [MemoryModel.model_validate(memory) for memory in memories]
+            except Exception:
+                return None
+
+    def get_memories_by_user_id(self, user_id: str) -> list[MemoryModel]:
+        with get_db() as db:
+            try:
+                memories = db.query(Memory).filter_by(user_id=user_id).all()
+                return [MemoryModel.model_validate(memory) for memory in memories]
+            except Exception:
+                return None
+
+    def get_memory_by_id(self, id: str) -> Optional[MemoryModel]:
+        with get_db() as db:
+            try:
+                memory = db.get(Memory, id)
+                return MemoryModel.model_validate(memory)
+            except Exception:
+                return None
+
+    def delete_memory_by_id(self, id: str) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Memory).filter_by(id=id).delete()
+                db.commit()
+
+                return True
+
+            except Exception:
+                return False
+
+    def delete_memories_by_user_id(self, user_id: str) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Memory).filter_by(user_id=user_id).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+    def delete_memory_by_id_and_user_id(self, id: str, user_id: str) -> bool:
+        with get_db() as db:
+            try:
+                db.query(Memory).filter_by(id=id, user_id=user_id).delete()
+                db.commit()
+
+                return True
+            except Exception:
+                return False
+
+
+Memories = MemoriesTable()
diff --git a/backend/open_webui/apps/webui/models/models.py b/backend/open_webui/apps/webui/models/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..50581bc730bffadcf4e9657d0338a5247d8a39bc
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/models.py
@@ -0,0 +1,274 @@
+import logging
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, JSONField, get_db
+from open_webui.env import SRC_LOG_LEVELS
+
+from open_webui.apps.webui.models.users import Users, UserResponse
+
+
+from pydantic import BaseModel, ConfigDict
+
+from sqlalchemy import or_, and_, func
+from sqlalchemy.dialects import postgresql, sqlite
+from sqlalchemy import BigInteger, Column, Text, JSON, Boolean
+
+
+from open_webui.utils.access_control import has_access
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+####################
+# Models DB Schema
+####################
+
+
+# ModelParams is a model for the data stored in the params field of the Model table
+class ModelParams(BaseModel):
+    model_config = ConfigDict(extra="allow")
+    pass
+
+
+# ModelMeta is a model for the data stored in the meta field of the Model table
+class ModelMeta(BaseModel):
+    profile_image_url: Optional[str] = "/static/favicon.png"
+
+    description: Optional[str] = None
+    """
+        User-facing description of the model.
+    """
+
+    capabilities: Optional[dict] = None
+
+    model_config = ConfigDict(extra="allow")
+
+    pass
+
+
+class Model(Base):
+    __tablename__ = "model"
+
+    id = Column(Text, primary_key=True)
+    """
+        The model's id as used in the API. If set to an existing model, it will override the model.
+    """
+    user_id = Column(Text)
+
+    base_model_id = Column(Text, nullable=True)
+    """
+        An optional pointer to the actual model that should be used when proxying requests.
+    """
+
+    name = Column(Text)
+    """
+        The human-readable display name of the model.
+    """
+
+    params = Column(JSONField)
+    """
+        Holds a JSON encoded blob of parameters, see `ModelParams`.
+    """
+
+    meta = Column(JSONField)
+    """
+        Holds a JSON encoded blob of metadata, see `ModelMeta`.
+    """
+
+    access_control = Column(JSON, nullable=True)  # Controls data access levels.
+    # Defines access control rules for this entry.
+    # - `None`: Public access, available to all users with the "user" role.
+    # - `{}`: Private access, restricted exclusively to the owner.
+    # - Custom permissions: Specific access control for reading and writing;
+    #   Can specify group or user-level restrictions:
+    #   {
+    #      "read": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      },
+    #      "write": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      }
+    #   }
+
+    is_active = Column(Boolean, default=True)
+
+    updated_at = Column(BigInteger)
+    created_at = Column(BigInteger)
+
+
+class ModelModel(BaseModel):
+    id: str
+    user_id: str
+    base_model_id: Optional[str] = None
+
+    name: str
+    params: ModelParams
+    meta: ModelMeta
+
+    access_control: Optional[dict] = None
+
+    is_active: bool
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class ModelUserResponse(ModelModel):
+    user: Optional[UserResponse] = None
+
+
+class ModelResponse(ModelModel):
+    pass
+
+
+class ModelForm(BaseModel):
+    id: str
+    base_model_id: Optional[str] = None
+    name: str
+    meta: ModelMeta
+    params: ModelParams
+    access_control: Optional[dict] = None
+    is_active: bool = True
+
+
+class ModelsTable:
+    def insert_new_model(
+        self, form_data: ModelForm, user_id: str
+    ) -> Optional[ModelModel]:
+        model = ModelModel(
+            **{
+                **form_data.model_dump(),
+                "user_id": user_id,
+                "created_at": int(time.time()),
+                "updated_at": int(time.time()),
+            }
+        )
+        try:
+            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:
+            print(e)
+            return None
+
+    def get_all_models(self) -> list[ModelModel]:
+        with get_db() as db:
+            return [ModelModel.model_validate(model) for model in db.query(Model).all()]
+
+    def get_models(self) -> list[ModelUserResponse]:
+        with get_db() as db:
+            models = []
+            for model in db.query(Model).filter(Model.base_model_id != None).all():
+                user = Users.get_user_by_id(model.user_id)
+                models.append(
+                    ModelUserResponse.model_validate(
+                        {
+                            **ModelModel.model_validate(model).model_dump(),
+                            "user": user.model_dump() if user else None,
+                        }
+                    )
+                )
+            return models
+
+    def get_base_models(self) -> list[ModelModel]:
+        with get_db() as db:
+            return [
+                ModelModel.model_validate(model)
+                for model in db.query(Model).filter(Model.base_model_id == None).all()
+            ]
+
+    def get_models_by_user_id(
+        self, user_id: str, permission: str = "write"
+    ) -> list[ModelUserResponse]:
+        models = self.get_models()
+        return [
+            model
+            for model in models
+            if model.user_id == user_id
+            or has_access(user_id, permission, model.access_control)
+        ]
+
+    def get_model_by_id(self, id: str) -> Optional[ModelModel]:
+        try:
+            with get_db() as db:
+                model = db.get(Model, id)
+                return ModelModel.model_validate(model)
+        except Exception:
+            return None
+
+    def toggle_model_by_id(self, id: str) -> Optional[ModelModel]:
+        with get_db() as db:
+            try:
+                is_active = db.query(Model).filter_by(id=id).first().is_active
+
+                db.query(Model).filter_by(id=id).update(
+                    {
+                        "is_active": not is_active,
+                        "updated_at": int(time.time()),
+                    }
+                )
+                db.commit()
+
+                return self.get_model_by_id(id)
+            except Exception:
+                return None
+
+    def update_model_by_id(self, id: str, model: ModelForm) -> Optional[ModelModel]:
+        try:
+            with get_db() as db:
+                # update only the fields that are present in the model
+                result = (
+                    db.query(Model)
+                    .filter_by(id=id)
+                    .update(model.model_dump(exclude={"id"}))
+                )
+                db.commit()
+
+                model = db.get(Model, id)
+                db.refresh(model)
+                return ModelModel.model_validate(model)
+        except Exception as e:
+            print(e)
+
+            return None
+
+    def delete_model_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Model).filter_by(id=id).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+    def delete_all_models(self) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Model).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+
+Models = ModelsTable()
diff --git a/backend/open_webui/apps/webui/models/prompts.py b/backend/open_webui/apps/webui/models/prompts.py
new file mode 100644
index 0000000000000000000000000000000000000000..fe9999195228e356a5c96483c61960314bdb3b73
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/prompts.py
@@ -0,0 +1,159 @@
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.apps.webui.models.users import Users, UserResponse
+
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text, JSON
+
+from open_webui.utils.access_control import has_access
+
+####################
+# Prompts DB Schema
+####################
+
+
+class Prompt(Base):
+    __tablename__ = "prompt"
+
+    command = Column(String, primary_key=True)
+    user_id = Column(String)
+    title = Column(Text)
+    content = Column(Text)
+    timestamp = Column(BigInteger)
+
+    access_control = Column(JSON, nullable=True)  # Controls data access levels.
+    # Defines access control rules for this entry.
+    # - `None`: Public access, available to all users with the "user" role.
+    # - `{}`: Private access, restricted exclusively to the owner.
+    # - Custom permissions: Specific access control for reading and writing;
+    #   Can specify group or user-level restrictions:
+    #   {
+    #      "read": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      },
+    #      "write": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      }
+    #   }
+
+
+class PromptModel(BaseModel):
+    command: str
+    user_id: str
+    title: str
+    content: str
+    timestamp: int  # timestamp in epoch
+
+    access_control: Optional[dict] = None
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class PromptUserResponse(PromptModel):
+    user: Optional[UserResponse] = None
+
+
+class PromptForm(BaseModel):
+    command: str
+    title: str
+    content: str
+    access_control: Optional[dict] = None
+
+
+class PromptsTable:
+    def insert_new_prompt(
+        self, user_id: str, form_data: PromptForm
+    ) -> Optional[PromptModel]:
+        prompt = PromptModel(
+            **{
+                "user_id": user_id,
+                **form_data.model_dump(),
+                "timestamp": int(time.time()),
+            }
+        )
+
+        try:
+            with get_db() as db:
+                result = Prompt(**prompt.model_dump())
+                db.add(result)
+                db.commit()
+                db.refresh(result)
+                if result:
+                    return PromptModel.model_validate(result)
+                else:
+                    return None
+        except Exception:
+            return None
+
+    def get_prompt_by_command(self, command: str) -> Optional[PromptModel]:
+        try:
+            with get_db() as db:
+                prompt = db.query(Prompt).filter_by(command=command).first()
+                return PromptModel.model_validate(prompt)
+        except Exception:
+            return None
+
+    def get_prompts(self) -> list[PromptUserResponse]:
+        with get_db() as db:
+            prompts = []
+
+            for prompt in db.query(Prompt).order_by(Prompt.timestamp.desc()).all():
+                user = Users.get_user_by_id(prompt.user_id)
+                prompts.append(
+                    PromptUserResponse.model_validate(
+                        {
+                            **PromptModel.model_validate(prompt).model_dump(),
+                            "user": user.model_dump() if user else None,
+                        }
+                    )
+                )
+
+            return prompts
+
+    def get_prompts_by_user_id(
+        self, user_id: str, permission: str = "write"
+    ) -> list[PromptUserResponse]:
+        prompts = self.get_prompts()
+
+        return [
+            prompt
+            for prompt in prompts
+            if prompt.user_id == user_id
+            or has_access(user_id, permission, prompt.access_control)
+        ]
+
+    def update_prompt_by_command(
+        self, command: str, form_data: PromptForm
+    ) -> Optional[PromptModel]:
+        try:
+            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.access_control = form_data.access_control
+                prompt.timestamp = int(time.time())
+                db.commit()
+                return PromptModel.model_validate(prompt)
+        except Exception:
+            return None
+
+    def delete_prompt_by_command(self, command: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Prompt).filter_by(command=command).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+
+Prompts = PromptsTable()
diff --git a/backend/open_webui/apps/webui/models/tags.py b/backend/open_webui/apps/webui/models/tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..7424a26604047257a9e0f189ab92da0ca4d04654
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/tags.py
@@ -0,0 +1,109 @@
+import logging
+import time
+import uuid
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, get_db
+
+
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, JSON, PrimaryKeyConstraint
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+####################
+# Tag DB Schema
+####################
+class Tag(Base):
+    __tablename__ = "tag"
+    id = Column(String)
+    name = Column(String)
+    user_id = Column(String)
+    meta = Column(JSON, nullable=True)
+
+    # Unique constraint ensuring (id, user_id) is unique, not just the `id` column
+    __table_args__ = (PrimaryKeyConstraint("id", "user_id", name="pk_id_user_id"),)
+
+
+class TagModel(BaseModel):
+    id: str
+    name: str
+    user_id: str
+    meta: Optional[dict] = None
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class TagChatIdForm(BaseModel):
+    name: str
+    chat_id: str
+
+
+class TagTable:
+    def insert_new_tag(self, name: str, user_id: str) -> Optional[TagModel]:
+        with get_db() as db:
+            id = name.replace(" ", "_").lower()
+            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:
+                print(e)
+                return None
+
+    def get_tag_by_name_and_user_id(
+        self, name: str, user_id: str
+    ) -> Optional[TagModel]:
+        try:
+            id = name.replace(" ", "_").lower()
+            with get_db() as db:
+                tag = db.query(Tag).filter_by(id=id, user_id=user_id).first()
+                return TagModel.model_validate(tag)
+        except Exception:
+            return None
+
+    def get_tags_by_user_id(self, user_id: str) -> list[TagModel]:
+        with get_db() as db:
+            return [
+                TagModel.model_validate(tag)
+                for tag in (db.query(Tag).filter_by(user_id=user_id).all())
+            ]
+
+    def get_tags_by_ids_and_user_id(
+        self, ids: list[str], user_id: str
+    ) -> list[TagModel]:
+        with get_db() as db:
+            return [
+                TagModel.model_validate(tag)
+                for tag in (
+                    db.query(Tag).filter(Tag.id.in_(ids), Tag.user_id == user_id).all()
+                )
+            ]
+
+    def delete_tag_by_name_and_user_id(self, name: str, user_id: str) -> bool:
+        try:
+            with get_db() as db:
+                id = name.replace(" ", "_").lower()
+                res = db.query(Tag).filter_by(id=id, user_id=user_id).delete()
+                log.debug(f"res: {res}")
+                db.commit()
+                return True
+        except Exception as e:
+            log.error(f"delete_tag: {e}")
+            return False
+
+
+Tags = TagTable()
diff --git a/backend/open_webui/apps/webui/models/tools.py b/backend/open_webui/apps/webui/models/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f798c3175829cc22e7fc4199cadeb991c70726b
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/tools.py
@@ -0,0 +1,258 @@
+import logging
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, JSONField, get_db
+from open_webui.apps.webui.models.users import Users, UserResponse
+from open_webui.env import SRC_LOG_LEVELS
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text, JSON
+
+from open_webui.utils.access_control import has_access
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+####################
+# Tools DB Schema
+####################
+
+
+class Tool(Base):
+    __tablename__ = "tool"
+
+    id = Column(String, primary_key=True)
+    user_id = Column(String)
+    name = Column(Text)
+    content = Column(Text)
+    specs = Column(JSONField)
+    meta = Column(JSONField)
+    valves = Column(JSONField)
+
+    access_control = Column(JSON, nullable=True)  # Controls data access levels.
+    # Defines access control rules for this entry.
+    # - `None`: Public access, available to all users with the "user" role.
+    # - `{}`: Private access, restricted exclusively to the owner.
+    # - Custom permissions: Specific access control for reading and writing;
+    #   Can specify group or user-level restrictions:
+    #   {
+    #      "read": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      },
+    #      "write": {
+    #          "group_ids": ["group_id1", "group_id2"],
+    #          "user_ids":  ["user_id1", "user_id2"]
+    #      }
+    #   }
+
+    updated_at = Column(BigInteger)
+    created_at = Column(BigInteger)
+
+
+class ToolMeta(BaseModel):
+    description: Optional[str] = None
+    manifest: Optional[dict] = {}
+
+
+class ToolModel(BaseModel):
+    id: str
+    user_id: str
+    name: str
+    content: str
+    specs: list[dict]
+    meta: ToolMeta
+    access_control: Optional[dict] = None
+
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class ToolUserModel(ToolModel):
+    user: Optional[UserResponse] = None
+
+
+class ToolResponse(BaseModel):
+    id: str
+    user_id: str
+    name: str
+    meta: ToolMeta
+    access_control: Optional[dict] = None
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+
+class ToolUserResponse(ToolResponse):
+    user: Optional[UserResponse] = None
+
+
+class ToolForm(BaseModel):
+    id: str
+    name: str
+    content: str
+    meta: ToolMeta
+    access_control: Optional[dict] = None
+
+
+class ToolValves(BaseModel):
+    valves: Optional[dict] = None
+
+
+class ToolsTable:
+    def insert_new_tool(
+        self, user_id: str, form_data: ToolForm, specs: list[dict]
+    ) -> Optional[ToolModel]:
+        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
+
+    def get_tool_by_id(self, id: str) -> Optional[ToolModel]:
+        try:
+            with get_db() as db:
+                tool = db.get(Tool, id)
+                return ToolModel.model_validate(tool)
+        except Exception:
+            return None
+
+    def get_tools(self) -> list[ToolUserModel]:
+        with get_db() as db:
+            tools = []
+            for tool in db.query(Tool).order_by(Tool.updated_at.desc()).all():
+                user = Users.get_user_by_id(tool.user_id)
+                tools.append(
+                    ToolUserModel.model_validate(
+                        {
+                            **ToolModel.model_validate(tool).model_dump(),
+                            "user": user.model_dump() if user else None,
+                        }
+                    )
+                )
+            return tools
+
+    def get_tools_by_user_id(
+        self, user_id: str, permission: str = "write"
+    ) -> list[ToolUserModel]:
+        tools = self.get_tools()
+
+        return [
+            tool
+            for tool in tools
+            if tool.user_id == user_id
+            or has_access(user_id, permission, tool.access_control)
+        ]
+
+    def get_tool_valves_by_id(self, id: str) -> Optional[dict]:
+        try:
+            with get_db() as db:
+                tool = db.get(Tool, id)
+                return tool.valves if tool.valves else {}
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_tool_valves_by_id(self, id: str, valves: dict) -> Optional[ToolValves]:
+        try:
+            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 Exception:
+            return None
+
+    def get_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump() if user.settings else {}
+
+            # Check if user has "tools" and "valves" settings
+            if "tools" not in user_settings:
+                user_settings["tools"] = {}
+            if "valves" not in user_settings["tools"]:
+                user_settings["tools"]["valves"] = {}
+
+            return user_settings["tools"]["valves"].get(id, {})
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_user_valves_by_id_and_user_id(
+        self, id: str, user_id: str, valves: dict
+    ) -> Optional[dict]:
+        try:
+            user = Users.get_user_by_id(user_id)
+            user_settings = user.settings.model_dump() if user.settings else {}
+
+            # Check if user has "tools" and "valves" settings
+            if "tools" not in user_settings:
+                user_settings["tools"] = {}
+            if "valves" not in user_settings["tools"]:
+                user_settings["tools"]["valves"] = {}
+
+            user_settings["tools"]["valves"][id] = valves
+
+            # Update the user settings in the database
+            Users.update_user_by_id(user_id, {"settings": user_settings})
+
+            return user_settings["tools"]["valves"][id]
+        except Exception as e:
+            print(f"An error occurred: {e}")
+            return None
+
+    def update_tool_by_id(self, id: str, updated: dict) -> Optional[ToolModel]:
+        try:
+            with get_db() as db:
+                db.query(Tool).filter_by(id=id).update(
+                    {**updated, "updated_at": int(time.time())}
+                )
+                db.commit()
+
+                tool = db.query(Tool).get(id)
+                db.refresh(tool)
+                return ToolModel.model_validate(tool)
+        except Exception:
+            return None
+
+    def delete_tool_by_id(self, id: str) -> bool:
+        try:
+            with get_db() as db:
+                db.query(Tool).filter_by(id=id).delete()
+                db.commit()
+
+                return True
+        except Exception:
+            return False
+
+
+Tools = ToolsTable()
diff --git a/backend/open_webui/apps/webui/models/users.py b/backend/open_webui/apps/webui/models/users.py
new file mode 100644
index 0000000000000000000000000000000000000000..5bbcc309943f3ce87f8fd637a072358fa8ebdd55
--- /dev/null
+++ b/backend/open_webui/apps/webui/models/users.py
@@ -0,0 +1,269 @@
+import time
+from typing import Optional
+
+from open_webui.apps.webui.internal.db import Base, JSONField, get_db
+from open_webui.apps.webui.models.chats import Chats
+from pydantic import BaseModel, ConfigDict
+from sqlalchemy import BigInteger, Column, String, Text
+
+####################
+# User DB Schema
+####################
+
+
+class User(Base):
+    __tablename__ = "user"
+
+    id = Column(String, primary_key=True)
+    name = Column(String)
+    email = Column(String)
+    role = Column(String)
+    profile_image_url = Column(Text)
+
+    last_active_at = Column(BigInteger)
+    updated_at = Column(BigInteger)
+    created_at = Column(BigInteger)
+
+    api_key = Column(String, nullable=True, unique=True)
+    settings = Column(JSONField, nullable=True)
+    info = Column(JSONField, nullable=True)
+
+    oauth_sub = Column(Text, unique=True)
+
+
+class UserSettings(BaseModel):
+    ui: Optional[dict] = {}
+    model_config = ConfigDict(extra="allow")
+    pass
+
+
+class UserModel(BaseModel):
+    id: str
+    name: str
+    email: str
+    role: str = "pending"
+    profile_image_url: str
+
+    last_active_at: int  # timestamp in epoch
+    updated_at: int  # timestamp in epoch
+    created_at: int  # timestamp in epoch
+
+    api_key: Optional[str] = None
+    settings: Optional[UserSettings] = None
+    info: Optional[dict] = None
+
+    oauth_sub: Optional[str] = None
+
+    model_config = ConfigDict(from_attributes=True)
+
+
+####################
+# Forms
+####################
+
+
+class UserResponse(BaseModel):
+    id: str
+    name: str
+    email: str
+    role: str
+    profile_image_url: str
+
+
+class UserRoleUpdateForm(BaseModel):
+    id: str
+    role: str
+
+
+class UserUpdateForm(BaseModel):
+    name: str
+    email: str
+    profile_image_url: str
+    password: Optional[str] = None
+
+
+class UsersTable:
+    def insert_new_user(
+        self,
+        id: str,
+        name: str,
+        email: str,
+        profile_image_url: str = "/user.png",
+        role: str = "pending",
+        oauth_sub: Optional[str] = None,
+    ) -> Optional[UserModel]:
+        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]:
+        try:
+            with get_db() as db:
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def get_user_by_api_key(self, api_key: str) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                user = db.query(User).filter_by(api_key=api_key).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def get_user_by_email(self, email: str) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                user = db.query(User).filter_by(email=email).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def get_user_by_oauth_sub(self, sub: str) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                user = db.query(User).filter_by(oauth_sub=sub).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def get_users(self, skip: int = 0, limit: int = 50) -> list[UserModel]:
+        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]:
+        with get_db() as db:
+            return db.query(User).count()
+
+    def get_first_user(self) -> UserModel:
+        try:
+            with get_db() as db:
+                user = db.query(User).order_by(User.created_at).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def update_user_role_by_id(self, id: str, role: str) -> Optional[UserModel]:
+        try:
+            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 Exception:
+            return None
+
+    def update_user_profile_image_url_by_id(
+        self, id: str, profile_image_url: str
+    ) -> Optional[UserModel]:
+        try:
+            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 Exception:
+            return None
+
+    def update_user_last_active_by_id(self, id: str) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update(
+                    {"last_active_at": int(time.time())}
+                )
+                db.commit()
+
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def update_user_oauth_sub_by_id(
+        self, id: str, oauth_sub: str
+    ) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update({"oauth_sub": oauth_sub})
+                db.commit()
+
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
+        except Exception:
+            return None
+
+    def update_user_by_id(self, id: str, updated: dict) -> Optional[UserModel]:
+        try:
+            with get_db() as db:
+                db.query(User).filter_by(id=id).update(updated)
+                db.commit()
+
+                user = db.query(User).filter_by(id=id).first()
+                return UserModel.model_validate(user)
+                # return UserModel(**user.dict())
+        except Exception:
+            return None
+
+    def delete_user_by_id(self, id: str) -> bool:
+        try:
+            # Delete User Chats
+            result = Chats.delete_chats_by_user_id(id)
+
+            if result:
+                with get_db() as db:
+                    # Delete User
+                    db.query(User).filter_by(id=id).delete()
+                    db.commit()
+
+                return True
+            else:
+                return False
+        except Exception:
+            return False
+
+    def update_user_api_key_by_id(self, id: str, api_key: str) -> str:
+        try:
+            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 Exception:
+            return False
+
+    def get_user_api_key_by_id(self, id: str) -> Optional[str]:
+        try:
+            with get_db() as db:
+                user = db.query(User).filter_by(id=id).first()
+                return user.api_key
+        except Exception:
+            return None
+
+
+Users = UsersTable()
diff --git a/backend/open_webui/apps/webui/routers/auths.py b/backend/open_webui/apps/webui/routers/auths.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f175f366f9b87ff745666f0a0aae15a16f314b9
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/auths.py
@@ -0,0 +1,781 @@
+import re
+import uuid
+import time
+import datetime
+import logging
+
+from open_webui.apps.webui.models.auths import (
+    AddUserForm,
+    ApiKey,
+    Auths,
+    Token,
+    LdapForm,
+    SigninForm,
+    SigninResponse,
+    SignupForm,
+    UpdatePasswordForm,
+    UpdateProfileForm,
+    UserResponse,
+)
+from open_webui.apps.webui.models.users import Users
+
+from open_webui.constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
+from open_webui.env import (
+    WEBUI_AUTH,
+    WEBUI_AUTH_TRUSTED_EMAIL_HEADER,
+    WEBUI_AUTH_TRUSTED_NAME_HEADER,
+    WEBUI_SESSION_COOKIE_SAME_SITE,
+    WEBUI_SESSION_COOKIE_SECURE,
+    SRC_LOG_LEVELS,
+)
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from fastapi.responses import Response
+from pydantic import BaseModel
+from open_webui.utils.misc import parse_duration, validate_email_format
+from open_webui.utils.utils import (
+    create_api_key,
+    create_token,
+    get_admin_user,
+    get_verified_user,
+    get_current_user,
+    get_password_hash,
+)
+from open_webui.utils.webhook import post_webhook
+from open_webui.utils.access_control import get_permissions
+
+from typing import Optional, List
+
+from ssl import CERT_REQUIRED, PROTOCOL_TLS
+from ldap3 import Server, Connection, ALL, Tls
+from ldap3.utils.conv import escape_filter_chars
+
+router = APIRouter()
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MAIN"])
+
+############################
+# GetSessionUser
+############################
+
+
+class SessionUserResponse(Token, UserResponse):
+    expires_at: Optional[int] = None
+    permissions: Optional[dict] = None
+
+
+@router.get("/", response_model=SessionUserResponse)
+async def get_session_user(
+    request: Request, response: Response, user=Depends(get_current_user)
+):
+    expires_delta = parse_duration(request.app.state.config.JWT_EXPIRES_IN)
+    expires_at = None
+    if expires_delta:
+        expires_at = int(time.time()) + int(expires_delta.total_seconds())
+
+    token = create_token(
+        data={"id": user.id},
+        expires_delta=expires_delta,
+    )
+
+    datetime_expires_at = (
+        datetime.datetime.fromtimestamp(expires_at, datetime.timezone.utc)
+        if expires_at
+        else None
+    )
+
+    # Set the cookie token
+    response.set_cookie(
+        key="token",
+        value=token,
+        expires=datetime_expires_at,
+        httponly=True,  # Ensures the cookie is not accessible via JavaScript
+        samesite=WEBUI_SESSION_COOKIE_SAME_SITE,
+        secure=WEBUI_SESSION_COOKIE_SECURE,
+    )
+
+    user_permissions = get_permissions(
+        user.id, request.app.state.config.USER_PERMISSIONS
+    )
+
+    return {
+        "token": token,
+        "token_type": "Bearer",
+        "expires_at": expires_at,
+        "id": user.id,
+        "email": user.email,
+        "name": user.name,
+        "role": user.role,
+        "profile_image_url": user.profile_image_url,
+        "permissions": user_permissions,
+    }
+
+
+############################
+# Update Profile
+############################
+
+
+@router.post("/update/profile", response_model=UserResponse)
+async def update_profile(
+    form_data: UpdateProfileForm, session_user=Depends(get_verified_user)
+):
+    if session_user:
+        user = Users.update_user_by_id(
+            session_user.id,
+            {"profile_image_url": form_data.profile_image_url, "name": form_data.name},
+        )
+        if user:
+            return user
+        else:
+            raise HTTPException(400, detail=ERROR_MESSAGES.DEFAULT())
+    else:
+        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+
+
+############################
+# Update Password
+############################
+
+
+@router.post("/update/password", response_model=bool)
+async def update_password(
+    form_data: UpdatePasswordForm, session_user=Depends(get_current_user)
+):
+    if WEBUI_AUTH_TRUSTED_EMAIL_HEADER:
+        raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED)
+    if session_user:
+        user = Auths.authenticate_user(session_user.email, form_data.password)
+
+        if user:
+            hashed = get_password_hash(form_data.new_password)
+            return Auths.update_user_password_by_id(user.id, hashed)
+        else:
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_PASSWORD)
+    else:
+        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+
+
+############################
+# LDAP Authentication
+############################
+@router.post("/ldap", response_model=SigninResponse)
+async def ldap_auth(request: Request, response: Response, form_data: LdapForm):
+    ENABLE_LDAP = request.app.state.config.ENABLE_LDAP
+    LDAP_SERVER_LABEL = request.app.state.config.LDAP_SERVER_LABEL
+    LDAP_SERVER_HOST = request.app.state.config.LDAP_SERVER_HOST
+    LDAP_SERVER_PORT = request.app.state.config.LDAP_SERVER_PORT
+    LDAP_ATTRIBUTE_FOR_USERNAME = request.app.state.config.LDAP_ATTRIBUTE_FOR_USERNAME
+    LDAP_SEARCH_BASE = request.app.state.config.LDAP_SEARCH_BASE
+    LDAP_SEARCH_FILTERS = request.app.state.config.LDAP_SEARCH_FILTERS
+    LDAP_APP_DN = request.app.state.config.LDAP_APP_DN
+    LDAP_APP_PASSWORD = request.app.state.config.LDAP_APP_PASSWORD
+    LDAP_USE_TLS = request.app.state.config.LDAP_USE_TLS
+    LDAP_CA_CERT_FILE = request.app.state.config.LDAP_CA_CERT_FILE
+    LDAP_CIPHERS = (
+        request.app.state.config.LDAP_CIPHERS
+        if request.app.state.config.LDAP_CIPHERS
+        else "ALL"
+    )
+
+    if not ENABLE_LDAP:
+        raise HTTPException(400, detail="LDAP authentication is not enabled")
+
+    try:
+        tls = Tls(
+            validate=CERT_REQUIRED,
+            version=PROTOCOL_TLS,
+            ca_certs_file=LDAP_CA_CERT_FILE,
+            ciphers=LDAP_CIPHERS,
+        )
+    except Exception as e:
+        log.error(f"An error occurred on TLS: {str(e)}")
+        raise HTTPException(400, detail=str(e))
+
+    try:
+        server = Server(
+            host=LDAP_SERVER_HOST,
+            port=LDAP_SERVER_PORT,
+            get_info=ALL,
+            use_ssl=LDAP_USE_TLS,
+            tls=tls,
+        )
+        connection_app = Connection(
+            server,
+            LDAP_APP_DN,
+            LDAP_APP_PASSWORD,
+            auto_bind="NONE",
+            authentication="SIMPLE",
+        )
+        if not connection_app.bind():
+            raise HTTPException(400, detail="Application account bind failed")
+
+        search_success = connection_app.search(
+            search_base=LDAP_SEARCH_BASE,
+            search_filter=f"(&({LDAP_ATTRIBUTE_FOR_USERNAME}={escape_filter_chars(form_data.user.lower())}){LDAP_SEARCH_FILTERS})",
+            attributes=[f"{LDAP_ATTRIBUTE_FOR_USERNAME}", "mail", "cn"],
+        )
+
+        if not search_success:
+            raise HTTPException(400, detail="User not found in the LDAP server")
+
+        entry = connection_app.entries[0]
+        username = str(entry[f"{LDAP_ATTRIBUTE_FOR_USERNAME}"]).lower()
+        mail = str(entry["mail"])
+        cn = str(entry["cn"])
+        user_dn = entry.entry_dn
+
+        if username == form_data.user.lower():
+            connection_user = Connection(
+                server,
+                user_dn,
+                form_data.password,
+                auto_bind="NONE",
+                authentication="SIMPLE",
+            )
+            if not connection_user.bind():
+                raise HTTPException(400, f"Authentication failed for {form_data.user}")
+
+            user = Users.get_user_by_email(mail)
+            if not user:
+                try:
+                    role = (
+                        "admin"
+                        if Users.get_num_users() == 0
+                        else request.app.state.config.DEFAULT_USER_ROLE
+                    )
+
+                    user = Auths.insert_new_auth(
+                        email=mail, password=str(uuid.uuid4()), name=cn, role=role
+                    )
+
+                    if not user:
+                        raise HTTPException(
+                            500, detail=ERROR_MESSAGES.CREATE_USER_ERROR
+                        )
+
+                except HTTPException:
+                    raise
+                except Exception as err:
+                    raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
+
+            user = Auths.authenticate_user_by_trusted_header(mail)
+
+            if user:
+                token = create_token(
+                    data={"id": user.id},
+                    expires_delta=parse_duration(
+                        request.app.state.config.JWT_EXPIRES_IN
+                    ),
+                )
+
+                # Set the cookie token
+                response.set_cookie(
+                    key="token",
+                    value=token,
+                    httponly=True,  # Ensures the cookie is not accessible via JavaScript
+                )
+
+                return {
+                    "token": token,
+                    "token_type": "Bearer",
+                    "id": user.id,
+                    "email": user.email,
+                    "name": user.name,
+                    "role": user.role,
+                    "profile_image_url": user.profile_image_url,
+                }
+            else:
+                raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+        else:
+            raise HTTPException(
+                400,
+                f"User {form_data.user} does not match the record. Search result: {str(entry[f'{LDAP_ATTRIBUTE_FOR_USERNAME}'])}",
+            )
+    except Exception as e:
+        raise HTTPException(400, detail=str(e))
+
+
+############################
+# SignIn
+############################
+
+
+@router.post("/signin", response_model=SessionUserResponse)
+async def signin(request: Request, response: Response, form_data: SigninForm):
+    if WEBUI_AUTH_TRUSTED_EMAIL_HEADER:
+        if WEBUI_AUTH_TRUSTED_EMAIL_HEADER not in request.headers:
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_TRUSTED_HEADER)
+
+        trusted_email = request.headers[WEBUI_AUTH_TRUSTED_EMAIL_HEADER].lower()
+        trusted_name = trusted_email
+        if WEBUI_AUTH_TRUSTED_NAME_HEADER:
+            trusted_name = request.headers.get(
+                WEBUI_AUTH_TRUSTED_NAME_HEADER, trusted_email
+            )
+        if not Users.get_user_by_email(trusted_email.lower()):
+            await signup(
+                request,
+                response,
+                SignupForm(
+                    email=trusted_email, password=str(uuid.uuid4()), name=trusted_name
+                ),
+            )
+        user = Auths.authenticate_user_by_trusted_header(trusted_email)
+    elif WEBUI_AUTH == False:
+        admin_email = "admin@localhost"
+        admin_password = "admin"
+
+        if Users.get_user_by_email(admin_email.lower()):
+            user = Auths.authenticate_user(admin_email.lower(), admin_password)
+        else:
+            if Users.get_num_users() != 0:
+                raise HTTPException(400, detail=ERROR_MESSAGES.EXISTING_USERS)
+
+            await signup(
+                request,
+                response,
+                SignupForm(email=admin_email, password=admin_password, name="User"),
+            )
+
+            user = Auths.authenticate_user(admin_email.lower(), admin_password)
+    else:
+        user = Auths.authenticate_user(form_data.email.lower(), form_data.password)
+
+    if user:
+
+        expires_delta = parse_duration(request.app.state.config.JWT_EXPIRES_IN)
+        expires_at = None
+        if expires_delta:
+            expires_at = int(time.time()) + int(expires_delta.total_seconds())
+
+        token = create_token(
+            data={"id": user.id},
+            expires_delta=expires_delta,
+        )
+
+        datetime_expires_at = (
+            datetime.datetime.fromtimestamp(expires_at, datetime.timezone.utc)
+            if expires_at
+            else None
+        )
+
+        # Set the cookie token
+        response.set_cookie(
+            key="token",
+            value=token,
+            expires=datetime_expires_at,
+            httponly=True,  # Ensures the cookie is not accessible via JavaScript
+            samesite=WEBUI_SESSION_COOKIE_SAME_SITE,
+            secure=WEBUI_SESSION_COOKIE_SECURE,
+        )
+
+        user_permissions = get_permissions(
+            user.id, request.app.state.config.USER_PERMISSIONS
+        )
+
+        return {
+            "token": token,
+            "token_type": "Bearer",
+            "expires_at": expires_at,
+            "id": user.id,
+            "email": user.email,
+            "name": user.name,
+            "role": user.role,
+            "profile_image_url": user.profile_image_url,
+            "permissions": user_permissions,
+        }
+    else:
+        raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+
+
+############################
+# SignUp
+############################
+
+
+@router.post("/signup", response_model=SessionUserResponse)
+async def signup(request: Request, response: Response, form_data: SignupForm):
+    if WEBUI_AUTH:
+        if (
+            not request.app.state.config.ENABLE_SIGNUP
+            or not request.app.state.config.ENABLE_LOGIN_FORM
+        ):
+            raise HTTPException(
+                status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
+            )
+    else:
+        if Users.get_num_users() != 0:
+            raise HTTPException(
+                status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
+            )
+
+    if not validate_email_format(form_data.email.lower()):
+        raise HTTPException(
+            status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
+        )
+
+    if Users.get_user_by_email(form_data.email.lower()):
+        raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
+
+    try:
+        role = (
+            "admin"
+            if Users.get_num_users() == 0
+            else request.app.state.config.DEFAULT_USER_ROLE
+        )
+
+        if Users.get_num_users() == 0:
+            # Disable signup after the first user is created
+            request.app.state.config.ENABLE_SIGNUP = False
+
+        hashed = get_password_hash(form_data.password)
+        user = Auths.insert_new_auth(
+            form_data.email.lower(),
+            hashed,
+            form_data.name,
+            form_data.profile_image_url,
+            role,
+        )
+
+        if user:
+            expires_delta = parse_duration(request.app.state.config.JWT_EXPIRES_IN)
+            expires_at = None
+            if expires_delta:
+                expires_at = int(time.time()) + int(expires_delta.total_seconds())
+
+            token = create_token(
+                data={"id": user.id},
+                expires_delta=expires_delta,
+            )
+
+            datetime_expires_at = (
+                datetime.datetime.fromtimestamp(expires_at, datetime.timezone.utc)
+                if expires_at
+                else None
+            )
+
+            # Set the cookie token
+            response.set_cookie(
+                key="token",
+                value=token,
+                expires=datetime_expires_at,
+                httponly=True,  # Ensures the cookie is not accessible via JavaScript
+                samesite=WEBUI_SESSION_COOKIE_SAME_SITE,
+                secure=WEBUI_SESSION_COOKIE_SECURE,
+            )
+
+            if request.app.state.config.WEBHOOK_URL:
+                post_webhook(
+                    request.app.state.config.WEBHOOK_URL,
+                    WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
+                    {
+                        "action": "signup",
+                        "message": WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
+                        "user": user.model_dump_json(exclude_none=True),
+                    },
+                )
+
+            user_permissions = get_permissions(
+                user.id, request.app.state.config.USER_PERMISSIONS
+            )
+
+            return {
+                "token": token,
+                "token_type": "Bearer",
+                "expires_at": expires_at,
+                "id": user.id,
+                "email": user.email,
+                "name": user.name,
+                "role": user.role,
+                "profile_image_url": user.profile_image_url,
+                "permissions": user_permissions,
+            }
+        else:
+            raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
+    except Exception as err:
+        raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
+
+
+@router.get("/signout")
+async def signout(response: Response):
+    response.delete_cookie("token")
+    return {"status": True}
+
+
+############################
+# AddUser
+############################
+
+
+@router.post("/add", response_model=SigninResponse)
+async def add_user(form_data: AddUserForm, user=Depends(get_admin_user)):
+    if not validate_email_format(form_data.email.lower()):
+        raise HTTPException(
+            status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.INVALID_EMAIL_FORMAT
+        )
+
+    if Users.get_user_by_email(form_data.email.lower()):
+        raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
+
+    try:
+        print(form_data)
+        hashed = get_password_hash(form_data.password)
+        user = Auths.insert_new_auth(
+            form_data.email.lower(),
+            hashed,
+            form_data.name,
+            form_data.profile_image_url,
+            form_data.role,
+        )
+
+        if user:
+            token = create_token(data={"id": user.id})
+            return {
+                "token": token,
+                "token_type": "Bearer",
+                "id": user.id,
+                "email": user.email,
+                "name": user.name,
+                "role": user.role,
+                "profile_image_url": user.profile_image_url,
+            }
+        else:
+            raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_USER_ERROR)
+    except Exception as err:
+        raise HTTPException(500, detail=ERROR_MESSAGES.DEFAULT(err))
+
+
+############################
+# GetAdminDetails
+############################
+
+
+@router.get("/admin/details")
+async def get_admin_details(request: Request, user=Depends(get_current_user)):
+    if request.app.state.config.SHOW_ADMIN_DETAILS:
+        admin_email = request.app.state.config.ADMIN_EMAIL
+        admin_name = None
+
+        print(admin_email, admin_name)
+
+        if admin_email:
+            admin = Users.get_user_by_email(admin_email)
+            if admin:
+                admin_name = admin.name
+        else:
+            admin = Users.get_first_user()
+            if admin:
+                admin_email = admin.email
+                admin_name = admin.name
+
+        return {
+            "name": admin_name,
+            "email": admin_email,
+        }
+    else:
+        raise HTTPException(400, detail=ERROR_MESSAGES.ACTION_PROHIBITED)
+
+
+############################
+# ToggleSignUp
+############################
+
+
+@router.get("/admin/config")
+async def get_admin_config(request: Request, user=Depends(get_admin_user)):
+    return {
+        "SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
+        "ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
+        "ENABLE_API_KEY": request.app.state.config.ENABLE_API_KEY,
+        "DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
+        "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
+        "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
+        "ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING,
+    }
+
+
+class AdminConfig(BaseModel):
+    SHOW_ADMIN_DETAILS: bool
+    ENABLE_SIGNUP: bool
+    ENABLE_API_KEY: bool
+    DEFAULT_USER_ROLE: str
+    JWT_EXPIRES_IN: str
+    ENABLE_COMMUNITY_SHARING: bool
+    ENABLE_MESSAGE_RATING: bool
+
+
+@router.post("/admin/config")
+async def update_admin_config(
+    request: Request, form_data: AdminConfig, user=Depends(get_admin_user)
+):
+    request.app.state.config.SHOW_ADMIN_DETAILS = form_data.SHOW_ADMIN_DETAILS
+    request.app.state.config.ENABLE_SIGNUP = form_data.ENABLE_SIGNUP
+    request.app.state.config.ENABLE_API_KEY = form_data.ENABLE_API_KEY
+
+    if form_data.DEFAULT_USER_ROLE in ["pending", "user", "admin"]:
+        request.app.state.config.DEFAULT_USER_ROLE = form_data.DEFAULT_USER_ROLE
+
+    pattern = r"^(-1|0|(-?\d+(\.\d+)?)(ms|s|m|h|d|w))$"
+
+    # Check if the input string matches the pattern
+    if re.match(pattern, form_data.JWT_EXPIRES_IN):
+        request.app.state.config.JWT_EXPIRES_IN = form_data.JWT_EXPIRES_IN
+
+    request.app.state.config.ENABLE_COMMUNITY_SHARING = (
+        form_data.ENABLE_COMMUNITY_SHARING
+    )
+    request.app.state.config.ENABLE_MESSAGE_RATING = form_data.ENABLE_MESSAGE_RATING
+
+    return {
+        "SHOW_ADMIN_DETAILS": request.app.state.config.SHOW_ADMIN_DETAILS,
+        "ENABLE_SIGNUP": request.app.state.config.ENABLE_SIGNUP,
+        "ENABLE_API_KEY": request.app.state.config.ENABLE_API_KEY,
+        "DEFAULT_USER_ROLE": request.app.state.config.DEFAULT_USER_ROLE,
+        "JWT_EXPIRES_IN": request.app.state.config.JWT_EXPIRES_IN,
+        "ENABLE_COMMUNITY_SHARING": request.app.state.config.ENABLE_COMMUNITY_SHARING,
+        "ENABLE_MESSAGE_RATING": request.app.state.config.ENABLE_MESSAGE_RATING,
+    }
+
+
+class LdapServerConfig(BaseModel):
+    label: str
+    host: str
+    port: Optional[int] = None
+    attribute_for_username: str = "uid"
+    app_dn: str
+    app_dn_password: str
+    search_base: str
+    search_filters: str = ""
+    use_tls: bool = True
+    certificate_path: Optional[str] = None
+    ciphers: Optional[str] = "ALL"
+
+
+@router.get("/admin/config/ldap/server", response_model=LdapServerConfig)
+async def get_ldap_server(request: Request, user=Depends(get_admin_user)):
+    return {
+        "label": request.app.state.config.LDAP_SERVER_LABEL,
+        "host": request.app.state.config.LDAP_SERVER_HOST,
+        "port": request.app.state.config.LDAP_SERVER_PORT,
+        "attribute_for_username": request.app.state.config.LDAP_ATTRIBUTE_FOR_USERNAME,
+        "app_dn": request.app.state.config.LDAP_APP_DN,
+        "app_dn_password": request.app.state.config.LDAP_APP_PASSWORD,
+        "search_base": request.app.state.config.LDAP_SEARCH_BASE,
+        "search_filters": request.app.state.config.LDAP_SEARCH_FILTERS,
+        "use_tls": request.app.state.config.LDAP_USE_TLS,
+        "certificate_path": request.app.state.config.LDAP_CA_CERT_FILE,
+        "ciphers": request.app.state.config.LDAP_CIPHERS,
+    }
+
+
+@router.post("/admin/config/ldap/server")
+async def update_ldap_server(
+    request: Request, form_data: LdapServerConfig, user=Depends(get_admin_user)
+):
+    required_fields = [
+        "label",
+        "host",
+        "attribute_for_username",
+        "app_dn",
+        "app_dn_password",
+        "search_base",
+    ]
+    for key in required_fields:
+        value = getattr(form_data, key)
+        if not value:
+            raise HTTPException(400, detail=f"Required field {key} is empty")
+
+    if form_data.use_tls and not form_data.certificate_path:
+        raise HTTPException(
+            400, detail="TLS is enabled but certificate file path is missing"
+        )
+
+    request.app.state.config.LDAP_SERVER_LABEL = form_data.label
+    request.app.state.config.LDAP_SERVER_HOST = form_data.host
+    request.app.state.config.LDAP_SERVER_PORT = form_data.port
+    request.app.state.config.LDAP_ATTRIBUTE_FOR_USERNAME = (
+        form_data.attribute_for_username
+    )
+    request.app.state.config.LDAP_APP_DN = form_data.app_dn
+    request.app.state.config.LDAP_APP_PASSWORD = form_data.app_dn_password
+    request.app.state.config.LDAP_SEARCH_BASE = form_data.search_base
+    request.app.state.config.LDAP_SEARCH_FILTERS = form_data.search_filters
+    request.app.state.config.LDAP_USE_TLS = form_data.use_tls
+    request.app.state.config.LDAP_CA_CERT_FILE = form_data.certificate_path
+    request.app.state.config.LDAP_CIPHERS = form_data.ciphers
+
+    return {
+        "label": request.app.state.config.LDAP_SERVER_LABEL,
+        "host": request.app.state.config.LDAP_SERVER_HOST,
+        "port": request.app.state.config.LDAP_SERVER_PORT,
+        "attribute_for_username": request.app.state.config.LDAP_ATTRIBUTE_FOR_USERNAME,
+        "app_dn": request.app.state.config.LDAP_APP_DN,
+        "app_dn_password": request.app.state.config.LDAP_APP_PASSWORD,
+        "search_base": request.app.state.config.LDAP_SEARCH_BASE,
+        "search_filters": request.app.state.config.LDAP_SEARCH_FILTERS,
+        "use_tls": request.app.state.config.LDAP_USE_TLS,
+        "certificate_path": request.app.state.config.LDAP_CA_CERT_FILE,
+        "ciphers": request.app.state.config.LDAP_CIPHERS,
+    }
+
+
+@router.get("/admin/config/ldap")
+async def get_ldap_config(request: Request, user=Depends(get_admin_user)):
+    return {"ENABLE_LDAP": request.app.state.config.ENABLE_LDAP}
+
+
+class LdapConfigForm(BaseModel):
+    enable_ldap: Optional[bool] = None
+
+
+@router.post("/admin/config/ldap")
+async def update_ldap_config(
+    request: Request, form_data: LdapConfigForm, user=Depends(get_admin_user)
+):
+    request.app.state.config.ENABLE_LDAP = form_data.enable_ldap
+    return {"ENABLE_LDAP": request.app.state.config.ENABLE_LDAP}
+
+
+############################
+# API Key
+############################
+
+
+# create api key
+@router.post("/api_key", response_model=ApiKey)
+async def generate_api_key(request: Request, user=Depends(get_current_user)):
+    if not request.app.state.config.ENABLE_API_KEY:
+        raise HTTPException(
+            status.HTTP_403_FORBIDDEN,
+            detail=ERROR_MESSAGES.API_KEY_CREATION_NOT_ALLOWED,
+        )
+
+    api_key = create_api_key()
+    success = Users.update_user_api_key_by_id(user.id, api_key)
+
+    if success:
+        return {
+            "api_key": api_key,
+        }
+    else:
+        raise HTTPException(500, detail=ERROR_MESSAGES.CREATE_API_KEY_ERROR)
+
+
+# delete api key
+@router.delete("/api_key", response_model=bool)
+async def delete_api_key(user=Depends(get_current_user)):
+    success = Users.update_user_api_key_by_id(user.id, None)
+    return success
+
+
+# get api key
+@router.get("/api_key", response_model=ApiKey)
+async def get_api_key(user=Depends(get_current_user)):
+    api_key = Users.get_user_api_key_by_id(user.id)
+    if api_key:
+        return {
+            "api_key": api_key,
+        }
+    else:
+        raise HTTPException(404, detail=ERROR_MESSAGES.API_KEY_NOT_FOUND)
diff --git a/backend/open_webui/apps/webui/routers/chats.py b/backend/open_webui/apps/webui/routers/chats.py
new file mode 100644
index 0000000000000000000000000000000000000000..db95337d532223c9bf06c1c41358f09e32ef7954
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/chats.py
@@ -0,0 +1,669 @@
+import json
+import logging
+from typing import Optional
+
+from open_webui.apps.webui.models.chats import (
+    ChatForm,
+    ChatImportForm,
+    ChatResponse,
+    Chats,
+    ChatTitleIdResponse,
+)
+from open_webui.apps.webui.models.tags import TagModel, Tags
+from open_webui.apps.webui.models.folders import Folders
+
+from open_webui.config import ENABLE_ADMIN_CHAT_ACCESS, ENABLE_ADMIN_EXPORT
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import SRC_LOG_LEVELS
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from pydantic import BaseModel
+
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_permission
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+router = APIRouter()
+
+############################
+# GetChatList
+############################
+
+
+@router.get("/", response_model=list[ChatTitleIdResponse])
+@router.get("/list", response_model=list[ChatTitleIdResponse])
+async def get_session_user_chat_list(
+    user=Depends(get_verified_user), page: Optional[int] = None
+):
+    if page is not None:
+        limit = 60
+        skip = (page - 1) * limit
+
+        return Chats.get_chat_title_id_list_by_user_id(user.id, skip=skip, limit=limit)
+    else:
+        return Chats.get_chat_title_id_list_by_user_id(user.id)
+
+
+############################
+# DeleteAllChats
+############################
+
+
+@router.delete("/", response_model=bool)
+async def delete_all_user_chats(request: Request, user=Depends(get_verified_user)):
+
+    if user.role == "user" and not has_permission(
+        user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    result = Chats.delete_chats_by_user_id(user.id)
+    return result
+
+
+############################
+# GetUserChatList
+############################
+
+
+@router.get("/list/user/{user_id}", response_model=list[ChatTitleIdResponse])
+async def get_user_chat_list_by_user_id(
+    user_id: str,
+    user=Depends(get_admin_user),
+    skip: int = 0,
+    limit: int = 50,
+):
+    if not ENABLE_ADMIN_CHAT_ACCESS:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+    return Chats.get_chat_list_by_user_id(
+        user_id, include_archived=True, skip=skip, limit=limit
+    )
+
+
+############################
+# CreateNewChat
+############################
+
+
+@router.post("/new", response_model=Optional[ChatResponse])
+async def create_new_chat(form_data: ChatForm, user=Depends(get_verified_user)):
+    try:
+        chat = Chats.insert_new_chat(user.id, form_data)
+        return ChatResponse(**chat.model_dump())
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# ImportChat
+############################
+
+
+@router.post("/import", response_model=Optional[ChatResponse])
+async def import_chat(form_data: ChatImportForm, user=Depends(get_verified_user)):
+    try:
+        chat = Chats.import_chat(user.id, form_data)
+        if chat:
+            tags = chat.meta.get("tags", [])
+            for tag_id in tags:
+                tag_id = tag_id.replace(" ", "_").lower()
+                tag_name = " ".join([word.capitalize() for word in tag_id.split("_")])
+                if (
+                    tag_id != "none"
+                    and Tags.get_tag_by_name_and_user_id(tag_name, user.id) is None
+                ):
+                    Tags.insert_new_tag(tag_name, user.id)
+
+        return ChatResponse(**chat.model_dump())
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# GetChats
+############################
+
+
+@router.get("/search", response_model=list[ChatTitleIdResponse])
+async def search_user_chats(
+    text: str, page: Optional[int] = None, user=Depends(get_verified_user)
+):
+    if page is None:
+        page = 1
+
+    limit = 60
+    skip = (page - 1) * limit
+
+    chat_list = [
+        ChatTitleIdResponse(**chat.model_dump())
+        for chat in Chats.get_chats_by_user_id_and_search_text(
+            user.id, text, skip=skip, limit=limit
+        )
+    ]
+
+    # Delete tag if no chat is found
+    words = text.strip().split(" ")
+    if page == 1 and len(words) == 1 and words[0].startswith("tag:"):
+        tag_id = words[0].replace("tag:", "")
+        if len(chat_list) == 0:
+            if Tags.get_tag_by_name_and_user_id(tag_id, user.id):
+                log.debug(f"deleting tag: {tag_id}")
+                Tags.delete_tag_by_name_and_user_id(tag_id, user.id)
+
+    return chat_list
+
+
+############################
+# GetChatsByFolderId
+############################
+
+
+@router.get("/folder/{folder_id}", response_model=list[ChatResponse])
+async def get_chats_by_folder_id(folder_id: str, user=Depends(get_verified_user)):
+    folder_ids = [folder_id]
+    children_folders = Folders.get_children_folders_by_id_and_user_id(
+        folder_id, user.id
+    )
+    if children_folders:
+        folder_ids.extend([folder.id for folder in children_folders])
+
+    return [
+        ChatResponse(**chat.model_dump())
+        for chat in Chats.get_chats_by_folder_ids_and_user_id(folder_ids, user.id)
+    ]
+
+
+############################
+# GetPinnedChats
+############################
+
+
+@router.get("/pinned", response_model=list[ChatResponse])
+async def get_user_pinned_chats(user=Depends(get_verified_user)):
+    return [
+        ChatResponse(**chat.model_dump())
+        for chat in Chats.get_pinned_chats_by_user_id(user.id)
+    ]
+
+
+############################
+# GetChats
+############################
+
+
+@router.get("/all", response_model=list[ChatResponse])
+async def get_user_chats(user=Depends(get_verified_user)):
+    return [
+        ChatResponse(**chat.model_dump())
+        for chat in Chats.get_chats_by_user_id(user.id)
+    ]
+
+
+############################
+# GetArchivedChats
+############################
+
+
+@router.get("/all/archived", response_model=list[ChatResponse])
+async def get_user_archived_chats(user=Depends(get_verified_user)):
+    return [
+        ChatResponse(**chat.model_dump())
+        for chat in Chats.get_archived_chats_by_user_id(user.id)
+    ]
+
+
+############################
+# GetAllTags
+############################
+
+
+@router.get("/all/tags", response_model=list[TagModel])
+async def get_all_user_tags(user=Depends(get_verified_user)):
+    try:
+        tags = Tags.get_tags_by_user_id(user.id)
+        return tags
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# GetAllChatsInDB
+############################
+
+
+@router.get("/all/db", response_model=list[ChatResponse])
+async def get_all_user_chats_in_db(user=Depends(get_admin_user)):
+    if not ENABLE_ADMIN_EXPORT:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+    return [ChatResponse(**chat.model_dump()) for chat in Chats.get_chats()]
+
+
+############################
+# GetArchivedChats
+############################
+
+
+@router.get("/archived", response_model=list[ChatTitleIdResponse])
+async def get_archived_session_user_chat_list(
+    user=Depends(get_verified_user), skip: int = 0, limit: int = 50
+):
+    return Chats.get_archived_chat_list_by_user_id(user.id, skip, limit)
+
+
+############################
+# ArchiveAllChats
+############################
+
+
+@router.post("/archive/all", response_model=bool)
+async def archive_all_chats(user=Depends(get_verified_user)):
+    return Chats.archive_all_chats_by_user_id(user.id)
+
+
+############################
+# GetSharedChatById
+############################
+
+
+@router.get("/share/{share_id}", response_model=Optional[ChatResponse])
+async def get_shared_chat_by_id(share_id: str, user=Depends(get_verified_user)):
+    if user.role == "pending":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+    if user.role == "user" or (user.role == "admin" and not ENABLE_ADMIN_CHAT_ACCESS):
+        chat = Chats.get_chat_by_share_id(share_id)
+    elif user.role == "admin" and ENABLE_ADMIN_CHAT_ACCESS:
+        chat = Chats.get_chat_by_id(share_id)
+
+    if chat:
+        return ChatResponse(**chat.model_dump())
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+
+############################
+# GetChatsByTags
+############################
+
+
+class TagForm(BaseModel):
+    name: str
+
+
+class TagFilterForm(TagForm):
+    skip: Optional[int] = 0
+    limit: Optional[int] = 50
+
+
+@router.post("/tags", response_model=list[ChatTitleIdResponse])
+async def get_user_chat_list_by_tag_name(
+    form_data: TagFilterForm, user=Depends(get_verified_user)
+):
+    chats = Chats.get_chat_list_by_user_id_and_tag_name(
+        user.id, form_data.name, form_data.skip, form_data.limit
+    )
+    if len(chats) == 0:
+        Tags.delete_tag_by_name_and_user_id(form_data.name, user.id)
+
+    return chats
+
+
+############################
+# GetChatById
+############################
+
+
+@router.get("/{id}", response_model=Optional[ChatResponse])
+async def get_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+
+    if chat:
+        return ChatResponse(**chat.model_dump())
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+
+############################
+# UpdateChatById
+############################
+
+
+@router.post("/{id}", response_model=Optional[ChatResponse])
+async def update_chat_by_id(
+    id: str, form_data: ChatForm, user=Depends(get_verified_user)
+):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        updated_chat = {**chat.chat, **form_data.chat}
+        chat = Chats.update_chat_by_id(id, updated_chat)
+        return ChatResponse(**chat.model_dump())
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+
+############################
+# DeleteChatById
+############################
+
+
+@router.delete("/{id}", response_model=bool)
+async def delete_chat_by_id(request: Request, id: str, user=Depends(get_verified_user)):
+    if user.role == "admin":
+        chat = Chats.get_chat_by_id(id)
+        for tag in chat.meta.get("tags", []):
+            if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 1:
+                Tags.delete_tag_by_name_and_user_id(tag, user.id)
+
+        result = Chats.delete_chat_by_id(id)
+
+        return result
+    else:
+        if not has_permission(
+            user.id, "chat.delete", request.app.state.config.USER_PERMISSIONS
+        ):
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+            )
+
+        chat = Chats.get_chat_by_id(id)
+        for tag in chat.meta.get("tags", []):
+            if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 1:
+                Tags.delete_tag_by_name_and_user_id(tag, user.id)
+
+        result = Chats.delete_chat_by_id_and_user_id(id, user.id)
+        return result
+
+
+############################
+# GetPinnedStatusById
+############################
+
+
+@router.get("/{id}/pinned", response_model=Optional[bool])
+async def get_pinned_status_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        return chat.pinned
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# PinChatById
+############################
+
+
+@router.post("/{id}/pin", response_model=Optional[ChatResponse])
+async def pin_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        chat = Chats.toggle_chat_pinned_by_id(id)
+        return chat
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# CloneChat
+############################
+
+
+@router.post("/{id}/clone", response_model=Optional[ChatResponse])
+async def clone_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        updated_chat = {
+            **chat.chat,
+            "originalChatId": chat.id,
+            "branchPointMessageId": chat.chat["history"]["currentId"],
+            "title": f"Clone of {chat.title}",
+        }
+
+        chat = Chats.insert_new_chat(user.id, ChatForm(**{"chat": updated_chat}))
+        return ChatResponse(**chat.model_dump())
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# ArchiveChat
+############################
+
+
+@router.post("/{id}/archive", response_model=Optional[ChatResponse])
+async def archive_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        chat = Chats.toggle_chat_archive_by_id(id)
+
+        # Delete tags if chat is archived
+        if chat.archived:
+            for tag_id in chat.meta.get("tags", []):
+                if Chats.count_chats_by_tag_name_and_user_id(tag_id, user.id) == 0:
+                    log.debug(f"deleting tag: {tag_id}")
+                    Tags.delete_tag_by_name_and_user_id(tag_id, user.id)
+        else:
+            for tag_id in chat.meta.get("tags", []):
+                tag = Tags.get_tag_by_name_and_user_id(tag_id, user.id)
+                if tag is None:
+                    log.debug(f"inserting tag: {tag_id}")
+                    tag = Tags.insert_new_tag(tag_id, user.id)
+
+        return ChatResponse(**chat.model_dump())
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# ShareChatById
+############################
+
+
+@router.post("/{id}/share", response_model=Optional[ChatResponse])
+async def share_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        if chat.share_id:
+            shared_chat = Chats.update_shared_chat_by_chat_id(chat.id)
+            return ChatResponse(**shared_chat.model_dump())
+
+        shared_chat = Chats.insert_shared_chat_by_chat_id(chat.id)
+        if not shared_chat:
+            raise HTTPException(
+                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+                detail=ERROR_MESSAGES.DEFAULT(),
+            )
+        return ChatResponse(**shared_chat.model_dump())
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+
+############################
+# DeletedSharedChatById
+############################
+
+
+@router.delete("/{id}/share", response_model=Optional[bool])
+async def delete_shared_chat_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        if not chat.share_id:
+            return False
+
+        result = Chats.delete_shared_chat_by_chat_id(id)
+        update_result = Chats.update_chat_share_id_by_id(id, None)
+
+        return result and update_result != None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+
+############################
+# UpdateChatFolderIdById
+############################
+
+
+class ChatFolderIdForm(BaseModel):
+    folder_id: Optional[str] = None
+
+
+@router.post("/{id}/folder", response_model=Optional[ChatResponse])
+async def update_chat_folder_id_by_id(
+    id: str, form_data: ChatFolderIdForm, user=Depends(get_verified_user)
+):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        chat = Chats.update_chat_folder_id_by_id_and_user_id(
+            id, user.id, form_data.folder_id
+        )
+        return ChatResponse(**chat.model_dump())
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# GetChatTagsById
+############################
+
+
+@router.get("/{id}/tags", response_model=list[TagModel])
+async def get_chat_tags_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        tags = chat.meta.get("tags", [])
+        return Tags.get_tags_by_ids_and_user_id(tags, user.id)
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+
+############################
+# AddChatTagById
+############################
+
+
+@router.post("/{id}/tags", response_model=list[TagModel])
+async def add_tag_by_id_and_tag_name(
+    id: str, form_data: TagForm, user=Depends(get_verified_user)
+):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        tags = chat.meta.get("tags", [])
+        tag_id = form_data.name.replace(" ", "_").lower()
+
+        if tag_id == "none":
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Tag name cannot be 'None'"),
+            )
+
+        print(tags, tag_id)
+        if tag_id not in tags:
+            Chats.add_chat_tag_by_id_and_user_id_and_tag_name(
+                id, user.id, form_data.name
+            )
+
+        chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+        tags = chat.meta.get("tags", [])
+        return Tags.get_tags_by_ids_and_user_id(tags, user.id)
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.DEFAULT()
+        )
+
+
+############################
+# DeleteChatTagById
+############################
+
+
+@router.delete("/{id}/tags", response_model=list[TagModel])
+async def delete_tag_by_id_and_tag_name(
+    id: str, form_data: TagForm, user=Depends(get_verified_user)
+):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        Chats.delete_tag_by_id_and_user_id_and_tag_name(id, user.id, form_data.name)
+
+        if Chats.count_chats_by_tag_name_and_user_id(form_data.name, user.id) == 0:
+            Tags.delete_tag_by_name_and_user_id(form_data.name, user.id)
+
+        chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+        tags = chat.meta.get("tags", [])
+        return Tags.get_tags_by_ids_and_user_id(tags, user.id)
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+
+############################
+# DeleteAllTagsById
+############################
+
+
+@router.delete("/{id}/tags/all", response_model=Optional[bool])
+async def delete_all_tags_by_id(id: str, user=Depends(get_verified_user)):
+    chat = Chats.get_chat_by_id_and_user_id(id, user.id)
+    if chat:
+        Chats.delete_all_tags_by_id_and_user_id(id, user.id)
+
+        for tag in chat.meta.get("tags", []):
+            if Chats.count_chats_by_tag_name_and_user_id(tag, user.id) == 0:
+                Tags.delete_tag_by_name_and_user_id(tag, user.id)
+
+        return True
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED, detail=ERROR_MESSAGES.NOT_FOUND
+        )
diff --git a/backend/open_webui/apps/webui/routers/configs.py b/backend/open_webui/apps/webui/routers/configs.py
new file mode 100644
index 0000000000000000000000000000000000000000..7466e6fda11d57e8e38ab0c110f69e97fc0c7a01
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/configs.py
@@ -0,0 +1,112 @@
+from fastapi import APIRouter, Depends, Request
+from pydantic import BaseModel
+
+from typing import Optional
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.config import get_config, save_config
+from open_webui.config import BannerModel
+
+
+router = APIRouter()
+
+
+############################
+# ImportConfig
+############################
+
+
+class ImportConfigForm(BaseModel):
+    config: dict
+
+
+@router.post("/import", response_model=dict)
+async def import_config(form_data: ImportConfigForm, user=Depends(get_admin_user)):
+    save_config(form_data.config)
+    return get_config()
+
+
+############################
+# ExportConfig
+############################
+
+
+@router.get("/export", response_model=dict)
+async def export_config(user=Depends(get_admin_user)):
+    return get_config()
+
+
+############################
+# SetDefaultModels
+############################
+class ModelsConfigForm(BaseModel):
+    DEFAULT_MODELS: Optional[str]
+    MODEL_ORDER_LIST: Optional[list[str]]
+
+
+@router.get("/models", response_model=ModelsConfigForm)
+async def get_models_config(request: Request, user=Depends(get_admin_user)):
+    return {
+        "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
+        "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
+    }
+
+
+@router.post("/models", response_model=ModelsConfigForm)
+async def set_models_config(
+    request: Request, form_data: ModelsConfigForm, user=Depends(get_admin_user)
+):
+    request.app.state.config.DEFAULT_MODELS = form_data.DEFAULT_MODELS
+    request.app.state.config.MODEL_ORDER_LIST = form_data.MODEL_ORDER_LIST
+    return {
+        "DEFAULT_MODELS": request.app.state.config.DEFAULT_MODELS,
+        "MODEL_ORDER_LIST": request.app.state.config.MODEL_ORDER_LIST,
+    }
+
+
+class PromptSuggestion(BaseModel):
+    title: list[str]
+    content: str
+
+
+class SetDefaultSuggestionsForm(BaseModel):
+    suggestions: list[PromptSuggestion]
+
+
+@router.post("/suggestions", response_model=list[PromptSuggestion])
+async def set_default_suggestions(
+    request: Request,
+    form_data: SetDefaultSuggestionsForm,
+    user=Depends(get_admin_user),
+):
+    data = form_data.model_dump()
+    request.app.state.config.DEFAULT_PROMPT_SUGGESTIONS = data["suggestions"]
+    return request.app.state.config.DEFAULT_PROMPT_SUGGESTIONS
+
+
+############################
+# SetBanners
+############################
+
+
+class SetBannersForm(BaseModel):
+    banners: list[BannerModel]
+
+
+@router.post("/banners", response_model=list[BannerModel])
+async def set_banners(
+    request: Request,
+    form_data: SetBannersForm,
+    user=Depends(get_admin_user),
+):
+    data = form_data.model_dump()
+    request.app.state.config.BANNERS = data["banners"]
+    return request.app.state.config.BANNERS
+
+
+@router.get("/banners", response_model=list[BannerModel])
+async def get_banners(
+    request: Request,
+    user=Depends(get_verified_user),
+):
+    return request.app.state.config.BANNERS
diff --git a/backend/open_webui/apps/webui/routers/evaluations.py b/backend/open_webui/apps/webui/routers/evaluations.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9e3bff29bfc9da372e3b5ac52d1cf3892181a99
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/evaluations.py
@@ -0,0 +1,159 @@
+from typing import Optional
+from fastapi import APIRouter, Depends, HTTPException, status, Request
+from pydantic import BaseModel
+
+from open_webui.apps.webui.models.users import Users, UserModel
+from open_webui.apps.webui.models.feedbacks import (
+    FeedbackModel,
+    FeedbackResponse,
+    FeedbackForm,
+    Feedbacks,
+)
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+router = APIRouter()
+
+
+############################
+# GetConfig
+############################
+
+
+@router.get("/config")
+async def get_config(request: Request, user=Depends(get_admin_user)):
+    return {
+        "ENABLE_EVALUATION_ARENA_MODELS": request.app.state.config.ENABLE_EVALUATION_ARENA_MODELS,
+        "EVALUATION_ARENA_MODELS": request.app.state.config.EVALUATION_ARENA_MODELS,
+    }
+
+
+############################
+# UpdateConfig
+############################
+
+
+class UpdateConfigForm(BaseModel):
+    ENABLE_EVALUATION_ARENA_MODELS: Optional[bool] = None
+    EVALUATION_ARENA_MODELS: Optional[list[dict]] = None
+
+
+@router.post("/config")
+async def update_config(
+    request: Request,
+    form_data: UpdateConfigForm,
+    user=Depends(get_admin_user),
+):
+    config = request.app.state.config
+    if form_data.ENABLE_EVALUATION_ARENA_MODELS is not None:
+        config.ENABLE_EVALUATION_ARENA_MODELS = form_data.ENABLE_EVALUATION_ARENA_MODELS
+    if form_data.EVALUATION_ARENA_MODELS is not None:
+        config.EVALUATION_ARENA_MODELS = form_data.EVALUATION_ARENA_MODELS
+    return {
+        "ENABLE_EVALUATION_ARENA_MODELS": config.ENABLE_EVALUATION_ARENA_MODELS,
+        "EVALUATION_ARENA_MODELS": config.EVALUATION_ARENA_MODELS,
+    }
+
+
+class FeedbackUserResponse(FeedbackResponse):
+    user: Optional[UserModel] = None
+
+
+@router.get("/feedbacks/all", response_model=list[FeedbackUserResponse])
+async def get_all_feedbacks(user=Depends(get_admin_user)):
+    feedbacks = Feedbacks.get_all_feedbacks()
+    return [
+        FeedbackUserResponse(
+            **feedback.model_dump(), user=Users.get_user_by_id(feedback.user_id)
+        )
+        for feedback in feedbacks
+    ]
+
+
+@router.delete("/feedbacks/all")
+async def delete_all_feedbacks(user=Depends(get_admin_user)):
+    success = Feedbacks.delete_all_feedbacks()
+    return success
+
+
+@router.get("/feedbacks/all/export", response_model=list[FeedbackModel])
+async def get_all_feedbacks(user=Depends(get_admin_user)):
+    feedbacks = Feedbacks.get_all_feedbacks()
+    return [
+        FeedbackModel(
+            **feedback.model_dump(), user=Users.get_user_by_id(feedback.user_id)
+        )
+        for feedback in feedbacks
+    ]
+
+
+@router.get("/feedbacks/user", response_model=list[FeedbackUserResponse])
+async def get_feedbacks(user=Depends(get_verified_user)):
+    feedbacks = Feedbacks.get_feedbacks_by_user_id(user.id)
+    return feedbacks
+
+
+@router.delete("/feedbacks", response_model=bool)
+async def delete_feedbacks(user=Depends(get_verified_user)):
+    success = Feedbacks.delete_feedbacks_by_user_id(user.id)
+    return success
+
+
+@router.post("/feedback", response_model=FeedbackModel)
+async def create_feedback(
+    request: Request,
+    form_data: FeedbackForm,
+    user=Depends(get_verified_user),
+):
+    feedback = Feedbacks.insert_new_feedback(user_id=user.id, form_data=form_data)
+    if not feedback:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(),
+        )
+
+    return feedback
+
+
+@router.get("/feedback/{id}", response_model=FeedbackModel)
+async def get_feedback_by_id(id: str, user=Depends(get_verified_user)):
+    feedback = Feedbacks.get_feedback_by_id_and_user_id(id=id, user_id=user.id)
+
+    if not feedback:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+    return feedback
+
+
+@router.post("/feedback/{id}", response_model=FeedbackModel)
+async def update_feedback_by_id(
+    id: str, form_data: FeedbackForm, user=Depends(get_verified_user)
+):
+    feedback = Feedbacks.update_feedback_by_id_and_user_id(
+        id=id, user_id=user.id, form_data=form_data
+    )
+
+    if not feedback:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+    return feedback
+
+
+@router.delete("/feedback/{id}")
+async def delete_feedback_by_id(id: str, user=Depends(get_verified_user)):
+    if user.role == "admin":
+        success = Feedbacks.delete_feedback_by_id(id=id)
+    else:
+        success = Feedbacks.delete_feedback_by_id_and_user_id(id=id, user_id=user.id)
+
+    if not success:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
+        )
+
+    return success
diff --git a/backend/open_webui/apps/webui/routers/files.py b/backend/open_webui/apps/webui/routers/files.py
new file mode 100644
index 0000000000000000000000000000000000000000..e7459a15f296c5a6107d11ecd860144eb1d8d110
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/files.py
@@ -0,0 +1,349 @@
+import logging
+import os
+import uuid
+from pathlib import Path
+from typing import Optional
+from pydantic import BaseModel
+import mimetypes
+
+from open_webui.storage.provider import Storage
+
+from open_webui.apps.webui.models.files import (
+    FileForm,
+    FileModel,
+    FileModelResponse,
+    Files,
+)
+from open_webui.apps.retrieval.main import process_file, ProcessFileForm
+
+from open_webui.config import UPLOAD_DIR
+from open_webui.env import SRC_LOG_LEVELS
+from open_webui.constants import ERROR_MESSAGES
+
+
+from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
+from fastapi.responses import FileResponse, StreamingResponse
+
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+router = APIRouter()
+
+############################
+# Upload File
+############################
+
+
+@router.post("/", response_model=FileModelResponse)
+def upload_file(file: UploadFile = File(...), user=Depends(get_verified_user)):
+    log.info(f"file.content_type: {file.content_type}")
+    try:
+        unsanitized_filename = file.filename
+        filename = os.path.basename(unsanitized_filename)
+
+        # replace filename with uuid
+        id = str(uuid.uuid4())
+        name = filename
+        filename = f"{id}_{filename}"
+        contents, file_path = Storage.upload_file(file.file, filename)
+
+        file_item = Files.insert_new_file(
+            user.id,
+            FileForm(
+                **{
+                    "id": id,
+                    "filename": name,
+                    "path": file_path,
+                    "meta": {
+                        "name": name,
+                        "content_type": file.content_type,
+                        "size": len(contents),
+                    },
+                }
+            ),
+        )
+
+        try:
+            process_file(ProcessFileForm(file_id=id))
+            file_item = Files.get_file_by_id(id=id)
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error processing file: {file_item.id}")
+            file_item = FileModelResponse(
+                **{
+                    **file_item.model_dump(),
+                    "error": str(e.detail) if hasattr(e, "detail") else str(e),
+                }
+            )
+
+        if file_item:
+            return file_item
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error uploading file"),
+            )
+
+    except Exception as e:
+        log.exception(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+############################
+# List Files
+############################
+
+
+@router.get("/", response_model=list[FileModelResponse])
+async def list_files(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        files = Files.get_files()
+    else:
+        files = Files.get_files_by_user_id(user.id)
+    return files
+
+
+############################
+# Delete All Files
+############################
+
+
+@router.delete("/all")
+async def delete_all_files(user=Depends(get_admin_user)):
+    result = Files.delete_all_files()
+    if result:
+        try:
+            Storage.delete_all_files()
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error deleting files")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
+            )
+        return {"message": "All files deleted successfully"}
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
+        )
+
+
+############################
+# Get File By Id
+############################
+
+
+@router.get("/{id}", response_model=Optional[FileModel])
+async def get_file_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+
+    if file and (file.user_id == user.id or user.role == "admin"):
+        return file
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Get File Data Content By Id
+############################
+
+
+@router.get("/{id}/data/content")
+async def get_file_data_content_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+
+    if file and (file.user_id == user.id or user.role == "admin"):
+        return {"content": file.data.get("content", "")}
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Update File Data Content By Id
+############################
+
+
+class ContentForm(BaseModel):
+    content: str
+
+
+@router.post("/{id}/data/content/update")
+async def update_file_data_content_by_id(
+    id: str, form_data: ContentForm, user=Depends(get_verified_user)
+):
+    file = Files.get_file_by_id(id)
+
+    if file and (file.user_id == user.id or user.role == "admin"):
+        try:
+            process_file(ProcessFileForm(file_id=id, content=form_data.content))
+            file = Files.get_file_by_id(id=id)
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error processing file: {file.id}")
+
+        return {"content": file.data.get("content", "")}
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Get File Content By Id
+############################
+
+
+@router.get("/{id}/content")
+async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+    if file and (file.user_id == user.id or user.role == "admin"):
+        try:
+            file_path = Storage.get_file(file.path)
+            file_path = Path(file_path)
+
+            # Check if the file already exists in the cache
+            if file_path.is_file():
+                print(f"file_path: {file_path}")
+                headers = {
+                    "Content-Disposition": f'attachment; filename="{file.meta.get("name", file.filename)}"'
+                }
+                return FileResponse(file_path, headers=headers)
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_404_NOT_FOUND,
+                    detail=ERROR_MESSAGES.NOT_FOUND,
+                )
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error getting file content")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error getting file content"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/{id}/content/html")
+async def get_html_file_content_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+    if file and (file.user_id == user.id or user.role == "admin"):
+        try:
+            file_path = Storage.get_file(file.path)
+            file_path = Path(file_path)
+
+            # Check if the file already exists in the cache
+            if file_path.is_file():
+                print(f"file_path: {file_path}")
+                return FileResponse(file_path)
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_404_NOT_FOUND,
+                    detail=ERROR_MESSAGES.NOT_FOUND,
+                )
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error getting file content")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error getting file content"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/{id}/content/{file_name}")
+async def get_file_content_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+
+    if file and (file.user_id == user.id or user.role == "admin"):
+        file_path = file.path
+        if file_path:
+            file_path = Storage.get_file(file_path)
+            file_path = Path(file_path)
+
+            # Check if the file already exists in the cache
+            if file_path.is_file():
+                print(f"file_path: {file_path}")
+                headers = {
+                    "Content-Disposition": f'attachment; filename="{file.meta.get("name", file.filename)}"'
+                }
+                return FileResponse(file_path, headers=headers)
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_404_NOT_FOUND,
+                    detail=ERROR_MESSAGES.NOT_FOUND,
+                )
+        else:
+            # File path doesn’t exist, return the content as .txt if possible
+            file_content = file.content.get("content", "")
+            file_name = file.filename
+
+            # Create a generator that encodes the file content
+            def generator():
+                yield file_content.encode("utf-8")
+
+            return StreamingResponse(
+                generator(),
+                media_type="text/plain",
+                headers={"Content-Disposition": f"attachment; filename={file_name}"},
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Delete File By Id
+############################
+
+
+@router.delete("/{id}")
+async def delete_file_by_id(id: str, user=Depends(get_verified_user)):
+    file = Files.get_file_by_id(id)
+    if file and (file.user_id == user.id or user.role == "admin"):
+        result = Files.delete_file_by_id(id)
+        if result:
+            try:
+                Storage.delete_file(file.filename)
+            except Exception as e:
+                log.exception(e)
+                log.error(f"Error deleting files")
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("Error deleting files"),
+                )
+            return {"message": "File deleted successfully"}
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error deleting file"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
diff --git a/backend/open_webui/apps/webui/routers/folders.py b/backend/open_webui/apps/webui/routers/folders.py
new file mode 100644
index 0000000000000000000000000000000000000000..36075c357bfc6d6892f284823e378eb6e49b989f
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/folders.py
@@ -0,0 +1,251 @@
+import logging
+import os
+import shutil
+import uuid
+from pathlib import Path
+from typing import Optional
+from pydantic import BaseModel
+import mimetypes
+
+
+from open_webui.apps.webui.models.folders import (
+    FolderForm,
+    FolderModel,
+    Folders,
+)
+from open_webui.apps.webui.models.chats import Chats
+
+from open_webui.config import UPLOAD_DIR
+from open_webui.env import SRC_LOG_LEVELS
+from open_webui.constants import ERROR_MESSAGES
+
+
+from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
+from fastapi.responses import FileResponse, StreamingResponse
+
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+
+router = APIRouter()
+
+
+############################
+# Get Folders
+############################
+
+
+@router.get("/", response_model=list[FolderModel])
+async def get_folders(user=Depends(get_verified_user)):
+    folders = Folders.get_folders_by_user_id(user.id)
+
+    return [
+        {
+            **folder.model_dump(),
+            "items": {
+                "chats": [
+                    {"title": chat.title, "id": chat.id}
+                    for chat in Chats.get_chats_by_folder_id_and_user_id(
+                        folder.id, user.id
+                    )
+                ]
+            },
+        }
+        for folder in folders
+    ]
+
+
+############################
+# Create Folder
+############################
+
+
+@router.post("/")
+def create_folder(form_data: FolderForm, user=Depends(get_verified_user)):
+    folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
+        None, user.id, form_data.name
+    )
+
+    if folder:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
+        )
+
+    try:
+        folder = Folders.insert_new_folder(user.id, form_data.name)
+        return folder
+    except Exception as e:
+        log.exception(e)
+        log.error("Error creating folder")
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT("Error creating folder"),
+        )
+
+
+############################
+# Get Folders By Id
+############################
+
+
+@router.get("/{id}", response_model=Optional[FolderModel])
+async def get_folder_by_id(id: str, user=Depends(get_verified_user)):
+    folder = Folders.get_folder_by_id_and_user_id(id, user.id)
+    if folder:
+        return folder
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Update Folder Name By Id
+############################
+
+
+@router.post("/{id}/update")
+async def update_folder_name_by_id(
+    id: str, form_data: FolderForm, user=Depends(get_verified_user)
+):
+    folder = Folders.get_folder_by_id_and_user_id(id, user.id)
+    if folder:
+        existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
+            folder.parent_id, user.id, form_data.name
+        )
+        if existing_folder:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
+            )
+
+        try:
+            folder = Folders.update_folder_name_by_id_and_user_id(
+                id, user.id, form_data.name
+            )
+
+            return folder
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error updating folder: {id}")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Update Folder Parent Id By Id
+############################
+
+
+class FolderParentIdForm(BaseModel):
+    parent_id: Optional[str] = None
+
+
+@router.post("/{id}/update/parent")
+async def update_folder_parent_id_by_id(
+    id: str, form_data: FolderParentIdForm, user=Depends(get_verified_user)
+):
+    folder = Folders.get_folder_by_id_and_user_id(id, user.id)
+    if folder:
+        existing_folder = Folders.get_folder_by_parent_id_and_user_id_and_name(
+            form_data.parent_id, user.id, folder.name
+        )
+
+        if existing_folder:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Folder already exists"),
+            )
+
+        try:
+            folder = Folders.update_folder_parent_id_by_id_and_user_id(
+                id, user.id, form_data.parent_id
+            )
+            return folder
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error updating folder: {id}")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Update Folder Is Expanded By Id
+############################
+
+
+class FolderIsExpandedForm(BaseModel):
+    is_expanded: bool
+
+
+@router.post("/{id}/update/expanded")
+async def update_folder_is_expanded_by_id(
+    id: str, form_data: FolderIsExpandedForm, user=Depends(get_verified_user)
+):
+    folder = Folders.get_folder_by_id_and_user_id(id, user.id)
+    if folder:
+        try:
+            folder = Folders.update_folder_is_expanded_by_id_and_user_id(
+                id, user.id, form_data.is_expanded
+            )
+            return folder
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error updating folder: {id}")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating folder"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# Delete Folder By Id
+############################
+
+
+@router.delete("/{id}")
+async def delete_folder_by_id(id: str, user=Depends(get_verified_user)):
+    folder = Folders.get_folder_by_id_and_user_id(id, user.id)
+    if folder:
+        try:
+            result = Folders.delete_folder_by_id_and_user_id(id, user.id)
+            if result:
+                return result
+            else:
+                raise Exception("Error deleting folder")
+        except Exception as e:
+            log.exception(e)
+            log.error(f"Error deleting folder: {id}")
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error deleting folder"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
diff --git a/backend/open_webui/apps/webui/routers/functions.py b/backend/open_webui/apps/webui/routers/functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..aeaceecfb101e103cad71c693b404c744ecf733f
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/functions.py
@@ -0,0 +1,405 @@
+import os
+from pathlib import Path
+from typing import Optional
+
+from open_webui.apps.webui.models.functions import (
+    FunctionForm,
+    FunctionModel,
+    FunctionResponse,
+    Functions,
+)
+from open_webui.apps.webui.utils import load_function_module_by_id, replace_imports
+from open_webui.config import CACHE_DIR
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+router = APIRouter()
+
+############################
+# GetFunctions
+############################
+
+
+@router.get("/", response_model=list[FunctionResponse])
+async def get_functions(user=Depends(get_verified_user)):
+    return Functions.get_functions()
+
+
+############################
+# ExportFunctions
+############################
+
+
+@router.get("/export", response_model=list[FunctionModel])
+async def get_functions(user=Depends(get_admin_user)):
+    return Functions.get_functions()
+
+
+############################
+# CreateNewFunction
+############################
+
+
+@router.post("/create", response_model=Optional[FunctionResponse])
+async def create_new_function(
+    request: Request, form_data: FunctionForm, user=Depends(get_admin_user)
+):
+    if not form_data.id.isidentifier():
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Only alphanumeric characters and underscores are allowed in the id",
+        )
+
+    form_data.id = form_data.id.lower()
+
+    function = Functions.get_function_by_id(form_data.id)
+    if function is None:
+        try:
+            form_data.content = replace_imports(form_data.content)
+            function_module, function_type, frontmatter = load_function_module_by_id(
+                form_data.id,
+                content=form_data.content,
+            )
+            form_data.meta.manifest = frontmatter
+
+            FUNCTIONS = request.app.state.FUNCTIONS
+            FUNCTIONS[form_data.id] = function_module
+
+            function = Functions.insert_new_function(user.id, function_type, form_data)
+
+            function_cache_dir = Path(CACHE_DIR) / "functions" / form_data.id
+            function_cache_dir.mkdir(parents=True, exist_ok=True)
+
+            if function:
+                return function
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("Error creating function"),
+                )
+        except Exception as e:
+            print(e)
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ID_TAKEN,
+        )
+
+
+############################
+# GetFunctionById
+############################
+
+
+@router.get("/id/{id}", response_model=Optional[FunctionModel])
+async def get_function_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+
+    if function:
+        return function
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# ToggleFunctionById
+############################
+
+
+@router.post("/id/{id}/toggle", response_model=Optional[FunctionModel])
+async def toggle_function_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        function = Functions.update_function_by_id(
+            id, {"is_active": not function.is_active}
+        )
+
+        if function:
+            return function
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating function"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# ToggleGlobalById
+############################
+
+
+@router.post("/id/{id}/toggle/global", response_model=Optional[FunctionModel])
+async def toggle_global_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        function = Functions.update_function_by_id(
+            id, {"is_global": not function.is_global}
+        )
+
+        if function:
+            return function
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating function"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateFunctionById
+############################
+
+
+@router.post("/id/{id}/update", response_model=Optional[FunctionModel])
+async def update_function_by_id(
+    request: Request, id: str, form_data: FunctionForm, user=Depends(get_admin_user)
+):
+    try:
+        form_data.content = replace_imports(form_data.content)
+        function_module, function_type, frontmatter = load_function_module_by_id(
+            id, content=form_data.content
+        )
+        form_data.meta.manifest = frontmatter
+
+        FUNCTIONS = request.app.state.FUNCTIONS
+        FUNCTIONS[id] = function_module
+
+        updated = {**form_data.model_dump(exclude={"id"}), "type": function_type}
+        print(updated)
+
+        function = Functions.update_function_by_id(id, updated)
+
+        if function:
+            return function
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating function"),
+            )
+
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+############################
+# DeleteFunctionById
+############################
+
+
+@router.delete("/id/{id}/delete", response_model=bool)
+async def delete_function_by_id(
+    request: Request, id: str, user=Depends(get_admin_user)
+):
+    result = Functions.delete_function_by_id(id)
+
+    if result:
+        FUNCTIONS = request.app.state.FUNCTIONS
+        if id in FUNCTIONS:
+            del FUNCTIONS[id]
+
+    return result
+
+
+############################
+# GetFunctionValves
+############################
+
+
+@router.get("/id/{id}/valves", response_model=Optional[dict])
+async def get_function_valves_by_id(id: str, user=Depends(get_admin_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        try:
+            valves = Functions.get_function_valves_by_id(id)
+            return valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# GetFunctionValvesSpec
+############################
+
+
+@router.get("/id/{id}/valves/spec", response_model=Optional[dict])
+async def get_function_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_admin_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "Valves"):
+            Valves = function_module.Valves
+            return Valves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateFunctionValves
+############################
+
+
+@router.post("/id/{id}/valves/update", response_model=Optional[dict])
+async def update_function_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_admin_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "Valves"):
+            Valves = function_module.Valves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                valves = Valves(**form_data)
+                Functions.update_function_valves_by_id(id, valves.model_dump())
+                return valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# FunctionUserValves
+############################
+
+
+@router.get("/id/{id}/valves/user", response_model=Optional[dict])
+async def get_function_user_valves_by_id(id: str, user=Depends(get_verified_user)):
+    function = Functions.get_function_by_id(id)
+    if function:
+        try:
+            user_valves = Functions.get_user_valves_by_id_and_user_id(id, user.id)
+            return user_valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(e),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/id/{id}/valves/user/spec", response_model=Optional[dict])
+async def get_function_user_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    function = Functions.get_function_by_id(id)
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "UserValves"):
+            UserValves = function_module.UserValves
+            return UserValves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.post("/id/{id}/valves/user/update", response_model=Optional[dict])
+async def update_function_user_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
+):
+    function = Functions.get_function_by_id(id)
+
+    if function:
+        if id in request.app.state.FUNCTIONS:
+            function_module = request.app.state.FUNCTIONS[id]
+        else:
+            function_module, function_type, frontmatter = load_function_module_by_id(id)
+            request.app.state.FUNCTIONS[id] = function_module
+
+        if hasattr(function_module, "UserValves"):
+            UserValves = function_module.UserValves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                user_valves = UserValves(**form_data)
+                Functions.update_user_valves_by_id_and_user_id(
+                    id, user.id, user_valves.model_dump()
+                )
+                return user_valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(e),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
diff --git a/backend/open_webui/apps/webui/routers/groups.py b/backend/open_webui/apps/webui/routers/groups.py
new file mode 100644
index 0000000000000000000000000000000000000000..59d7d0052b79d108dcbf95775754bdb1c55b38a0
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/groups.py
@@ -0,0 +1,120 @@
+import os
+from pathlib import Path
+from typing import Optional
+
+from open_webui.apps.webui.models.groups import (
+    Groups,
+    GroupForm,
+    GroupUpdateForm,
+    GroupResponse,
+)
+
+from open_webui.config import CACHE_DIR
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from open_webui.utils.utils import get_admin_user, get_verified_user
+
+router = APIRouter()
+
+############################
+# GetFunctions
+############################
+
+
+@router.get("/", response_model=list[GroupResponse])
+async def get_groups(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        return Groups.get_groups()
+    else:
+        return Groups.get_groups_by_member_id(user.id)
+
+
+############################
+# CreateNewGroup
+############################
+
+
+@router.post("/create", response_model=Optional[GroupResponse])
+async def create_new_function(form_data: GroupForm, user=Depends(get_admin_user)):
+    try:
+        group = Groups.insert_new_group(user.id, form_data)
+        if group:
+            return group
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error creating group"),
+            )
+    except Exception as e:
+        print(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+############################
+# GetGroupById
+############################
+
+
+@router.get("/id/{id}", response_model=Optional[GroupResponse])
+async def get_group_by_id(id: str, user=Depends(get_admin_user)):
+    group = Groups.get_group_by_id(id)
+    if group:
+        return group
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateGroupById
+############################
+
+
+@router.post("/id/{id}/update", response_model=Optional[GroupResponse])
+async def update_group_by_id(
+    id: str, form_data: GroupUpdateForm, user=Depends(get_admin_user)
+):
+    try:
+        group = Groups.update_group_by_id(id, form_data)
+        if group:
+            return group
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating group"),
+            )
+    except Exception as e:
+        print(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
+
+
+############################
+# DeleteGroupById
+############################
+
+
+@router.delete("/id/{id}/delete", response_model=bool)
+async def delete_group_by_id(id: str, user=Depends(get_admin_user)):
+    try:
+        result = Groups.delete_group_by_id(id)
+        if result:
+            return result
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error deleting group"),
+            )
+    except Exception as e:
+        print(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(e),
+        )
diff --git a/backend/open_webui/apps/webui/routers/knowledge.py b/backend/open_webui/apps/webui/routers/knowledge.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b063cda26a80ba1477e1147b0c0a85dbcc1da53
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/knowledge.py
@@ -0,0 +1,510 @@
+import json
+from typing import Optional, Union
+from pydantic import BaseModel
+from fastapi import APIRouter, Depends, HTTPException, status, Request
+import logging
+
+from open_webui.apps.webui.models.knowledge import (
+    Knowledges,
+    KnowledgeForm,
+    KnowledgeResponse,
+    KnowledgeUserResponse,
+)
+from open_webui.apps.webui.models.files import Files, FileModel
+from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
+from open_webui.apps.retrieval.main import process_file, ProcessFileForm
+
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access, has_permission
+
+
+from open_webui.env import SRC_LOG_LEVELS
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+router = APIRouter()
+
+############################
+# getKnowledgeBases
+############################
+
+
+@router.get("/", response_model=list[KnowledgeUserResponse])
+async def get_knowledge(user=Depends(get_verified_user)):
+    knowledge_bases = []
+
+    if user.role == "admin":
+        knowledge_bases = Knowledges.get_knowledge_bases()
+    else:
+        knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "read")
+
+    # Get files for each knowledge base
+    knowledge_with_files = []
+    for knowledge_base in knowledge_bases:
+        files = []
+        if knowledge_base.data:
+            files = Files.get_file_metadatas_by_ids(
+                knowledge_base.data.get("file_ids", [])
+            )
+
+            # Check if all files exist
+            if len(files) != len(knowledge_base.data.get("file_ids", [])):
+                missing_files = list(
+                    set(knowledge_base.data.get("file_ids", []))
+                    - set([file.id for file in files])
+                )
+                if missing_files:
+                    data = knowledge_base.data or {}
+                    file_ids = data.get("file_ids", [])
+
+                    for missing_file in missing_files:
+                        file_ids.remove(missing_file)
+
+                    data["file_ids"] = file_ids
+                    Knowledges.update_knowledge_data_by_id(
+                        id=knowledge_base.id, data=data
+                    )
+
+                    files = Files.get_file_metadatas_by_ids(file_ids)
+
+        knowledge_with_files.append(
+            KnowledgeUserResponse(
+                **knowledge_base.model_dump(),
+                files=files,
+            )
+        )
+
+    return knowledge_with_files
+
+
+@router.get("/list", response_model=list[KnowledgeUserResponse])
+async def get_knowledge_list(user=Depends(get_verified_user)):
+    knowledge_bases = []
+
+    if user.role == "admin":
+        knowledge_bases = Knowledges.get_knowledge_bases()
+    else:
+        knowledge_bases = Knowledges.get_knowledge_bases_by_user_id(user.id, "write")
+
+    # Get files for each knowledge base
+    knowledge_with_files = []
+    for knowledge_base in knowledge_bases:
+        files = []
+        if knowledge_base.data:
+            files = Files.get_file_metadatas_by_ids(
+                knowledge_base.data.get("file_ids", [])
+            )
+
+            # Check if all files exist
+            if len(files) != len(knowledge_base.data.get("file_ids", [])):
+                missing_files = list(
+                    set(knowledge_base.data.get("file_ids", []))
+                    - set([file.id for file in files])
+                )
+                if missing_files:
+                    data = knowledge_base.data or {}
+                    file_ids = data.get("file_ids", [])
+
+                    for missing_file in missing_files:
+                        file_ids.remove(missing_file)
+
+                    data["file_ids"] = file_ids
+                    Knowledges.update_knowledge_data_by_id(
+                        id=knowledge_base.id, data=data
+                    )
+
+                    files = Files.get_file_metadatas_by_ids(file_ids)
+
+        knowledge_with_files.append(
+            KnowledgeUserResponse(
+                **knowledge_base.model_dump(),
+                files=files,
+            )
+        )
+    return knowledge_with_files
+
+
+############################
+# CreateNewKnowledge
+############################
+
+
+@router.post("/create", response_model=Optional[KnowledgeResponse])
+async def create_new_knowledge(
+    request: Request, form_data: KnowledgeForm, user=Depends(get_verified_user)
+):
+    if user.role != "admin" and not has_permission(
+        user.id, "workspace.knowledge", request.app.state.config.USER_PERMISSIONS
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    knowledge = Knowledges.insert_new_knowledge(user.id, form_data)
+
+    if knowledge:
+        return knowledge
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.FILE_EXISTS,
+        )
+
+
+############################
+# GetKnowledgeById
+############################
+
+
+class KnowledgeFilesResponse(KnowledgeResponse):
+    files: list[FileModel]
+
+
+@router.get("/{id}", response_model=Optional[KnowledgeFilesResponse])
+async def get_knowledge_by_id(id: str, user=Depends(get_verified_user)):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+
+    if knowledge:
+
+        if (
+            user.role == "admin"
+            or knowledge.user_id == user.id
+            or has_access(user.id, "read", knowledge.access_control)
+        ):
+
+            file_ids = knowledge.data.get("file_ids", []) if knowledge.data else []
+            files = Files.get_files_by_ids(file_ids)
+
+            return KnowledgeFilesResponse(
+                **knowledge.model_dump(),
+                files=files,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateKnowledgeById
+############################
+
+
+@router.post("/{id}/update", response_model=Optional[KnowledgeFilesResponse])
+async def update_knowledge_by_id(
+    id: str,
+    form_data: KnowledgeForm,
+    user=Depends(get_verified_user),
+):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    knowledge = Knowledges.update_knowledge_by_id(id=id, form_data=form_data)
+    if knowledge:
+        file_ids = knowledge.data.get("file_ids", []) if knowledge.data else []
+        files = Files.get_files_by_ids(file_ids)
+
+        return KnowledgeFilesResponse(
+            **knowledge.model_dump(),
+            files=files,
+        )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ID_TAKEN,
+        )
+
+
+############################
+# AddFileToKnowledge
+############################
+
+
+class KnowledgeFileIdForm(BaseModel):
+    file_id: str
+
+
+@router.post("/{id}/file/add", response_model=Optional[KnowledgeFilesResponse])
+def add_file_to_knowledge_by_id(
+    id: str,
+    form_data: KnowledgeFileIdForm,
+    user=Depends(get_verified_user),
+):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    file = Files.get_file_by_id(form_data.file_id)
+    if not file:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+    if not file.data:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.FILE_NOT_PROCESSED,
+        )
+
+    # Add content to the vector database
+    try:
+        process_file(ProcessFileForm(file_id=form_data.file_id, collection_name=id))
+    except Exception as e:
+        log.debug(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=str(e),
+        )
+
+    if knowledge:
+        data = knowledge.data or {}
+        file_ids = data.get("file_ids", [])
+
+        if form_data.file_id not in file_ids:
+            file_ids.append(form_data.file_id)
+            data["file_ids"] = file_ids
+
+            knowledge = Knowledges.update_knowledge_data_by_id(id=id, data=data)
+
+            if knowledge:
+                files = Files.get_files_by_ids(file_ids)
+
+                return KnowledgeFilesResponse(
+                    **knowledge.model_dump(),
+                    files=files,
+                )
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("knowledge"),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("file_id"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.post("/{id}/file/update", response_model=Optional[KnowledgeFilesResponse])
+def update_file_from_knowledge_by_id(
+    id: str,
+    form_data: KnowledgeFileIdForm,
+    user=Depends(get_verified_user),
+):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    file = Files.get_file_by_id(form_data.file_id)
+    if not file:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    # Remove content from the vector database
+    VECTOR_DB_CLIENT.delete(
+        collection_name=knowledge.id, filter={"file_id": form_data.file_id}
+    )
+
+    # Add content to the vector database
+    try:
+        process_file(ProcessFileForm(file_id=form_data.file_id, collection_name=id))
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=str(e),
+        )
+
+    if knowledge:
+        data = knowledge.data or {}
+        file_ids = data.get("file_ids", [])
+
+        files = Files.get_files_by_ids(file_ids)
+
+        return KnowledgeFilesResponse(
+            **knowledge.model_dump(),
+            files=files,
+        )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# RemoveFileFromKnowledge
+############################
+
+
+@router.post("/{id}/file/remove", response_model=Optional[KnowledgeFilesResponse])
+def remove_file_from_knowledge_by_id(
+    id: str,
+    form_data: KnowledgeFileIdForm,
+    user=Depends(get_verified_user),
+):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    file = Files.get_file_by_id(form_data.file_id)
+    if not file:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    # Remove content from the vector database
+    VECTOR_DB_CLIENT.delete(
+        collection_name=knowledge.id, filter={"file_id": form_data.file_id}
+    )
+
+    result = VECTOR_DB_CLIENT.query(
+        collection_name=knowledge.id,
+        filter={"file_id": form_data.file_id},
+    )
+
+    Files.delete_file_by_id(form_data.file_id)
+
+    if knowledge:
+        data = knowledge.data or {}
+        file_ids = data.get("file_ids", [])
+
+        if form_data.file_id in file_ids:
+            file_ids.remove(form_data.file_id)
+            data["file_ids"] = file_ids
+
+            knowledge = Knowledges.update_knowledge_data_by_id(id=id, data=data)
+
+            if knowledge:
+                files = Files.get_files_by_ids(file_ids)
+
+                return KnowledgeFilesResponse(
+                    **knowledge.model_dump(),
+                    files=files,
+                )
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("knowledge"),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("file_id"),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# DeleteKnowledgeById
+############################
+
+
+@router.delete("/{id}/delete", response_model=bool)
+async def delete_knowledge_by_id(id: str, user=Depends(get_verified_user)):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    try:
+        VECTOR_DB_CLIENT.delete_collection(collection_name=id)
+    except Exception as e:
+        log.debug(e)
+        pass
+    result = Knowledges.delete_knowledge_by_id(id=id)
+    return result
+
+
+############################
+# ResetKnowledgeById
+############################
+
+
+@router.post("/{id}/reset", response_model=Optional[KnowledgeResponse])
+async def reset_knowledge_by_id(id: str, user=Depends(get_verified_user)):
+    knowledge = Knowledges.get_knowledge_by_id(id=id)
+    if not knowledge:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if knowledge.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    try:
+        VECTOR_DB_CLIENT.delete_collection(collection_name=id)
+    except Exception as e:
+        log.debug(e)
+        pass
+
+    knowledge = Knowledges.update_knowledge_data_by_id(id=id, data={"file_ids": []})
+
+    return knowledge
diff --git a/backend/open_webui/apps/webui/routers/memories.py b/backend/open_webui/apps/webui/routers/memories.py
new file mode 100644
index 0000000000000000000000000000000000000000..ccf84a9d4cb40b101ca9aa06574a79893817013a
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/memories.py
@@ -0,0 +1,190 @@
+from fastapi import APIRouter, Depends, HTTPException, Request
+from pydantic import BaseModel
+import logging
+from typing import Optional
+
+from open_webui.apps.webui.models.memories import Memories, MemoryModel
+from open_webui.apps.retrieval.vector.connector import VECTOR_DB_CLIENT
+from open_webui.utils.utils import get_verified_user
+from open_webui.env import SRC_LOG_LEVELS
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+router = APIRouter()
+
+
+@router.get("/ef")
+async def get_embeddings(request: Request):
+    return {"result": request.app.state.EMBEDDING_FUNCTION("hello world")}
+
+
+############################
+# GetMemories
+############################
+
+
+@router.get("/", response_model=list[MemoryModel])
+async def get_memories(user=Depends(get_verified_user)):
+    return Memories.get_memories_by_user_id(user.id)
+
+
+############################
+# AddMemory
+############################
+
+
+class AddMemoryForm(BaseModel):
+    content: str
+
+
+class MemoryUpdateModel(BaseModel):
+    content: Optional[str] = None
+
+
+@router.post("/add", response_model=Optional[MemoryModel])
+async def add_memory(
+    request: Request,
+    form_data: AddMemoryForm,
+    user=Depends(get_verified_user),
+):
+    memory = Memories.insert_new_memory(user.id, form_data.content)
+
+    VECTOR_DB_CLIENT.upsert(
+        collection_name=f"user-memory-{user.id}",
+        items=[
+            {
+                "id": memory.id,
+                "text": memory.content,
+                "vector": request.app.state.EMBEDDING_FUNCTION(memory.content),
+                "metadata": {"created_at": memory.created_at},
+            }
+        ],
+    )
+
+    return memory
+
+
+############################
+# QueryMemory
+############################
+
+
+class QueryMemoryForm(BaseModel):
+    content: str
+    k: Optional[int] = 1
+
+
+@router.post("/query")
+async def query_memory(
+    request: Request, form_data: QueryMemoryForm, user=Depends(get_verified_user)
+):
+    results = VECTOR_DB_CLIENT.search(
+        collection_name=f"user-memory-{user.id}",
+        vectors=[request.app.state.EMBEDDING_FUNCTION(form_data.content)],
+        limit=form_data.k,
+    )
+
+    return results
+
+
+############################
+# ResetMemoryFromVectorDB
+############################
+@router.post("/reset", response_model=bool)
+async def reset_memory_from_vector_db(
+    request: Request, user=Depends(get_verified_user)
+):
+    VECTOR_DB_CLIENT.delete_collection(f"user-memory-{user.id}")
+
+    memories = Memories.get_memories_by_user_id(user.id)
+    VECTOR_DB_CLIENT.upsert(
+        collection_name=f"user-memory-{user.id}",
+        items=[
+            {
+                "id": memory.id,
+                "text": memory.content,
+                "vector": request.app.state.EMBEDDING_FUNCTION(memory.content),
+                "metadata": {
+                    "created_at": memory.created_at,
+                    "updated_at": memory.updated_at,
+                },
+            }
+            for memory in memories
+        ],
+    )
+
+    return True
+
+
+############################
+# DeleteMemoriesByUserId
+############################
+
+
+@router.delete("/delete/user", response_model=bool)
+async def delete_memory_by_user_id(user=Depends(get_verified_user)):
+    result = Memories.delete_memories_by_user_id(user.id)
+
+    if result:
+        try:
+            VECTOR_DB_CLIENT.delete_collection(f"user-memory-{user.id}")
+        except Exception as e:
+            log.error(e)
+        return True
+
+    return False
+
+
+############################
+# UpdateMemoryById
+############################
+
+
+@router.post("/{memory_id}/update", response_model=Optional[MemoryModel])
+async def update_memory_by_id(
+    memory_id: str,
+    request: Request,
+    form_data: MemoryUpdateModel,
+    user=Depends(get_verified_user),
+):
+    memory = Memories.update_memory_by_id(memory_id, form_data.content)
+    if memory is None:
+        raise HTTPException(status_code=404, detail="Memory not found")
+
+    if form_data.content is not None:
+        VECTOR_DB_CLIENT.upsert(
+            collection_name=f"user-memory-{user.id}",
+            items=[
+                {
+                    "id": memory.id,
+                    "text": memory.content,
+                    "vector": request.app.state.EMBEDDING_FUNCTION(memory.content),
+                    "metadata": {
+                        "created_at": memory.created_at,
+                        "updated_at": memory.updated_at,
+                    },
+                }
+            ],
+        )
+
+    return memory
+
+
+############################
+# DeleteMemoryById
+############################
+
+
+@router.delete("/{memory_id}", response_model=bool)
+async def delete_memory_by_id(memory_id: str, user=Depends(get_verified_user)):
+    result = Memories.delete_memory_by_id_and_user_id(memory_id, user.id)
+
+    if result:
+        VECTOR_DB_CLIENT.delete(
+            collection_name=f"user-memory-{user.id}", ids=[memory_id]
+        )
+        return True
+
+    return False
diff --git a/backend/open_webui/apps/webui/routers/models.py b/backend/open_webui/apps/webui/routers/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a80853855d79f08a68cc4dc55b6dcd999ffcf58
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/models.py
@@ -0,0 +1,189 @@
+from typing import Optional
+
+from open_webui.apps.webui.models.models import (
+    ModelForm,
+    ModelModel,
+    ModelResponse,
+    ModelUserResponse,
+    Models,
+)
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+
+
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access, has_permission
+
+
+router = APIRouter()
+
+
+###########################
+# GetModels
+###########################
+
+
+@router.get("/", response_model=list[ModelUserResponse])
+async def get_models(id: Optional[str] = None, user=Depends(get_verified_user)):
+    if user.role == "admin":
+        return Models.get_models()
+    else:
+        return Models.get_models_by_user_id(user.id)
+
+
+###########################
+# GetBaseModels
+###########################
+
+
+@router.get("/base", response_model=list[ModelResponse])
+async def get_base_models(user=Depends(get_admin_user)):
+    return Models.get_base_models()
+
+
+############################
+# CreateNewModel
+############################
+
+
+@router.post("/create", response_model=Optional[ModelModel])
+async def create_new_model(
+    request: Request,
+    form_data: ModelForm,
+    user=Depends(get_verified_user),
+):
+    if user.role != "admin" and not has_permission(
+        user.id, "workspace.models", request.app.state.config.USER_PERMISSIONS
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    model = Models.get_model_by_id(form_data.id)
+    if model:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.MODEL_ID_TAKEN,
+        )
+
+    else:
+        model = Models.insert_new_model(form_data, user.id)
+        if model:
+            return model
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.DEFAULT(),
+            )
+
+
+###########################
+# GetModelById
+###########################
+
+
+# Note: We're not using the typical url path param here, but instead using a query parameter to allow '/' in the id
+@router.get("/model", response_model=Optional[ModelResponse])
+async def get_model_by_id(id: str, user=Depends(get_verified_user)):
+    model = Models.get_model_by_id(id)
+    if model:
+        if (
+            user.role == "admin"
+            or model.user_id == user.id
+            or has_access(user.id, "read", model.access_control)
+        ):
+            return model
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# ToggelModelById
+############################
+
+
+@router.post("/model/toggle", response_model=Optional[ModelResponse])
+async def toggle_model_by_id(id: str, user=Depends(get_verified_user)):
+    model = Models.get_model_by_id(id)
+    if model:
+        if (
+            user.role == "admin"
+            or model.user_id == user.id
+            or has_access(user.id, "write", model.access_control)
+        ):
+            model = Models.toggle_model_by_id(id)
+
+            if model:
+                return model
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("Error updating function"),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.UNAUTHORIZED,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateModelById
+############################
+
+
+@router.post("/model/update", response_model=Optional[ModelModel])
+async def update_model_by_id(
+    id: str,
+    form_data: ModelForm,
+    user=Depends(get_verified_user),
+):
+    model = Models.get_model_by_id(id)
+
+    if not model:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    model = Models.update_model_by_id(id, form_data)
+    return model
+
+
+############################
+# DeleteModelById
+############################
+
+
+@router.delete("/model/delete", response_model=bool)
+async def delete_model_by_id(id: str, user=Depends(get_verified_user)):
+    model = Models.get_model_by_id(id)
+    if not model:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if model.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    result = Models.delete_model_by_id(id)
+    return result
+
+
+@router.delete("/delete/all", response_model=bool)
+async def delete_all_models(user=Depends(get_admin_user)):
+    result = Models.delete_all_models()
+    return result
diff --git a/backend/open_webui/apps/webui/routers/prompts.py b/backend/open_webui/apps/webui/routers/prompts.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cacde606cc16396731079c8b6edd5ee7623904f
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/prompts.py
@@ -0,0 +1,152 @@
+from typing import Optional
+
+from open_webui.apps.webui.models.prompts import (
+    PromptForm,
+    PromptUserResponse,
+    PromptModel,
+    Prompts,
+)
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, status, Request
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access, has_permission
+
+router = APIRouter()
+
+############################
+# GetPrompts
+############################
+
+
+@router.get("/", response_model=list[PromptModel])
+async def get_prompts(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        prompts = Prompts.get_prompts()
+    else:
+        prompts = Prompts.get_prompts_by_user_id(user.id, "read")
+
+    return prompts
+
+
+@router.get("/list", response_model=list[PromptUserResponse])
+async def get_prompt_list(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        prompts = Prompts.get_prompts()
+    else:
+        prompts = Prompts.get_prompts_by_user_id(user.id, "write")
+
+    return prompts
+
+
+############################
+# CreateNewPrompt
+############################
+
+
+@router.post("/create", response_model=Optional[PromptModel])
+async def create_new_prompt(
+    request: Request, form_data: PromptForm, user=Depends(get_verified_user)
+):
+    if user.role != "admin" and not has_permission(
+        user.id, "workspace.prompts", request.app.state.config.USER_PERMISSIONS
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    prompt = Prompts.get_prompt_by_command(form_data.command)
+    if prompt is None:
+        prompt = Prompts.insert_new_prompt(user.id, form_data)
+
+        if prompt:
+            return prompt
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(),
+        )
+    raise HTTPException(
+        status_code=status.HTTP_400_BAD_REQUEST,
+        detail=ERROR_MESSAGES.COMMAND_TAKEN,
+    )
+
+
+############################
+# GetPromptByCommand
+############################
+
+
+@router.get("/command/{command}", response_model=Optional[PromptModel])
+async def get_prompt_by_command(command: str, user=Depends(get_verified_user)):
+    prompt = Prompts.get_prompt_by_command(f"/{command}")
+
+    if prompt:
+        if (
+            user.role == "admin"
+            or prompt.user_id == user.id
+            or has_access(user.id, "read", prompt.access_control)
+        ):
+            return prompt
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdatePromptByCommand
+############################
+
+
+@router.post("/command/{command}/update", response_model=Optional[PromptModel])
+async def update_prompt_by_command(
+    command: str,
+    form_data: PromptForm,
+    user=Depends(get_verified_user),
+):
+    prompt = Prompts.get_prompt_by_command(f"/{command}")
+    if not prompt:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if prompt.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    prompt = Prompts.update_prompt_by_command(f"/{command}", form_data)
+    if prompt:
+        return prompt
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+
+############################
+# DeletePromptByCommand
+############################
+
+
+@router.delete("/command/{command}/delete", response_model=bool)
+async def delete_prompt_by_command(command: str, user=Depends(get_verified_user)):
+    prompt = Prompts.get_prompt_by_command(f"/{command}")
+    if not prompt:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if prompt.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+
+    result = Prompts.delete_prompt_by_command(f"/{command}")
+    return result
diff --git a/backend/open_webui/apps/webui/routers/tools.py b/backend/open_webui/apps/webui/routers/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0523ddac8c112b2e2f13ff1c06e2ca908dbd0ed
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/tools.py
@@ -0,0 +1,418 @@
+from pathlib import Path
+from typing import Optional
+
+from open_webui.apps.webui.models.tools import (
+    ToolForm,
+    ToolModel,
+    ToolResponse,
+    ToolUserResponse,
+    Tools,
+)
+from open_webui.apps.webui.utils import load_tools_module_by_id, replace_imports
+from open_webui.config import CACHE_DIR
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from open_webui.utils.tools import get_tools_specs
+from open_webui.utils.utils import get_admin_user, get_verified_user
+from open_webui.utils.access_control import has_access, has_permission
+
+
+router = APIRouter()
+
+############################
+# GetTools
+############################
+
+
+@router.get("/", response_model=list[ToolUserResponse])
+async def get_tools(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        tools = Tools.get_tools()
+    else:
+        tools = Tools.get_tools_by_user_id(user.id, "read")
+    return tools
+
+
+############################
+# GetToolList
+############################
+
+
+@router.get("/list", response_model=list[ToolUserResponse])
+async def get_tool_list(user=Depends(get_verified_user)):
+    if user.role == "admin":
+        tools = Tools.get_tools()
+    else:
+        tools = Tools.get_tools_by_user_id(user.id, "write")
+    return tools
+
+
+############################
+# ExportTools
+############################
+
+
+@router.get("/export", response_model=list[ToolModel])
+async def export_tools(user=Depends(get_admin_user)):
+    tools = Tools.get_tools()
+    return tools
+
+
+############################
+# CreateNewTools
+############################
+
+
+@router.post("/create", response_model=Optional[ToolResponse])
+async def create_new_tools(
+    request: Request,
+    form_data: ToolForm,
+    user=Depends(get_verified_user),
+):
+    if user.role != "admin" and not has_permission(
+        user.id, "workspace.knowledge", request.app.state.config.USER_PERMISSIONS
+    ):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    if not form_data.id.isidentifier():
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Only alphanumeric characters and underscores are allowed in the id",
+        )
+
+    form_data.id = form_data.id.lower()
+
+    tools = Tools.get_tool_by_id(form_data.id)
+    if tools is None:
+        try:
+            form_data.content = replace_imports(form_data.content)
+            tools_module, frontmatter = load_tools_module_by_id(
+                form_data.id, content=form_data.content
+            )
+            form_data.meta.manifest = frontmatter
+
+            TOOLS = request.app.state.TOOLS
+            TOOLS[form_data.id] = tools_module
+
+            specs = get_tools_specs(TOOLS[form_data.id])
+            tools = Tools.insert_new_tool(user.id, form_data, specs)
+
+            tool_cache_dir = Path(CACHE_DIR) / "tools" / form_data.id
+            tool_cache_dir.mkdir(parents=True, exist_ok=True)
+
+            if tools:
+                return tools
+            else:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT("Error creating tools"),
+                )
+        except Exception as e:
+            print(e)
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(str(e)),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.ID_TAKEN,
+        )
+
+
+############################
+# GetToolsById
+############################
+
+
+@router.get("/id/{id}", response_model=Optional[ToolModel])
+async def get_tools_by_id(id: str, user=Depends(get_verified_user)):
+    tools = Tools.get_tool_by_id(id)
+
+    if tools:
+        if (
+            user.role == "admin"
+            or tools.user_id == user.id
+            or has_access(user.id, "read", tools.access_control)
+        ):
+            return tools
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateToolsById
+############################
+
+
+@router.post("/id/{id}/update", response_model=Optional[ToolModel])
+async def update_tools_by_id(
+    request: Request,
+    id: str,
+    form_data: ToolForm,
+    user=Depends(get_verified_user),
+):
+    tools = Tools.get_tool_by_id(id)
+    if not tools:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if tools.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    try:
+        form_data.content = replace_imports(form_data.content)
+        tools_module, frontmatter = load_tools_module_by_id(
+            id, content=form_data.content
+        )
+        form_data.meta.manifest = frontmatter
+
+        TOOLS = request.app.state.TOOLS
+        TOOLS[id] = tools_module
+
+        specs = get_tools_specs(TOOLS[id])
+
+        updated = {
+            **form_data.model_dump(exclude={"id"}),
+            "specs": specs,
+        }
+
+        print(updated)
+        tools = Tools.update_tool_by_id(id, updated)
+
+        if tools:
+            return tools
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT("Error updating tools"),
+            )
+
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(str(e)),
+        )
+
+
+############################
+# DeleteToolsById
+############################
+
+
+@router.delete("/id/{id}/delete", response_model=bool)
+async def delete_tools_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    tools = Tools.get_tool_by_id(id)
+    if not tools:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+    if tools.user_id != user.id and user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+    result = Tools.delete_tool_by_id(id)
+    if result:
+        TOOLS = request.app.state.TOOLS
+        if id in TOOLS:
+            del TOOLS[id]
+
+    return result
+
+
+############################
+# GetToolValves
+############################
+
+
+@router.get("/id/{id}/valves", response_model=Optional[dict])
+async def get_tools_valves_by_id(id: str, user=Depends(get_verified_user)):
+    tools = Tools.get_tool_by_id(id)
+    if tools:
+        try:
+            valves = Tools.get_tool_valves_by_id(id)
+            return valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(str(e)),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# GetToolValvesSpec
+############################
+
+
+@router.get("/id/{id}/valves/spec", response_model=Optional[dict])
+async def get_tools_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    tools = Tools.get_tool_by_id(id)
+    if tools:
+        if id in request.app.state.TOOLS:
+            tools_module = request.app.state.TOOLS[id]
+        else:
+            tools_module, _ = load_tools_module_by_id(id)
+            request.app.state.TOOLS[id] = tools_module
+
+        if hasattr(tools_module, "Valves"):
+            Valves = tools_module.Valves
+            return Valves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+############################
+# UpdateToolValves
+############################
+
+
+@router.post("/id/{id}/valves/update", response_model=Optional[dict])
+async def update_tools_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
+):
+    tools = Tools.get_tool_by_id(id)
+    if not tools:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+    if id in request.app.state.TOOLS:
+        tools_module = request.app.state.TOOLS[id]
+    else:
+        tools_module, _ = load_tools_module_by_id(id)
+        request.app.state.TOOLS[id] = tools_module
+
+    if not hasattr(tools_module, "Valves"):
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+    Valves = tools_module.Valves
+
+    try:
+        form_data = {k: v for k, v in form_data.items() if v is not None}
+        valves = Valves(**form_data)
+        Tools.update_tool_valves_by_id(id, valves.model_dump())
+        return valves.model_dump()
+    except Exception as e:
+        print(e)
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(str(e)),
+        )
+
+
+############################
+# ToolUserValves
+############################
+
+
+@router.get("/id/{id}/valves/user", response_model=Optional[dict])
+async def get_tools_user_valves_by_id(id: str, user=Depends(get_verified_user)):
+    tools = Tools.get_tool_by_id(id)
+    if tools:
+        try:
+            user_valves = Tools.get_user_valves_by_id_and_user_id(id, user.id)
+            return user_valves
+        except Exception as e:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.DEFAULT(str(e)),
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.get("/id/{id}/valves/user/spec", response_model=Optional[dict])
+async def get_tools_user_valves_spec_by_id(
+    request: Request, id: str, user=Depends(get_verified_user)
+):
+    tools = Tools.get_tool_by_id(id)
+    if tools:
+        if id in request.app.state.TOOLS:
+            tools_module = request.app.state.TOOLS[id]
+        else:
+            tools_module, _ = load_tools_module_by_id(id)
+            request.app.state.TOOLS[id] = tools_module
+
+        if hasattr(tools_module, "UserValves"):
+            UserValves = tools_module.UserValves
+            return UserValves.schema()
+        return None
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
+
+
+@router.post("/id/{id}/valves/user/update", response_model=Optional[dict])
+async def update_tools_user_valves_by_id(
+    request: Request, id: str, form_data: dict, user=Depends(get_verified_user)
+):
+    tools = Tools.get_tool_by_id(id)
+
+    if tools:
+        if id in request.app.state.TOOLS:
+            tools_module = request.app.state.TOOLS[id]
+        else:
+            tools_module, _ = load_tools_module_by_id(id)
+            request.app.state.TOOLS[id] = tools_module
+
+        if hasattr(tools_module, "UserValves"):
+            UserValves = tools_module.UserValves
+
+            try:
+                form_data = {k: v for k, v in form_data.items() if v is not None}
+                user_valves = UserValves(**form_data)
+                Tools.update_user_valves_by_id_and_user_id(
+                    id, user.id, user_valves.model_dump()
+                )
+                return user_valves.model_dump()
+            except Exception as e:
+                print(e)
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.DEFAULT(str(e)),
+                )
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.NOT_FOUND,
+        )
diff --git a/backend/open_webui/apps/webui/routers/users.py b/backend/open_webui/apps/webui/routers/users.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6b91a5c3083d6a4a2016920834bde8e629b74de
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/users.py
@@ -0,0 +1,295 @@
+import logging
+from typing import Optional
+
+from open_webui.apps.webui.models.auths import Auths
+from open_webui.apps.webui.models.chats import Chats
+from open_webui.apps.webui.models.users import (
+    UserModel,
+    UserRoleUpdateForm,
+    Users,
+    UserSettings,
+    UserUpdateForm,
+)
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import SRC_LOG_LEVELS
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from pydantic import BaseModel
+from open_webui.utils.utils import get_admin_user, get_password_hash, get_verified_user
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MODELS"])
+
+router = APIRouter()
+
+############################
+# GetUsers
+############################
+
+
+@router.get("/", response_model=list[UserModel])
+async def get_users(skip: int = 0, limit: int = 50, user=Depends(get_admin_user)):
+    return Users.get_users(skip, limit)
+
+
+############################
+# User Groups
+############################
+
+
+@router.get("/groups")
+async def get_user_groups(user=Depends(get_verified_user)):
+    return Users.get_user_groups(user.id)
+
+
+############################
+# User Permissions
+############################
+
+
+@router.get("/permissions")
+async def get_user_permissisions(user=Depends(get_verified_user)):
+    return Users.get_user_groups(user.id)
+
+
+############################
+# User Default Permissions
+############################
+class WorkspacePermissions(BaseModel):
+    models: bool
+    knowledge: bool
+    prompts: bool
+    tools: bool
+
+
+class ChatPermissions(BaseModel):
+    file_upload: bool
+    delete: bool
+    edit: bool
+    temporary: bool
+
+
+class UserPermissions(BaseModel):
+    workspace: WorkspacePermissions
+    chat: ChatPermissions
+
+
+@router.get("/default/permissions")
+async def get_user_permissions(request: Request, user=Depends(get_admin_user)):
+    return request.app.state.config.USER_PERMISSIONS
+
+
+@router.post("/default/permissions")
+async def update_user_permissions(
+    request: Request, form_data: UserPermissions, user=Depends(get_admin_user)
+):
+    request.app.state.config.USER_PERMISSIONS = form_data.model_dump()
+    return request.app.state.config.USER_PERMISSIONS
+
+
+############################
+# UpdateUserRole
+############################
+
+
+@router.post("/update/role", response_model=Optional[UserModel])
+async def update_user_role(form_data: UserRoleUpdateForm, user=Depends(get_admin_user)):
+    if user.id != form_data.id and form_data.id != Users.get_first_user().id:
+        return Users.update_user_role_by_id(form_data.id, form_data.role)
+
+    raise HTTPException(
+        status_code=status.HTTP_403_FORBIDDEN,
+        detail=ERROR_MESSAGES.ACTION_PROHIBITED,
+    )
+
+
+############################
+# GetUserSettingsBySessionUser
+############################
+
+
+@router.get("/user/settings", response_model=Optional[UserSettings])
+async def get_user_settings_by_session_user(user=Depends(get_verified_user)):
+    user = Users.get_user_by_id(user.id)
+    if user:
+        return user.settings
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.USER_NOT_FOUND,
+        )
+
+
+############################
+# UpdateUserSettingsBySessionUser
+############################
+
+
+@router.post("/user/settings/update", response_model=UserSettings)
+async def update_user_settings_by_session_user(
+    form_data: UserSettings, user=Depends(get_verified_user)
+):
+    user = Users.update_user_by_id(user.id, {"settings": form_data.model_dump()})
+    if user:
+        return user.settings
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.USER_NOT_FOUND,
+        )
+
+
+############################
+# GetUserInfoBySessionUser
+############################
+
+
+@router.get("/user/info", response_model=Optional[dict])
+async def get_user_info_by_session_user(user=Depends(get_verified_user)):
+    user = Users.get_user_by_id(user.id)
+    if user:
+        return user.info
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.USER_NOT_FOUND,
+        )
+
+
+############################
+# UpdateUserInfoBySessionUser
+############################
+
+
+@router.post("/user/info/update", response_model=Optional[dict])
+async def update_user_info_by_session_user(
+    form_data: dict, user=Depends(get_verified_user)
+):
+    user = Users.get_user_by_id(user.id)
+    if user:
+        if user.info is None:
+            user.info = {}
+
+        user = Users.update_user_by_id(user.id, {"info": {**user.info, **form_data}})
+        if user:
+            return user.info
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.USER_NOT_FOUND,
+            )
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.USER_NOT_FOUND,
+        )
+
+
+############################
+# GetUserById
+############################
+
+
+class UserResponse(BaseModel):
+    name: str
+    profile_image_url: str
+
+
+@router.get("/{user_id}", response_model=UserResponse)
+async def get_user_by_id(user_id: str, user=Depends(get_verified_user)):
+    # Check if user_id is a shared chat
+    # If it is, get the user_id from the chat
+    if user_id.startswith("shared-"):
+        chat_id = user_id.replace("shared-", "")
+        chat = Chats.get_chat_by_id(chat_id)
+        if chat:
+            user_id = chat.user_id
+        else:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=ERROR_MESSAGES.USER_NOT_FOUND,
+            )
+
+    user = Users.get_user_by_id(user_id)
+
+    if user:
+        return UserResponse(name=user.name, profile_image_url=user.profile_image_url)
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.USER_NOT_FOUND,
+        )
+
+
+############################
+# UpdateUserById
+############################
+
+
+@router.post("/{user_id}/update", response_model=Optional[UserModel])
+async def update_user_by_id(
+    user_id: str,
+    form_data: UserUpdateForm,
+    session_user=Depends(get_admin_user),
+):
+    user = Users.get_user_by_id(user_id)
+
+    if user:
+        if form_data.email.lower() != user.email:
+            email_user = Users.get_user_by_email(form_data.email.lower())
+            if email_user:
+                raise HTTPException(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    detail=ERROR_MESSAGES.EMAIL_TAKEN,
+                )
+
+        if form_data.password:
+            hashed = get_password_hash(form_data.password)
+            log.debug(f"hashed: {hashed}")
+            Auths.update_user_password_by_id(user_id, hashed)
+
+        Auths.update_email_by_id(user_id, form_data.email.lower())
+        updated_user = Users.update_user_by_id(
+            user_id,
+            {
+                "name": form_data.name,
+                "email": form_data.email.lower(),
+                "profile_image_url": form_data.profile_image_url,
+            },
+        )
+
+        if updated_user:
+            return updated_user
+
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DEFAULT(),
+        )
+
+    raise HTTPException(
+        status_code=status.HTTP_400_BAD_REQUEST,
+        detail=ERROR_MESSAGES.USER_NOT_FOUND,
+    )
+
+
+############################
+# DeleteUserById
+############################
+
+
+@router.delete("/{user_id}", response_model=bool)
+async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)):
+    if user.id != user_id:
+        result = Auths.delete_auth_by_id(user_id)
+
+        if result:
+            return True
+
+        raise HTTPException(
+            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
+            detail=ERROR_MESSAGES.DELETE_USER_ERROR,
+        )
+
+    raise HTTPException(
+        status_code=status.HTTP_403_FORBIDDEN,
+        detail=ERROR_MESSAGES.ACTION_PROHIBITED,
+    )
diff --git a/backend/open_webui/apps/webui/routers/utils.py b/backend/open_webui/apps/webui/routers/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..0ab0f6b156c66ace9820cad38622f918e2f13117
--- /dev/null
+++ b/backend/open_webui/apps/webui/routers/utils.py
@@ -0,0 +1,99 @@
+import black
+import markdown
+
+from open_webui.apps.webui.models.chats import ChatTitleMessagesForm
+from open_webui.config import DATA_DIR, ENABLE_ADMIN_EXPORT
+from open_webui.constants import ERROR_MESSAGES
+from fastapi import APIRouter, Depends, HTTPException, Response, status
+from pydantic import BaseModel
+from starlette.responses import FileResponse
+from open_webui.utils.misc import get_gravatar_url
+from open_webui.utils.pdf_generator import PDFGenerator
+from open_webui.utils.utils import get_admin_user
+
+router = APIRouter()
+
+
+@router.get("/gravatar")
+async def get_gravatar(
+    email: str,
+):
+    return get_gravatar_url(email)
+
+
+class CodeFormatRequest(BaseModel):
+    code: str
+
+
+@router.post("/code/format")
+async def format_code(request: CodeFormatRequest):
+    try:
+        formatted_code = black.format_str(request.code, mode=black.Mode())
+        return {"code": formatted_code}
+    except black.NothingChanged:
+        return {"code": request.code}
+    except Exception as e:
+        raise HTTPException(status_code=400, detail=str(e))
+
+
+class MarkdownForm(BaseModel):
+    md: str
+
+
+@router.post("/markdown")
+async def get_html_from_markdown(
+    form_data: MarkdownForm,
+):
+    return {"html": markdown.markdown(form_data.md)}
+
+
+class ChatForm(BaseModel):
+    title: str
+    messages: list[dict]
+
+
+@router.post("/pdf")
+async def download_chat_as_pdf(
+    form_data: ChatTitleMessagesForm,
+):
+    try:
+        pdf_bytes = PDFGenerator(form_data).generate_chat_pdf()
+
+        return Response(
+            content=pdf_bytes,
+            media_type="application/pdf",
+            headers={"Content-Disposition": "attachment;filename=chat.pdf"},
+        )
+    except Exception as e:
+        print(e)
+        raise HTTPException(status_code=400, detail=str(e))
+
+
+@router.get("/db/download")
+async def download_db(user=Depends(get_admin_user)):
+    if not ENABLE_ADMIN_EXPORT:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+    from open_webui.apps.webui.internal.db import engine
+
+    if engine.name != "sqlite":
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=ERROR_MESSAGES.DB_NOT_SQLITE,
+        )
+    return FileResponse(
+        engine.url.database,
+        media_type="application/octet-stream",
+        filename="webui.db",
+    )
+
+
+@router.get("/litellm/config")
+async def download_litellm_config_yaml(user=Depends(get_admin_user)):
+    return FileResponse(
+        f"{DATA_DIR}/litellm/config.yaml",
+        media_type="application/octet-stream",
+        filename="config.yaml",
+    )
diff --git a/backend/open_webui/apps/webui/utils.py b/backend/open_webui/apps/webui/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..054158b3ef4af788264b41163475444562084738
--- /dev/null
+++ b/backend/open_webui/apps/webui/utils.py
@@ -0,0 +1,175 @@
+import os
+import re
+import subprocess
+import sys
+from importlib import util
+import types
+import tempfile
+import logging
+
+from open_webui.env import SRC_LOG_LEVELS
+from open_webui.apps.webui.models.functions import Functions
+from open_webui.apps.webui.models.tools import Tools
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MAIN"])
+
+
+def extract_frontmatter(content):
+    """
+    Extract frontmatter as a dictionary from the provided content string.
+    """
+    frontmatter = {}
+    frontmatter_started = False
+    frontmatter_ended = False
+    frontmatter_pattern = re.compile(r"^\s*([a-z_]+):\s*(.*)\s*$", re.IGNORECASE)
+
+    try:
+        lines = content.splitlines()
+        if len(lines) < 1 or lines[0].strip() != '"""':
+            # The content doesn't start with triple quotes
+            return {}
+
+        frontmatter_started = True
+
+        for line in lines[1:]:
+            if '"""' in line:
+                if frontmatter_started:
+                    frontmatter_ended = True
+                    break
+
+            if frontmatter_started and not frontmatter_ended:
+                match = frontmatter_pattern.match(line)
+                if match:
+                    key, value = match.groups()
+                    frontmatter[key.strip()] = value.strip()
+
+    except Exception as e:
+        print(f"An error occurred: {e}")
+        return {}
+
+    return frontmatter
+
+
+def replace_imports(content):
+    """
+    Replace the import paths in the content.
+    """
+    replacements = {
+        "from utils": "from open_webui.utils",
+        "from apps": "from open_webui.apps",
+        "from main": "from open_webui.main",
+        "from config": "from open_webui.config",
+    }
+
+    for old, new in replacements.items():
+        content = content.replace(old, new)
+
+    return content
+
+
+def load_tools_module_by_id(toolkit_id, content=None):
+
+    if content is None:
+        tool = Tools.get_tool_by_id(toolkit_id)
+        if not tool:
+            raise Exception(f"Toolkit not found: {toolkit_id}")
+
+        content = tool.content
+
+        content = replace_imports(content)
+        Tools.update_tool_by_id(toolkit_id, {"content": content})
+    else:
+        frontmatter = extract_frontmatter(content)
+        # Install required packages found within the frontmatter
+        install_frontmatter_requirements(frontmatter.get("requirements", ""))
+
+    module_name = f"tool_{toolkit_id}"
+    module = types.ModuleType(module_name)
+    sys.modules[module_name] = module
+
+    # Create a temporary file and use it to define `__file__` so
+    # that it works as expected from the module's perspective.
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.close()
+    try:
+        with open(temp_file.name, "w", encoding="utf-8") as f:
+            f.write(content)
+        module.__dict__["__file__"] = temp_file.name
+
+        # Executing the modified content in the created module's namespace
+        exec(content, module.__dict__)
+        frontmatter = extract_frontmatter(content)
+        log.info(f"Loaded module: {module.__name__}")
+
+        # Create and return the object if the class 'Tools' is found in the module
+        if hasattr(module, "Tools"):
+            return module.Tools(), frontmatter
+        else:
+            raise Exception("No Tools class found in the module")
+    except Exception as e:
+        log.error(f"Error loading module: {toolkit_id}: {e}")
+        del sys.modules[module_name]  # Clean up
+        raise e
+    finally:
+        os.unlink(temp_file.name)
+
+
+def load_function_module_by_id(function_id, content=None):
+    if content is None:
+        function = Functions.get_function_by_id(function_id)
+        if not function:
+            raise Exception(f"Function not found: {function_id}")
+        content = function.content
+
+        content = replace_imports(content)
+        Functions.update_function_by_id(function_id, {"content": content})
+    else:
+        frontmatter = extract_frontmatter(content)
+        install_frontmatter_requirements(frontmatter.get("requirements", ""))
+
+    module_name = f"function_{function_id}"
+    module = types.ModuleType(module_name)
+    sys.modules[module_name] = module
+
+    # Create a temporary file and use it to define `__file__` so
+    # that it works as expected from the module's perspective.
+    temp_file = tempfile.NamedTemporaryFile(delete=False)
+    temp_file.close()
+    try:
+        with open(temp_file.name, "w", encoding="utf-8") as f:
+            f.write(content)
+        module.__dict__["__file__"] = temp_file.name
+
+        # Execute the modified content in the created module's namespace
+        exec(content, module.__dict__)
+        frontmatter = extract_frontmatter(content)
+        log.info(f"Loaded module: {module.__name__}")
+
+        # Create appropriate object based on available class type in the module
+        if hasattr(module, "Pipe"):
+            return module.Pipe(), "pipe", frontmatter
+        elif hasattr(module, "Filter"):
+            return module.Filter(), "filter", frontmatter
+        elif hasattr(module, "Action"):
+            return module.Action(), "action", frontmatter
+        else:
+            raise Exception("No Function class found in the module")
+    except Exception as e:
+        log.error(f"Error loading module: {function_id}: {e}")
+        del sys.modules[module_name]  # Cleanup by removing the module in case of error
+
+        Functions.update_function_by_id(function_id, {"is_active": False})
+        raise e
+    finally:
+        os.unlink(temp_file.name)
+
+
+def install_frontmatter_requirements(requirements):
+    if requirements:
+        req_list = [req.strip() for req in requirements.split(",")]
+        for req in req_list:
+            log.info(f"Installing requirement: {req}")
+            subprocess.check_call([sys.executable, "-m", "pip", "install", req])
+    else:
+        log.info("No requirements found in frontmatter.")
diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0a0f63b522e31b9ef6b4de7cda1b1ac22da7619
--- /dev/null
+++ b/backend/open_webui/config.py
@@ -0,0 +1,1842 @@
+import json
+import logging
+import os
+import shutil
+from datetime import datetime
+from pathlib import Path
+from typing import Generic, Optional, TypeVar
+from urllib.parse import urlparse
+
+import chromadb
+import requests
+import yaml
+from open_webui.apps.webui.internal.db import Base, get_db
+from open_webui.env import (
+    OPEN_WEBUI_DIR,
+    DATA_DIR,
+    ENV,
+    FRONTEND_BUILD_DIR,
+    WEBUI_AUTH,
+    WEBUI_FAVICON_URL,
+    WEBUI_NAME,
+    log,
+    DATABASE_URL,
+)
+from pydantic import BaseModel
+from sqlalchemy import JSON, Column, DateTime, Integer, func
+
+
+class EndpointFilter(logging.Filter):
+    def filter(self, record: logging.LogRecord) -> bool:
+        return record.getMessage().find("/health") == -1
+
+
+# Filter out /endpoint
+logging.getLogger("uvicorn.access").addFilter(EndpointFilter())
+
+####################################
+# Config helpers
+####################################
+
+
+# Function to run the alembic migrations
+def run_migrations():
+    print("Running migrations")
+    try:
+        from alembic import command
+        from alembic.config import Config
+
+        alembic_cfg = Config(OPEN_WEBUI_DIR / "alembic.ini")
+
+        # Set the script location dynamically
+        migrations_path = OPEN_WEBUI_DIR / "migrations"
+        alembic_cfg.set_main_option("script_location", str(migrations_path))
+
+        command.upgrade(alembic_cfg, "head")
+    except Exception as e:
+        print(f"Error: {e}")
+
+
+run_migrations()
+
+
+class Config(Base):
+    __tablename__ = "config"
+
+    id = Column(Integer, primary_key=True)
+    data = Column(JSON, nullable=False)
+    version = Column(Integer, nullable=False, default=0)
+    created_at = Column(DateTime, nullable=False, server_default=func.now())
+    updated_at = Column(DateTime, nullable=True, onupdate=func.now())
+
+
+def load_json_config():
+    with open(f"{DATA_DIR}/config.json", "r") as file:
+        return json.load(file)
+
+
+def save_to_db(data):
+    with get_db() as db:
+        existing_config = db.query(Config).first()
+        if not existing_config:
+            new_config = Config(data=data, version=0)
+            db.add(new_config)
+        else:
+            existing_config.data = data
+            existing_config.updated_at = datetime.now()
+            db.add(existing_config)
+        db.commit()
+
+
+def reset_config():
+    with get_db() as db:
+        db.query(Config).delete()
+        db.commit()
+
+
+# When initializing, check if config.json exists and migrate it to the database
+if os.path.exists(f"{DATA_DIR}/config.json"):
+    data = load_json_config()
+    save_to_db(data)
+    os.rename(f"{DATA_DIR}/config.json", f"{DATA_DIR}/old_config.json")
+
+DEFAULT_CONFIG = {
+    "version": 0,
+    "ui": {
+        "default_locale": "",
+        "prompt_suggestions": [
+            {
+                "title": [
+                    "Help me study",
+                    "vocabulary for a college entrance exam",
+                ],
+                "content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
+            },
+            {
+                "title": [
+                    "Give me ideas",
+                    "for what to do with my kids' art",
+                ],
+                "content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.",
+            },
+            {
+                "title": ["Tell me a fun fact", "about the Roman Empire"],
+                "content": "Tell me a random fun fact about the Roman Empire",
+            },
+            {
+                "title": [
+                    "Show me a code snippet",
+                    "of a website's sticky header",
+                ],
+                "content": "Show me a code snippet of a website's sticky header in CSS and JavaScript.",
+            },
+            {
+                "title": [
+                    "Explain options trading",
+                    "if I'm familiar with buying and selling stocks",
+                ],
+                "content": "Explain options trading in simple terms if I'm familiar with buying and selling stocks.",
+            },
+            {
+                "title": ["Overcome procrastination", "give me tips"],
+                "content": "Could you start by asking me about instances when I procrastinate the most and then give me some suggestions to overcome it?",
+            },
+            {
+                "title": [
+                    "Grammar check",
+                    "rewrite it for better readability ",
+                ],
+                "content": 'Check the following sentence for grammar and clarity: "[sentence]". Rewrite it for better readability while maintaining its original meaning.',
+            },
+        ],
+    },
+}
+
+
+def get_config():
+    with get_db() as db:
+        config_entry = db.query(Config).order_by(Config.id.desc()).first()
+        return config_entry.data if config_entry else DEFAULT_CONFIG
+
+
+CONFIG_DATA = get_config()
+
+
+def get_config_value(config_path: str):
+    path_parts = config_path.split(".")
+    cur_config = CONFIG_DATA
+    for key in path_parts:
+        if key in cur_config:
+            cur_config = cur_config[key]
+        else:
+            return None
+    return cur_config
+
+
+PERSISTENT_CONFIG_REGISTRY = []
+
+
+def save_config(config):
+    global CONFIG_DATA
+    global PERSISTENT_CONFIG_REGISTRY
+    try:
+        save_to_db(config)
+        CONFIG_DATA = config
+
+        # Trigger updates on all registered PersistentConfig entries
+        for config_item in PERSISTENT_CONFIG_REGISTRY:
+            config_item.update()
+    except Exception as e:
+        log.exception(e)
+        return False
+    return True
+
+
+T = TypeVar("T")
+
+
+class PersistentConfig(Generic[T]):
+    def __init__(self, env_name: str, config_path: str, env_value: T):
+        self.env_name = env_name
+        self.config_path = config_path
+        self.env_value = env_value
+        self.config_value = get_config_value(config_path)
+        if self.config_value is not None:
+            log.info(f"'{env_name}' loaded from the latest database entry")
+            self.value = self.config_value
+        else:
+            self.value = env_value
+
+        PERSISTENT_CONFIG_REGISTRY.append(self)
+
+    def __str__(self):
+        return str(self.value)
+
+    @property
+    def __dict__(self):
+        raise TypeError(
+            "PersistentConfig object cannot be converted to dict, use config_get or .value instead."
+        )
+
+    def __getattribute__(self, item):
+        if item == "__dict__":
+            raise TypeError(
+                "PersistentConfig object cannot be converted to dict, use config_get or .value instead."
+            )
+        return super().__getattribute__(item)
+
+    def update(self):
+        new_value = get_config_value(self.config_path)
+        if new_value is not None:
+            self.value = new_value
+            log.info(f"Updated {self.env_name} to new value {self.value}")
+
+    def save(self):
+        log.info(f"Saving '{self.env_name}' to the database")
+        path_parts = self.config_path.split(".")
+        sub_config = CONFIG_DATA
+        for key in path_parts[:-1]:
+            if key not in sub_config:
+                sub_config[key] = {}
+            sub_config = sub_config[key]
+        sub_config[path_parts[-1]] = self.value
+        save_to_db(CONFIG_DATA)
+        self.config_value = self.value
+
+
+class AppConfig:
+    _state: dict[str, PersistentConfig]
+
+    def __init__(self):
+        super().__setattr__("_state", {})
+
+    def __setattr__(self, key, value):
+        if isinstance(value, PersistentConfig):
+            self._state[key] = value
+        else:
+            self._state[key].value = value
+            self._state[key].save()
+
+    def __getattr__(self, key):
+        return self._state[key].value
+
+
+####################################
+# WEBUI_AUTH (Required for security)
+####################################
+
+ENABLE_API_KEY = PersistentConfig(
+    "ENABLE_API_KEY",
+    "auth.api_key.enable",
+    os.environ.get("ENABLE_API_KEY", "True").lower() == "true",
+)
+
+
+JWT_EXPIRES_IN = PersistentConfig(
+    "JWT_EXPIRES_IN", "auth.jwt_expiry", os.environ.get("JWT_EXPIRES_IN", "-1")
+)
+
+####################################
+# OAuth config
+####################################
+
+ENABLE_OAUTH_SIGNUP = PersistentConfig(
+    "ENABLE_OAUTH_SIGNUP",
+    "oauth.enable_signup",
+    os.environ.get("ENABLE_OAUTH_SIGNUP", "False").lower() == "true",
+)
+
+OAUTH_MERGE_ACCOUNTS_BY_EMAIL = PersistentConfig(
+    "OAUTH_MERGE_ACCOUNTS_BY_EMAIL",
+    "oauth.merge_accounts_by_email",
+    os.environ.get("OAUTH_MERGE_ACCOUNTS_BY_EMAIL", "False").lower() == "true",
+)
+
+OAUTH_PROVIDERS = {}
+
+GOOGLE_CLIENT_ID = PersistentConfig(
+    "GOOGLE_CLIENT_ID",
+    "oauth.google.client_id",
+    os.environ.get("GOOGLE_CLIENT_ID", ""),
+)
+
+GOOGLE_CLIENT_SECRET = PersistentConfig(
+    "GOOGLE_CLIENT_SECRET",
+    "oauth.google.client_secret",
+    os.environ.get("GOOGLE_CLIENT_SECRET", ""),
+)
+
+GOOGLE_OAUTH_SCOPE = PersistentConfig(
+    "GOOGLE_OAUTH_SCOPE",
+    "oauth.google.scope",
+    os.environ.get("GOOGLE_OAUTH_SCOPE", "openid email profile"),
+)
+
+GOOGLE_REDIRECT_URI = PersistentConfig(
+    "GOOGLE_REDIRECT_URI",
+    "oauth.google.redirect_uri",
+    os.environ.get("GOOGLE_REDIRECT_URI", ""),
+)
+
+MICROSOFT_CLIENT_ID = PersistentConfig(
+    "MICROSOFT_CLIENT_ID",
+    "oauth.microsoft.client_id",
+    os.environ.get("MICROSOFT_CLIENT_ID", ""),
+)
+
+MICROSOFT_CLIENT_SECRET = PersistentConfig(
+    "MICROSOFT_CLIENT_SECRET",
+    "oauth.microsoft.client_secret",
+    os.environ.get("MICROSOFT_CLIENT_SECRET", ""),
+)
+
+MICROSOFT_CLIENT_TENANT_ID = PersistentConfig(
+    "MICROSOFT_CLIENT_TENANT_ID",
+    "oauth.microsoft.tenant_id",
+    os.environ.get("MICROSOFT_CLIENT_TENANT_ID", ""),
+)
+
+MICROSOFT_OAUTH_SCOPE = PersistentConfig(
+    "MICROSOFT_OAUTH_SCOPE",
+    "oauth.microsoft.scope",
+    os.environ.get("MICROSOFT_OAUTH_SCOPE", "openid email profile"),
+)
+
+MICROSOFT_REDIRECT_URI = PersistentConfig(
+    "MICROSOFT_REDIRECT_URI",
+    "oauth.microsoft.redirect_uri",
+    os.environ.get("MICROSOFT_REDIRECT_URI", ""),
+)
+
+OAUTH_CLIENT_ID = PersistentConfig(
+    "OAUTH_CLIENT_ID",
+    "oauth.oidc.client_id",
+    os.environ.get("OAUTH_CLIENT_ID", ""),
+)
+
+OAUTH_CLIENT_SECRET = PersistentConfig(
+    "OAUTH_CLIENT_SECRET",
+    "oauth.oidc.client_secret",
+    os.environ.get("OAUTH_CLIENT_SECRET", ""),
+)
+
+OPENID_PROVIDER_URL = PersistentConfig(
+    "OPENID_PROVIDER_URL",
+    "oauth.oidc.provider_url",
+    os.environ.get("OPENID_PROVIDER_URL", ""),
+)
+
+OPENID_REDIRECT_URI = PersistentConfig(
+    "OPENID_REDIRECT_URI",
+    "oauth.oidc.redirect_uri",
+    os.environ.get("OPENID_REDIRECT_URI", ""),
+)
+
+OAUTH_SCOPES = PersistentConfig(
+    "OAUTH_SCOPES",
+    "oauth.oidc.scopes",
+    os.environ.get("OAUTH_SCOPES", "openid email profile"),
+)
+
+OAUTH_PROVIDER_NAME = PersistentConfig(
+    "OAUTH_PROVIDER_NAME",
+    "oauth.oidc.provider_name",
+    os.environ.get("OAUTH_PROVIDER_NAME", "SSO"),
+)
+
+OAUTH_USERNAME_CLAIM = PersistentConfig(
+    "OAUTH_USERNAME_CLAIM",
+    "oauth.oidc.username_claim",
+    os.environ.get("OAUTH_USERNAME_CLAIM", "name"),
+)
+
+OAUTH_PICTURE_CLAIM = PersistentConfig(
+    "OAUTH_PICTURE_CLAIM",
+    "oauth.oidc.avatar_claim",
+    os.environ.get("OAUTH_PICTURE_CLAIM", "picture"),
+)
+
+OAUTH_EMAIL_CLAIM = PersistentConfig(
+    "OAUTH_EMAIL_CLAIM",
+    "oauth.oidc.email_claim",
+    os.environ.get("OAUTH_EMAIL_CLAIM", "email"),
+)
+
+ENABLE_OAUTH_ROLE_MANAGEMENT = PersistentConfig(
+    "ENABLE_OAUTH_ROLE_MANAGEMENT",
+    "oauth.enable_role_mapping",
+    os.environ.get("ENABLE_OAUTH_ROLE_MANAGEMENT", "False").lower() == "true",
+)
+
+OAUTH_ROLES_CLAIM = PersistentConfig(
+    "OAUTH_ROLES_CLAIM",
+    "oauth.roles_claim",
+    os.environ.get("OAUTH_ROLES_CLAIM", "roles"),
+)
+
+OAUTH_ALLOWED_ROLES = PersistentConfig(
+    "OAUTH_ALLOWED_ROLES",
+    "oauth.allowed_roles",
+    [
+        role.strip()
+        for role in os.environ.get("OAUTH_ALLOWED_ROLES", "user,admin").split(",")
+    ],
+)
+
+OAUTH_ADMIN_ROLES = PersistentConfig(
+    "OAUTH_ADMIN_ROLES",
+    "oauth.admin_roles",
+    [role.strip() for role in os.environ.get("OAUTH_ADMIN_ROLES", "admin").split(",")],
+)
+
+
+def load_oauth_providers():
+    OAUTH_PROVIDERS.clear()
+    if GOOGLE_CLIENT_ID.value and GOOGLE_CLIENT_SECRET.value:
+        OAUTH_PROVIDERS["google"] = {
+            "client_id": GOOGLE_CLIENT_ID.value,
+            "client_secret": GOOGLE_CLIENT_SECRET.value,
+            "server_metadata_url": "https://accounts.google.com/.well-known/openid-configuration",
+            "scope": GOOGLE_OAUTH_SCOPE.value,
+            "redirect_uri": GOOGLE_REDIRECT_URI.value,
+        }
+
+    if (
+        MICROSOFT_CLIENT_ID.value
+        and MICROSOFT_CLIENT_SECRET.value
+        and MICROSOFT_CLIENT_TENANT_ID.value
+    ):
+        OAUTH_PROVIDERS["microsoft"] = {
+            "client_id": MICROSOFT_CLIENT_ID.value,
+            "client_secret": MICROSOFT_CLIENT_SECRET.value,
+            "server_metadata_url": f"https://login.microsoftonline.com/{MICROSOFT_CLIENT_TENANT_ID.value}/v2.0/.well-known/openid-configuration",
+            "scope": MICROSOFT_OAUTH_SCOPE.value,
+            "redirect_uri": MICROSOFT_REDIRECT_URI.value,
+        }
+
+    if (
+        OAUTH_CLIENT_ID.value
+        and OAUTH_CLIENT_SECRET.value
+        and OPENID_PROVIDER_URL.value
+    ):
+        OAUTH_PROVIDERS["oidc"] = {
+            "client_id": OAUTH_CLIENT_ID.value,
+            "client_secret": OAUTH_CLIENT_SECRET.value,
+            "server_metadata_url": OPENID_PROVIDER_URL.value,
+            "scope": OAUTH_SCOPES.value,
+            "name": OAUTH_PROVIDER_NAME.value,
+            "redirect_uri": OPENID_REDIRECT_URI.value,
+        }
+
+
+load_oauth_providers()
+
+####################################
+# Static DIR
+####################################
+
+STATIC_DIR = Path(os.getenv("STATIC_DIR", OPEN_WEBUI_DIR / "static")).resolve()
+
+frontend_favicon = FRONTEND_BUILD_DIR / "static" / "favicon.png"
+
+if frontend_favicon.exists():
+    try:
+        shutil.copyfile(frontend_favicon, STATIC_DIR / "favicon.png")
+    except Exception as e:
+        logging.error(f"An error occurred: {e}")
+else:
+    logging.warning(f"Frontend favicon not found at {frontend_favicon}")
+
+frontend_splash = FRONTEND_BUILD_DIR / "static" / "splash.png"
+
+if frontend_splash.exists():
+    try:
+        shutil.copyfile(frontend_splash, STATIC_DIR / "splash.png")
+    except Exception as e:
+        logging.error(f"An error occurred: {e}")
+else:
+    logging.warning(f"Frontend splash not found at {frontend_splash}")
+
+
+####################################
+# CUSTOM_NAME
+####################################
+
+CUSTOM_NAME = os.environ.get("CUSTOM_NAME", "")
+
+if CUSTOM_NAME:
+    try:
+        r = requests.get(f"https://api.openwebui.com/api/v1/custom/{CUSTOM_NAME}")
+        data = r.json()
+        if r.ok:
+            if "logo" in data:
+                WEBUI_FAVICON_URL = url = (
+                    f"https://api.openwebui.com{data['logo']}"
+                    if data["logo"][0] == "/"
+                    else data["logo"]
+                )
+
+                r = requests.get(url, stream=True)
+                if r.status_code == 200:
+                    with open(f"{STATIC_DIR}/favicon.png", "wb") as f:
+                        r.raw.decode_content = True
+                        shutil.copyfileobj(r.raw, f)
+
+            if "splash" in data:
+                url = (
+                    f"https://api.openwebui.com{data['splash']}"
+                    if data["splash"][0] == "/"
+                    else data["splash"]
+                )
+
+                r = requests.get(url, stream=True)
+                if r.status_code == 200:
+                    with open(f"{STATIC_DIR}/splash.png", "wb") as f:
+                        r.raw.decode_content = True
+                        shutil.copyfileobj(r.raw, f)
+
+            WEBUI_NAME = data["name"]
+    except Exception as e:
+        log.exception(e)
+        pass
+
+
+####################################
+# STORAGE PROVIDER
+####################################
+
+STORAGE_PROVIDER = os.environ.get("STORAGE_PROVIDER", "")  # defaults to local, s3
+
+S3_ACCESS_KEY_ID = os.environ.get("S3_ACCESS_KEY_ID", None)
+S3_SECRET_ACCESS_KEY = os.environ.get("S3_SECRET_ACCESS_KEY", None)
+S3_REGION_NAME = os.environ.get("S3_REGION_NAME", None)
+S3_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME", None)
+S3_ENDPOINT_URL = os.environ.get("S3_ENDPOINT_URL", None)
+
+####################################
+# File Upload DIR
+####################################
+
+UPLOAD_DIR = f"{DATA_DIR}/uploads"
+Path(UPLOAD_DIR).mkdir(parents=True, exist_ok=True)
+
+
+####################################
+# Cache DIR
+####################################
+
+CACHE_DIR = f"{DATA_DIR}/cache"
+Path(CACHE_DIR).mkdir(parents=True, exist_ok=True)
+
+####################################
+# OLLAMA_BASE_URL
+####################################
+
+ENABLE_OLLAMA_API = PersistentConfig(
+    "ENABLE_OLLAMA_API",
+    "ollama.enable",
+    os.environ.get("ENABLE_OLLAMA_API", "True").lower() == "true",
+)
+
+OLLAMA_API_BASE_URL = os.environ.get(
+    "OLLAMA_API_BASE_URL", "http://localhost:11434/api"
+)
+
+OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "")
+if OLLAMA_BASE_URL:
+    # Remove trailing slash
+    OLLAMA_BASE_URL = (
+        OLLAMA_BASE_URL[:-1] if OLLAMA_BASE_URL.endswith("/") else OLLAMA_BASE_URL
+    )
+
+
+K8S_FLAG = os.environ.get("K8S_FLAG", "")
+USE_OLLAMA_DOCKER = os.environ.get("USE_OLLAMA_DOCKER", "false")
+
+if OLLAMA_BASE_URL == "" and OLLAMA_API_BASE_URL != "":
+    OLLAMA_BASE_URL = (
+        OLLAMA_API_BASE_URL[:-4]
+        if OLLAMA_API_BASE_URL.endswith("/api")
+        else OLLAMA_API_BASE_URL
+    )
+
+if ENV == "prod":
+    if OLLAMA_BASE_URL == "/ollama" and not K8S_FLAG:
+        if USE_OLLAMA_DOCKER.lower() == "true":
+            # if you use all-in-one docker container (Open WebUI + Ollama)
+            # with the docker build arg USE_OLLAMA=true (--build-arg="USE_OLLAMA=true") this only works with http://localhost:11434
+            OLLAMA_BASE_URL = "http://localhost:11434"
+        else:
+            OLLAMA_BASE_URL = "http://host.docker.internal:11434"
+    elif K8S_FLAG:
+        OLLAMA_BASE_URL = "http://ollama-service.open-webui.svc.cluster.local:11434"
+
+
+OLLAMA_BASE_URLS = os.environ.get("OLLAMA_BASE_URLS", "")
+OLLAMA_BASE_URLS = OLLAMA_BASE_URLS if OLLAMA_BASE_URLS != "" else OLLAMA_BASE_URL
+
+OLLAMA_BASE_URLS = [url.strip() for url in OLLAMA_BASE_URLS.split(";")]
+OLLAMA_BASE_URLS = PersistentConfig(
+    "OLLAMA_BASE_URLS", "ollama.base_urls", OLLAMA_BASE_URLS
+)
+
+OLLAMA_API_CONFIGS = PersistentConfig(
+    "OLLAMA_API_CONFIGS",
+    "ollama.api_configs",
+    {},
+)
+
+####################################
+# OPENAI_API
+####################################
+
+
+ENABLE_OPENAI_API = PersistentConfig(
+    "ENABLE_OPENAI_API",
+    "openai.enable",
+    os.environ.get("ENABLE_OPENAI_API", "True").lower() == "true",
+)
+
+
+OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
+OPENAI_API_BASE_URL = os.environ.get("OPENAI_API_BASE_URL", "")
+
+
+if OPENAI_API_BASE_URL == "":
+    OPENAI_API_BASE_URL = "https://api.openai.com/v1"
+
+OPENAI_API_KEYS = os.environ.get("OPENAI_API_KEYS", "")
+OPENAI_API_KEYS = OPENAI_API_KEYS if OPENAI_API_KEYS != "" else OPENAI_API_KEY
+
+OPENAI_API_KEYS = [url.strip() for url in OPENAI_API_KEYS.split(";")]
+OPENAI_API_KEYS = PersistentConfig(
+    "OPENAI_API_KEYS", "openai.api_keys", OPENAI_API_KEYS
+)
+
+OPENAI_API_BASE_URLS = os.environ.get("OPENAI_API_BASE_URLS", "")
+OPENAI_API_BASE_URLS = (
+    OPENAI_API_BASE_URLS if OPENAI_API_BASE_URLS != "" else OPENAI_API_BASE_URL
+)
+
+OPENAI_API_BASE_URLS = [
+    url.strip() if url != "" else "https://api.openai.com/v1"
+    for url in OPENAI_API_BASE_URLS.split(";")
+]
+OPENAI_API_BASE_URLS = PersistentConfig(
+    "OPENAI_API_BASE_URLS", "openai.api_base_urls", OPENAI_API_BASE_URLS
+)
+
+OPENAI_API_CONFIGS = PersistentConfig(
+    "OPENAI_API_CONFIGS",
+    "openai.api_configs",
+    {},
+)
+
+# Get the actual OpenAI API key based on the base URL
+OPENAI_API_KEY = ""
+try:
+    OPENAI_API_KEY = OPENAI_API_KEYS.value[
+        OPENAI_API_BASE_URLS.value.index("https://api.openai.com/v1")
+    ]
+except Exception:
+    pass
+OPENAI_API_BASE_URL = "https://api.openai.com/v1"
+
+####################################
+# WEBUI
+####################################
+
+ENABLE_SIGNUP = PersistentConfig(
+    "ENABLE_SIGNUP",
+    "ui.enable_signup",
+    (
+        False
+        if not WEBUI_AUTH
+        else os.environ.get("ENABLE_SIGNUP", "True").lower() == "true"
+    ),
+)
+
+ENABLE_LOGIN_FORM = PersistentConfig(
+    "ENABLE_LOGIN_FORM",
+    "ui.ENABLE_LOGIN_FORM",
+    os.environ.get("ENABLE_LOGIN_FORM", "True").lower() == "true",
+)
+
+
+DEFAULT_LOCALE = PersistentConfig(
+    "DEFAULT_LOCALE",
+    "ui.default_locale",
+    os.environ.get("DEFAULT_LOCALE", ""),
+)
+
+DEFAULT_MODELS = PersistentConfig(
+    "DEFAULT_MODELS", "ui.default_models", os.environ.get("DEFAULT_MODELS", None)
+)
+
+DEFAULT_PROMPT_SUGGESTIONS = PersistentConfig(
+    "DEFAULT_PROMPT_SUGGESTIONS",
+    "ui.prompt_suggestions",
+    [
+        {
+            "title": ["Help me study", "vocabulary for a college entrance exam"],
+            "content": "Help me study vocabulary: write a sentence for me to fill in the blank, and I'll try to pick the correct option.",
+        },
+        {
+            "title": ["Give me ideas", "for what to do with my kids' art"],
+            "content": "What are 5 creative things I could do with my kids' art? I don't want to throw them away, but it's also so much clutter.",
+        },
+        {
+            "title": ["Tell me a fun fact", "about the Roman Empire"],
+            "content": "Tell me a random fun fact about the Roman Empire",
+        },
+        {
+            "title": ["Show me a code snippet", "of a website's sticky header"],
+            "content": "Show me a code snippet of a website's sticky header in CSS and JavaScript.",
+        },
+        {
+            "title": [
+                "Explain options trading",
+                "if I'm familiar with buying and selling stocks",
+            ],
+            "content": "Explain options trading in simple terms if I'm familiar with buying and selling stocks.",
+        },
+        {
+            "title": ["Overcome procrastination", "give me tips"],
+            "content": "Could you start by asking me about instances when I procrastinate the most and then give me some suggestions to overcome it?",
+        },
+    ],
+)
+
+MODEL_ORDER_LIST = PersistentConfig(
+    "MODEL_ORDER_LIST",
+    "ui.model_order_list",
+    [],
+)
+
+DEFAULT_USER_ROLE = PersistentConfig(
+    "DEFAULT_USER_ROLE",
+    "ui.default_user_role",
+    os.getenv("DEFAULT_USER_ROLE", "pending"),
+)
+
+USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS = (
+    os.environ.get("USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS", "False").lower()
+    == "true"
+)
+
+USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ACCESS = (
+    os.environ.get("USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ACCESS", "False").lower()
+    == "true"
+)
+
+USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS = (
+    os.environ.get("USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS", "False").lower()
+    == "true"
+)
+
+USER_PERMISSIONS_WORKSPACE_TOOLS_ACCESS = (
+    os.environ.get("USER_PERMISSIONS_WORKSPACE_TOOLS_ACCESS", "False").lower() == "true"
+)
+
+USER_PERMISSIONS_CHAT_FILE_UPLOAD = (
+    os.environ.get("USER_PERMISSIONS_CHAT_FILE_UPLOAD", "True").lower() == "true"
+)
+
+USER_PERMISSIONS_CHAT_DELETE = (
+    os.environ.get("USER_PERMISSIONS_CHAT_DELETE", "True").lower() == "true"
+)
+
+USER_PERMISSIONS_CHAT_EDIT = (
+    os.environ.get("USER_PERMISSIONS_CHAT_EDIT", "True").lower() == "true"
+)
+
+USER_PERMISSIONS_CHAT_TEMPORARY = (
+    os.environ.get("USER_PERMISSIONS_CHAT_TEMPORARY", "True").lower() == "true"
+)
+
+USER_PERMISSIONS = PersistentConfig(
+    "USER_PERMISSIONS",
+    "user.permissions",
+    {
+        "workspace": {
+            "models": USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS,
+            "knowledge": USER_PERMISSIONS_WORKSPACE_KNOWLEDGE_ACCESS,
+            "prompts": USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS,
+            "tools": USER_PERMISSIONS_WORKSPACE_TOOLS_ACCESS,
+        },
+        "chat": {
+            "file_upload": USER_PERMISSIONS_CHAT_FILE_UPLOAD,
+            "delete": USER_PERMISSIONS_CHAT_DELETE,
+            "edit": USER_PERMISSIONS_CHAT_EDIT,
+            "temporary": USER_PERMISSIONS_CHAT_TEMPORARY,
+        },
+    },
+)
+
+
+ENABLE_EVALUATION_ARENA_MODELS = PersistentConfig(
+    "ENABLE_EVALUATION_ARENA_MODELS",
+    "evaluation.arena.enable",
+    os.environ.get("ENABLE_EVALUATION_ARENA_MODELS", "True").lower() == "true",
+)
+EVALUATION_ARENA_MODELS = PersistentConfig(
+    "EVALUATION_ARENA_MODELS",
+    "evaluation.arena.models",
+    [],
+)
+
+DEFAULT_ARENA_MODEL = {
+    "id": "arena-model",
+    "name": "Arena Model",
+    "meta": {
+        "profile_image_url": "/favicon.png",
+        "description": "Submit your questions to anonymous AI chatbots and vote on the best response.",
+        "model_ids": None,
+    },
+}
+
+WEBHOOK_URL = PersistentConfig(
+    "WEBHOOK_URL", "webhook_url", os.environ.get("WEBHOOK_URL", "")
+)
+
+ENABLE_ADMIN_EXPORT = os.environ.get("ENABLE_ADMIN_EXPORT", "True").lower() == "true"
+
+ENABLE_ADMIN_CHAT_ACCESS = (
+    os.environ.get("ENABLE_ADMIN_CHAT_ACCESS", "True").lower() == "true"
+)
+
+ENABLE_COMMUNITY_SHARING = PersistentConfig(
+    "ENABLE_COMMUNITY_SHARING",
+    "ui.enable_community_sharing",
+    os.environ.get("ENABLE_COMMUNITY_SHARING", "True").lower() == "true",
+)
+
+ENABLE_MESSAGE_RATING = PersistentConfig(
+    "ENABLE_MESSAGE_RATING",
+    "ui.enable_message_rating",
+    os.environ.get("ENABLE_MESSAGE_RATING", "True").lower() == "true",
+)
+
+
+def validate_cors_origins(origins):
+    for origin in origins:
+        if origin != "*":
+            validate_cors_origin(origin)
+
+
+def validate_cors_origin(origin):
+    parsed_url = urlparse(origin)
+
+    # Check if the scheme is either http or https
+    if parsed_url.scheme not in ["http", "https"]:
+        raise ValueError(
+            f"Invalid scheme in CORS_ALLOW_ORIGIN: '{origin}'. Only 'http' and 'https' are allowed."
+        )
+
+    # Ensure that the netloc (domain + port) is present, indicating it's a valid URL
+    if not parsed_url.netloc:
+        raise ValueError(f"Invalid URL structure in CORS_ALLOW_ORIGIN: '{origin}'.")
+
+
+# For production, you should only need one host as
+# fastapi serves the svelte-kit built frontend and backend from the same host and port.
+# To test CORS_ALLOW_ORIGIN locally, you can set something like
+# CORS_ALLOW_ORIGIN=http://localhost:5173;http://localhost:8080
+# in your .env file depending on your frontend port, 5173 in this case.
+CORS_ALLOW_ORIGIN = os.environ.get("CORS_ALLOW_ORIGIN", "*").split(";")
+
+if "*" in CORS_ALLOW_ORIGIN:
+    log.warning(
+        "\n\nWARNING: CORS_ALLOW_ORIGIN IS SET TO '*' - NOT RECOMMENDED FOR PRODUCTION DEPLOYMENTS.\n"
+    )
+
+validate_cors_origins(CORS_ALLOW_ORIGIN)
+
+
+class BannerModel(BaseModel):
+    id: str
+    type: str
+    title: Optional[str] = None
+    content: str
+    dismissible: bool
+    timestamp: int
+
+
+try:
+    banners = json.loads(os.environ.get("WEBUI_BANNERS", "[]"))
+    banners = [BannerModel(**banner) for banner in banners]
+except Exception as e:
+    print(f"Error loading WEBUI_BANNERS: {e}")
+    banners = []
+
+WEBUI_BANNERS = PersistentConfig("WEBUI_BANNERS", "ui.banners", banners)
+
+
+SHOW_ADMIN_DETAILS = PersistentConfig(
+    "SHOW_ADMIN_DETAILS",
+    "auth.admin.show",
+    os.environ.get("SHOW_ADMIN_DETAILS", "true").lower() == "true",
+)
+
+ADMIN_EMAIL = PersistentConfig(
+    "ADMIN_EMAIL",
+    "auth.admin.email",
+    os.environ.get("ADMIN_EMAIL", None),
+)
+
+
+####################################
+# TASKS
+####################################
+
+
+TASK_MODEL = PersistentConfig(
+    "TASK_MODEL",
+    "task.model.default",
+    os.environ.get("TASK_MODEL", ""),
+)
+
+TASK_MODEL_EXTERNAL = PersistentConfig(
+    "TASK_MODEL_EXTERNAL",
+    "task.model.external",
+    os.environ.get("TASK_MODEL_EXTERNAL", ""),
+)
+
+TITLE_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
+    "TITLE_GENERATION_PROMPT_TEMPLATE",
+    "task.title.prompt_template",
+    os.environ.get("TITLE_GENERATION_PROMPT_TEMPLATE", ""),
+)
+
+TAGS_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
+    "TAGS_GENERATION_PROMPT_TEMPLATE",
+    "task.tags.prompt_template",
+    os.environ.get("TAGS_GENERATION_PROMPT_TEMPLATE", ""),
+)
+
+ENABLE_TAGS_GENERATION = PersistentConfig(
+    "ENABLE_TAGS_GENERATION",
+    "task.tags.enable",
+    os.environ.get("ENABLE_TAGS_GENERATION", "True").lower() == "true",
+)
+
+
+ENABLE_SEARCH_QUERY_GENERATION = PersistentConfig(
+    "ENABLE_SEARCH_QUERY_GENERATION",
+    "task.query.search.enable",
+    os.environ.get("ENABLE_SEARCH_QUERY_GENERATION", "True").lower() == "true",
+)
+
+ENABLE_RETRIEVAL_QUERY_GENERATION = PersistentConfig(
+    "ENABLE_RETRIEVAL_QUERY_GENERATION",
+    "task.query.retrieval.enable",
+    os.environ.get("ENABLE_RETRIEVAL_QUERY_GENERATION", "True").lower() == "true",
+)
+
+
+QUERY_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
+    "QUERY_GENERATION_PROMPT_TEMPLATE",
+    "task.query.prompt_template",
+    os.environ.get("QUERY_GENERATION_PROMPT_TEMPLATE", ""),
+)
+
+DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE = """### Task:
+Analyze the chat history to determine the necessity of generating search queries, in the given language. By default, **prioritize generating 1-3 broad and relevant search queries** unless it is absolutely certain that no additional information is required. The aim is to retrieve comprehensive, updated, and valuable information even with minimal uncertainty. If no search is unequivocally needed, return an empty list.
+
+### Guidelines:
+- Respond **EXCLUSIVELY** with a JSON object. Any form of extra commentary, explanation, or additional text is strictly prohibited.
+- When generating search queries, respond in the format: { "queries": ["query1", "query2"] }, ensuring each query is distinct, concise, and relevant to the topic.
+- If and only if it is entirely certain that no useful results can be retrieved by a search, return: { "queries": [] }.
+- Err on the side of suggesting search queries if there is **any chance** they might provide useful or updated information.
+- Be concise and focused on composing high-quality search queries, avoiding unnecessary elaboration, commentary, or assumptions.
+- Today's date is: {{CURRENT_DATE}}.
+- Always prioritize providing actionable and broad queries that maximize informational coverage.
+
+### Output:
+Strictly return in JSON format: 
+{
+  "queries": ["query1", "query2"]
+}
+
+### Chat History:
+<chat_history>
+{{MESSAGES:END:6}}
+</chat_history>
+"""
+
+ENABLE_AUTOCOMPLETE_GENERATION = PersistentConfig(
+    "ENABLE_AUTOCOMPLETE_GENERATION",
+    "task.autocomplete.enable",
+    os.environ.get("ENABLE_AUTOCOMPLETE_GENERATION", "True").lower() == "true",
+)
+
+AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = PersistentConfig(
+    "AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH",
+    "task.autocomplete.input_max_length",
+    int(os.environ.get("AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH", "-1")),
+)
+
+AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
+    "AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE",
+    "task.autocomplete.prompt_template",
+    os.environ.get("AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE", ""),
+)
+
+
+DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = """### Task:
+You are an autocompletion system. Continue the text in `<text>` based on the **completion type** in `<type>` and the given language.  
+
+### **Instructions**:
+1. Analyze `<text>` for context and meaning.  
+2. Use `<type>` to guide your output:  
+   - **General**: Provide a natural, concise continuation.  
+   - **Search Query**: Complete as if generating a realistic search query.  
+3. Start as if you are directly continuing `<text>`. Do **not** repeat, paraphrase, or respond as a model. Simply complete the text.  
+4. Ensure the continuation:
+   - Flows naturally from `<text>`.  
+   - Avoids repetition, overexplaining, or unrelated ideas.  
+5. If unsure, return: `{ "text": "" }`.  
+
+### **Output Rules**:
+- Respond only in JSON format: `{ "text": "<your_completion>" }`.
+
+### **Examples**:
+#### Example 1:  
+Input:  
+<type>General</type>  
+<text>The sun was setting over the horizon, painting the sky</text>  
+Output:  
+{ "text": "with vibrant shades of orange and pink." }
+
+#### Example 2:  
+Input:  
+<type>Search Query</type>  
+<text>Top-rated restaurants in</text>  
+Output:  
+{ "text": "New York City for Italian cuisine." }  
+
+---
+### Context:
+<chat_history>
+{{MESSAGES:END:6}}
+</chat_history>
+<type>{{TYPE}}</type>  
+<text>{{PROMPT}}</text>  
+#### Output:
+"""
+
+TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = PersistentConfig(
+    "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE",
+    "task.tools.prompt_template",
+    os.environ.get("TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE", ""),
+)
+
+
+####################################
+# Vector Database
+####################################
+
+VECTOR_DB = os.environ.get("VECTOR_DB", "chroma")
+
+# Chroma
+CHROMA_DATA_PATH = f"{DATA_DIR}/vector_db"
+CHROMA_TENANT = os.environ.get("CHROMA_TENANT", chromadb.DEFAULT_TENANT)
+CHROMA_DATABASE = os.environ.get("CHROMA_DATABASE", chromadb.DEFAULT_DATABASE)
+CHROMA_HTTP_HOST = os.environ.get("CHROMA_HTTP_HOST", "")
+CHROMA_HTTP_PORT = int(os.environ.get("CHROMA_HTTP_PORT", "8000"))
+CHROMA_CLIENT_AUTH_PROVIDER = os.environ.get("CHROMA_CLIENT_AUTH_PROVIDER", "")
+CHROMA_CLIENT_AUTH_CREDENTIALS = os.environ.get("CHROMA_CLIENT_AUTH_CREDENTIALS", "")
+# Comma-separated list of header=value pairs
+CHROMA_HTTP_HEADERS = os.environ.get("CHROMA_HTTP_HEADERS", "")
+if CHROMA_HTTP_HEADERS:
+    CHROMA_HTTP_HEADERS = dict(
+        [pair.split("=") for pair in CHROMA_HTTP_HEADERS.split(",")]
+    )
+else:
+    CHROMA_HTTP_HEADERS = None
+CHROMA_HTTP_SSL = os.environ.get("CHROMA_HTTP_SSL", "false").lower() == "true"
+# this uses the model defined in the Dockerfile ENV variable. If you dont use docker or docker based deployments such as k8s, the default embedding model will be used (sentence-transformers/all-MiniLM-L6-v2)
+
+# Milvus
+
+MILVUS_URI = os.environ.get("MILVUS_URI", f"{DATA_DIR}/vector_db/milvus.db")
+
+# Qdrant
+QDRANT_URI = os.environ.get("QDRANT_URI", None)
+QDRANT_API_KEY = os.environ.get("QDRANT_API_KEY", None)
+
+# OpenSearch
+OPENSEARCH_URI = os.environ.get("OPENSEARCH_URI", "https://localhost:9200")
+OPENSEARCH_SSL = os.environ.get("OPENSEARCH_SSL", True)
+OPENSEARCH_CERT_VERIFY = os.environ.get("OPENSEARCH_CERT_VERIFY", False)
+OPENSEARCH_USERNAME = os.environ.get("OPENSEARCH_USERNAME", None)
+OPENSEARCH_PASSWORD = os.environ.get("OPENSEARCH_PASSWORD", None)
+
+# Pgvector
+PGVECTOR_DB_URL = os.environ.get("PGVECTOR_DB_URL", DATABASE_URL)
+if VECTOR_DB == "pgvector" and not PGVECTOR_DB_URL.startswith("postgres"):
+    raise ValueError(
+        "Pgvector requires setting PGVECTOR_DB_URL or using Postgres with vector extension as the primary database."
+    )
+
+####################################
+# Information Retrieval (RAG)
+####################################
+
+# RAG Content Extraction
+CONTENT_EXTRACTION_ENGINE = PersistentConfig(
+    "CONTENT_EXTRACTION_ENGINE",
+    "rag.CONTENT_EXTRACTION_ENGINE",
+    os.environ.get("CONTENT_EXTRACTION_ENGINE", "").lower(),
+)
+
+TIKA_SERVER_URL = PersistentConfig(
+    "TIKA_SERVER_URL",
+    "rag.tika_server_url",
+    os.getenv("TIKA_SERVER_URL", "http://tika:9998"),  # Default for sidecar deployment
+)
+
+RAG_TOP_K = PersistentConfig(
+    "RAG_TOP_K", "rag.top_k", int(os.environ.get("RAG_TOP_K", "3"))
+)
+RAG_RELEVANCE_THRESHOLD = PersistentConfig(
+    "RAG_RELEVANCE_THRESHOLD",
+    "rag.relevance_threshold",
+    float(os.environ.get("RAG_RELEVANCE_THRESHOLD", "0.0")),
+)
+
+ENABLE_RAG_HYBRID_SEARCH = PersistentConfig(
+    "ENABLE_RAG_HYBRID_SEARCH",
+    "rag.enable_hybrid_search",
+    os.environ.get("ENABLE_RAG_HYBRID_SEARCH", "").lower() == "true",
+)
+
+RAG_FILE_MAX_COUNT = PersistentConfig(
+    "RAG_FILE_MAX_COUNT",
+    "rag.file.max_count",
+    (
+        int(os.environ.get("RAG_FILE_MAX_COUNT"))
+        if os.environ.get("RAG_FILE_MAX_COUNT")
+        else None
+    ),
+)
+
+RAG_FILE_MAX_SIZE = PersistentConfig(
+    "RAG_FILE_MAX_SIZE",
+    "rag.file.max_size",
+    (
+        int(os.environ.get("RAG_FILE_MAX_SIZE"))
+        if os.environ.get("RAG_FILE_MAX_SIZE")
+        else None
+    ),
+)
+
+ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION = PersistentConfig(
+    "ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION",
+    "rag.enable_web_loader_ssl_verification",
+    os.environ.get("ENABLE_RAG_WEB_LOADER_SSL_VERIFICATION", "True").lower() == "true",
+)
+
+RAG_EMBEDDING_ENGINE = PersistentConfig(
+    "RAG_EMBEDDING_ENGINE",
+    "rag.embedding_engine",
+    os.environ.get("RAG_EMBEDDING_ENGINE", ""),
+)
+
+PDF_EXTRACT_IMAGES = PersistentConfig(
+    "PDF_EXTRACT_IMAGES",
+    "rag.pdf_extract_images",
+    os.environ.get("PDF_EXTRACT_IMAGES", "False").lower() == "true",
+)
+
+RAG_EMBEDDING_MODEL = PersistentConfig(
+    "RAG_EMBEDDING_MODEL",
+    "rag.embedding_model",
+    os.environ.get("RAG_EMBEDDING_MODEL", "sentence-transformers/all-MiniLM-L6-v2"),
+)
+log.info(f"Embedding model set: {RAG_EMBEDDING_MODEL.value}")
+
+RAG_EMBEDDING_MODEL_AUTO_UPDATE = (
+    os.environ.get("RAG_EMBEDDING_MODEL_AUTO_UPDATE", "True").lower() == "true"
+)
+
+RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE = (
+    os.environ.get("RAG_EMBEDDING_MODEL_TRUST_REMOTE_CODE", "True").lower() == "true"
+)
+
+RAG_EMBEDDING_BATCH_SIZE = PersistentConfig(
+    "RAG_EMBEDDING_BATCH_SIZE",
+    "rag.embedding_batch_size",
+    int(
+        os.environ.get("RAG_EMBEDDING_BATCH_SIZE")
+        or os.environ.get("RAG_EMBEDDING_OPENAI_BATCH_SIZE", "1")
+    ),
+)
+
+RAG_RERANKING_MODEL = PersistentConfig(
+    "RAG_RERANKING_MODEL",
+    "rag.reranking_model",
+    os.environ.get("RAG_RERANKING_MODEL", ""),
+)
+if RAG_RERANKING_MODEL.value != "":
+    log.info(f"Reranking model set: {RAG_RERANKING_MODEL.value}")
+
+RAG_RERANKING_MODEL_AUTO_UPDATE = (
+    os.environ.get("RAG_RERANKING_MODEL_AUTO_UPDATE", "True").lower() == "true"
+)
+
+RAG_RERANKING_MODEL_TRUST_REMOTE_CODE = (
+    os.environ.get("RAG_RERANKING_MODEL_TRUST_REMOTE_CODE", "True").lower() == "true"
+)
+
+
+RAG_TEXT_SPLITTER = PersistentConfig(
+    "RAG_TEXT_SPLITTER",
+    "rag.text_splitter",
+    os.environ.get("RAG_TEXT_SPLITTER", ""),
+)
+
+
+TIKTOKEN_CACHE_DIR = os.environ.get("TIKTOKEN_CACHE_DIR", f"{CACHE_DIR}/tiktoken")
+TIKTOKEN_ENCODING_NAME = PersistentConfig(
+    "TIKTOKEN_ENCODING_NAME",
+    "rag.tiktoken_encoding_name",
+    os.environ.get("TIKTOKEN_ENCODING_NAME", "cl100k_base"),
+)
+
+
+CHUNK_SIZE = PersistentConfig(
+    "CHUNK_SIZE", "rag.chunk_size", int(os.environ.get("CHUNK_SIZE", "1000"))
+)
+CHUNK_OVERLAP = PersistentConfig(
+    "CHUNK_OVERLAP",
+    "rag.chunk_overlap",
+    int(os.environ.get("CHUNK_OVERLAP", "100")),
+)
+
+DEFAULT_RAG_TEMPLATE = """### Task:
+Respond to the user query using the provided context, incorporating inline citations in the format [source_id] **only when the <source_id> tag is explicitly provided** in the context.
+
+### Guidelines:
+- If you don't know the answer, clearly state that.
+- If uncertain, ask the user for clarification.
+- Respond in the same language as the user's query.
+- If the context is unreadable or of poor quality, inform the user and provide the best possible answer.
+- If the answer isn't present in the context but you possess the knowledge, explain this to the user and provide the answer using your own understanding.
+- **Only include inline citations using [source_id] when a <source_id> tag is explicitly provided in the context.**  
+- Do not cite if the <source_id> tag is not provided in the context.  
+- Do not use XML tags in your response.
+- Ensure citations are concise and directly related to the information provided.
+
+### Example of Citation:
+If the user asks about a specific topic and the information is found in "whitepaper.pdf" with a provided <source_id>, the response should include the citation like so:  
+* "According to the study, the proposed method increases efficiency by 20% [whitepaper.pdf]."
+If no <source_id> is present, the response should omit the citation.
+
+### Output:
+Provide a clear and direct response to the user's query, including inline citations in the format [source_id] only when the <source_id> tag is present in the context.
+
+<context>
+{{CONTEXT}}
+</context>
+
+<user_query>
+{{QUERY}}
+</user_query>
+"""
+
+RAG_TEMPLATE = PersistentConfig(
+    "RAG_TEMPLATE",
+    "rag.template",
+    os.environ.get("RAG_TEMPLATE", DEFAULT_RAG_TEMPLATE),
+)
+
+RAG_OPENAI_API_BASE_URL = PersistentConfig(
+    "RAG_OPENAI_API_BASE_URL",
+    "rag.openai_api_base_url",
+    os.getenv("RAG_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL),
+)
+RAG_OPENAI_API_KEY = PersistentConfig(
+    "RAG_OPENAI_API_KEY",
+    "rag.openai_api_key",
+    os.getenv("RAG_OPENAI_API_KEY", OPENAI_API_KEY),
+)
+
+RAG_OLLAMA_BASE_URL = PersistentConfig(
+    "RAG_OLLAMA_BASE_URL",
+    "rag.ollama.url",
+    os.getenv("RAG_OLLAMA_BASE_URL", OLLAMA_BASE_URL),
+)
+
+RAG_OLLAMA_API_KEY = PersistentConfig(
+    "RAG_OLLAMA_API_KEY",
+    "rag.ollama.key",
+    os.getenv("RAG_OLLAMA_API_KEY", ""),
+)
+
+
+ENABLE_RAG_LOCAL_WEB_FETCH = (
+    os.getenv("ENABLE_RAG_LOCAL_WEB_FETCH", "False").lower() == "true"
+)
+
+YOUTUBE_LOADER_LANGUAGE = PersistentConfig(
+    "YOUTUBE_LOADER_LANGUAGE",
+    "rag.youtube_loader_language",
+    os.getenv("YOUTUBE_LOADER_LANGUAGE", "en").split(","),
+)
+
+YOUTUBE_LOADER_PROXY_URL = PersistentConfig(
+    "YOUTUBE_LOADER_PROXY_URL",
+    "rag.youtube_loader_proxy_url",
+    os.getenv("YOUTUBE_LOADER_PROXY_URL", ""),
+)
+
+
+ENABLE_RAG_WEB_SEARCH = PersistentConfig(
+    "ENABLE_RAG_WEB_SEARCH",
+    "rag.web.search.enable",
+    os.getenv("ENABLE_RAG_WEB_SEARCH", "False").lower() == "true",
+)
+
+RAG_WEB_SEARCH_ENGINE = PersistentConfig(
+    "RAG_WEB_SEARCH_ENGINE",
+    "rag.web.search.engine",
+    os.getenv("RAG_WEB_SEARCH_ENGINE", ""),
+)
+
+# You can provide a list of your own websites to filter after performing a web search.
+# This ensures the highest level of safety and reliability of the information sources.
+RAG_WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig(
+    "RAG_WEB_SEARCH_DOMAIN_FILTER_LIST",
+    "rag.rag.web.search.domain.filter_list",
+    [
+        # "wikipedia.com",
+        # "wikimedia.org",
+        # "wikidata.org",
+    ],
+)
+
+SEARXNG_QUERY_URL = PersistentConfig(
+    "SEARXNG_QUERY_URL",
+    "rag.web.search.searxng_query_url",
+    os.getenv("SEARXNG_QUERY_URL", ""),
+)
+
+GOOGLE_PSE_API_KEY = PersistentConfig(
+    "GOOGLE_PSE_API_KEY",
+    "rag.web.search.google_pse_api_key",
+    os.getenv("GOOGLE_PSE_API_KEY", ""),
+)
+
+GOOGLE_PSE_ENGINE_ID = PersistentConfig(
+    "GOOGLE_PSE_ENGINE_ID",
+    "rag.web.search.google_pse_engine_id",
+    os.getenv("GOOGLE_PSE_ENGINE_ID", ""),
+)
+
+BRAVE_SEARCH_API_KEY = PersistentConfig(
+    "BRAVE_SEARCH_API_KEY",
+    "rag.web.search.brave_search_api_key",
+    os.getenv("BRAVE_SEARCH_API_KEY", ""),
+)
+
+MOJEEK_SEARCH_API_KEY = PersistentConfig(
+    "MOJEEK_SEARCH_API_KEY",
+    "rag.web.search.mojeek_search_api_key",
+    os.getenv("MOJEEK_SEARCH_API_KEY", ""),
+)
+
+SERPSTACK_API_KEY = PersistentConfig(
+    "SERPSTACK_API_KEY",
+    "rag.web.search.serpstack_api_key",
+    os.getenv("SERPSTACK_API_KEY", ""),
+)
+
+SERPSTACK_HTTPS = PersistentConfig(
+    "SERPSTACK_HTTPS",
+    "rag.web.search.serpstack_https",
+    os.getenv("SERPSTACK_HTTPS", "True").lower() == "true",
+)
+
+SERPER_API_KEY = PersistentConfig(
+    "SERPER_API_KEY",
+    "rag.web.search.serper_api_key",
+    os.getenv("SERPER_API_KEY", ""),
+)
+
+SERPLY_API_KEY = PersistentConfig(
+    "SERPLY_API_KEY",
+    "rag.web.search.serply_api_key",
+    os.getenv("SERPLY_API_KEY", ""),
+)
+
+TAVILY_API_KEY = PersistentConfig(
+    "TAVILY_API_KEY",
+    "rag.web.search.tavily_api_key",
+    os.getenv("TAVILY_API_KEY", ""),
+)
+
+JINA_API_KEY = PersistentConfig(
+    "JINA_API_KEY",
+    "rag.web.search.jina_api_key",
+    os.getenv("JINA_API_KEY", ""),
+)
+
+SEARCHAPI_API_KEY = PersistentConfig(
+    "SEARCHAPI_API_KEY",
+    "rag.web.search.searchapi_api_key",
+    os.getenv("SEARCHAPI_API_KEY", ""),
+)
+
+SEARCHAPI_ENGINE = PersistentConfig(
+    "SEARCHAPI_ENGINE",
+    "rag.web.search.searchapi_engine",
+    os.getenv("SEARCHAPI_ENGINE", ""),
+)
+
+BING_SEARCH_V7_ENDPOINT = PersistentConfig(
+    "BING_SEARCH_V7_ENDPOINT",
+    "rag.web.search.bing_search_v7_endpoint",
+    os.environ.get(
+        "BING_SEARCH_V7_ENDPOINT", "https://api.bing.microsoft.com/v7.0/search"
+    ),
+)
+
+BING_SEARCH_V7_SUBSCRIPTION_KEY = PersistentConfig(
+    "BING_SEARCH_V7_SUBSCRIPTION_KEY",
+    "rag.web.search.bing_search_v7_subscription_key",
+    os.environ.get("BING_SEARCH_V7_SUBSCRIPTION_KEY", ""),
+)
+
+
+RAG_WEB_SEARCH_RESULT_COUNT = PersistentConfig(
+    "RAG_WEB_SEARCH_RESULT_COUNT",
+    "rag.web.search.result_count",
+    int(os.getenv("RAG_WEB_SEARCH_RESULT_COUNT", "3")),
+)
+
+RAG_WEB_SEARCH_CONCURRENT_REQUESTS = PersistentConfig(
+    "RAG_WEB_SEARCH_CONCURRENT_REQUESTS",
+    "rag.web.search.concurrent_requests",
+    int(os.getenv("RAG_WEB_SEARCH_CONCURRENT_REQUESTS", "10")),
+)
+
+
+####################################
+# Images
+####################################
+
+IMAGE_GENERATION_ENGINE = PersistentConfig(
+    "IMAGE_GENERATION_ENGINE",
+    "image_generation.engine",
+    os.getenv("IMAGE_GENERATION_ENGINE", "openai"),
+)
+
+ENABLE_IMAGE_GENERATION = PersistentConfig(
+    "ENABLE_IMAGE_GENERATION",
+    "image_generation.enable",
+    os.environ.get("ENABLE_IMAGE_GENERATION", "").lower() == "true",
+)
+AUTOMATIC1111_BASE_URL = PersistentConfig(
+    "AUTOMATIC1111_BASE_URL",
+    "image_generation.automatic1111.base_url",
+    os.getenv("AUTOMATIC1111_BASE_URL", ""),
+)
+AUTOMATIC1111_API_AUTH = PersistentConfig(
+    "AUTOMATIC1111_API_AUTH",
+    "image_generation.automatic1111.api_auth",
+    os.getenv("AUTOMATIC1111_API_AUTH", ""),
+)
+
+AUTOMATIC1111_CFG_SCALE = PersistentConfig(
+    "AUTOMATIC1111_CFG_SCALE",
+    "image_generation.automatic1111.cfg_scale",
+    (
+        float(os.environ.get("AUTOMATIC1111_CFG_SCALE"))
+        if os.environ.get("AUTOMATIC1111_CFG_SCALE")
+        else None
+    ),
+)
+
+
+AUTOMATIC1111_SAMPLER = PersistentConfig(
+    "AUTOMATIC1111_SAMPLER",
+    "image_generation.automatic1111.sampler",
+    (
+        os.environ.get("AUTOMATIC1111_SAMPLER")
+        if os.environ.get("AUTOMATIC1111_SAMPLER")
+        else None
+    ),
+)
+
+AUTOMATIC1111_SCHEDULER = PersistentConfig(
+    "AUTOMATIC1111_SCHEDULER",
+    "image_generation.automatic1111.scheduler",
+    (
+        os.environ.get("AUTOMATIC1111_SCHEDULER")
+        if os.environ.get("AUTOMATIC1111_SCHEDULER")
+        else None
+    ),
+)
+
+COMFYUI_BASE_URL = PersistentConfig(
+    "COMFYUI_BASE_URL",
+    "image_generation.comfyui.base_url",
+    os.getenv("COMFYUI_BASE_URL", ""),
+)
+
+COMFYUI_DEFAULT_WORKFLOW = """
+{
+  "3": {
+    "inputs": {
+      "seed": 0,
+      "steps": 20,
+      "cfg": 8,
+      "sampler_name": "euler",
+      "scheduler": "normal",
+      "denoise": 1,
+      "model": [
+        "4",
+        0
+      ],
+      "positive": [
+        "6",
+        0
+      ],
+      "negative": [
+        "7",
+        0
+      ],
+      "latent_image": [
+        "5",
+        0
+      ]
+    },
+    "class_type": "KSampler",
+    "_meta": {
+      "title": "KSampler"
+    }
+  },
+  "4": {
+    "inputs": {
+      "ckpt_name": "model.safetensors"
+    },
+    "class_type": "CheckpointLoaderSimple",
+    "_meta": {
+      "title": "Load Checkpoint"
+    }
+  },
+  "5": {
+    "inputs": {
+      "width": 512,
+      "height": 512,
+      "batch_size": 1
+    },
+    "class_type": "EmptyLatentImage",
+    "_meta": {
+      "title": "Empty Latent Image"
+    }
+  },
+  "6": {
+    "inputs": {
+      "text": "Prompt",
+      "clip": [
+        "4",
+        1
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "7": {
+    "inputs": {
+      "text": "",
+      "clip": [
+        "4",
+        1
+      ]
+    },
+    "class_type": "CLIPTextEncode",
+    "_meta": {
+      "title": "CLIP Text Encode (Prompt)"
+    }
+  },
+  "8": {
+    "inputs": {
+      "samples": [
+        "3",
+        0
+      ],
+      "vae": [
+        "4",
+        2
+      ]
+    },
+    "class_type": "VAEDecode",
+    "_meta": {
+      "title": "VAE Decode"
+    }
+  },
+  "9": {
+    "inputs": {
+      "filename_prefix": "ComfyUI",
+      "images": [
+        "8",
+        0
+      ]
+    },
+    "class_type": "SaveImage",
+    "_meta": {
+      "title": "Save Image"
+    }
+  }
+}
+"""
+
+
+COMFYUI_WORKFLOW = PersistentConfig(
+    "COMFYUI_WORKFLOW",
+    "image_generation.comfyui.workflow",
+    os.getenv("COMFYUI_WORKFLOW", COMFYUI_DEFAULT_WORKFLOW),
+)
+
+COMFYUI_WORKFLOW_NODES = PersistentConfig(
+    "COMFYUI_WORKFLOW",
+    "image_generation.comfyui.nodes",
+    [],
+)
+
+IMAGES_OPENAI_API_BASE_URL = PersistentConfig(
+    "IMAGES_OPENAI_API_BASE_URL",
+    "image_generation.openai.api_base_url",
+    os.getenv("IMAGES_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL),
+)
+IMAGES_OPENAI_API_KEY = PersistentConfig(
+    "IMAGES_OPENAI_API_KEY",
+    "image_generation.openai.api_key",
+    os.getenv("IMAGES_OPENAI_API_KEY", OPENAI_API_KEY),
+)
+
+IMAGE_SIZE = PersistentConfig(
+    "IMAGE_SIZE", "image_generation.size", os.getenv("IMAGE_SIZE", "512x512")
+)
+
+IMAGE_STEPS = PersistentConfig(
+    "IMAGE_STEPS", "image_generation.steps", int(os.getenv("IMAGE_STEPS", 50))
+)
+
+IMAGE_GENERATION_MODEL = PersistentConfig(
+    "IMAGE_GENERATION_MODEL",
+    "image_generation.model",
+    os.getenv("IMAGE_GENERATION_MODEL", ""),
+)
+
+####################################
+# Audio
+####################################
+
+# Transcription
+WHISPER_MODEL = PersistentConfig(
+    "WHISPER_MODEL",
+    "audio.stt.whisper_model",
+    os.getenv("WHISPER_MODEL", "base"),
+)
+
+WHISPER_MODEL_DIR = os.getenv("WHISPER_MODEL_DIR", f"{CACHE_DIR}/whisper/models")
+WHISPER_MODEL_AUTO_UPDATE = (
+    os.environ.get("WHISPER_MODEL_AUTO_UPDATE", "").lower() == "true"
+)
+
+
+AUDIO_STT_OPENAI_API_BASE_URL = PersistentConfig(
+    "AUDIO_STT_OPENAI_API_BASE_URL",
+    "audio.stt.openai.api_base_url",
+    os.getenv("AUDIO_STT_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL),
+)
+
+AUDIO_STT_OPENAI_API_KEY = PersistentConfig(
+    "AUDIO_STT_OPENAI_API_KEY",
+    "audio.stt.openai.api_key",
+    os.getenv("AUDIO_STT_OPENAI_API_KEY", OPENAI_API_KEY),
+)
+
+AUDIO_STT_ENGINE = PersistentConfig(
+    "AUDIO_STT_ENGINE",
+    "audio.stt.engine",
+    os.getenv("AUDIO_STT_ENGINE", ""),
+)
+
+AUDIO_STT_MODEL = PersistentConfig(
+    "AUDIO_STT_MODEL",
+    "audio.stt.model",
+    os.getenv("AUDIO_STT_MODEL", ""),
+)
+
+AUDIO_TTS_OPENAI_API_BASE_URL = PersistentConfig(
+    "AUDIO_TTS_OPENAI_API_BASE_URL",
+    "audio.tts.openai.api_base_url",
+    os.getenv("AUDIO_TTS_OPENAI_API_BASE_URL", OPENAI_API_BASE_URL),
+)
+AUDIO_TTS_OPENAI_API_KEY = PersistentConfig(
+    "AUDIO_TTS_OPENAI_API_KEY",
+    "audio.tts.openai.api_key",
+    os.getenv("AUDIO_TTS_OPENAI_API_KEY", OPENAI_API_KEY),
+)
+
+AUDIO_TTS_API_KEY = PersistentConfig(
+    "AUDIO_TTS_API_KEY",
+    "audio.tts.api_key",
+    os.getenv("AUDIO_TTS_API_KEY", ""),
+)
+
+AUDIO_TTS_ENGINE = PersistentConfig(
+    "AUDIO_TTS_ENGINE",
+    "audio.tts.engine",
+    os.getenv("AUDIO_TTS_ENGINE", ""),
+)
+
+
+AUDIO_TTS_MODEL = PersistentConfig(
+    "AUDIO_TTS_MODEL",
+    "audio.tts.model",
+    os.getenv("AUDIO_TTS_MODEL", "tts-1"),  # OpenAI default model
+)
+
+AUDIO_TTS_VOICE = PersistentConfig(
+    "AUDIO_TTS_VOICE",
+    "audio.tts.voice",
+    os.getenv("AUDIO_TTS_VOICE", "alloy"),  # OpenAI default voice
+)
+
+AUDIO_TTS_SPLIT_ON = PersistentConfig(
+    "AUDIO_TTS_SPLIT_ON",
+    "audio.tts.split_on",
+    os.getenv("AUDIO_TTS_SPLIT_ON", "punctuation"),
+)
+
+AUDIO_TTS_AZURE_SPEECH_REGION = PersistentConfig(
+    "AUDIO_TTS_AZURE_SPEECH_REGION",
+    "audio.tts.azure.speech_region",
+    os.getenv("AUDIO_TTS_AZURE_SPEECH_REGION", "eastus"),
+)
+
+AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT = PersistentConfig(
+    "AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT",
+    "audio.tts.azure.speech_output_format",
+    os.getenv(
+        "AUDIO_TTS_AZURE_SPEECH_OUTPUT_FORMAT", "audio-24khz-160kbitrate-mono-mp3"
+    ),
+)
+
+
+####################################
+# LDAP
+####################################
+
+ENABLE_LDAP = PersistentConfig(
+    "ENABLE_LDAP",
+    "ldap.enable",
+    os.environ.get("ENABLE_LDAP", "false").lower() == "true",
+)
+
+LDAP_SERVER_LABEL = PersistentConfig(
+    "LDAP_SERVER_LABEL",
+    "ldap.server.label",
+    os.environ.get("LDAP_SERVER_LABEL", "LDAP Server"),
+)
+
+LDAP_SERVER_HOST = PersistentConfig(
+    "LDAP_SERVER_HOST",
+    "ldap.server.host",
+    os.environ.get("LDAP_SERVER_HOST", "localhost"),
+)
+
+LDAP_SERVER_PORT = PersistentConfig(
+    "LDAP_SERVER_PORT",
+    "ldap.server.port",
+    int(os.environ.get("LDAP_SERVER_PORT", "389")),
+)
+
+LDAP_ATTRIBUTE_FOR_USERNAME = PersistentConfig(
+    "LDAP_ATTRIBUTE_FOR_USERNAME",
+    "ldap.server.attribute_for_username",
+    os.environ.get("LDAP_ATTRIBUTE_FOR_USERNAME", "uid"),
+)
+
+LDAP_APP_DN = PersistentConfig(
+    "LDAP_APP_DN", "ldap.server.app_dn", os.environ.get("LDAP_APP_DN", "")
+)
+
+LDAP_APP_PASSWORD = PersistentConfig(
+    "LDAP_APP_PASSWORD",
+    "ldap.server.app_password",
+    os.environ.get("LDAP_APP_PASSWORD", ""),
+)
+
+LDAP_SEARCH_BASE = PersistentConfig(
+    "LDAP_SEARCH_BASE", "ldap.server.users_dn", os.environ.get("LDAP_SEARCH_BASE", "")
+)
+
+LDAP_SEARCH_FILTERS = PersistentConfig(
+    "LDAP_SEARCH_FILTER",
+    "ldap.server.search_filter",
+    os.environ.get("LDAP_SEARCH_FILTER", ""),
+)
+
+LDAP_USE_TLS = PersistentConfig(
+    "LDAP_USE_TLS",
+    "ldap.server.use_tls",
+    os.environ.get("LDAP_USE_TLS", "True").lower() == "true",
+)
+
+LDAP_CA_CERT_FILE = PersistentConfig(
+    "LDAP_CA_CERT_FILE",
+    "ldap.server.ca_cert_file",
+    os.environ.get("LDAP_CA_CERT_FILE", ""),
+)
+
+LDAP_CIPHERS = PersistentConfig(
+    "LDAP_CIPHERS", "ldap.server.ciphers", os.environ.get("LDAP_CIPHERS", "ALL")
+)
diff --git a/backend/open_webui/constants.py b/backend/open_webui/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5fdfabfb8e407547d15d9594851e7f22c639619
--- /dev/null
+++ b/backend/open_webui/constants.py
@@ -0,0 +1,118 @@
+from enum import Enum
+
+
+class MESSAGES(str, Enum):
+    DEFAULT = lambda msg="": f"{msg if msg else ''}"
+    MODEL_ADDED = lambda model="": f"The model '{model}' has been added successfully."
+    MODEL_DELETED = (
+        lambda model="": f"The model '{model}' has been deleted successfully."
+    )
+
+
+class WEBHOOK_MESSAGES(str, Enum):
+    DEFAULT = lambda msg="": f"{msg if msg else ''}"
+    USER_SIGNUP = lambda username="": (
+        f"New user signed up: {username}" if username else "New user signed up"
+    )
+
+
+class ERROR_MESSAGES(str, Enum):
+    def __str__(self) -> str:
+        return super().__str__()
+
+    DEFAULT = (
+        lambda err="": f'{"Something went wrong :/" if err == "" else "[ERROR: " + str(err) + "]"}'
+    )
+    ENV_VAR_NOT_FOUND = "Required environment variable not found. Terminating now."
+    CREATE_USER_ERROR = "Oops! Something went wrong while creating your account. Please try again later. If the issue persists, contact support for assistance."
+    DELETE_USER_ERROR = "Oops! Something went wrong. We encountered an issue while trying to delete the user. Please give it another shot."
+    EMAIL_MISMATCH = "Uh-oh! This email does not match the email your provider is registered with. Please check your email and try again."
+    EMAIL_TAKEN = "Uh-oh! This email is already registered. Sign in with your existing account or choose another email to start anew."
+    USERNAME_TAKEN = (
+        "Uh-oh! This username is already registered. Please choose another username."
+    )
+    COMMAND_TAKEN = "Uh-oh! This command is already registered. Please choose another command string."
+    FILE_EXISTS = "Uh-oh! This file is already registered. Please choose another file."
+
+    ID_TAKEN = "Uh-oh! This id is already registered. Please choose another id string."
+    MODEL_ID_TAKEN = "Uh-oh! This model id is already registered. Please choose another model id string."
+    NAME_TAG_TAKEN = "Uh-oh! This name tag is already registered. Please choose another name tag string."
+
+    INVALID_TOKEN = (
+        "Your session has expired or the token is invalid. Please sign in again."
+    )
+    INVALID_CRED = "The email or password provided is incorrect. Please check for typos and try logging in again."
+    INVALID_EMAIL_FORMAT = "The email format you entered is invalid. Please double-check and make sure you're using a valid email address (e.g., yourname@example.com)."
+    INVALID_PASSWORD = (
+        "The password provided is incorrect. Please check for typos and try again."
+    )
+    INVALID_TRUSTED_HEADER = "Your provider has not provided a trusted header. Please contact your administrator for assistance."
+
+    EXISTING_USERS = "You can't turn off authentication because there are existing users. If you want to disable WEBUI_AUTH, make sure your web interface doesn't have any existing users and is a fresh installation."
+
+    UNAUTHORIZED = "401 Unauthorized"
+    ACCESS_PROHIBITED = "You do not have permission to access this resource. Please contact your administrator for assistance."
+    ACTION_PROHIBITED = (
+        "The requested action has been restricted as a security measure."
+    )
+
+    FILE_NOT_SENT = "FILE_NOT_SENT"
+    FILE_NOT_SUPPORTED = "Oops! It seems like the file format you're trying to upload is not supported. Please upload a file with a supported format (e.g., JPG, PNG, PDF, TXT) and try again."
+
+    NOT_FOUND = "We could not find what you're looking for :/"
+    USER_NOT_FOUND = "We could not find what you're looking for :/"
+    API_KEY_NOT_FOUND = "Oops! It looks like there's a hiccup. The API key is missing. Please make sure to provide a valid API key to access this feature."
+    API_KEY_NOT_ALLOWED = "Use of API key is not enabled in the environment."
+
+    MALICIOUS = "Unusual activities detected, please try again in a few minutes."
+
+    PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance."
+    INCORRECT_FORMAT = (
+        lambda err="": f"Invalid format. Please use the correct format{err}"
+    )
+    RATE_LIMIT_EXCEEDED = "API rate limit exceeded"
+
+    MODEL_NOT_FOUND = lambda name="": f"Model '{name}' was not found"
+    OPENAI_NOT_FOUND = lambda name="": "OpenAI API was not found"
+    OLLAMA_NOT_FOUND = "WebUI could not connect to Ollama"
+    CREATE_API_KEY_ERROR = "Oops! Something went wrong while creating your API key. Please try again later. If the issue persists, contact support for assistance."
+    API_KEY_CREATION_NOT_ALLOWED = "API key creation is not allowed in the environment."
+
+    EMPTY_CONTENT = "The content provided is empty. Please ensure that there is text or data present before proceeding."
+
+    DB_NOT_SQLITE = "This feature is only available when running with SQLite databases."
+
+    INVALID_URL = (
+        "Oops! The URL you provided is invalid. Please double-check and try again."
+    )
+
+    WEB_SEARCH_ERROR = (
+        lambda err="": f"{err if err else 'Oops! Something went wrong while searching the web.'}"
+    )
+
+    OLLAMA_API_DISABLED = (
+        "The Ollama API is disabled. Please enable it to use this feature."
+    )
+
+    FILE_TOO_LARGE = (
+        lambda size="": f"Oops! The file you're trying to upload is too large. Please upload a file that is less than {size}."
+    )
+
+    DUPLICATE_CONTENT = (
+        "Duplicate content detected. Please provide unique content to proceed."
+    )
+    FILE_NOT_PROCESSED = "Extracted content is not available for this file. Please ensure that the file is processed before proceeding."
+
+
+class TASKS(str, Enum):
+    def __str__(self) -> str:
+        return super().__str__()
+
+    DEFAULT = lambda task="": f"{task if task else 'generation'}"
+    TITLE_GENERATION = "title_generation"
+    TAGS_GENERATION = "tags_generation"
+    EMOJI_GENERATION = "emoji_generation"
+    QUERY_GENERATION = "query_generation"
+    AUTOCOMPLETE_GENERATION = "autocomplete_generation"
+    FUNCTION_CALLING = "function_calling"
+    MOA_RESPONSE_GENERATION = "moa_response_generation"
diff --git a/backend/open_webui/env.py b/backend/open_webui/env.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1b350ead4b899d463e12e7453e2becd6224aa38
--- /dev/null
+++ b/backend/open_webui/env.py
@@ -0,0 +1,396 @@
+import importlib.metadata
+import json
+import logging
+import os
+import pkgutil
+import sys
+import shutil
+from pathlib import Path
+
+import markdown
+from bs4 import BeautifulSoup
+from open_webui.constants import ERROR_MESSAGES
+
+####################################
+# Load .env file
+####################################
+
+OPEN_WEBUI_DIR = Path(__file__).parent  # the path containing this file
+print(OPEN_WEBUI_DIR)
+
+BACKEND_DIR = OPEN_WEBUI_DIR.parent  # the path containing this file
+BASE_DIR = BACKEND_DIR.parent  # the path containing the backend/
+
+print(BACKEND_DIR)
+print(BASE_DIR)
+
+try:
+    from dotenv import find_dotenv, load_dotenv
+
+    load_dotenv(find_dotenv(str(BASE_DIR / ".env")))
+except ImportError:
+    print("dotenv not installed, skipping...")
+
+DOCKER = os.environ.get("DOCKER", "False").lower() == "true"
+
+# device type embedding models - "cpu" (default), "cuda" (nvidia gpu required) or "mps" (apple silicon) - choosing this right can lead to better performance
+USE_CUDA = os.environ.get("USE_CUDA_DOCKER", "false")
+
+if USE_CUDA.lower() == "true":
+    try:
+        import torch
+
+        assert torch.cuda.is_available(), "CUDA not available"
+        DEVICE_TYPE = "cuda"
+    except Exception as e:
+        cuda_error = (
+            "Error when testing CUDA but USE_CUDA_DOCKER is true. "
+            f"Resetting USE_CUDA_DOCKER to false: {e}"
+        )
+        os.environ["USE_CUDA_DOCKER"] = "false"
+        USE_CUDA = "false"
+        DEVICE_TYPE = "cpu"
+else:
+    DEVICE_TYPE = "cpu"
+
+
+####################################
+# LOGGING
+####################################
+
+log_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]
+
+GLOBAL_LOG_LEVEL = os.environ.get("GLOBAL_LOG_LEVEL", "").upper()
+if GLOBAL_LOG_LEVEL in log_levels:
+    logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL, force=True)
+else:
+    GLOBAL_LOG_LEVEL = "INFO"
+
+log = logging.getLogger(__name__)
+log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
+
+if "cuda_error" in locals():
+    log.exception(cuda_error)
+
+log_sources = [
+    "AUDIO",
+    "COMFYUI",
+    "CONFIG",
+    "DB",
+    "IMAGES",
+    "MAIN",
+    "MODELS",
+    "OLLAMA",
+    "OPENAI",
+    "RAG",
+    "WEBHOOK",
+    "SOCKET",
+]
+
+SRC_LOG_LEVELS = {}
+
+for source in log_sources:
+    log_env_var = source + "_LOG_LEVEL"
+    SRC_LOG_LEVELS[source] = os.environ.get(log_env_var, "").upper()
+    if SRC_LOG_LEVELS[source] not in log_levels:
+        SRC_LOG_LEVELS[source] = GLOBAL_LOG_LEVEL
+    log.info(f"{log_env_var}: {SRC_LOG_LEVELS[source]}")
+
+log.setLevel(SRC_LOG_LEVELS["CONFIG"])
+
+
+WEBUI_NAME = os.environ.get("WEBUI_NAME", "Open WebUI")
+if WEBUI_NAME != "Open WebUI":
+    WEBUI_NAME += " (Open WebUI)"
+
+WEBUI_URL = os.environ.get("WEBUI_URL", "http://localhost:3000")
+
+WEBUI_FAVICON_URL = "https://openwebui.com/favicon.png"
+
+
+####################################
+# ENV (dev,test,prod)
+####################################
+
+ENV = os.environ.get("ENV", "dev")
+
+FROM_INIT_PY = os.environ.get("FROM_INIT_PY", "False").lower() == "true"
+
+if FROM_INIT_PY:
+    PACKAGE_DATA = {"version": importlib.metadata.version("open-webui")}
+else:
+    try:
+        PACKAGE_DATA = json.loads((BASE_DIR / "package.json").read_text())
+    except Exception:
+        PACKAGE_DATA = {"version": "0.0.0"}
+
+
+VERSION = PACKAGE_DATA["version"]
+
+
+# Function to parse each section
+def parse_section(section):
+    items = []
+    for li in section.find_all("li"):
+        # Extract raw HTML string
+        raw_html = str(li)
+
+        # Extract text without HTML tags
+        text = li.get_text(separator=" ", strip=True)
+
+        # Split into title and content
+        parts = text.split(": ", 1)
+        title = parts[0].strip() if len(parts) > 1 else ""
+        content = parts[1].strip() if len(parts) > 1 else text
+
+        items.append({"title": title, "content": content, "raw": raw_html})
+    return items
+
+
+try:
+    changelog_path = BASE_DIR / "CHANGELOG.md"
+    with open(str(changelog_path.absolute()), "r", encoding="utf8") as file:
+        changelog_content = file.read()
+
+except Exception:
+    changelog_content = (pkgutil.get_data("open_webui", "CHANGELOG.md") or b"").decode()
+
+
+# Convert markdown content to HTML
+html_content = markdown.markdown(changelog_content)
+
+# Parse the HTML content
+soup = BeautifulSoup(html_content, "html.parser")
+
+# Initialize JSON structure
+changelog_json = {}
+
+# Iterate over each version
+for version in soup.find_all("h2"):
+    version_number = version.get_text().strip().split(" - ")[0][1:-1]  # Remove brackets
+    date = version.get_text().strip().split(" - ")[1]
+
+    version_data = {"date": date}
+
+    # Find the next sibling that is a h3 tag (section title)
+    current = version.find_next_sibling()
+
+    while current and current.name != "h2":
+        if current.name == "h3":
+            section_title = current.get_text().lower()  # e.g., "added", "fixed"
+            section_items = parse_section(current.find_next_sibling("ul"))
+            version_data[section_title] = section_items
+
+        # Move to the next element
+        current = current.find_next_sibling()
+
+    changelog_json[version_number] = version_data
+
+
+CHANGELOG = changelog_json
+
+####################################
+# SAFE_MODE
+####################################
+
+SAFE_MODE = os.environ.get("SAFE_MODE", "false").lower() == "true"
+
+####################################
+# ENABLE_FORWARD_USER_INFO_HEADERS
+####################################
+
+ENABLE_FORWARD_USER_INFO_HEADERS = (
+    os.environ.get("ENABLE_FORWARD_USER_INFO_HEADERS", "False").lower() == "true"
+)
+
+
+####################################
+# WEBUI_BUILD_HASH
+####################################
+
+WEBUI_BUILD_HASH = os.environ.get("WEBUI_BUILD_HASH", "dev-build")
+
+####################################
+# DATA/FRONTEND BUILD DIR
+####################################
+
+DATA_DIR = Path(os.getenv("DATA_DIR", BACKEND_DIR / "data")).resolve()
+
+if FROM_INIT_PY:
+    NEW_DATA_DIR = Path(os.getenv("DATA_DIR", OPEN_WEBUI_DIR / "data")).resolve()
+    NEW_DATA_DIR.mkdir(parents=True, exist_ok=True)
+
+    # Check if the data directory exists in the package directory
+    if DATA_DIR.exists() and DATA_DIR != NEW_DATA_DIR:
+        log.info(f"Moving {DATA_DIR} to {NEW_DATA_DIR}")
+        for item in DATA_DIR.iterdir():
+            dest = NEW_DATA_DIR / item.name
+            if item.is_dir():
+                shutil.copytree(item, dest, dirs_exist_ok=True)
+            else:
+                shutil.copy2(item, dest)
+
+        # Zip the data directory
+        shutil.make_archive(DATA_DIR.parent / "open_webui_data", "zip", DATA_DIR)
+
+        # Remove the old data directory
+        shutil.rmtree(DATA_DIR)
+
+    DATA_DIR = Path(os.getenv("DATA_DIR", OPEN_WEBUI_DIR / "data"))
+
+
+STATIC_DIR = Path(os.getenv("STATIC_DIR", OPEN_WEBUI_DIR / "static"))
+
+FONTS_DIR = Path(os.getenv("FONTS_DIR", OPEN_WEBUI_DIR / "static" / "fonts"))
+
+FRONTEND_BUILD_DIR = Path(os.getenv("FRONTEND_BUILD_DIR", BASE_DIR / "build")).resolve()
+
+if FROM_INIT_PY:
+    FRONTEND_BUILD_DIR = Path(
+        os.getenv("FRONTEND_BUILD_DIR", OPEN_WEBUI_DIR / "frontend")
+    ).resolve()
+
+
+####################################
+# Database
+####################################
+
+# Check if the file exists
+if os.path.exists(f"{DATA_DIR}/ollama.db"):
+    # Rename the file
+    os.rename(f"{DATA_DIR}/ollama.db", f"{DATA_DIR}/webui.db")
+    log.info("Database migrated from Ollama-WebUI successfully.")
+else:
+    pass
+
+DATABASE_URL = os.environ.get("DATABASE_URL", f"sqlite:///{DATA_DIR}/webui.db")
+
+# Replace the postgres:// with postgresql://
+if "postgres://" in DATABASE_URL:
+    DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql://")
+
+DATABASE_POOL_SIZE = os.environ.get("DATABASE_POOL_SIZE", 0)
+
+if DATABASE_POOL_SIZE == "":
+    DATABASE_POOL_SIZE = 0
+else:
+    try:
+        DATABASE_POOL_SIZE = int(DATABASE_POOL_SIZE)
+    except Exception:
+        DATABASE_POOL_SIZE = 0
+
+DATABASE_POOL_MAX_OVERFLOW = os.environ.get("DATABASE_POOL_MAX_OVERFLOW", 0)
+
+if DATABASE_POOL_MAX_OVERFLOW == "":
+    DATABASE_POOL_MAX_OVERFLOW = 0
+else:
+    try:
+        DATABASE_POOL_MAX_OVERFLOW = int(DATABASE_POOL_MAX_OVERFLOW)
+    except Exception:
+        DATABASE_POOL_MAX_OVERFLOW = 0
+
+DATABASE_POOL_TIMEOUT = os.environ.get("DATABASE_POOL_TIMEOUT", 30)
+
+if DATABASE_POOL_TIMEOUT == "":
+    DATABASE_POOL_TIMEOUT = 30
+else:
+    try:
+        DATABASE_POOL_TIMEOUT = int(DATABASE_POOL_TIMEOUT)
+    except Exception:
+        DATABASE_POOL_TIMEOUT = 30
+
+DATABASE_POOL_RECYCLE = os.environ.get("DATABASE_POOL_RECYCLE", 3600)
+
+if DATABASE_POOL_RECYCLE == "":
+    DATABASE_POOL_RECYCLE = 3600
+else:
+    try:
+        DATABASE_POOL_RECYCLE = int(DATABASE_POOL_RECYCLE)
+    except Exception:
+        DATABASE_POOL_RECYCLE = 3600
+
+RESET_CONFIG_ON_START = (
+    os.environ.get("RESET_CONFIG_ON_START", "False").lower() == "true"
+)
+
+####################################
+# REDIS
+####################################
+
+REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
+
+####################################
+# WEBUI_AUTH (Required for security)
+####################################
+
+WEBUI_AUTH = os.environ.get("WEBUI_AUTH", "True").lower() == "true"
+WEBUI_AUTH_TRUSTED_EMAIL_HEADER = os.environ.get(
+    "WEBUI_AUTH_TRUSTED_EMAIL_HEADER", None
+)
+WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get("WEBUI_AUTH_TRUSTED_NAME_HEADER", None)
+
+BYPASS_MODEL_ACCESS_CONTROL = (
+    os.environ.get("BYPASS_MODEL_ACCESS_CONTROL", "False").lower() == "true"
+)
+
+####################################
+# WEBUI_SECRET_KEY
+####################################
+
+WEBUI_SECRET_KEY = os.environ.get(
+    "WEBUI_SECRET_KEY",
+    os.environ.get(
+        "WEBUI_JWT_SECRET_KEY", "t0p-s3cr3t"
+    ),  # DEPRECATED: remove at next major version
+)
+
+WEBUI_SESSION_COOKIE_SAME_SITE = os.environ.get(
+    "WEBUI_SESSION_COOKIE_SAME_SITE",
+    os.environ.get("WEBUI_SESSION_COOKIE_SAME_SITE", "lax"),
+)
+
+WEBUI_SESSION_COOKIE_SECURE = os.environ.get(
+    "WEBUI_SESSION_COOKIE_SECURE",
+    os.environ.get("WEBUI_SESSION_COOKIE_SECURE", "false").lower() == "true",
+)
+
+if WEBUI_AUTH and WEBUI_SECRET_KEY == "":
+    raise ValueError(ERROR_MESSAGES.ENV_VAR_NOT_FOUND)
+
+ENABLE_WEBSOCKET_SUPPORT = (
+    os.environ.get("ENABLE_WEBSOCKET_SUPPORT", "True").lower() == "true"
+)
+
+WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
+
+WEBSOCKET_REDIS_URL = os.environ.get("WEBSOCKET_REDIS_URL", REDIS_URL)
+
+AIOHTTP_CLIENT_TIMEOUT = os.environ.get("AIOHTTP_CLIENT_TIMEOUT", "")
+
+if AIOHTTP_CLIENT_TIMEOUT == "":
+    AIOHTTP_CLIENT_TIMEOUT = None
+else:
+    try:
+        AIOHTTP_CLIENT_TIMEOUT = int(AIOHTTP_CLIENT_TIMEOUT)
+    except Exception:
+        AIOHTTP_CLIENT_TIMEOUT = 300
+
+AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = os.environ.get(
+    "AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST", "5"
+)
+
+if AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST == "":
+    AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = None
+else:
+    try:
+        AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = int(
+            AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST
+        )
+    except Exception:
+        AIOHTTP_CLIENT_TIMEOUT_OPENAI_MODEL_LIST = 5
+
+####################################
+# OFFLINE_MODE
+####################################
+
+OFFLINE_MODE = os.environ.get("OFFLINE_MODE", "false").lower() == "true"
diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..1bf221beb83b943cb638b728d03070868a63b8ed
--- /dev/null
+++ b/backend/open_webui/main.py
@@ -0,0 +1,2804 @@
+import asyncio
+import inspect
+import json
+import logging
+import mimetypes
+import os
+import shutil
+import sys
+import time
+import random
+from contextlib import asynccontextmanager
+from typing import Optional
+
+from aiocache import cached
+import aiohttp
+import requests
+from fastapi import (
+    Depends,
+    FastAPI,
+    File,
+    Form,
+    HTTPException,
+    Request,
+    UploadFile,
+    status,
+)
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import JSONResponse, RedirectResponse
+from fastapi.staticfiles import StaticFiles
+from pydantic import BaseModel
+from sqlalchemy import text
+from starlette.exceptions import HTTPException as StarletteHTTPException
+from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.middleware.sessions import SessionMiddleware
+from starlette.responses import Response, StreamingResponse
+
+from open_webui.apps.audio.main import app as audio_app
+from open_webui.apps.images.main import app as images_app
+from open_webui.apps.ollama.main import (
+    app as ollama_app,
+    get_all_models as get_ollama_models,
+    generate_chat_completion as generate_ollama_chat_completion,
+    GenerateChatCompletionForm,
+)
+from open_webui.apps.openai.main import (
+    app as openai_app,
+    generate_chat_completion as generate_openai_chat_completion,
+    get_all_models as get_openai_models,
+    get_all_models_responses as get_openai_models_responses,
+)
+from open_webui.apps.retrieval.main import app as retrieval_app
+from open_webui.apps.retrieval.utils import get_sources_from_files
+
+
+from open_webui.apps.socket.main import (
+    app as socket_app,
+    periodic_usage_pool_cleanup,
+    get_event_call,
+    get_event_emitter,
+)
+from open_webui.apps.webui.internal.db import Session
+from open_webui.apps.webui.main import (
+    app as webui_app,
+    generate_function_chat_completion,
+    get_all_models as get_open_webui_models,
+)
+from open_webui.apps.webui.models.functions import Functions
+from open_webui.apps.webui.models.models import Models
+from open_webui.apps.webui.models.users import UserModel, Users
+from open_webui.apps.webui.utils import load_function_module_by_id
+from open_webui.config import (
+    CACHE_DIR,
+    CORS_ALLOW_ORIGIN,
+    DEFAULT_LOCALE,
+    ENABLE_ADMIN_CHAT_ACCESS,
+    ENABLE_ADMIN_EXPORT,
+    ENABLE_OLLAMA_API,
+    ENABLE_OPENAI_API,
+    ENABLE_TAGS_GENERATION,
+    ENV,
+    FRONTEND_BUILD_DIR,
+    OAUTH_PROVIDERS,
+    STATIC_DIR,
+    TASK_MODEL,
+    TASK_MODEL_EXTERNAL,
+    ENABLE_SEARCH_QUERY_GENERATION,
+    ENABLE_RETRIEVAL_QUERY_GENERATION,
+    QUERY_GENERATION_PROMPT_TEMPLATE,
+    DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
+    TITLE_GENERATION_PROMPT_TEMPLATE,
+    TAGS_GENERATION_PROMPT_TEMPLATE,
+    ENABLE_AUTOCOMPLETE_GENERATION,
+    AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
+    AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE,
+    DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE,
+    TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
+    WEBHOOK_URL,
+    WEBUI_AUTH,
+    WEBUI_NAME,
+    AppConfig,
+    reset_config,
+)
+from open_webui.constants import TASKS
+from open_webui.env import (
+    CHANGELOG,
+    GLOBAL_LOG_LEVEL,
+    SAFE_MODE,
+    SRC_LOG_LEVELS,
+    VERSION,
+    WEBUI_BUILD_HASH,
+    WEBUI_SECRET_KEY,
+    WEBUI_SESSION_COOKIE_SAME_SITE,
+    WEBUI_SESSION_COOKIE_SECURE,
+    WEBUI_URL,
+    BYPASS_MODEL_ACCESS_CONTROL,
+    RESET_CONFIG_ON_START,
+    OFFLINE_MODE,
+)
+from open_webui.utils.misc import (
+    add_or_update_system_message,
+    get_last_user_message,
+    prepend_to_first_user_message_content,
+)
+from open_webui.utils.oauth import oauth_manager
+from open_webui.utils.payload import convert_payload_openai_to_ollama
+from open_webui.utils.response import (
+    convert_response_ollama_to_openai,
+    convert_streaming_response_ollama_to_openai,
+)
+from open_webui.utils.security_headers import SecurityHeadersMiddleware
+from open_webui.utils.task import (
+    rag_template,
+    title_generation_template,
+    query_generation_template,
+    autocomplete_generation_template,
+    tags_generation_template,
+    emoji_generation_template,
+    moa_response_generation_template,
+    tools_function_calling_generation_template,
+)
+from open_webui.utils.tools import get_tools
+from open_webui.utils.utils import (
+    decode_token,
+    get_admin_user,
+    get_current_user,
+    get_http_authorization_cred,
+    get_verified_user,
+)
+from open_webui.utils.access_control import has_access
+
+if SAFE_MODE:
+    print("SAFE MODE ENABLED")
+    Functions.deactivate_all_functions()
+
+logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["MAIN"])
+
+
+class SPAStaticFiles(StaticFiles):
+    async def get_response(self, path: str, scope):
+        try:
+            return await super().get_response(path, scope)
+        except (HTTPException, StarletteHTTPException) as ex:
+            if ex.status_code == 404:
+                return await super().get_response("index.html", scope)
+            else:
+                raise ex
+
+
+print(
+    rf"""
+  ___                    __        __   _     _   _ ___
+ / _ \ _ __   ___ _ __   \ \      / /__| |__ | | | |_ _|
+| | | | '_ \ / _ \ '_ \   \ \ /\ / / _ \ '_ \| | | || |
+| |_| | |_) |  __/ | | |   \ V  V /  __/ |_) | |_| || |
+ \___/| .__/ \___|_| |_|    \_/\_/ \___|_.__/ \___/|___|
+      |_|
+
+
+v{VERSION} - building the best open-source AI user interface.
+{f"Commit: {WEBUI_BUILD_HASH}" if WEBUI_BUILD_HASH != "dev-build" else ""}
+https://github.com/open-webui/open-webui
+"""
+)
+
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+    if RESET_CONFIG_ON_START:
+        reset_config()
+
+    asyncio.create_task(periodic_usage_pool_cleanup())
+    yield
+
+
+app = FastAPI(
+    docs_url="/docs" if ENV == "dev" else None,
+    openapi_url="/openapi.json" if ENV == "dev" else None,
+    redoc_url=None,
+    lifespan=lifespan,
+)
+
+app.state.config = AppConfig()
+
+app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
+app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
+
+app.state.config.WEBHOOK_URL = WEBHOOK_URL
+
+app.state.config.TASK_MODEL = TASK_MODEL
+app.state.config.TASK_MODEL_EXTERNAL = TASK_MODEL_EXTERNAL
+
+app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE
+
+app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = ENABLE_AUTOCOMPLETE_GENERATION
+app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = (
+    AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH
+)
+
+app.state.config.ENABLE_TAGS_GENERATION = ENABLE_TAGS_GENERATION
+app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = TAGS_GENERATION_PROMPT_TEMPLATE
+
+
+app.state.config.ENABLE_SEARCH_QUERY_GENERATION = ENABLE_SEARCH_QUERY_GENERATION
+app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION = ENABLE_RETRIEVAL_QUERY_GENERATION
+app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE = QUERY_GENERATION_PROMPT_TEMPLATE
+
+app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE = (
+    AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
+)
+
+app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
+    TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
+)
+
+##################################
+#
+# ChatCompletion Middleware
+#
+##################################
+
+
+def get_filter_function_ids(model):
+    def get_priority(function_id):
+        function = Functions.get_function_by_id(function_id)
+        if function is not None and hasattr(function, "valves"):
+            # TODO: Fix FunctionModel
+            return (function.valves if function.valves else {}).get("priority", 0)
+        return 0
+
+    filter_ids = [function.id for function in Functions.get_global_filter_functions()]
+    if "info" in model and "meta" in model["info"]:
+        filter_ids.extend(model["info"]["meta"].get("filterIds", []))
+        filter_ids = list(set(filter_ids))
+
+    enabled_filter_ids = [
+        function.id
+        for function in Functions.get_functions_by_type("filter", active_only=True)
+    ]
+
+    filter_ids = [
+        filter_id for filter_id in filter_ids if filter_id in enabled_filter_ids
+    ]
+
+    filter_ids.sort(key=get_priority)
+    return filter_ids
+
+
+async def chat_completion_filter_functions_handler(body, model, extra_params):
+    skip_files = None
+
+    filter_ids = get_filter_function_ids(model)
+    for filter_id in filter_ids:
+        filter = Functions.get_function_by_id(filter_id)
+        if not filter:
+            continue
+
+        if filter_id in webui_app.state.FUNCTIONS:
+            function_module = webui_app.state.FUNCTIONS[filter_id]
+        else:
+            function_module, _, _ = load_function_module_by_id(filter_id)
+            webui_app.state.FUNCTIONS[filter_id] = function_module
+
+        # Check if the function has a file_handler variable
+        if hasattr(function_module, "file_handler"):
+            skip_files = function_module.file_handler
+
+        if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
+            valves = Functions.get_function_valves_by_id(filter_id)
+            function_module.valves = function_module.Valves(
+                **(valves if valves else {})
+            )
+
+        if not hasattr(function_module, "inlet"):
+            continue
+
+        try:
+            inlet = function_module.inlet
+
+            # Get the signature of the function
+            sig = inspect.signature(inlet)
+            params = {"body": body} | {
+                k: v
+                for k, v in {
+                    **extra_params,
+                    "__model__": model,
+                    "__id__": filter_id,
+                }.items()
+                if k in sig.parameters
+            }
+
+            if "__user__" in params and hasattr(function_module, "UserValves"):
+                try:
+                    params["__user__"]["valves"] = function_module.UserValves(
+                        **Functions.get_user_valves_by_id_and_user_id(
+                            filter_id, params["__user__"]["id"]
+                        )
+                    )
+                except Exception as e:
+                    print(e)
+
+            if inspect.iscoroutinefunction(inlet):
+                body = await inlet(**params)
+            else:
+                body = inlet(**params)
+
+        except Exception as e:
+            print(f"Error: {e}")
+            raise e
+
+    if skip_files and "files" in body.get("metadata", {}):
+        del body["metadata"]["files"]
+
+    return body, {}
+
+
+def get_tools_function_calling_payload(messages, task_model_id, content):
+    user_message = get_last_user_message(messages)
+    history = "\n".join(
+        f"{message['role'].upper()}: \"\"\"{message['content']}\"\"\""
+        for message in messages[::-1][:4]
+    )
+
+    prompt = f"History:\n{history}\nQuery: {user_message}"
+
+    return {
+        "model": task_model_id,
+        "messages": [
+            {"role": "system", "content": content},
+            {"role": "user", "content": f"Query: {prompt}"},
+        ],
+        "stream": False,
+        "metadata": {"task": str(TASKS.FUNCTION_CALLING)},
+    }
+
+
+async def get_content_from_response(response) -> Optional[str]:
+    content = None
+    if hasattr(response, "body_iterator"):
+        async for chunk in response.body_iterator:
+            data = json.loads(chunk.decode("utf-8"))
+            content = data["choices"][0]["message"]["content"]
+
+        # Cleanup any remaining background tasks if necessary
+        if response.background is not None:
+            await response.background()
+    else:
+        content = response["choices"][0]["message"]["content"]
+    return content
+
+
+def get_task_model_id(
+    default_model_id: str, task_model: str, task_model_external: str, models
+) -> str:
+    # Set the task model
+    task_model_id = default_model_id
+    # Check if the user has a custom task model and use that model
+    if models[task_model_id]["owned_by"] == "ollama":
+        if task_model and task_model in models:
+            task_model_id = task_model
+    else:
+        if task_model_external and task_model_external in models:
+            task_model_id = task_model_external
+
+    return task_model_id
+
+
+async def chat_completion_tools_handler(
+    body: dict, user: UserModel, models, extra_params: dict
+) -> tuple[dict, dict]:
+    # If tool_ids field is present, call the functions
+    metadata = body.get("metadata", {})
+
+    tool_ids = metadata.get("tool_ids", None)
+    log.debug(f"{tool_ids=}")
+    if not tool_ids:
+        return body, {}
+
+    skip_files = False
+    sources = []
+
+    task_model_id = get_task_model_id(
+        body["model"],
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+    tools = get_tools(
+        webui_app,
+        tool_ids,
+        user,
+        {
+            **extra_params,
+            "__model__": models[task_model_id],
+            "__messages__": body["messages"],
+            "__files__": metadata.get("files", []),
+        },
+    )
+    log.info(f"{tools=}")
+
+    specs = [tool["spec"] for tool in tools.values()]
+    tools_specs = json.dumps(specs)
+
+    if app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE != "":
+        template = app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
+    else:
+        template = """Available Tools: {{TOOLS}}\nReturn an empty string if no tools match the query. If a function tool matches, construct and return a JSON object in the format {\"name\": \"functionName\", \"parameters\": {\"requiredFunctionParamKey\": \"requiredFunctionParamValue\"}} using the appropriate tool and its parameters. Only return the object and limit the response to the JSON object without additional text."""
+
+    tools_function_calling_prompt = tools_function_calling_generation_template(
+        template, tools_specs
+    )
+    log.info(f"{tools_function_calling_prompt=}")
+    payload = get_tools_function_calling_payload(
+        body["messages"], task_model_id, tools_function_calling_prompt
+    )
+
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        raise e
+
+    try:
+        response = await generate_chat_completions(form_data=payload, user=user)
+        log.debug(f"{response=}")
+        content = await get_content_from_response(response)
+        log.debug(f"{content=}")
+
+        if not content:
+            return body, {}
+
+        try:
+            content = content[content.find("{") : content.rfind("}") + 1]
+            if not content:
+                raise Exception("No JSON object found in the response")
+
+            result = json.loads(content)
+
+            tool_function_name = result.get("name", None)
+            if tool_function_name not in tools:
+                return body, {}
+
+            tool_function_params = result.get("parameters", {})
+
+            try:
+                required_params = (
+                    tools[tool_function_name]
+                    .get("spec", {})
+                    .get("parameters", {})
+                    .get("required", [])
+                )
+                tool_function = tools[tool_function_name]["callable"]
+                tool_function_params = {
+                    k: v
+                    for k, v in tool_function_params.items()
+                    if k in required_params
+                }
+                tool_output = await tool_function(**tool_function_params)
+
+            except Exception as e:
+                tool_output = str(e)
+
+            print(tools[tool_function_name]["citation"])
+
+            if isinstance(tool_output, str):
+                if tools[tool_function_name]["citation"]:
+                    sources.append(
+                        {
+                            "source": {
+                                "name": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
+                            },
+                            "document": [tool_output],
+                            "metadata": [
+                                {
+                                    "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
+                                }
+                            ],
+                        }
+                    )
+                else:
+                    sources.append(
+                        {
+                            "source": {},
+                            "document": [tool_output],
+                            "metadata": [
+                                {
+                                    "source": f"TOOL:{tools[tool_function_name]['toolkit_id']}/{tool_function_name}"
+                                }
+                            ],
+                        }
+                    )
+
+                if tools[tool_function_name]["file_handler"]:
+                    skip_files = True
+
+        except Exception as e:
+            log.exception(f"Error: {e}")
+            content = None
+    except Exception as e:
+        log.exception(f"Error: {e}")
+        content = None
+
+    log.debug(f"tool_contexts: {sources}")
+
+    if skip_files and "files" in body.get("metadata", {}):
+        del body["metadata"]["files"]
+
+    return body, {"sources": sources}
+
+
+async def chat_completion_files_handler(
+    body: dict, user: UserModel
+) -> tuple[dict, dict[str, list]]:
+    sources = []
+
+    if files := body.get("metadata", {}).get("files", None):
+        try:
+            queries_response = await generate_queries(
+                {
+                    "model": body["model"],
+                    "messages": body["messages"],
+                    "type": "retrieval",
+                },
+                user,
+            )
+            queries_response = queries_response["choices"][0]["message"]["content"]
+
+            try:
+                bracket_start = queries_response.find("{")
+                bracket_end = queries_response.rfind("}") + 1
+
+                if bracket_start == -1 or bracket_end == -1:
+                    raise Exception("No JSON object found in the response")
+
+                queries_response = queries_response[bracket_start:bracket_end]
+                queries_response = json.loads(queries_response)
+            except Exception as e:
+                queries_response = {"queries": [queries_response]}
+
+            queries = queries_response.get("queries", [])
+        except Exception as e:
+            queries = []
+
+        if len(queries) == 0:
+            queries = [get_last_user_message(body["messages"])]
+
+        sources = get_sources_from_files(
+            files=files,
+            queries=queries,
+            embedding_function=retrieval_app.state.EMBEDDING_FUNCTION,
+            k=retrieval_app.state.config.TOP_K,
+            reranking_function=retrieval_app.state.sentence_transformer_rf,
+            r=retrieval_app.state.config.RELEVANCE_THRESHOLD,
+            hybrid_search=retrieval_app.state.config.ENABLE_RAG_HYBRID_SEARCH,
+        )
+
+        log.debug(f"rag_contexts:sources: {sources}")
+    return body, {"sources": sources}
+
+
+def is_chat_completion_request(request):
+    return request.method == "POST" and any(
+        endpoint in request.url.path
+        for endpoint in ["/ollama/api/chat", "/chat/completions"]
+    )
+
+
+async def get_body_and_model_and_user(request, models):
+    # Read the original request body
+    body = await request.body()
+    body_str = body.decode("utf-8")
+    body = json.loads(body_str) if body_str else {}
+
+    model_id = body["model"]
+    if model_id not in models:
+        raise Exception("Model not found")
+    model = models[model_id]
+
+    user = get_current_user(
+        request,
+        get_http_authorization_cred(request.headers.get("Authorization")),
+    )
+
+    return body, model, user
+
+
+class ChatCompletionMiddleware(BaseHTTPMiddleware):
+    async def dispatch(self, request: Request, call_next):
+        if not is_chat_completion_request(request):
+            return await call_next(request)
+        log.debug(f"request.url.path: {request.url.path}")
+
+        model_list = await get_all_models()
+        models = {model["id"]: model for model in model_list}
+
+        try:
+            body, model, user = await get_body_and_model_and_user(request, models)
+        except Exception as e:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+
+        model_info = Models.get_model_by_id(model["id"])
+        if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+            if model.get("arena"):
+                if not has_access(
+                    user.id,
+                    type="read",
+                    access_control=model.get("info", {})
+                    .get("meta", {})
+                    .get("access_control", {}),
+                ):
+                    raise HTTPException(
+                        status_code=403,
+                        detail="Model not found",
+                    )
+            else:
+                if not model_info:
+                    return JSONResponse(
+                        status_code=status.HTTP_404_NOT_FOUND,
+                        content={"detail": "Model not found"},
+                    )
+                elif not (
+                    user.id == model_info.user_id
+                    or has_access(
+                        user.id, type="read", access_control=model_info.access_control
+                    )
+                ):
+                    return JSONResponse(
+                        status_code=status.HTTP_403_FORBIDDEN,
+                        content={"detail": "User does not have access to the model"},
+                    )
+
+        metadata = {
+            "chat_id": body.pop("chat_id", None),
+            "message_id": body.pop("id", None),
+            "session_id": body.pop("session_id", None),
+            "tool_ids": body.get("tool_ids", None),
+            "files": body.get("files", None),
+        }
+        body["metadata"] = metadata
+
+        extra_params = {
+            "__event_emitter__": get_event_emitter(metadata),
+            "__event_call__": get_event_call(metadata),
+            "__user__": {
+                "id": user.id,
+                "email": user.email,
+                "name": user.name,
+                "role": user.role,
+            },
+            "__metadata__": metadata,
+        }
+
+        # Initialize data_items to store additional data to be sent to the client
+        # Initialize contexts and citation
+        data_items = []
+        sources = []
+
+        try:
+            body, flags = await chat_completion_filter_functions_handler(
+                body, model, extra_params
+            )
+        except Exception as e:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+
+        tool_ids = body.pop("tool_ids", None)
+        files = body.pop("files", None)
+
+        metadata = {
+            **metadata,
+            "tool_ids": tool_ids,
+            "files": files,
+        }
+        body["metadata"] = metadata
+
+        try:
+            body, flags = await chat_completion_tools_handler(
+                body, user, models, extra_params
+            )
+            sources.extend(flags.get("sources", []))
+        except Exception as e:
+            log.exception(e)
+
+        try:
+            body, flags = await chat_completion_files_handler(body, user)
+            sources.extend(flags.get("sources", []))
+        except Exception as e:
+            log.exception(e)
+
+        # If context is not empty, insert it into the messages
+        if len(sources) > 0:
+            context_string = ""
+            for source_idx, source in enumerate(sources):
+                source_id = source.get("source", {}).get("name", "")
+
+                if "document" in source:
+                    for doc_idx, doc_context in enumerate(source["document"]):
+                        metadata = source.get("metadata")
+                        doc_source_id = None
+
+                        if metadata:
+                            doc_source_id = metadata[doc_idx].get("source", source_id)
+
+                        if source_id:
+                            context_string += f"<source><source_id>{doc_source_id if doc_source_id is not None else source_id}</source_id><source_context>{doc_context}</source_context></source>\n"
+                        else:
+                            # If there is no source_id, then do not include the source_id tag
+                            context_string += f"<source><source_context>{doc_context}</source_context></source>\n"
+
+            context_string = context_string.strip()
+            prompt = get_last_user_message(body["messages"])
+
+            if prompt is None:
+                raise Exception("No user message found")
+            if (
+                retrieval_app.state.config.RELEVANCE_THRESHOLD == 0
+                and context_string.strip() == ""
+            ):
+                log.debug(
+                    f"With a 0 relevancy threshold for RAG, the context cannot be empty"
+                )
+
+            # Workaround for Ollama 2.0+ system prompt issue
+            # TODO: replace with add_or_update_system_message
+            if model["owned_by"] == "ollama":
+                body["messages"] = prepend_to_first_user_message_content(
+                    rag_template(
+                        retrieval_app.state.config.RAG_TEMPLATE, context_string, prompt
+                    ),
+                    body["messages"],
+                )
+            else:
+                body["messages"] = add_or_update_system_message(
+                    rag_template(
+                        retrieval_app.state.config.RAG_TEMPLATE, context_string, prompt
+                    ),
+                    body["messages"],
+                )
+
+        # If there are citations, add them to the data_items
+        sources = [
+            source for source in sources if source.get("source", {}).get("name", "")
+        ]
+        if len(sources) > 0:
+            data_items.append({"sources": sources})
+
+        modified_body_bytes = json.dumps(body).encode("utf-8")
+        # Replace the request body with the modified one
+        request._body = modified_body_bytes
+        # Set custom header to ensure content-length matches new body length
+        request.headers.__dict__["_list"] = [
+            (b"content-length", str(len(modified_body_bytes)).encode("utf-8")),
+            *[(k, v) for k, v in request.headers.raw if k.lower() != b"content-length"],
+        ]
+
+        response = await call_next(request)
+        if not isinstance(response, StreamingResponse):
+            return response
+
+        content_type = response.headers["Content-Type"]
+        is_openai = "text/event-stream" in content_type
+        is_ollama = "application/x-ndjson" in content_type
+        if not is_openai and not is_ollama:
+            return response
+
+        def wrap_item(item):
+            return f"data: {item}\n\n" if is_openai else f"{item}\n"
+
+        async def stream_wrapper(original_generator, data_items):
+            for item in data_items:
+                yield wrap_item(json.dumps(item))
+
+            async for data in original_generator:
+                yield data
+
+        return StreamingResponse(
+            stream_wrapper(response.body_iterator, data_items),
+            headers=dict(response.headers),
+        )
+
+    async def _receive(self, body: bytes):
+        return {"type": "http.request", "body": body, "more_body": False}
+
+
+app.add_middleware(ChatCompletionMiddleware)
+
+
+##################################
+#
+# Pipeline Middleware
+#
+##################################
+
+
+def get_sorted_filters(model_id, models):
+    filters = [
+        model
+        for model in models.values()
+        if "pipeline" in model
+        and "type" in model["pipeline"]
+        and model["pipeline"]["type"] == "filter"
+        and (
+            model["pipeline"]["pipelines"] == ["*"]
+            or any(
+                model_id == target_model_id
+                for target_model_id in model["pipeline"]["pipelines"]
+            )
+        )
+    ]
+    sorted_filters = sorted(filters, key=lambda x: x["pipeline"]["priority"])
+    return sorted_filters
+
+
+def filter_pipeline(payload, user, models):
+    user = {"id": user.id, "email": user.email, "name": user.name, "role": user.role}
+    model_id = payload["model"]
+
+    sorted_filters = get_sorted_filters(model_id, models)
+    model = models[model_id]
+
+    if "pipeline" in model:
+        sorted_filters.append(model)
+
+    for filter in sorted_filters:
+        r = None
+        try:
+            urlIdx = filter["urlIdx"]
+
+            url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+            key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+            if key == "":
+                continue
+
+            headers = {"Authorization": f"Bearer {key}"}
+            r = requests.post(
+                f"{url}/{filter['id']}/filter/inlet",
+                headers=headers,
+                json={
+                    "user": user,
+                    "body": payload,
+                },
+            )
+
+            r.raise_for_status()
+            payload = r.json()
+        except Exception as e:
+            # Handle connection error here
+            print(f"Connection error: {e}")
+
+            if r is not None:
+                res = r.json()
+                if "detail" in res:
+                    raise Exception(r.status_code, res["detail"])
+
+    return payload
+
+
+class PipelineMiddleware(BaseHTTPMiddleware):
+    async def dispatch(self, request: Request, call_next):
+        if not is_chat_completion_request(request):
+            return await call_next(request)
+
+        log.debug(f"request.url.path: {request.url.path}")
+
+        # Read the original request body
+        body = await request.body()
+        # Decode body to string
+        body_str = body.decode("utf-8")
+        # Parse string to JSON
+        data = json.loads(body_str) if body_str else {}
+
+        try:
+            user = get_current_user(
+                request,
+                get_http_authorization_cred(request.headers["Authorization"]),
+            )
+        except KeyError as e:
+            if len(e.args) > 1:
+                return JSONResponse(
+                    status_code=e.args[0],
+                    content={"detail": e.args[1]},
+                )
+            else:
+                return JSONResponse(
+                    status_code=status.HTTP_401_UNAUTHORIZED,
+                    content={"detail": "Not authenticated"},
+                )
+        except HTTPException as e:
+            return JSONResponse(
+                status_code=e.status_code,
+                content={"detail": e.detail},
+            )
+
+        model_list = await get_all_models()
+        models = {model["id"]: model for model in model_list}
+
+        try:
+            data = filter_pipeline(data, user, models)
+        except Exception as e:
+            if len(e.args) > 1:
+                return JSONResponse(
+                    status_code=e.args[0],
+                    content={"detail": e.args[1]},
+                )
+            else:
+                return JSONResponse(
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                    content={"detail": str(e)},
+                )
+
+        modified_body_bytes = json.dumps(data).encode("utf-8")
+        # Replace the request body with the modified one
+        request._body = modified_body_bytes
+        # Set custom header to ensure content-length matches new body length
+        request.headers.__dict__["_list"] = [
+            (b"content-length", str(len(modified_body_bytes)).encode("utf-8")),
+            *[(k, v) for k, v in request.headers.raw if k.lower() != b"content-length"],
+        ]
+
+        response = await call_next(request)
+        return response
+
+    async def _receive(self, body: bytes):
+        return {"type": "http.request", "body": body, "more_body": False}
+
+
+app.add_middleware(PipelineMiddleware)
+
+
+from urllib.parse import urlencode, parse_qs, urlparse
+
+
+class RedirectMiddleware(BaseHTTPMiddleware):
+    async def dispatch(self, request: Request, call_next):
+        # Check if the request is a GET request
+        if request.method == "GET":
+            path = request.url.path
+            query_params = dict(parse_qs(urlparse(str(request.url)).query))
+
+            # Check for the specific watch path and the presence of 'v' parameter
+            if path.endswith("/watch") and "v" in query_params:
+                video_id = query_params["v"][0]  # Extract the first 'v' parameter
+                encoded_video_id = urlencode({"youtube": video_id})
+                redirect_url = f"/?{encoded_video_id}"
+                return RedirectResponse(url=redirect_url)
+
+        # Proceed with the normal flow of other requests
+        response = await call_next(request)
+        return response
+
+
+# Add the middleware to the app
+app.add_middleware(RedirectMiddleware)
+
+
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=CORS_ALLOW_ORIGIN,
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+app.add_middleware(SecurityHeadersMiddleware)
+
+
+@app.middleware("http")
+async def commit_session_after_request(request: Request, call_next):
+    response = await call_next(request)
+    # log.debug("Commit session after request")
+    Session.commit()
+    return response
+
+
+@app.middleware("http")
+async def check_url(request: Request, call_next):
+    start_time = int(time.time())
+    request.state.enable_api_key = webui_app.state.config.ENABLE_API_KEY
+    response = await call_next(request)
+    process_time = int(time.time()) - start_time
+    response.headers["X-Process-Time"] = str(process_time)
+    return response
+
+
+@app.middleware("http")
+async def update_embedding_function(request: Request, call_next):
+    response = await call_next(request)
+    if "/embedding/update" in request.url.path:
+        webui_app.state.EMBEDDING_FUNCTION = retrieval_app.state.EMBEDDING_FUNCTION
+    return response
+
+
+@app.middleware("http")
+async def inspect_websocket(request: Request, call_next):
+    if (
+        "/ws/socket.io" in request.url.path
+        and request.query_params.get("transport") == "websocket"
+    ):
+        upgrade = (request.headers.get("Upgrade") or "").lower()
+        connection = (request.headers.get("Connection") or "").lower().split(",")
+        # Check that there's the correct headers for an upgrade, else reject the connection
+        # This is to work around this upstream issue: https://github.com/miguelgrinberg/python-engineio/issues/367
+        if upgrade != "websocket" or "upgrade" not in connection:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": "Invalid WebSocket upgrade request"},
+            )
+    return await call_next(request)
+
+
+app.mount("/ws", socket_app)
+app.mount("/ollama", ollama_app)
+app.mount("/openai", openai_app)
+
+app.mount("/images/api/v1", images_app)
+app.mount("/audio/api/v1", audio_app)
+app.mount("/retrieval/api/v1", retrieval_app)
+
+app.mount("/api/v1", webui_app)
+
+webui_app.state.EMBEDDING_FUNCTION = retrieval_app.state.EMBEDDING_FUNCTION
+
+
+async def get_all_base_models():
+    open_webui_models = []
+    openai_models = []
+    ollama_models = []
+
+    if app.state.config.ENABLE_OPENAI_API:
+        openai_models = await get_openai_models()
+        openai_models = openai_models["data"]
+
+    if app.state.config.ENABLE_OLLAMA_API:
+        ollama_models = await get_ollama_models()
+        ollama_models = [
+            {
+                "id": model["model"],
+                "name": model["name"],
+                "object": "model",
+                "created": int(time.time()),
+                "owned_by": "ollama",
+                "ollama": model,
+            }
+            for model in ollama_models["models"]
+        ]
+
+    open_webui_models = await get_open_webui_models()
+
+    models = open_webui_models + openai_models + ollama_models
+    return models
+
+
+@cached(ttl=3)
+async def get_all_models():
+    models = await get_all_base_models()
+
+    # If there are no models, return an empty list
+    if len([model for model in models if not model.get("arena", False)]) == 0:
+        return []
+
+    global_action_ids = [
+        function.id for function in Functions.get_global_action_functions()
+    ]
+    enabled_action_ids = [
+        function.id
+        for function in Functions.get_functions_by_type("action", active_only=True)
+    ]
+
+    custom_models = Models.get_all_models()
+    for custom_model in custom_models:
+        if custom_model.base_model_id is None:
+            for model in models:
+                if (
+                    custom_model.id == model["id"]
+                    or custom_model.id == model["id"].split(":")[0]
+                ):
+                    if custom_model.is_active:
+                        model["name"] = custom_model.name
+                        model["info"] = custom_model.model_dump()
+
+                        action_ids = []
+                        if "info" in model and "meta" in model["info"]:
+                            action_ids.extend(
+                                model["info"]["meta"].get("actionIds", [])
+                            )
+
+                        model["action_ids"] = action_ids
+                    else:
+                        models.remove(model)
+
+        elif custom_model.is_active and (
+            custom_model.id not in [model["id"] for model in models]
+        ):
+            owned_by = "openai"
+            pipe = None
+            action_ids = []
+
+            for model in models:
+                if (
+                    custom_model.base_model_id == model["id"]
+                    or custom_model.base_model_id == model["id"].split(":")[0]
+                ):
+                    owned_by = model["owned_by"]
+                    if "pipe" in model:
+                        pipe = model["pipe"]
+                    break
+
+            if custom_model.meta:
+                meta = custom_model.meta.model_dump()
+                if "actionIds" in meta:
+                    action_ids.extend(meta["actionIds"])
+
+            models.append(
+                {
+                    "id": f"{custom_model.id}",
+                    "name": custom_model.name,
+                    "object": "model",
+                    "created": custom_model.created_at,
+                    "owned_by": owned_by,
+                    "info": custom_model.model_dump(),
+                    "preset": True,
+                    **({"pipe": pipe} if pipe is not None else {}),
+                    "action_ids": action_ids,
+                }
+            )
+
+    # Process action_ids to get the actions
+    def get_action_items_from_module(function, module):
+        actions = []
+        if hasattr(module, "actions"):
+            actions = module.actions
+            return [
+                {
+                    "id": f"{function.id}.{action['id']}",
+                    "name": action.get("name", f"{function.name} ({action['id']})"),
+                    "description": function.meta.description,
+                    "icon_url": action.get(
+                        "icon_url", function.meta.manifest.get("icon_url", None)
+                    ),
+                }
+                for action in actions
+            ]
+        else:
+            return [
+                {
+                    "id": function.id,
+                    "name": function.name,
+                    "description": function.meta.description,
+                    "icon_url": function.meta.manifest.get("icon_url", None),
+                }
+            ]
+
+    def get_function_module_by_id(function_id):
+        if function_id in webui_app.state.FUNCTIONS:
+            function_module = webui_app.state.FUNCTIONS[function_id]
+        else:
+            function_module, _, _ = load_function_module_by_id(function_id)
+            webui_app.state.FUNCTIONS[function_id] = function_module
+
+    for model in models:
+        action_ids = [
+            action_id
+            for action_id in list(set(model.pop("action_ids", []) + global_action_ids))
+            if action_id in enabled_action_ids
+        ]
+
+        model["actions"] = []
+        for action_id in action_ids:
+            action_function = Functions.get_function_by_id(action_id)
+            if action_function is None:
+                raise Exception(f"Action not found: {action_id}")
+
+            function_module = get_function_module_by_id(action_id)
+            model["actions"].extend(
+                get_action_items_from_module(action_function, function_module)
+            )
+    log.debug(f"get_all_models() returned {len(models)} models")
+
+    return models
+
+
+@app.get("/api/models")
+async def get_models(user=Depends(get_verified_user)):
+    models = await get_all_models()
+
+    # Filter out filter pipelines
+    models = [
+        model
+        for model in models
+        if "pipeline" not in model or model["pipeline"].get("type", None) != "filter"
+    ]
+
+    model_order_list = webui_app.state.config.MODEL_ORDER_LIST
+    if model_order_list:
+        model_order_dict = {model_id: i for i, model_id in enumerate(model_order_list)}
+        # Sort models by order list priority, with fallback for those not in the list
+        models.sort(
+            key=lambda x: (model_order_dict.get(x["id"], float("inf")), x["name"])
+        )
+
+    # Filter out models that the user does not have access to
+    if user.role == "user" and not BYPASS_MODEL_ACCESS_CONTROL:
+        filtered_models = []
+        for model in models:
+            if model.get("arena"):
+                if has_access(
+                    user.id,
+                    type="read",
+                    access_control=model.get("info", {})
+                    .get("meta", {})
+                    .get("access_control", {}),
+                ):
+                    filtered_models.append(model)
+                continue
+
+            model_info = Models.get_model_by_id(model["id"])
+            if model_info:
+                if user.id == model_info.user_id or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                ):
+                    filtered_models.append(model)
+        models = filtered_models
+
+    log.debug(
+        f"/api/models returned filtered models accessible to the user: {json.dumps([model['id'] for model in models])}"
+    )
+
+    return {"data": models}
+
+
+@app.get("/api/models/base")
+async def get_base_models(user=Depends(get_admin_user)):
+    models = await get_all_base_models()
+
+    # Filter out arena models
+    models = [model for model in models if not model.get("arena", False)]
+    return {"data": models}
+
+
+@app.post("/api/chat/completions")
+async def generate_chat_completions(
+    form_data: dict, user=Depends(get_verified_user), bypass_filter: bool = False
+):
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    model = models[model_id]
+
+    # Check if user has access to the model
+    if not bypass_filter and user.role == "user":
+        if model.get("arena"):
+            if not has_access(
+                user.id,
+                type="read",
+                access_control=model.get("info", {})
+                .get("meta", {})
+                .get("access_control", {}),
+            ):
+                raise HTTPException(
+                    status_code=403,
+                    detail="Model not found",
+                )
+        else:
+            model_info = Models.get_model_by_id(model_id)
+            if not model_info:
+                raise HTTPException(
+                    status_code=404,
+                    detail="Model not found",
+                )
+            elif not (
+                user.id == model_info.user_id
+                or has_access(
+                    user.id, type="read", access_control=model_info.access_control
+                )
+            ):
+                raise HTTPException(
+                    status_code=403,
+                    detail="Model not found",
+                )
+
+    if model["owned_by"] == "arena":
+        model_ids = model.get("info", {}).get("meta", {}).get("model_ids")
+        filter_mode = model.get("info", {}).get("meta", {}).get("filter_mode")
+        if model_ids and filter_mode == "exclude":
+            model_ids = [
+                model["id"]
+                for model in await get_all_models()
+                if model.get("owned_by") != "arena" and model["id"] not in model_ids
+            ]
+
+        selected_model_id = None
+        if isinstance(model_ids, list) and model_ids:
+            selected_model_id = random.choice(model_ids)
+        else:
+            model_ids = [
+                model["id"]
+                for model in await get_all_models()
+                if model.get("owned_by") != "arena"
+            ]
+            selected_model_id = random.choice(model_ids)
+
+        form_data["model"] = selected_model_id
+
+        if form_data.get("stream") == True:
+
+            async def stream_wrapper(stream):
+                yield f"data: {json.dumps({'selected_model_id': selected_model_id})}\n\n"
+                async for chunk in stream:
+                    yield chunk
+
+            response = await generate_chat_completions(
+                form_data, user, bypass_filter=True
+            )
+            return StreamingResponse(
+                stream_wrapper(response.body_iterator), media_type="text/event-stream"
+            )
+        else:
+            return {
+                **(
+                    await generate_chat_completions(form_data, user, bypass_filter=True)
+                ),
+                "selected_model_id": selected_model_id,
+            }
+
+    if model.get("pipe"):
+        # Below does not require bypass_filter because this is the only route the uses this function and it is already bypassing the filter
+        return await generate_function_chat_completion(
+            form_data, user=user, models=models
+        )
+    if model["owned_by"] == "ollama":
+        # Using /ollama/api/chat endpoint
+        form_data = convert_payload_openai_to_ollama(form_data)
+        form_data = GenerateChatCompletionForm(**form_data)
+        response = await generate_ollama_chat_completion(
+            form_data=form_data, user=user, bypass_filter=bypass_filter
+        )
+        if form_data.stream:
+            response.headers["content-type"] = "text/event-stream"
+            return StreamingResponse(
+                convert_streaming_response_ollama_to_openai(response),
+                headers=dict(response.headers),
+            )
+        else:
+            return convert_response_ollama_to_openai(response)
+    else:
+        return await generate_openai_chat_completion(
+            form_data, user=user, bypass_filter=bypass_filter
+        )
+
+
+@app.post("/api/chat/completed")
+async def chat_completed(form_data: dict, user=Depends(get_verified_user)):
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    data = form_data
+    model_id = data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    model = models[model_id]
+    sorted_filters = get_sorted_filters(model_id, models)
+    if "pipeline" in model:
+        sorted_filters = [model] + sorted_filters
+
+    for filter in sorted_filters:
+        r = None
+        try:
+            urlIdx = filter["urlIdx"]
+
+            url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+            key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+            if key != "":
+                headers = {"Authorization": f"Bearer {key}"}
+                r = requests.post(
+                    f"{url}/{filter['id']}/filter/outlet",
+                    headers=headers,
+                    json={
+                        "user": {
+                            "id": user.id,
+                            "name": user.name,
+                            "email": user.email,
+                            "role": user.role,
+                        },
+                        "body": data,
+                    },
+                )
+
+                r.raise_for_status()
+                data = r.json()
+        except Exception as e:
+            # Handle connection error here
+            print(f"Connection error: {e}")
+
+            if r is not None:
+                try:
+                    res = r.json()
+                    if "detail" in res:
+                        return JSONResponse(
+                            status_code=r.status_code,
+                            content=res,
+                        )
+                except Exception:
+                    pass
+
+            else:
+                pass
+
+    __event_emitter__ = get_event_emitter(
+        {
+            "chat_id": data["chat_id"],
+            "message_id": data["id"],
+            "session_id": data["session_id"],
+        }
+    )
+
+    __event_call__ = get_event_call(
+        {
+            "chat_id": data["chat_id"],
+            "message_id": data["id"],
+            "session_id": data["session_id"],
+        }
+    )
+
+    def get_priority(function_id):
+        function = Functions.get_function_by_id(function_id)
+        if function is not None and hasattr(function, "valves"):
+            # TODO: Fix FunctionModel to include vavles
+            return (function.valves if function.valves else {}).get("priority", 0)
+        return 0
+
+    filter_ids = [function.id for function in Functions.get_global_filter_functions()]
+    if "info" in model and "meta" in model["info"]:
+        filter_ids.extend(model["info"]["meta"].get("filterIds", []))
+        filter_ids = list(set(filter_ids))
+
+    enabled_filter_ids = [
+        function.id
+        for function in Functions.get_functions_by_type("filter", active_only=True)
+    ]
+    filter_ids = [
+        filter_id for filter_id in filter_ids if filter_id in enabled_filter_ids
+    ]
+
+    # Sort filter_ids by priority, using the get_priority function
+    filter_ids.sort(key=get_priority)
+
+    for filter_id in filter_ids:
+        filter = Functions.get_function_by_id(filter_id)
+        if not filter:
+            continue
+
+        if filter_id in webui_app.state.FUNCTIONS:
+            function_module = webui_app.state.FUNCTIONS[filter_id]
+        else:
+            function_module, _, _ = load_function_module_by_id(filter_id)
+            webui_app.state.FUNCTIONS[filter_id] = function_module
+
+        if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
+            valves = Functions.get_function_valves_by_id(filter_id)
+            function_module.valves = function_module.Valves(
+                **(valves if valves else {})
+            )
+
+        if not hasattr(function_module, "outlet"):
+            continue
+        try:
+            outlet = function_module.outlet
+
+            # Get the signature of the function
+            sig = inspect.signature(outlet)
+            params = {"body": data}
+
+            # Extra parameters to be passed to the function
+            extra_params = {
+                "__model__": model,
+                "__id__": filter_id,
+                "__event_emitter__": __event_emitter__,
+                "__event_call__": __event_call__,
+            }
+
+            # Add extra params in contained in function signature
+            for key, value in extra_params.items():
+                if key in sig.parameters:
+                    params[key] = value
+
+            if "__user__" in sig.parameters:
+                __user__ = {
+                    "id": user.id,
+                    "email": user.email,
+                    "name": user.name,
+                    "role": user.role,
+                }
+
+                try:
+                    if hasattr(function_module, "UserValves"):
+                        __user__["valves"] = function_module.UserValves(
+                            **Functions.get_user_valves_by_id_and_user_id(
+                                filter_id, user.id
+                            )
+                        )
+                except Exception as e:
+                    print(e)
+
+                params = {**params, "__user__": __user__}
+
+            if inspect.iscoroutinefunction(outlet):
+                data = await outlet(**params)
+            else:
+                data = outlet(**params)
+
+        except Exception as e:
+            print(f"Error: {e}")
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+
+    return data
+
+
+@app.post("/api/chat/actions/{action_id}")
+async def chat_action(action_id: str, form_data: dict, user=Depends(get_verified_user)):
+    if "." in action_id:
+        action_id, sub_action_id = action_id.split(".")
+    else:
+        sub_action_id = None
+
+    action = Functions.get_function_by_id(action_id)
+    if not action:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Action not found",
+        )
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    data = form_data
+    model_id = data["model"]
+
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+    model = models[model_id]
+
+    __event_emitter__ = get_event_emitter(
+        {
+            "chat_id": data["chat_id"],
+            "message_id": data["id"],
+            "session_id": data["session_id"],
+        }
+    )
+    __event_call__ = get_event_call(
+        {
+            "chat_id": data["chat_id"],
+            "message_id": data["id"],
+            "session_id": data["session_id"],
+        }
+    )
+
+    if action_id in webui_app.state.FUNCTIONS:
+        function_module = webui_app.state.FUNCTIONS[action_id]
+    else:
+        function_module, _, _ = load_function_module_by_id(action_id)
+        webui_app.state.FUNCTIONS[action_id] = function_module
+
+    if hasattr(function_module, "valves") and hasattr(function_module, "Valves"):
+        valves = Functions.get_function_valves_by_id(action_id)
+        function_module.valves = function_module.Valves(**(valves if valves else {}))
+
+    if hasattr(function_module, "action"):
+        try:
+            action = function_module.action
+
+            # Get the signature of the function
+            sig = inspect.signature(action)
+            params = {"body": data}
+
+            # Extra parameters to be passed to the function
+            extra_params = {
+                "__model__": model,
+                "__id__": sub_action_id if sub_action_id is not None else action_id,
+                "__event_emitter__": __event_emitter__,
+                "__event_call__": __event_call__,
+            }
+
+            # Add extra params in contained in function signature
+            for key, value in extra_params.items():
+                if key in sig.parameters:
+                    params[key] = value
+
+            if "__user__" in sig.parameters:
+                __user__ = {
+                    "id": user.id,
+                    "email": user.email,
+                    "name": user.name,
+                    "role": user.role,
+                }
+
+                try:
+                    if hasattr(function_module, "UserValves"):
+                        __user__["valves"] = function_module.UserValves(
+                            **Functions.get_user_valves_by_id_and_user_id(
+                                action_id, user.id
+                            )
+                        )
+                except Exception as e:
+                    print(e)
+
+                params = {**params, "__user__": __user__}
+
+            if inspect.iscoroutinefunction(action):
+                data = await action(**params)
+            else:
+                data = action(**params)
+
+        except Exception as e:
+            print(f"Error: {e}")
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+
+    return data
+
+
+##################################
+#
+# Task Endpoints
+#
+##################################
+
+
+# TODO: Refactor task API endpoints below into a separate file
+
+
+@app.get("/api/task/config")
+async def get_task_config(user=Depends(get_verified_user)):
+    return {
+        "TASK_MODEL": app.state.config.TASK_MODEL,
+        "TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL,
+        "TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE,
+        "ENABLE_AUTOCOMPLETE_GENERATION": app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
+        "AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
+        "TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
+        "ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION,
+        "ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
+        "ENABLE_RETRIEVAL_QUERY_GENERATION": app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION,
+        "QUERY_GENERATION_PROMPT_TEMPLATE": app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE,
+        "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE": app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
+    }
+
+
+class TaskConfigForm(BaseModel):
+    TASK_MODEL: Optional[str]
+    TASK_MODEL_EXTERNAL: Optional[str]
+    TITLE_GENERATION_PROMPT_TEMPLATE: str
+    ENABLE_AUTOCOMPLETE_GENERATION: bool
+    AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: int
+    TAGS_GENERATION_PROMPT_TEMPLATE: str
+    ENABLE_TAGS_GENERATION: bool
+    ENABLE_SEARCH_QUERY_GENERATION: bool
+    ENABLE_RETRIEVAL_QUERY_GENERATION: bool
+    QUERY_GENERATION_PROMPT_TEMPLATE: str
+    TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE: str
+
+
+@app.post("/api/task/config/update")
+async def update_task_config(form_data: TaskConfigForm, user=Depends(get_admin_user)):
+    app.state.config.TASK_MODEL = form_data.TASK_MODEL
+    app.state.config.TASK_MODEL_EXTERNAL = form_data.TASK_MODEL_EXTERNAL
+    app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = (
+        form_data.TITLE_GENERATION_PROMPT_TEMPLATE
+    )
+
+    app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = (
+        form_data.ENABLE_AUTOCOMPLETE_GENERATION
+    )
+    app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH = (
+        form_data.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH
+    )
+
+    app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = (
+        form_data.TAGS_GENERATION_PROMPT_TEMPLATE
+    )
+    app.state.config.ENABLE_TAGS_GENERATION = form_data.ENABLE_TAGS_GENERATION
+    app.state.config.ENABLE_SEARCH_QUERY_GENERATION = (
+        form_data.ENABLE_SEARCH_QUERY_GENERATION
+    )
+    app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION = (
+        form_data.ENABLE_RETRIEVAL_QUERY_GENERATION
+    )
+
+    app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE = (
+        form_data.QUERY_GENERATION_PROMPT_TEMPLATE
+    )
+    app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
+        form_data.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
+    )
+
+    return {
+        "TASK_MODEL": app.state.config.TASK_MODEL,
+        "TASK_MODEL_EXTERNAL": app.state.config.TASK_MODEL_EXTERNAL,
+        "TITLE_GENERATION_PROMPT_TEMPLATE": app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE,
+        "ENABLE_AUTOCOMPLETE_GENERATION": app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
+        "AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
+        "TAGS_GENERATION_PROMPT_TEMPLATE": app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
+        "ENABLE_TAGS_GENERATION": app.state.config.ENABLE_TAGS_GENERATION,
+        "ENABLE_SEARCH_QUERY_GENERATION": app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
+        "ENABLE_RETRIEVAL_QUERY_GENERATION": app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION,
+        "QUERY_GENERATION_PROMPT_TEMPLATE": app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE,
+        "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE": app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
+    }
+
+
+@app.post("/api/task/title/completions")
+async def generate_title(form_data: dict, user=Depends(get_verified_user)):
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(
+        f"generating chat title using model {task_model_id} for user {user.email} "
+    )
+
+    if app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE != "":
+        template = app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE
+    else:
+        template = """Create a concise, 3-5 word title with an emoji as a title for the chat history, in the given language. Suitable Emojis for the summary can be used to enhance understanding but avoid quotation marks or special formatting. RESPOND ONLY WITH THE TITLE TEXT.
+
+Examples of titles:
+📉 Stock Market Trends
+🍪 Perfect Chocolate Chip Recipe
+Evolution of Music Streaming
+Remote Work Productivity Tips
+Artificial Intelligence in Healthcare
+🎮 Video Game Development Insights
+
+<chat_history>
+{{MESSAGES:END:2}}
+</chat_history>"""
+
+    content = title_generation_template(
+        template,
+        form_data["messages"],
+        {
+            "name": user.name,
+            "location": user.info.get("location") if user.info else None,
+        },
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": False,
+        **(
+            {"max_tokens": 50}
+            if models[task_model_id]["owned_by"] == "ollama"
+            else {
+                "max_completion_tokens": 50,
+            }
+        ),
+        "metadata": {
+            "task": str(TASKS.TITLE_GENERATION),
+            "task_body": form_data,
+            "chat_id": form_data.get("chat_id", None),
+        },
+    }
+
+    # Handle pipeline filters
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+@app.post("/api/task/tags/completions")
+async def generate_chat_tags(form_data: dict, user=Depends(get_verified_user)):
+
+    if not app.state.config.ENABLE_TAGS_GENERATION:
+        return JSONResponse(
+            status_code=status.HTTP_200_OK,
+            content={"detail": "Tags generation is disabled"},
+        )
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(
+        f"generating chat tags using model {task_model_id} for user {user.email} "
+    )
+
+    if app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE != "":
+        template = app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE
+    else:
+        template = """### Task:
+Generate 1-3 broad tags categorizing the main themes of the chat history, along with 1-3 more specific subtopic tags.
+
+### Guidelines:
+- Start with high-level domains (e.g. Science, Technology, Philosophy, Arts, Politics, Business, Health, Sports, Entertainment, Education)
+- Consider including relevant subfields/subdomains if they are strongly represented throughout the conversation
+- If content is too short (less than 3 messages) or too diverse, use only ["General"]
+- Use the chat's primary language; default to English if multilingual
+- Prioritize accuracy over specificity
+
+### Output:
+JSON format: { "tags": ["tag1", "tag2", "tag3"] }
+
+### Chat History:
+<chat_history>
+{{MESSAGES:END:6}}
+</chat_history>"""
+
+    content = tags_generation_template(
+        template, form_data["messages"], {"name": user.name}
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": False,
+        "metadata": {
+            "task": str(TASKS.TAGS_GENERATION),
+            "task_body": form_data,
+            "chat_id": form_data.get("chat_id", None),
+        },
+    }
+
+    # Handle pipeline filters
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+@app.post("/api/task/queries/completions")
+async def generate_queries(form_data: dict, user=Depends(get_verified_user)):
+
+    type = form_data.get("type")
+    if type == "web_search":
+        if not app.state.config.ENABLE_SEARCH_QUERY_GENERATION:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=f"Search query generation is disabled",
+            )
+    elif type == "retrieval":
+        if not app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=f"Query generation is disabled",
+            )
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(
+        f"generating {type} queries using model {task_model_id} for user {user.email}"
+    )
+
+    if (app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE).strip() != "":
+        template = app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE
+    else:
+        template = DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE
+
+    content = query_generation_template(
+        template, form_data["messages"], {"name": user.name}
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": False,
+        "metadata": {
+            "task": str(TASKS.QUERY_GENERATION),
+            "task_body": form_data,
+            "chat_id": form_data.get("chat_id", None),
+        },
+    }
+
+    # Handle pipeline filters
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+@app.post("/api/task/auto/completions")
+async def generate_autocompletion(form_data: dict, user=Depends(get_verified_user)):
+    if not app.state.config.ENABLE_AUTOCOMPLETE_GENERATION:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=f"Autocompletion generation is disabled",
+        )
+
+    type = form_data.get("type")
+    prompt = form_data.get("prompt")
+    messages = form_data.get("messages")
+
+    if app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH > 0:
+        if len(prompt) > app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH:
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail=f"Input prompt exceeds maximum length of {app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH}",
+            )
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(
+        f"generating autocompletion using model {task_model_id} for user {user.email}"
+    )
+
+    if (app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE).strip() != "":
+        template = app.state.config.AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
+    else:
+        template = DEFAULT_AUTOCOMPLETE_GENERATION_PROMPT_TEMPLATE
+
+    content = autocomplete_generation_template(
+        template, prompt, messages, type, {"name": user.name}
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": False,
+        "metadata": {
+            "task": str(TASKS.AUTOCOMPLETE_GENERATION),
+            "task_body": form_data,
+            "chat_id": form_data.get("chat_id", None),
+        },
+    }
+
+    print(payload)
+
+    # Handle pipeline filters
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+@app.post("/api/task/emoji/completions")
+async def generate_emoji(form_data: dict, user=Depends(get_verified_user)):
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(f"generating emoji using model {task_model_id} for user {user.email} ")
+
+    template = '''
+Your task is to reflect the speaker's likely facial expression through a fitting emoji. Interpret emotions from the message and reflect their facial expression using fitting, diverse emojis (e.g., 😊, 😢, 😡, 😱).
+
+Message: """{{prompt}}"""
+'''
+    content = emoji_generation_template(
+        template,
+        form_data["prompt"],
+        {
+            "name": user.name,
+            "location": user.info.get("location") if user.info else None,
+        },
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": False,
+        **(
+            {"max_tokens": 4}
+            if models[task_model_id]["owned_by"] == "ollama"
+            else {
+                "max_completion_tokens": 4,
+            }
+        ),
+        "chat_id": form_data.get("chat_id", None),
+        "metadata": {"task": str(TASKS.EMOJI_GENERATION), "task_body": form_data},
+    }
+
+    # Handle pipeline filters
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+@app.post("/api/task/moa/completions")
+async def generate_moa_response(form_data: dict, user=Depends(get_verified_user)):
+
+    model_list = await get_all_models()
+    models = {model["id"]: model for model in model_list}
+
+    model_id = form_data["model"]
+    if model_id not in models:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="Model not found",
+        )
+
+    # Check if the user has a custom task model
+    # If the user has a custom task model, use that model
+    task_model_id = get_task_model_id(
+        model_id,
+        app.state.config.TASK_MODEL,
+        app.state.config.TASK_MODEL_EXTERNAL,
+        models,
+    )
+
+    log.debug(f"generating MOA model {task_model_id} for user {user.email} ")
+
+    template = """You have been provided with a set of responses from various models to the latest user query: "{{prompt}}"
+
+Your task is to synthesize these responses into a single, high-quality response. It is crucial to critically evaluate the information provided in these responses, recognizing that some of it may be biased or incorrect. Your response should not simply replicate the given answers but should offer a refined, accurate, and comprehensive reply to the instruction. Ensure your response is well-structured, coherent, and adheres to the highest standards of accuracy and reliability.
+
+Responses from models: {{responses}}"""
+
+    content = moa_response_generation_template(
+        template,
+        form_data["prompt"],
+        form_data["responses"],
+    )
+
+    payload = {
+        "model": task_model_id,
+        "messages": [{"role": "user", "content": content}],
+        "stream": form_data.get("stream", False),
+        "chat_id": form_data.get("chat_id", None),
+        "metadata": {
+            "task": str(TASKS.MOA_RESPONSE_GENERATION),
+            "task_body": form_data,
+        },
+    }
+
+    try:
+        payload = filter_pipeline(payload, user, models)
+    except Exception as e:
+        if len(e.args) > 1:
+            return JSONResponse(
+                status_code=e.args[0],
+                content={"detail": e.args[1]},
+            )
+        else:
+            return JSONResponse(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                content={"detail": str(e)},
+            )
+    if "chat_id" in payload:
+        del payload["chat_id"]
+
+    return await generate_chat_completions(form_data=payload, user=user)
+
+
+##################################
+#
+# Pipelines Endpoints
+#
+##################################
+
+
+# TODO: Refactor pipelines API endpoints below into a separate file
+
+
+@app.get("/api/pipelines/list")
+async def get_pipelines_list(user=Depends(get_admin_user)):
+    responses = await get_openai_models_responses()
+
+    log.debug(f"get_pipelines_list: get_openai_models_responses returned {responses}")
+    urlIdxs = [
+        idx
+        for idx, response in enumerate(responses)
+        if response is not None and "pipelines" in response
+    ]
+
+    return {
+        "data": [
+            {
+                "url": openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx],
+                "idx": urlIdx,
+            }
+            for urlIdx in urlIdxs
+        ]
+    }
+
+
+@app.post("/api/pipelines/upload")
+async def upload_pipeline(
+    urlIdx: int = Form(...), file: UploadFile = File(...), user=Depends(get_admin_user)
+):
+    print("upload_pipeline", urlIdx, file.filename)
+    # Check if the uploaded file is a python file
+    if not (file.filename and file.filename.endswith(".py")):
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Only Python (.py) files are allowed.",
+        )
+
+    upload_folder = f"{CACHE_DIR}/pipelines"
+    os.makedirs(upload_folder, exist_ok=True)
+    file_path = os.path.join(upload_folder, file.filename)
+
+    r = None
+    try:
+        # Save the uploaded file
+        with open(file_path, "wb") as buffer:
+            shutil.copyfileobj(file.file, buffer)
+
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+
+        with open(file_path, "rb") as f:
+            files = {"file": f}
+            r = requests.post(f"{url}/pipelines/upload", headers=headers, files=files)
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+        status_code = status.HTTP_404_NOT_FOUND
+        if r is not None:
+            status_code = r.status_code
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=status_code,
+            detail=detail,
+        )
+    finally:
+        # Ensure the file is deleted after the upload is completed or on failure
+        if os.path.exists(file_path):
+            os.remove(file_path)
+
+
+class AddPipelineForm(BaseModel):
+    url: str
+    urlIdx: int
+
+
+@app.post("/api/pipelines/add")
+async def add_pipeline(form_data: AddPipelineForm, user=Depends(get_admin_user)):
+    r = None
+    try:
+        urlIdx = form_data.urlIdx
+
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.post(
+            f"{url}/pipelines/add", headers=headers, json={"url": form_data.url}
+        )
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+class DeletePipelineForm(BaseModel):
+    id: str
+    urlIdx: int
+
+
+@app.delete("/api/pipelines/delete")
+async def delete_pipeline(form_data: DeletePipelineForm, user=Depends(get_admin_user)):
+    r = None
+    try:
+        urlIdx = form_data.urlIdx
+
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.delete(
+            f"{url}/pipelines/delete", headers=headers, json={"id": form_data.id}
+        )
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+@app.get("/api/pipelines")
+async def get_pipelines(urlIdx: Optional[int] = None, user=Depends(get_admin_user)):
+    r = None
+    try:
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.get(f"{url}/pipelines", headers=headers)
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+@app.get("/api/pipelines/{pipeline_id}/valves")
+async def get_pipeline_valves(
+    urlIdx: Optional[int],
+    pipeline_id: str,
+    user=Depends(get_admin_user),
+):
+    r = None
+    try:
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.get(f"{url}/{pipeline_id}/valves", headers=headers)
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+@app.get("/api/pipelines/{pipeline_id}/valves/spec")
+async def get_pipeline_valves_spec(
+    urlIdx: Optional[int],
+    pipeline_id: str,
+    user=Depends(get_admin_user),
+):
+    r = None
+    try:
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.get(f"{url}/{pipeline_id}/valves/spec", headers=headers)
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+@app.post("/api/pipelines/{pipeline_id}/valves/update")
+async def update_pipeline_valves(
+    urlIdx: Optional[int],
+    pipeline_id: str,
+    form_data: dict,
+    user=Depends(get_admin_user),
+):
+    r = None
+    try:
+        url = openai_app.state.config.OPENAI_API_BASE_URLS[urlIdx]
+        key = openai_app.state.config.OPENAI_API_KEYS[urlIdx]
+
+        headers = {"Authorization": f"Bearer {key}"}
+        r = requests.post(
+            f"{url}/{pipeline_id}/valves/update",
+            headers=headers,
+            json={**form_data},
+        )
+
+        r.raise_for_status()
+        data = r.json()
+
+        return {**data}
+    except Exception as e:
+        # Handle connection error here
+        print(f"Connection error: {e}")
+
+        detail = "Pipeline not found"
+
+        if r is not None:
+            try:
+                res = r.json()
+                if "detail" in res:
+                    detail = res["detail"]
+            except Exception:
+                pass
+
+        raise HTTPException(
+            status_code=(r.status_code if r is not None else status.HTTP_404_NOT_FOUND),
+            detail=detail,
+        )
+
+
+##################################
+#
+# Config Endpoints
+#
+##################################
+
+
+@app.get("/api/config")
+async def get_app_config(request: Request):
+    user = None
+    if "token" in request.cookies:
+        token = request.cookies.get("token")
+        try:
+            data = decode_token(token)
+        except Exception as e:
+            log.debug(e)
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail="Invalid token",
+            )
+        if data is not None and "id" in data:
+            user = Users.get_user_by_id(data["id"])
+
+    onboarding = False
+    if user is None:
+        user_count = Users.get_num_users()
+        onboarding = user_count == 0
+
+    return {
+        **({"onboarding": True} if onboarding else {}),
+        "status": True,
+        "name": WEBUI_NAME,
+        "version": VERSION,
+        "default_locale": str(DEFAULT_LOCALE),
+        "oauth": {
+            "providers": {
+                name: config.get("name", name)
+                for name, config in OAUTH_PROVIDERS.items()
+            }
+        },
+        "features": {
+            "auth": WEBUI_AUTH,
+            "auth_trusted_header": bool(webui_app.state.AUTH_TRUSTED_EMAIL_HEADER),
+            "enable_ldap": webui_app.state.config.ENABLE_LDAP,
+            "enable_api_key": webui_app.state.config.ENABLE_API_KEY,
+            "enable_signup": webui_app.state.config.ENABLE_SIGNUP,
+            "enable_login_form": webui_app.state.config.ENABLE_LOGIN_FORM,
+            **(
+                {
+                    "enable_web_search": retrieval_app.state.config.ENABLE_RAG_WEB_SEARCH,
+                    "enable_image_generation": images_app.state.config.ENABLED,
+                    "enable_community_sharing": webui_app.state.config.ENABLE_COMMUNITY_SHARING,
+                    "enable_message_rating": webui_app.state.config.ENABLE_MESSAGE_RATING,
+                    "enable_admin_export": ENABLE_ADMIN_EXPORT,
+                    "enable_admin_chat_access": ENABLE_ADMIN_CHAT_ACCESS,
+                }
+                if user is not None
+                else {}
+            ),
+        },
+        **(
+            {
+                "default_models": webui_app.state.config.DEFAULT_MODELS,
+                "default_prompt_suggestions": webui_app.state.config.DEFAULT_PROMPT_SUGGESTIONS,
+                "audio": {
+                    "tts": {
+                        "engine": audio_app.state.config.TTS_ENGINE,
+                        "voice": audio_app.state.config.TTS_VOICE,
+                        "split_on": audio_app.state.config.TTS_SPLIT_ON,
+                    },
+                    "stt": {
+                        "engine": audio_app.state.config.STT_ENGINE,
+                    },
+                },
+                "file": {
+                    "max_size": retrieval_app.state.config.FILE_MAX_SIZE,
+                    "max_count": retrieval_app.state.config.FILE_MAX_COUNT,
+                },
+                "permissions": {**webui_app.state.config.USER_PERMISSIONS},
+            }
+            if user is not None
+            else {}
+        ),
+    }
+
+
+# TODO: webhook endpoint should be under config endpoints
+
+
+@app.get("/api/webhook")
+async def get_webhook_url(user=Depends(get_admin_user)):
+    return {
+        "url": app.state.config.WEBHOOK_URL,
+    }
+
+
+class UrlForm(BaseModel):
+    url: str
+
+
+@app.post("/api/webhook")
+async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
+    app.state.config.WEBHOOK_URL = form_data.url
+    webui_app.state.WEBHOOK_URL = app.state.config.WEBHOOK_URL
+    return {"url": app.state.config.WEBHOOK_URL}
+
+
+@app.get("/api/version")
+async def get_app_version():
+    return {
+        "version": VERSION,
+    }
+
+
+@app.get("/api/changelog")
+async def get_app_changelog():
+    return {key: CHANGELOG[key] for idx, key in enumerate(CHANGELOG) if idx < 5}
+
+
+@app.get("/api/version/updates")
+async def get_app_latest_release_version():
+    if OFFLINE_MODE:
+        log.debug(
+            f"Offline mode is enabled, returning current version as latest version"
+        )
+        return {"current": VERSION, "latest": VERSION}
+    try:
+        timeout = aiohttp.ClientTimeout(total=1)
+        async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
+            async with session.get(
+                "https://api.github.com/repos/open-webui/open-webui/releases/latest"
+            ) as response:
+                response.raise_for_status()
+                data = await response.json()
+                latest_version = data["tag_name"]
+
+                return {"current": VERSION, "latest": latest_version[1:]}
+    except Exception as e:
+        log.debug(e)
+        return {"current": VERSION, "latest": VERSION}
+
+
+############################
+# OAuth Login & Callback
+############################
+
+# SessionMiddleware is used by authlib for oauth
+if len(OAUTH_PROVIDERS) > 0:
+    app.add_middleware(
+        SessionMiddleware,
+        secret_key=WEBUI_SECRET_KEY,
+        session_cookie="oui-session",
+        same_site=WEBUI_SESSION_COOKIE_SAME_SITE,
+        https_only=WEBUI_SESSION_COOKIE_SECURE,
+    )
+
+
+@app.get("/oauth/{provider}/login")
+async def oauth_login(provider: str, request: Request):
+    return await oauth_manager.handle_login(provider, request)
+
+
+# OAuth login logic is as follows:
+# 1. Attempt to find a user with matching subject ID, tied to the provider
+# 2. If OAUTH_MERGE_ACCOUNTS_BY_EMAIL is true, find a user with the email address provided via OAuth
+#    - This is considered insecure in general, as OAuth providers do not always verify email addresses
+# 3. If there is no user, and ENABLE_OAUTH_SIGNUP is true, create a user
+#    - Email addresses are considered unique, so we fail registration if the email address is already taken
+@app.get("/oauth/{provider}/callback")
+async def oauth_callback(provider: str, request: Request, response: Response):
+    return await oauth_manager.handle_callback(provider, request, response)
+
+
+@app.get("/manifest.json")
+async def get_manifest_json():
+    return {
+        "name": WEBUI_NAME,
+        "short_name": WEBUI_NAME,
+        "description": "Open WebUI is an open, extensible, user-friendly interface for AI that adapts to your workflow.",
+        "start_url": "/",
+        "display": "standalone",
+        "background_color": "#343541",
+        "orientation": "natural",
+        "icons": [
+            {
+                "src": "/static/logo.png",
+                "type": "image/png",
+                "sizes": "500x500",
+                "purpose": "any",
+            },
+            {
+                "src": "/static/logo.png",
+                "type": "image/png",
+                "sizes": "500x500",
+                "purpose": "maskable",
+            },
+        ],
+    }
+
+
+@app.get("/opensearch.xml")
+async def get_opensearch_xml():
+    xml_content = rf"""
+    <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
+    <ShortName>{WEBUI_NAME}</ShortName>
+    <Description>Search {WEBUI_NAME}</Description>
+    <InputEncoding>UTF-8</InputEncoding>
+    <Image width="16" height="16" type="image/x-icon">{WEBUI_URL}/static/favicon.png</Image>
+    <Url type="text/html" method="get" template="{WEBUI_URL}/?q={"{searchTerms}"}"/>
+    <moz:SearchForm>{WEBUI_URL}</moz:SearchForm>
+    </OpenSearchDescription>
+    """
+    return Response(content=xml_content, media_type="application/xml")
+
+
+@app.get("/health")
+async def healthcheck():
+    return {"status": True}
+
+
+@app.get("/health/db")
+async def healthcheck_with_db():
+    Session.execute(text("SELECT 1;")).all()
+    return {"status": True}
+
+
+app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
+app.mount("/cache", StaticFiles(directory=CACHE_DIR), name="cache")
+
+
+if os.path.exists(FRONTEND_BUILD_DIR):
+    mimetypes.add_type("text/javascript", ".js")
+    app.mount(
+        "/",
+        SPAStaticFiles(directory=FRONTEND_BUILD_DIR, html=True),
+        name="spa-static-files",
+    )
+else:
+    log.warning(
+        f"Frontend build directory not found at '{FRONTEND_BUILD_DIR}'. Serving API only."
+    )
diff --git a/backend/open_webui/migrations/README b/backend/open_webui/migrations/README
new file mode 100644
index 0000000000000000000000000000000000000000..f1d93dff9dbd52e0a9fc8ce4d68e0784c07da697
--- /dev/null
+++ b/backend/open_webui/migrations/README
@@ -0,0 +1,4 @@
+Generic single-database configuration.
+
+Create new migrations with
+DATABASE_URL=<replace with actual url> alembic revision --autogenerate -m "a description"
diff --git a/backend/open_webui/migrations/env.py b/backend/open_webui/migrations/env.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e860c8a0590f2401008ac80e9343de351e4e0ed
--- /dev/null
+++ b/backend/open_webui/migrations/env.py
@@ -0,0 +1,81 @@
+from logging.config import fileConfig
+
+from alembic import context
+from open_webui.apps.webui.models.auths import Auth
+from open_webui.env import DATABASE_URL
+from sqlalchemy import engine_from_config, pool
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+if config.config_file_name is not None:
+    fileConfig(config.config_file_name, disable_existing_loggers=False)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = Auth.metadata
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+DB_URL = DATABASE_URL
+
+if DB_URL:
+    config.set_main_option("sqlalchemy.url", DB_URL.replace("%", "%%"))
+
+
+def run_migrations_offline() -> None:
+    """Run migrations in 'offline' mode.
+
+    This configures the context with just a URL
+    and not an Engine, though an Engine is acceptable
+    here as well.  By skipping the Engine creation
+    we don't even need a DBAPI to be available.
+
+    Calls to context.execute() here emit the given string to the
+    script output.
+
+    """
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(
+        url=url,
+        target_metadata=target_metadata,
+        literal_binds=True,
+        dialect_opts={"paramstyle": "named"},
+    )
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+def run_migrations_online() -> None:
+    """Run migrations in 'online' mode.
+
+    In this scenario we need to create an Engine
+    and associate a connection with the context.
+
+    """
+    connectable = engine_from_config(
+        config.get_section(config.config_ini_section, {}),
+        prefix="sqlalchemy.",
+        poolclass=pool.NullPool,
+    )
+
+    with connectable.connect() as connection:
+        context.configure(connection=connection, target_metadata=target_metadata)
+
+        with context.begin_transaction():
+            context.run_migrations()
+
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()
diff --git a/backend/open_webui/migrations/script.py.mako b/backend/open_webui/migrations/script.py.mako
new file mode 100644
index 0000000000000000000000000000000000000000..01e730e77d23fdb17a24aa5294a8b68f1a98f85a
--- /dev/null
+++ b/backend/open_webui/migrations/script.py.mako
@@ -0,0 +1,27 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision | comma,n}
+Create Date: ${create_date}
+
+"""
+from typing import Sequence, Union
+
+from alembic import op
+import sqlalchemy as sa
+import open_webui.apps.webui.internal.db
+${imports if imports else ""}
+
+# revision identifiers, used by Alembic.
+revision: str = ${repr(up_revision)}
+down_revision: Union[str, None] = ${repr(down_revision)}
+branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
+depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
+
+
+def upgrade() -> None:
+    ${upgrades if upgrades else "pass"}
+
+
+def downgrade() -> None:
+    ${downgrades if downgrades else "pass"}
diff --git a/backend/open_webui/migrations/util.py b/backend/open_webui/migrations/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..955066602a5d94a5d001b169694748d49efed1c1
--- /dev/null
+++ b/backend/open_webui/migrations/util.py
@@ -0,0 +1,15 @@
+from alembic import op
+from sqlalchemy import Inspector
+
+
+def get_existing_tables():
+    con = op.get_bind()
+    inspector = Inspector.from_engine(con)
+    tables = set(inspector.get_table_names())
+    return tables
+
+
+def get_revision_id():
+    import uuid
+
+    return str(uuid.uuid4()).replace("-", "")[:12]
diff --git a/backend/open_webui/migrations/versions/1af9b942657b_migrate_tags.py b/backend/open_webui/migrations/versions/1af9b942657b_migrate_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a0ab1b491d5a136d19fd1d79788a88c60a8c941
--- /dev/null
+++ b/backend/open_webui/migrations/versions/1af9b942657b_migrate_tags.py
@@ -0,0 +1,151 @@
+"""Migrate tags
+
+Revision ID: 1af9b942657b
+Revises: 242a2047eae0
+Create Date: 2024-10-09 21:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.sql import table, select, update, column
+from sqlalchemy.engine.reflection import Inspector
+
+import json
+
+revision = "1af9b942657b"
+down_revision = "242a2047eae0"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # Setup an inspection on the existing table to avoid issues
+    conn = op.get_bind()
+    inspector = Inspector.from_engine(conn)
+
+    # Clean up potential leftover temp table from previous failures
+    conn.execute(sa.text("DROP TABLE IF EXISTS _alembic_tmp_tag"))
+
+    # Check if the 'tag' table exists
+    tables = inspector.get_table_names()
+
+    # Step 1: Modify Tag table using batch mode for SQLite support
+    if "tag" in tables:
+        # Get the current columns in the 'tag' table
+        columns = [col["name"] for col in inspector.get_columns("tag")]
+
+        # Get any existing unique constraints on the 'tag' table
+        current_constraints = inspector.get_unique_constraints("tag")
+
+        with op.batch_alter_table("tag", schema=None) as batch_op:
+            # Check if the unique constraint already exists
+            if not any(
+                constraint["name"] == "uq_id_user_id"
+                for constraint in current_constraints
+            ):
+                # Create unique constraint if it doesn't exist
+                batch_op.create_unique_constraint("uq_id_user_id", ["id", "user_id"])
+
+            # Check if the 'data' column exists before trying to drop it
+            if "data" in columns:
+                batch_op.drop_column("data")
+
+            # Check if the 'meta' column needs to be created
+            if "meta" not in columns:
+                # Add the 'meta' column if it doesn't already exist
+                batch_op.add_column(sa.Column("meta", sa.JSON(), nullable=True))
+
+    tag = table(
+        "tag",
+        column("id", sa.String()),
+        column("name", sa.String()),
+        column("user_id", sa.String()),
+        column("meta", sa.JSON()),
+    )
+
+    # Step 2: Migrate tags
+    conn = op.get_bind()
+    result = conn.execute(sa.select(tag.c.id, tag.c.name, tag.c.user_id))
+
+    tag_updates = {}
+    for row in result:
+        new_id = row.name.replace(" ", "_").lower()
+        tag_updates[row.id] = new_id
+
+    for tag_id, new_tag_id in tag_updates.items():
+        print(f"Updating tag {tag_id} to {new_tag_id}")
+        if new_tag_id == "pinned":
+            # delete tag
+            delete_stmt = sa.delete(tag).where(tag.c.id == tag_id)
+            conn.execute(delete_stmt)
+        else:
+            # Check if the new_tag_id already exists in the database
+            existing_tag_query = sa.select(tag.c.id).where(tag.c.id == new_tag_id)
+            existing_tag_result = conn.execute(existing_tag_query).fetchone()
+
+            if existing_tag_result:
+                # Handle duplicate case: the new_tag_id already exists
+                print(
+                    f"Tag {new_tag_id} already exists. Removing current tag with ID {tag_id} to avoid duplicates."
+                )
+                # Option 1: Delete the current tag if an update to new_tag_id would cause duplication
+                delete_stmt = sa.delete(tag).where(tag.c.id == tag_id)
+                conn.execute(delete_stmt)
+            else:
+                update_stmt = sa.update(tag).where(tag.c.id == tag_id)
+                update_stmt = update_stmt.values(id=new_tag_id)
+                conn.execute(update_stmt)
+
+    # Add columns `pinned` and `meta` to 'chat'
+    op.add_column("chat", sa.Column("pinned", sa.Boolean(), nullable=True))
+    op.add_column(
+        "chat", sa.Column("meta", sa.JSON(), nullable=False, server_default="{}")
+    )
+
+    chatidtag = table(
+        "chatidtag", column("chat_id", sa.String()), column("tag_name", sa.String())
+    )
+    chat = table(
+        "chat",
+        column("id", sa.String()),
+        column("pinned", sa.Boolean()),
+        column("meta", sa.JSON()),
+    )
+
+    # Fetch existing tags
+    conn = op.get_bind()
+    result = conn.execute(sa.select(chatidtag.c.chat_id, chatidtag.c.tag_name))
+
+    chat_updates = {}
+    for row in result:
+        chat_id = row.chat_id
+        tag_name = row.tag_name.replace(" ", "_").lower()
+
+        if tag_name == "pinned":
+            # Specifically handle 'pinned' tag
+            if chat_id not in chat_updates:
+                chat_updates[chat_id] = {"pinned": True, "meta": {}}
+            else:
+                chat_updates[chat_id]["pinned"] = True
+        else:
+            if chat_id not in chat_updates:
+                chat_updates[chat_id] = {"pinned": False, "meta": {"tags": [tag_name]}}
+            else:
+                tags = chat_updates[chat_id]["meta"].get("tags", [])
+                tags.append(tag_name)
+
+                chat_updates[chat_id]["meta"]["tags"] = list(set(tags))
+
+    # Update chats based on accumulated changes
+    for chat_id, updates in chat_updates.items():
+        update_stmt = sa.update(chat).where(chat.c.id == chat_id)
+        update_stmt = update_stmt.values(
+            meta=updates.get("meta", {}), pinned=updates.get("pinned", False)
+        )
+        conn.execute(update_stmt)
+    pass
+
+
+def downgrade():
+    pass
diff --git a/backend/open_webui/migrations/versions/242a2047eae0_update_chat_table.py b/backend/open_webui/migrations/versions/242a2047eae0_update_chat_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..6017da31695b37185c30c085f751f5d72b392e5d
--- /dev/null
+++ b/backend/open_webui/migrations/versions/242a2047eae0_update_chat_table.py
@@ -0,0 +1,107 @@
+"""Update chat table
+
+Revision ID: 242a2047eae0
+Revises: 6a39f3d8e55c
+Create Date: 2024-10-09 21:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.sql import table, select, update
+
+import json
+
+revision = "242a2047eae0"
+down_revision = "6a39f3d8e55c"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    conn = op.get_bind()
+    inspector = sa.inspect(conn)
+
+    columns = inspector.get_columns("chat")
+    column_dict = {col["name"]: col for col in columns}
+
+    chat_column = column_dict.get("chat")
+    old_chat_exists = "old_chat" in column_dict
+
+    if chat_column:
+        if isinstance(chat_column["type"], sa.Text):
+            print("Converting 'chat' column to JSON")
+
+            if old_chat_exists:
+                print("Dropping old 'old_chat' column")
+                op.drop_column("chat", "old_chat")
+
+            # Step 1: Rename current 'chat' column to 'old_chat'
+            print("Renaming 'chat' column to 'old_chat'")
+            op.alter_column(
+                "chat", "chat", new_column_name="old_chat", existing_type=sa.Text()
+            )
+
+            # Step 2: Add new 'chat' column of type JSON
+            print("Adding new 'chat' column of type JSON")
+            op.add_column("chat", sa.Column("chat", sa.JSON(), nullable=True))
+        else:
+            # If the column is already JSON, no need to do anything
+            pass
+
+    # Step 3: Migrate data from 'old_chat' to 'chat'
+    chat_table = table(
+        "chat",
+        sa.Column("id", sa.String(), primary_key=True),
+        sa.Column("old_chat", sa.Text()),
+        sa.Column("chat", sa.JSON()),
+    )
+
+    # - Selecting all data from the table
+    connection = op.get_bind()
+    results = connection.execute(select(chat_table.c.id, chat_table.c.old_chat))
+    for row in results:
+        try:
+            # Convert text JSON to actual JSON object, assuming the text is in JSON format
+            json_data = json.loads(row.old_chat)
+        except json.JSONDecodeError:
+            json_data = None  # Handle cases where the text cannot be converted to JSON
+
+        connection.execute(
+            sa.update(chat_table)
+            .where(chat_table.c.id == row.id)
+            .values(chat=json_data)
+        )
+
+    # Step 4: Drop 'old_chat' column
+    print("Dropping 'old_chat' column")
+    op.drop_column("chat", "old_chat")
+
+
+def downgrade():
+    # Step 1: Add 'old_chat' column back as Text
+    op.add_column("chat", sa.Column("old_chat", sa.Text(), nullable=True))
+
+    # Step 2: Convert 'chat' JSON data back to text and store in 'old_chat'
+    chat_table = table(
+        "chat",
+        sa.Column("id", sa.String(), primary_key=True),
+        sa.Column("chat", sa.JSON()),
+        sa.Column("old_chat", sa.Text()),
+    )
+
+    connection = op.get_bind()
+    results = connection.execute(select(chat_table.c.id, chat_table.c.chat))
+    for row in results:
+        text_data = json.dumps(row.chat) if row.chat is not None else None
+        connection.execute(
+            sa.update(chat_table)
+            .where(chat_table.c.id == row.id)
+            .values(old_chat=text_data)
+        )
+
+    # Step 3: Remove the new 'chat' JSON column
+    op.drop_column("chat", "chat")
+
+    # Step 4: Rename 'old_chat' back to 'chat'
+    op.alter_column("chat", "old_chat", new_column_name="chat", existing_type=sa.Text())
diff --git a/backend/open_webui/migrations/versions/3ab32c4b8f59_update_tags.py b/backend/open_webui/migrations/versions/3ab32c4b8f59_update_tags.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e010424b05242c15f6998f88f7efa4cde30aa73
--- /dev/null
+++ b/backend/open_webui/migrations/versions/3ab32c4b8f59_update_tags.py
@@ -0,0 +1,81 @@
+"""Update tags
+
+Revision ID: 3ab32c4b8f59
+Revises: 1af9b942657b
+Create Date: 2024-10-09 21:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.sql import table, select, update, column
+from sqlalchemy.engine.reflection import Inspector
+
+import json
+
+revision = "3ab32c4b8f59"
+down_revision = "1af9b942657b"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    conn = op.get_bind()
+    inspector = Inspector.from_engine(conn)
+
+    # Inspecting the 'tag' table constraints and structure
+    existing_pk = inspector.get_pk_constraint("tag")
+    unique_constraints = inspector.get_unique_constraints("tag")
+    existing_indexes = inspector.get_indexes("tag")
+
+    print(f"Primary Key: {existing_pk}")
+    print(f"Unique Constraints: {unique_constraints}")
+    print(f"Indexes: {existing_indexes}")
+
+    with op.batch_alter_table("tag", schema=None) as batch_op:
+        # Drop existing primary key constraint if it exists
+        if existing_pk and existing_pk.get("constrained_columns"):
+            pk_name = existing_pk.get("name")
+            if pk_name:
+                print(f"Dropping primary key constraint: {pk_name}")
+                batch_op.drop_constraint(pk_name, type_="primary")
+
+        # Now create the new primary key with the combination of 'id' and 'user_id'
+        print("Creating new primary key with 'id' and 'user_id'.")
+        batch_op.create_primary_key("pk_id_user_id", ["id", "user_id"])
+
+        # Drop unique constraints that could conflict with the new primary key
+        for constraint in unique_constraints:
+            if (
+                constraint["name"] == "uq_id_user_id"
+            ):  # Adjust this name according to what is actually returned by the inspector
+                print(f"Dropping unique constraint: {constraint['name']}")
+                batch_op.drop_constraint(constraint["name"], type_="unique")
+
+        for index in existing_indexes:
+            if index["unique"]:
+                if not any(
+                    constraint["name"] == index["name"]
+                    for constraint in unique_constraints
+                ):
+                    # You are attempting to drop unique indexes
+                    print(f"Dropping unique index: {index['name']}")
+                    batch_op.drop_index(index["name"])
+
+
+def downgrade():
+    conn = op.get_bind()
+    inspector = Inspector.from_engine(conn)
+
+    current_pk = inspector.get_pk_constraint("tag")
+
+    with op.batch_alter_table("tag", schema=None) as batch_op:
+        # Drop the current primary key first, if it matches the one we know we added in upgrade
+        if current_pk and "pk_id_user_id" == current_pk.get("name"):
+            batch_op.drop_constraint("pk_id_user_id", type_="primary")
+
+        # Restore the original primary key
+        batch_op.create_primary_key("pk_id", ["id"])
+
+        # Since primary key on just 'id' is restored, we now add back any unique constraints if necessary
+        batch_op.create_unique_constraint("uq_id_user_id", ["id", "user_id"])
diff --git a/backend/open_webui/migrations/versions/4ace53fd72c8_update_folder_table_datetime.py b/backend/open_webui/migrations/versions/4ace53fd72c8_update_folder_table_datetime.py
new file mode 100644
index 0000000000000000000000000000000000000000..16f7967c8ec8c4e6c65e8d17c1ff2f8ac4a0a102
--- /dev/null
+++ b/backend/open_webui/migrations/versions/4ace53fd72c8_update_folder_table_datetime.py
@@ -0,0 +1,67 @@
+"""Update folder table and change DateTime to BigInteger for timestamp fields
+
+Revision ID: 4ace53fd72c8
+Revises: af906e964978
+Create Date: 2024-10-23 03:00:00.000000
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+
+revision = "4ace53fd72c8"
+down_revision = "af906e964978"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # Perform safe alterations using batch operation
+    with op.batch_alter_table("folder", schema=None) as batch_op:
+        # Step 1: Remove server defaults for created_at and updated_at
+        batch_op.alter_column(
+            "created_at",
+            server_default=None,  # Removing server default
+        )
+        batch_op.alter_column(
+            "updated_at",
+            server_default=None,  # Removing server default
+        )
+
+        # Step 2: Change the column types to BigInteger for created_at
+        batch_op.alter_column(
+            "created_at",
+            type_=sa.BigInteger(),
+            existing_type=sa.DateTime(),
+            existing_nullable=False,
+            postgresql_using="extract(epoch from created_at)::bigint",  # Conversion for PostgreSQL
+        )
+
+        # Change the column types to BigInteger for updated_at
+        batch_op.alter_column(
+            "updated_at",
+            type_=sa.BigInteger(),
+            existing_type=sa.DateTime(),
+            existing_nullable=False,
+            postgresql_using="extract(epoch from updated_at)::bigint",  # Conversion for PostgreSQL
+        )
+
+
+def downgrade():
+    # Downgrade: Convert columns back to DateTime and restore defaults
+    with op.batch_alter_table("folder", schema=None) as batch_op:
+        batch_op.alter_column(
+            "created_at",
+            type_=sa.DateTime(),
+            existing_type=sa.BigInteger(),
+            existing_nullable=False,
+            server_default=sa.func.now(),  # Restoring server default on downgrade
+        )
+        batch_op.alter_column(
+            "updated_at",
+            type_=sa.DateTime(),
+            existing_type=sa.BigInteger(),
+            existing_nullable=False,
+            server_default=sa.func.now(),  # Restoring server default on downgrade
+            onupdate=sa.func.now(),  # Restoring onupdate behavior if it was there
+        )
diff --git a/backend/open_webui/migrations/versions/6a39f3d8e55c_add_knowledge_table.py b/backend/open_webui/migrations/versions/6a39f3d8e55c_add_knowledge_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..881e6ae64126281e03ef09397f212d2e257c9623
--- /dev/null
+++ b/backend/open_webui/migrations/versions/6a39f3d8e55c_add_knowledge_table.py
@@ -0,0 +1,80 @@
+"""Add knowledge table
+
+Revision ID: 6a39f3d8e55c
+Revises: c0fbf31ca0db
+Create Date: 2024-10-01 14:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.sql import table, column, select
+import json
+
+
+revision = "6a39f3d8e55c"
+down_revision = "c0fbf31ca0db"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # Creating the 'knowledge' table
+    print("Creating knowledge table")
+    knowledge_table = op.create_table(
+        "knowledge",
+        sa.Column("id", sa.Text(), primary_key=True),
+        sa.Column("user_id", sa.Text(), nullable=False),
+        sa.Column("name", sa.Text(), nullable=False),
+        sa.Column("description", sa.Text(), nullable=True),
+        sa.Column("data", sa.JSON(), nullable=True),
+        sa.Column("meta", sa.JSON(), nullable=True),
+        sa.Column("created_at", sa.BigInteger(), nullable=False),
+        sa.Column("updated_at", sa.BigInteger(), nullable=True),
+    )
+
+    print("Migrating data from document table to knowledge table")
+    # Representation of the existing 'document' table
+    document_table = table(
+        "document",
+        column("collection_name", sa.String()),
+        column("user_id", sa.String()),
+        column("name", sa.String()),
+        column("title", sa.Text()),
+        column("content", sa.Text()),
+        column("timestamp", sa.BigInteger()),
+    )
+
+    # Select all from existing document table
+    documents = op.get_bind().execute(
+        select(
+            document_table.c.collection_name,
+            document_table.c.user_id,
+            document_table.c.name,
+            document_table.c.title,
+            document_table.c.content,
+            document_table.c.timestamp,
+        )
+    )
+
+    # Insert data into knowledge table from document table
+    for doc in documents:
+        op.get_bind().execute(
+            knowledge_table.insert().values(
+                id=doc.collection_name,
+                user_id=doc.user_id,
+                description=doc.name,
+                meta={
+                    "legacy": True,
+                    "document": True,
+                    "tags": json.loads(doc.content or "{}").get("tags", []),
+                },
+                name=doc.title,
+                created_at=doc.timestamp,
+                updated_at=doc.timestamp,  # using created_at for both created_at and updated_at in project
+            )
+        )
+
+
+def downgrade():
+    op.drop_table("knowledge")
diff --git a/backend/open_webui/migrations/versions/7e5b5dc7342b_init.py b/backend/open_webui/migrations/versions/7e5b5dc7342b_init.py
new file mode 100644
index 0000000000000000000000000000000000000000..607a7b2c91dd021e4bf8a3923ccf21e04ca7de88
--- /dev/null
+++ b/backend/open_webui/migrations/versions/7e5b5dc7342b_init.py
@@ -0,0 +1,204 @@
+"""init
+
+Revision ID: 7e5b5dc7342b
+Revises:
+Create Date: 2024-06-24 13:15:33.808998
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+import open_webui.apps.webui.internal.db
+from open_webui.apps.webui.internal.db import JSONField
+from open_webui.migrations.util import get_existing_tables
+
+# revision identifiers, used by Alembic.
+revision: str = "7e5b5dc7342b"
+down_revision: Union[str, None] = None
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade() -> None:
+    existing_tables = set(get_existing_tables())
+
+    # ### commands auto generated by Alembic - please adjust! ###
+    if "auth" not in existing_tables:
+        op.create_table(
+            "auth",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("email", sa.String(), nullable=True),
+            sa.Column("password", sa.Text(), nullable=True),
+            sa.Column("active", sa.Boolean(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "chat" not in existing_tables:
+        op.create_table(
+            "chat",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("title", sa.Text(), nullable=True),
+            sa.Column("chat", sa.Text(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("share_id", sa.Text(), nullable=True),
+            sa.Column("archived", sa.Boolean(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+            sa.UniqueConstraint("share_id"),
+        )
+
+    if "chatidtag" not in existing_tables:
+        op.create_table(
+            "chatidtag",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("tag_name", sa.String(), nullable=True),
+            sa.Column("chat_id", sa.String(), nullable=True),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("timestamp", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "document" not in existing_tables:
+        op.create_table(
+            "document",
+            sa.Column("collection_name", sa.String(), nullable=False),
+            sa.Column("name", sa.String(), nullable=True),
+            sa.Column("title", sa.Text(), nullable=True),
+            sa.Column("filename", sa.Text(), nullable=True),
+            sa.Column("content", sa.Text(), nullable=True),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("timestamp", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("collection_name"),
+            sa.UniqueConstraint("name"),
+        )
+
+    if "file" not in existing_tables:
+        op.create_table(
+            "file",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("filename", sa.Text(), nullable=True),
+            sa.Column("meta", JSONField(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "function" not in existing_tables:
+        op.create_table(
+            "function",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("name", sa.Text(), nullable=True),
+            sa.Column("type", sa.Text(), nullable=True),
+            sa.Column("content", sa.Text(), nullable=True),
+            sa.Column("meta", JSONField(), nullable=True),
+            sa.Column("valves", JSONField(), nullable=True),
+            sa.Column("is_active", sa.Boolean(), nullable=True),
+            sa.Column("is_global", sa.Boolean(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "memory" not in existing_tables:
+        op.create_table(
+            "memory",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("content", sa.Text(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "model" not in existing_tables:
+        op.create_table(
+            "model",
+            sa.Column("id", sa.Text(), nullable=False),
+            sa.Column("user_id", sa.Text(), nullable=True),
+            sa.Column("base_model_id", sa.Text(), nullable=True),
+            sa.Column("name", sa.Text(), nullable=True),
+            sa.Column("params", JSONField(), nullable=True),
+            sa.Column("meta", JSONField(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "prompt" not in existing_tables:
+        op.create_table(
+            "prompt",
+            sa.Column("command", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("title", sa.Text(), nullable=True),
+            sa.Column("content", sa.Text(), nullable=True),
+            sa.Column("timestamp", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("command"),
+        )
+
+    if "tag" not in existing_tables:
+        op.create_table(
+            "tag",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("name", sa.String(), nullable=True),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("data", sa.Text(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "tool" not in existing_tables:
+        op.create_table(
+            "tool",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("user_id", sa.String(), nullable=True),
+            sa.Column("name", sa.Text(), nullable=True),
+            sa.Column("content", sa.Text(), nullable=True),
+            sa.Column("specs", JSONField(), nullable=True),
+            sa.Column("meta", JSONField(), nullable=True),
+            sa.Column("valves", JSONField(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+        )
+
+    if "user" not in existing_tables:
+        op.create_table(
+            "user",
+            sa.Column("id", sa.String(), nullable=False),
+            sa.Column("name", sa.String(), nullable=True),
+            sa.Column("email", sa.String(), nullable=True),
+            sa.Column("role", sa.String(), nullable=True),
+            sa.Column("profile_image_url", sa.Text(), nullable=True),
+            sa.Column("last_active_at", sa.BigInteger(), nullable=True),
+            sa.Column("updated_at", sa.BigInteger(), nullable=True),
+            sa.Column("created_at", sa.BigInteger(), nullable=True),
+            sa.Column("api_key", sa.String(), nullable=True),
+            sa.Column("settings", JSONField(), nullable=True),
+            sa.Column("info", JSONField(), nullable=True),
+            sa.Column("oauth_sub", sa.Text(), nullable=True),
+            sa.PrimaryKeyConstraint("id"),
+            sa.UniqueConstraint("api_key"),
+            sa.UniqueConstraint("oauth_sub"),
+        )
+    # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table("user")
+    op.drop_table("tool")
+    op.drop_table("tag")
+    op.drop_table("prompt")
+    op.drop_table("model")
+    op.drop_table("memory")
+    op.drop_table("function")
+    op.drop_table("file")
+    op.drop_table("document")
+    op.drop_table("chatidtag")
+    op.drop_table("chat")
+    op.drop_table("auth")
+    # ### end Alembic commands ###
diff --git a/backend/open_webui/migrations/versions/922e7a387820_add_group_table.py b/backend/open_webui/migrations/versions/922e7a387820_add_group_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7521158441299b2b520b54a9cb61c377de7df24
--- /dev/null
+++ b/backend/open_webui/migrations/versions/922e7a387820_add_group_table.py
@@ -0,0 +1,85 @@
+"""Add group table
+
+Revision ID: 922e7a387820
+Revises: 4ace53fd72c8
+Create Date: 2024-11-14 03:00:00.000000
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+
+revision = "922e7a387820"
+down_revision = "4ace53fd72c8"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.create_table(
+        "group",
+        sa.Column("id", sa.Text(), nullable=False, primary_key=True, unique=True),
+        sa.Column("user_id", sa.Text(), nullable=True),
+        sa.Column("name", sa.Text(), nullable=True),
+        sa.Column("description", sa.Text(), nullable=True),
+        sa.Column("data", sa.JSON(), nullable=True),
+        sa.Column("meta", sa.JSON(), nullable=True),
+        sa.Column("permissions", sa.JSON(), nullable=True),
+        sa.Column("user_ids", sa.JSON(), nullable=True),
+        sa.Column("created_at", sa.BigInteger(), nullable=True),
+        sa.Column("updated_at", sa.BigInteger(), nullable=True),
+    )
+
+    # Add 'access_control' column to 'model' table
+    op.add_column(
+        "model",
+        sa.Column("access_control", sa.JSON(), nullable=True),
+    )
+
+    # Add 'is_active' column to 'model' table
+    op.add_column(
+        "model",
+        sa.Column(
+            "is_active",
+            sa.Boolean(),
+            nullable=False,
+            server_default=sa.sql.expression.true(),
+        ),
+    )
+
+    # Add 'access_control' column to 'knowledge' table
+    op.add_column(
+        "knowledge",
+        sa.Column("access_control", sa.JSON(), nullable=True),
+    )
+
+    # Add 'access_control' column to 'prompt' table
+    op.add_column(
+        "prompt",
+        sa.Column("access_control", sa.JSON(), nullable=True),
+    )
+
+    # Add 'access_control' column to 'tools' table
+    op.add_column(
+        "tool",
+        sa.Column("access_control", sa.JSON(), nullable=True),
+    )
+
+
+def downgrade():
+    op.drop_table("group")
+
+    # Drop 'access_control' column from 'model' table
+    op.drop_column("model", "access_control")
+
+    # Drop 'is_active' column from 'model' table
+    op.drop_column("model", "is_active")
+
+    # Drop 'access_control' column from 'knowledge' table
+    op.drop_column("knowledge", "access_control")
+
+    # Drop 'access_control' column from 'prompt' table
+    op.drop_column("prompt", "access_control")
+
+    # Drop 'access_control' column from 'tools' table
+    op.drop_column("tool", "access_control")
diff --git a/backend/open_webui/migrations/versions/af906e964978_add_feedback_table.py b/backend/open_webui/migrations/versions/af906e964978_add_feedback_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..9116aa38843259b8e7c7f855b46cec124587bc46
--- /dev/null
+++ b/backend/open_webui/migrations/versions/af906e964978_add_feedback_table.py
@@ -0,0 +1,51 @@
+"""Add feedback table
+
+Revision ID: af906e964978
+Revises: c29facfe716b
+Create Date: 2024-10-20 17:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+
+# Revision identifiers, used by Alembic.
+revision = "af906e964978"
+down_revision = "c29facfe716b"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### Create feedback table ###
+    op.create_table(
+        "feedback",
+        sa.Column(
+            "id", sa.Text(), primary_key=True
+        ),  # Unique identifier for each feedback (TEXT type)
+        sa.Column(
+            "user_id", sa.Text(), nullable=True
+        ),  # ID of the user providing the feedback (TEXT type)
+        sa.Column(
+            "version", sa.BigInteger(), default=0
+        ),  # Version of feedback (BIGINT type)
+        sa.Column("type", sa.Text(), nullable=True),  # Type of feedback (TEXT type)
+        sa.Column("data", sa.JSON(), nullable=True),  # Feedback data (JSON type)
+        sa.Column(
+            "meta", sa.JSON(), nullable=True
+        ),  # Metadata for feedback (JSON type)
+        sa.Column(
+            "snapshot", sa.JSON(), nullable=True
+        ),  # snapshot data for feedback (JSON type)
+        sa.Column(
+            "created_at", sa.BigInteger(), nullable=False
+        ),  # Feedback creation timestamp (BIGINT representing epoch)
+        sa.Column(
+            "updated_at", sa.BigInteger(), nullable=False
+        ),  # Feedback update timestamp (BIGINT representing epoch)
+    )
+
+
+def downgrade():
+    # ### Drop feedback table ###
+    op.drop_table("feedback")
diff --git a/backend/open_webui/migrations/versions/c0fbf31ca0db_update_file_table.py b/backend/open_webui/migrations/versions/c0fbf31ca0db_update_file_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f7f2abf706f8fdeb03503a05497744c74bac8e5
--- /dev/null
+++ b/backend/open_webui/migrations/versions/c0fbf31ca0db_update_file_table.py
@@ -0,0 +1,32 @@
+"""Update file table
+
+Revision ID: c0fbf31ca0db
+Revises: ca81bd47c050
+Create Date: 2024-09-20 15:26:35.241684
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "c0fbf31ca0db"
+down_revision: Union[str, None] = "ca81bd47c050"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column("file", sa.Column("hash", sa.Text(), nullable=True))
+    op.add_column("file", sa.Column("data", sa.JSON(), nullable=True))
+    op.add_column("file", sa.Column("updated_at", sa.BigInteger(), nullable=True))
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column("file", "updated_at")
+    op.drop_column("file", "data")
+    op.drop_column("file", "hash")
diff --git a/backend/open_webui/migrations/versions/c29facfe716b_update_file_table_path.py b/backend/open_webui/migrations/versions/c29facfe716b_update_file_table_path.py
new file mode 100644
index 0000000000000000000000000000000000000000..de82854b8811ed75c85da2c4c3e9229cef89f028
--- /dev/null
+++ b/backend/open_webui/migrations/versions/c29facfe716b_update_file_table_path.py
@@ -0,0 +1,79 @@
+"""Update file table path
+
+Revision ID: c29facfe716b
+Revises: c69f45358db4
+Create Date: 2024-10-20 17:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+import json
+from sqlalchemy.sql import table, column
+from sqlalchemy import String, Text, JSON, and_
+
+
+revision = "c29facfe716b"
+down_revision = "c69f45358db4"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # 1. Add the `path` column to the "file" table.
+    op.add_column("file", sa.Column("path", sa.Text(), nullable=True))
+
+    # 2. Convert the `meta` column from Text/JSONField to `JSON()`
+    # Use Alembic's default batch_op for dialect compatibility.
+    with op.batch_alter_table("file", schema=None) as batch_op:
+        batch_op.alter_column(
+            "meta",
+            type_=sa.JSON(),
+            existing_type=sa.Text(),
+            existing_nullable=True,
+            nullable=True,
+            postgresql_using="meta::json",
+        )
+
+    # 3. Migrate legacy data from `meta` JSONField
+    # Fetch and process `meta` data from the table, add values to the new `path` column as necessary.
+    # We will use SQLAlchemy core bindings to ensure safety across different databases.
+
+    file_table = table(
+        "file", column("id", String), column("meta", JSON), column("path", Text)
+    )
+
+    # Create connection to the database
+    connection = op.get_bind()
+
+    # Get the rows where `meta` has a path and `path` column is null (new column)
+    # Loop through each row in the result set to update the path
+    results = connection.execute(
+        sa.select(file_table.c.id, file_table.c.meta).where(
+            and_(file_table.c.path.is_(None), file_table.c.meta.isnot(None))
+        )
+    ).fetchall()
+
+    # Iterate over each row to extract and update the `path` from `meta` column
+    for row in results:
+        if "path" in row.meta:
+            # Extract the `path` field from the `meta` JSON
+            path = row.meta.get("path")
+
+            # Update the `file` table with the new `path` value
+            connection.execute(
+                file_table.update()
+                .where(file_table.c.id == row.id)
+                .values({"path": path})
+            )
+
+
+def downgrade():
+    # 1. Remove the `path` column
+    op.drop_column("file", "path")
+
+    # 2. Revert the `meta` column back to Text/JSONField
+    with op.batch_alter_table("file", schema=None) as batch_op:
+        batch_op.alter_column(
+            "meta", type_=sa.Text(), existing_type=sa.JSON(), existing_nullable=True
+        )
diff --git a/backend/open_webui/migrations/versions/c69f45358db4_add_folder_table.py b/backend/open_webui/migrations/versions/c69f45358db4_add_folder_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..83e0dc28edcfe76e5742faa8c078523b1cfb4269
--- /dev/null
+++ b/backend/open_webui/migrations/versions/c69f45358db4_add_folder_table.py
@@ -0,0 +1,50 @@
+"""Add folder table
+
+Revision ID: c69f45358db4
+Revises: 3ab32c4b8f59
+Create Date: 2024-10-16 02:02:35.241684
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+
+revision = "c69f45358db4"
+down_revision = "3ab32c4b8f59"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.create_table(
+        "folder",
+        sa.Column("id", sa.Text(), nullable=False),
+        sa.Column("parent_id", sa.Text(), nullable=True),
+        sa.Column("user_id", sa.Text(), nullable=False),
+        sa.Column("name", sa.Text(), nullable=False),
+        sa.Column("items", sa.JSON(), nullable=True),
+        sa.Column("meta", sa.JSON(), nullable=True),
+        sa.Column("is_expanded", sa.Boolean(), default=False, nullable=False),
+        sa.Column(
+            "created_at", sa.DateTime(), server_default=sa.func.now(), nullable=False
+        ),
+        sa.Column(
+            "updated_at",
+            sa.DateTime(),
+            nullable=False,
+            server_default=sa.func.now(),
+            onupdate=sa.func.now(),
+        ),
+        sa.PrimaryKeyConstraint("id", "user_id"),
+    )
+
+    op.add_column(
+        "chat",
+        sa.Column("folder_id", sa.Text(), nullable=True),
+    )
+
+
+def downgrade():
+    op.drop_column("chat", "folder_id")
+
+    op.drop_table("folder")
diff --git a/backend/open_webui/migrations/versions/ca81bd47c050_add_config_table.py b/backend/open_webui/migrations/versions/ca81bd47c050_add_config_table.py
new file mode 100644
index 0000000000000000000000000000000000000000..1540aa6a7f263880b89d7e84bf86ead434c904f7
--- /dev/null
+++ b/backend/open_webui/migrations/versions/ca81bd47c050_add_config_table.py
@@ -0,0 +1,41 @@
+"""Add config table
+
+Revision ID: ca81bd47c050
+Revises: 7e5b5dc7342b
+Create Date: 2024-08-25 15:26:35.241684
+
+"""
+
+from typing import Sequence, Union
+
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision: str = "ca81bd47c050"
+down_revision: Union[str, None] = "7e5b5dc7342b"
+branch_labels: Union[str, Sequence[str], None] = None
+depends_on: Union[str, Sequence[str], None] = None
+
+
+def upgrade():
+    op.create_table(
+        "config",
+        sa.Column("id", sa.Integer, primary_key=True),
+        sa.Column("data", sa.JSON(), nullable=False),
+        sa.Column("version", sa.Integer, nullable=False),
+        sa.Column(
+            "created_at", sa.DateTime(), nullable=False, server_default=sa.func.now()
+        ),
+        sa.Column(
+            "updated_at",
+            sa.DateTime(),
+            nullable=True,
+            server_default=sa.func.now(),
+            onupdate=sa.func.now(),
+        ),
+    )
+
+
+def downgrade():
+    op.drop_table("config")
diff --git a/backend/open_webui/static/assets/pdf-style.css b/backend/open_webui/static/assets/pdf-style.css
new file mode 100644
index 0000000000000000000000000000000000000000..db9ac83ddb823bd4d10297ac59c1955ed082369f
--- /dev/null
+++ b/backend/open_webui/static/assets/pdf-style.css
@@ -0,0 +1,319 @@
+/* HTML and Body */
+@font-face {
+	font-family: 'NotoSans';
+	src: url('fonts/NotoSans-Variable.ttf');
+}
+
+@font-face {
+	font-family: 'NotoSansJP';
+	src: url('fonts/NotoSansJP-Variable.ttf');
+}
+
+@font-face {
+	font-family: 'NotoSansKR';
+	src: url('fonts/NotoSansKR-Variable.ttf');
+}
+
+@font-face {
+	font-family: 'NotoSansSC';
+	src: url('fonts/NotoSansSC-Variable.ttf');
+}
+
+@font-face {
+	font-family: 'NotoSansSC-Regular';
+	src: url('fonts/NotoSansSC-Regular.ttf');
+}
+
+html {
+	font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'NotoSans', 'NotoSansJP', 'NotoSansKR',
+		'NotoSansSC', 'STSong-Light', 'MSung-Light', 'HeiseiMin-W3', 'HYSMyeongJo-Medium', Roboto,
+		'Helvetica Neue', Arial, sans-serif;
+	font-size: 14px; /* Default font size */
+	line-height: 1.5;
+}
+
+*,
+*::before,
+*::after {
+	box-sizing: inherit;
+}
+
+body {
+	margin: 0;
+	color: #212529;
+	background-color: #fff;
+	width: auto;
+}
+
+/* Typography */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+	font-weight: 500;
+	margin: 0;
+}
+
+h1 {
+	font-size: 2.5rem;
+}
+
+h2 {
+	font-size: 2rem;
+}
+
+h3 {
+	font-size: 1.75rem;
+}
+
+h4 {
+	font-size: 1.5rem;
+}
+
+h5 {
+	font-size: 1.25rem;
+}
+
+h6 {
+	font-size: 1rem;
+}
+
+p {
+	margin-top: 0;
+	margin-bottom: 1rem;
+}
+
+/* Grid System */
+.container {
+	width: 100%;
+	padding-right: 15px;
+	padding-left: 15px;
+	margin-right: auto;
+	margin-left: auto;
+}
+
+/* Utilities */
+.text-center {
+	text-align: center;
+}
+
+/* Additional Text Utilities */
+.text-muted {
+	color: #6c757d; /* Muted text color */
+}
+
+/* Small Text */
+small {
+	font-size: 80%; /* Smaller font size relative to the base */
+	color: #6c757d; /* Lighter text color for secondary information */
+	margin-bottom: 0;
+	margin-top: 0;
+}
+
+/* Strong Element Styles */
+strong {
+	font-weight: bolder; /* Ensures the text is bold */
+	color: inherit; /* Inherits the color from its parent element */
+}
+
+/* link */
+a {
+	color: #007bff;
+	text-decoration: none;
+	background-color: transparent;
+}
+
+a:hover {
+	color: #0056b3;
+	text-decoration: underline;
+}
+
+/* General styles for lists */
+ol,
+ul,
+li {
+	padding-left: 40px; /* Increase padding to move bullet points to the right */
+	margin-left: 20px; /* Indent lists from the left */
+}
+
+/* Ordered list styles */
+ol {
+	list-style-type: decimal; /* Use numbers for ordered lists */
+	margin-bottom: 10px; /* Space after each list */
+}
+
+ol li {
+	margin-bottom: 0.5rem; /* Space between ordered list items */
+}
+
+/* Unordered list styles */
+ul {
+	list-style-type: disc; /* Use bullets for unordered lists */
+	margin-bottom: 10px; /* Space after each list */
+}
+
+ul li {
+	margin-bottom: 0.5rem; /* Space between unordered list items */
+}
+
+/* List item styles */
+li {
+	margin-bottom: 5px; /* Space between list items */
+	line-height: 1.5; /* Line height for better readability */
+}
+
+/* Nested lists */
+ol ol,
+ol ul,
+ul ol,
+ul ul {
+	padding-left: 20px;
+	margin-left: 30px; /* Further indent nested lists */
+	margin-bottom: 0; /* Remove extra margin at the bottom of nested lists */
+}
+
+/* Code blocks */
+pre {
+	background-color: #f4f4f4;
+	padding: 10px;
+	overflow-x: auto;
+	max-width: 100%; /* Ensure it doesn't overflow the page */
+	width: 80%; /* Set a specific width for a container-like appearance */
+	margin: 0 1em; /* Center the pre block */
+	box-sizing: border-box; /* Include padding in the width */
+	border: 1px solid #ccc; /* Optional: Add a border for better definition */
+	border-radius: 4px; /* Optional: Add rounded corners */
+}
+
+code {
+	font-family: 'Courier New', Courier, monospace;
+	background-color: #f4f4f4;
+	padding: 2px 4px;
+	border-radius: 4px;
+	box-sizing: border-box; /* Include padding in the width */
+}
+
+.message {
+	margin-top: 8px;
+	margin-bottom: 8px;
+	max-width: 100%;
+	overflow-wrap: break-word;
+}
+
+/* Table Styles */
+table {
+	width: 100%;
+	margin-bottom: 1rem;
+	color: #212529;
+	border-collapse: collapse; /* Removes the space between borders */
+}
+
+th,
+td {
+	margin: 0;
+	padding: 0.75rem;
+	vertical-align: top;
+	border-top: 1px solid #dee2e6;
+}
+
+thead th {
+	vertical-align: bottom;
+	border-bottom: 2px solid #dee2e6;
+}
+
+tbody + tbody {
+	border-top: 2px solid #dee2e6;
+}
+
+/* markdown-section styles */
+.markdown-section blockquote,
+.markdown-section h1,
+.markdown-section h2,
+.markdown-section h3,
+.markdown-section h4,
+.markdown-section h5,
+.markdown-section h6,
+.markdown-section p,
+.markdown-section pre,
+.markdown-section table,
+.markdown-section ul {
+	/* Give most block elements margin top and bottom */
+	margin-top: 1rem;
+}
+
+/* Remove top margin if it's the first child */
+.markdown-section blockquote:first-child,
+.markdown-section h1:first-child,
+.markdown-section h2:first-child,
+.markdown-section h3:first-child,
+.markdown-section h4:first-child,
+.markdown-section h5:first-child,
+.markdown-section h6:first-child,
+.markdown-section p:first-child,
+.markdown-section pre:first-child,
+.markdown-section table:first-child,
+.markdown-section ul:first-child {
+	margin-top: 0;
+}
+
+/* Remove top margin of <ul> following a <p> */
+.markdown-section p + ul {
+	margin-top: 0;
+}
+
+/* Remove bottom margin of <p> if it is followed by a <ul> */
+/* Note: :has is not supported in CSS, so you would need JavaScript for this behavior */
+.markdown-section p {
+	margin-bottom: 0;
+}
+
+/* Add a rule to reset margin-bottom for <p> not followed by <ul> */
+.markdown-section p + ul {
+	margin-top: 0;
+}
+
+/* List item styles */
+.markdown-section li {
+	padding: 2px;
+}
+
+.markdown-section li p {
+	margin-bottom: 0;
+	padding: 0;
+}
+
+/* Avoid margins for nested lists */
+.markdown-section li > ul {
+	margin-top: 0;
+	margin-bottom: 0;
+}
+
+/* Table styles */
+.markdown-section table {
+	width: 100%;
+	border-collapse: collapse;
+	margin: 1rem 0;
+}
+
+.markdown-section th,
+.markdown-section td {
+	border: 1px solid #ddd;
+	padding: 0.5rem;
+	text-align: left;
+}
+
+.markdown-section th {
+	background-color: #f2f2f2;
+}
+
+.markdown-section pre {
+	padding: 10px;
+	margin: 10px;
+}
+
+.markdown-section pre code {
+	position: relative;
+	color: rgb(172, 0, 95);
+}
diff --git a/backend/open_webui/static/favicon.png b/backend/open_webui/static/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b2074780847581edf9cf2ed0d2e9ebd8ff08c56
Binary files /dev/null and b/backend/open_webui/static/favicon.png differ
diff --git a/backend/open_webui/static/fonts/NotoSans-Bold.ttf b/backend/open_webui/static/fonts/NotoSans-Bold.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..56310ad1ad635e6ac20478daff20f14f0647e3ed
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSans-Bold.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf382cad35e731fc4f13b1bf068c5085cd17bee2141014cc94919c140529488d
+size 582604
diff --git a/backend/open_webui/static/fonts/NotoSans-Italic.ttf b/backend/open_webui/static/fonts/NotoSans-Italic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..1ad4f126bc92b2188effdf3f0db8e4a260ecc5c4
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSans-Italic.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:380a500e3dda76d955dadc77053227cc61149814737dc9f7d973d09415ad851f
+size 597000
diff --git a/backend/open_webui/static/fonts/NotoSans-Regular.ttf b/backend/open_webui/static/fonts/NotoSans-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..defecf9b7931c29b64603ca1eed7eb4f513d42ad
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSans-Regular.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3be6b371cef19ed6add589bd106444ab74c9793bc812d3159298b73d00ee011c
+size 582748
diff --git a/backend/open_webui/static/fonts/NotoSans-Variable.ttf b/backend/open_webui/static/fonts/NotoSans-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..1e0b14b76564180e3fc9f59c50efd2742cc1e878
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSans-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:74df1f61ab9d4bfaa961c65f8dc991deaae2885b0a6a6d6a60ed23980b3c8554
+size 2490816
diff --git a/backend/open_webui/static/fonts/NotoSansJP-Regular.ttf b/backend/open_webui/static/fonts/NotoSansJP-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..34e480073b8a55f1e9476c669acde48e049bd0b5
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansJP-Regular.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fb3df01b4182734d021d79ec5bac17903bb681e926a059c59ed81a373d612241
+size 5732824
diff --git a/backend/open_webui/static/fonts/NotoSansJP-Variable.ttf b/backend/open_webui/static/fonts/NotoSansJP-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..d4a816c22ac73995af0112a8ea0d9d195c8e864e
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansJP-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33119a596d1bfae91165585c90c6e9752cf2c9120b45c18388fb81724b3ec64b
+size 9586480
diff --git a/backend/open_webui/static/fonts/NotoSansKR-Regular.ttf b/backend/open_webui/static/fonts/NotoSansKR-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..c847342886ba5d1e326844ba2d679b9d0bdaa805
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansKR-Regular.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9db318b65ee9c575a43e7efd273dbdd1afef26e467eea3e1073a50e1a6595f6d
+size 6192764
diff --git a/backend/open_webui/static/fonts/NotoSansKR-Variable.ttf b/backend/open_webui/static/fonts/NotoSansKR-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..a4af1a68197b794946d4303a264fc78c334974a2
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansKR-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d2267a83d089cb1a517a4f901676d05d283346e650d1b1845d601cbd696a98e
+size 10361060
diff --git a/backend/open_webui/static/fonts/NotoSansSC-Regular.ttf b/backend/open_webui/static/fonts/NotoSansSC-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..6326270d98c00a198618b1fb3cf7dd71b4fd93f1
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansSC-Regular.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5cf8b2a0576d5680284ab03a7a8219499d59bbe981a79bb3dc0031f251c39736
+size 10560616
diff --git a/backend/open_webui/static/fonts/NotoSansSC-Variable.ttf b/backend/open_webui/static/fonts/NotoSansSC-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..05e0ad417a9e92b52630f22fb3e1cb5e7c3522aa
--- /dev/null
+++ b/backend/open_webui/static/fonts/NotoSansSC-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2e68d43ae2c504f4e302a9cf522ecc3f06ef66d724cade58bbe13a3a4af70512
+size 17805476
diff --git a/backend/open_webui/static/logo.png b/backend/open_webui/static/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..519af1db620dbf4de3694660dae7abd7392f0b3c
Binary files /dev/null and b/backend/open_webui/static/logo.png differ
diff --git a/backend/open_webui/static/splash.png b/backend/open_webui/static/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..389196ca6a364b9e4b7daa0fc13be463b914b251
Binary files /dev/null and b/backend/open_webui/static/splash.png differ
diff --git a/backend/open_webui/static/user-import.csv b/backend/open_webui/static/user-import.csv
new file mode 100644
index 0000000000000000000000000000000000000000..918a92aad71d708ae13fedb8b91f79c29a5b3e9d
--- /dev/null
+++ b/backend/open_webui/static/user-import.csv
@@ -0,0 +1 @@
+Name,Email,Password,Role
diff --git a/backend/open_webui/storage/provider.py b/backend/open_webui/storage/provider.py
new file mode 100644
index 0000000000000000000000000000000000000000..76e4fc48f6c64ba9043430549a23ecf6c6220761
--- /dev/null
+++ b/backend/open_webui/storage/provider.py
@@ -0,0 +1,167 @@
+import os
+import boto3
+from botocore.exceptions import ClientError
+import shutil
+
+
+from typing import BinaryIO, Tuple, Optional, Union
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.config import (
+    STORAGE_PROVIDER,
+    S3_ACCESS_KEY_ID,
+    S3_SECRET_ACCESS_KEY,
+    S3_BUCKET_NAME,
+    S3_REGION_NAME,
+    S3_ENDPOINT_URL,
+    UPLOAD_DIR,
+)
+
+
+import boto3
+from botocore.exceptions import ClientError
+from typing import BinaryIO, Tuple, Optional
+
+
+class StorageProvider:
+    def __init__(self, provider: Optional[str] = None):
+        self.storage_provider: str = provider or STORAGE_PROVIDER
+
+        self.s3_client = None
+        self.s3_bucket_name: Optional[str] = None
+
+        if self.storage_provider == "s3":
+            self._initialize_s3()
+
+    def _initialize_s3(self) -> None:
+        """Initializes the S3 client and bucket name if using S3 storage."""
+        self.s3_client = boto3.client(
+            "s3",
+            region_name=S3_REGION_NAME,
+            endpoint_url=S3_ENDPOINT_URL,
+            aws_access_key_id=S3_ACCESS_KEY_ID,
+            aws_secret_access_key=S3_SECRET_ACCESS_KEY,
+        )
+        self.bucket_name = S3_BUCKET_NAME
+
+    def _upload_to_s3(self, file_path: str, filename: str) -> Tuple[bytes, str]:
+        """Handles uploading of the file to S3 storage."""
+        if not self.s3_client:
+            raise RuntimeError("S3 Client is not initialized.")
+
+        try:
+            self.s3_client.upload_file(file_path, self.bucket_name, filename)
+            return (
+                open(file_path, "rb").read(),
+                "s3://" + self.bucket_name + "/" + filename,
+            )
+        except ClientError as e:
+            raise RuntimeError(f"Error uploading file to S3: {e}")
+
+    def _upload_to_local(self, contents: bytes, filename: str) -> Tuple[bytes, str]:
+        """Handles uploading of the file to local storage."""
+        file_path = f"{UPLOAD_DIR}/{filename}"
+        with open(file_path, "wb") as f:
+            f.write(contents)
+        return contents, file_path
+
+    def _get_file_from_s3(self, file_path: str) -> str:
+        """Handles downloading of the file from S3 storage."""
+        if not self.s3_client:
+            raise RuntimeError("S3 Client is not initialized.")
+
+        try:
+            bucket_name, key = file_path.split("//")[1].split("/")
+            local_file_path = f"{UPLOAD_DIR}/{key}"
+            self.s3_client.download_file(bucket_name, key, local_file_path)
+            return local_file_path
+        except ClientError as e:
+            raise RuntimeError(f"Error downloading file from S3: {e}")
+
+    def _get_file_from_local(self, file_path: str) -> str:
+        """Handles downloading of the file from local storage."""
+        return file_path
+
+    def _delete_from_s3(self, filename: str) -> None:
+        """Handles deletion of the file from S3 storage."""
+        if not self.s3_client:
+            raise RuntimeError("S3 Client is not initialized.")
+
+        try:
+            self.s3_client.delete_object(Bucket=self.bucket_name, Key=filename)
+        except ClientError as e:
+            raise RuntimeError(f"Error deleting file from S3: {e}")
+
+    def _delete_from_local(self, filename: str) -> None:
+        """Handles deletion of the file from local storage."""
+        file_path = f"{UPLOAD_DIR}/{filename}"
+        if os.path.isfile(file_path):
+            os.remove(file_path)
+        else:
+            print(f"File {file_path} not found in local storage.")
+
+    def _delete_all_from_s3(self) -> None:
+        """Handles deletion of all files from S3 storage."""
+        if not self.s3_client:
+            raise RuntimeError("S3 Client is not initialized.")
+
+        try:
+            response = self.s3_client.list_objects_v2(Bucket=self.bucket_name)
+            if "Contents" in response:
+                for content in response["Contents"]:
+                    self.s3_client.delete_object(
+                        Bucket=self.bucket_name, Key=content["Key"]
+                    )
+        except ClientError as e:
+            raise RuntimeError(f"Error deleting all files from S3: {e}")
+
+    def _delete_all_from_local(self) -> None:
+        """Handles deletion of all files from local storage."""
+        if os.path.exists(UPLOAD_DIR):
+            for filename in os.listdir(UPLOAD_DIR):
+                file_path = os.path.join(UPLOAD_DIR, filename)
+                try:
+                    if os.path.isfile(file_path) or os.path.islink(file_path):
+                        os.unlink(file_path)  # Remove the file or link
+                    elif os.path.isdir(file_path):
+                        shutil.rmtree(file_path)  # Remove the directory
+                except Exception as e:
+                    print(f"Failed to delete {file_path}. Reason: {e}")
+        else:
+            print(f"Directory {UPLOAD_DIR} not found in local storage.")
+
+    def upload_file(self, file: BinaryIO, filename: str) -> Tuple[bytes, str]:
+        """Uploads a file either to S3 or the local file system."""
+        contents = file.read()
+        if not contents:
+            raise ValueError(ERROR_MESSAGES.EMPTY_CONTENT)
+        contents, file_path = self._upload_to_local(contents, filename)
+
+        if self.storage_provider == "s3":
+            return self._upload_to_s3(file_path, filename)
+        return contents, file_path
+
+    def get_file(self, file_path: str) -> str:
+        """Downloads a file either from S3 or the local file system and returns the file path."""
+        if self.storage_provider == "s3":
+            return self._get_file_from_s3(file_path)
+        return self._get_file_from_local(file_path)
+
+    def delete_file(self, filename: str) -> None:
+        """Deletes a file either from S3 or the local file system."""
+        if self.storage_provider == "s3":
+            self._delete_from_s3(filename)
+
+        # Always delete from local storage
+        self._delete_from_local(filename)
+
+    def delete_all_files(self) -> None:
+        """Deletes all files from the storage."""
+        if self.storage_provider == "s3":
+            self._delete_all_from_s3()
+
+        # Always delete from local storage
+        self._delete_all_from_local()
+
+
+Storage = StorageProvider(provider=STORAGE_PROVIDER)
diff --git a/backend/open_webui/test/__init__.py b/backend/open_webui/test/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/backend/open_webui/test/apps/webui/routers/test_auths.py b/backend/open_webui/test/apps/webui/routers/test_auths.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc14fb8ddcf4257e031ff126678e3f99bd282b9d
--- /dev/null
+++ b/backend/open_webui/test/apps/webui/routers/test_auths.py
@@ -0,0 +1,200 @@
+from test.util.abstract_integration_test import AbstractPostgresTest
+from test.util.mock_user import mock_webui_user
+
+
+class TestAuths(AbstractPostgresTest):
+    BASE_PATH = "/api/v1/auths"
+
+    def setup_class(cls):
+        super().setup_class()
+        from open_webui.apps.webui.models.auths import Auths
+        from open_webui.apps.webui.models.users import Users
+
+        cls.users = Users
+        cls.auths = Auths
+
+    def test_get_session_user(self):
+        with mock_webui_user():
+            response = self.fast_api_client.get(self.create_url(""))
+        assert response.status_code == 200
+        assert response.json() == {
+            "id": "1",
+            "name": "John Doe",
+            "email": "john.doe@openwebui.com",
+            "role": "user",
+            "profile_image_url": "/user.png",
+        }
+
+    def test_update_profile(self):
+        from open_webui.utils.utils import get_password_hash
+
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password=get_password_hash("old_password"),
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="user",
+        )
+
+        with mock_webui_user(id=user.id):
+            response = self.fast_api_client.post(
+                self.create_url("/update/profile"),
+                json={"name": "John Doe 2", "profile_image_url": "/user2.png"},
+            )
+        assert response.status_code == 200
+        db_user = self.users.get_user_by_id(user.id)
+        assert db_user.name == "John Doe 2"
+        assert db_user.profile_image_url == "/user2.png"
+
+    def test_update_password(self):
+        from open_webui.utils.utils import get_password_hash
+
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password=get_password_hash("old_password"),
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="user",
+        )
+
+        with mock_webui_user(id=user.id):
+            response = self.fast_api_client.post(
+                self.create_url("/update/password"),
+                json={"password": "old_password", "new_password": "new_password"},
+            )
+        assert response.status_code == 200
+
+        old_auth = self.auths.authenticate_user(
+            "john.doe@openwebui.com", "old_password"
+        )
+        assert old_auth is None
+        new_auth = self.auths.authenticate_user(
+            "john.doe@openwebui.com", "new_password"
+        )
+        assert new_auth is not None
+
+    def test_signin(self):
+        from open_webui.utils.utils import get_password_hash
+
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password=get_password_hash("password"),
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="user",
+        )
+        response = self.fast_api_client.post(
+            self.create_url("/signin"),
+            json={"email": "john.doe@openwebui.com", "password": "password"},
+        )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] == user.id
+        assert data["name"] == "John Doe"
+        assert data["email"] == "john.doe@openwebui.com"
+        assert data["role"] == "user"
+        assert data["profile_image_url"] == "/user.png"
+        assert data["token"] is not None and len(data["token"]) > 0
+        assert data["token_type"] == "Bearer"
+
+    def test_signup(self):
+        response = self.fast_api_client.post(
+            self.create_url("/signup"),
+            json={
+                "name": "John Doe",
+                "email": "john.doe@openwebui.com",
+                "password": "password",
+            },
+        )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] is not None and len(data["id"]) > 0
+        assert data["name"] == "John Doe"
+        assert data["email"] == "john.doe@openwebui.com"
+        assert data["role"] in ["admin", "user", "pending"]
+        assert data["profile_image_url"] == "/user.png"
+        assert data["token"] is not None and len(data["token"]) > 0
+        assert data["token_type"] == "Bearer"
+
+    def test_add_user(self):
+        with mock_webui_user():
+            response = self.fast_api_client.post(
+                self.create_url("/add"),
+                json={
+                    "name": "John Doe 2",
+                    "email": "john.doe2@openwebui.com",
+                    "password": "password2",
+                    "role": "admin",
+                },
+            )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] is not None and len(data["id"]) > 0
+        assert data["name"] == "John Doe 2"
+        assert data["email"] == "john.doe2@openwebui.com"
+        assert data["role"] == "admin"
+        assert data["profile_image_url"] == "/user.png"
+        assert data["token"] is not None and len(data["token"]) > 0
+        assert data["token_type"] == "Bearer"
+
+    def test_get_admin_details(self):
+        self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password="password",
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="admin",
+        )
+        with mock_webui_user():
+            response = self.fast_api_client.get(self.create_url("/admin/details"))
+
+        assert response.status_code == 200
+        assert response.json() == {
+            "name": "John Doe",
+            "email": "john.doe@openwebui.com",
+        }
+
+    def test_create_api_key_(self):
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password="password",
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="admin",
+        )
+        with mock_webui_user(id=user.id):
+            response = self.fast_api_client.post(self.create_url("/api_key"))
+        assert response.status_code == 200
+        data = response.json()
+        assert data["api_key"] is not None
+        assert len(data["api_key"]) > 0
+
+    def test_delete_api_key(self):
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password="password",
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="admin",
+        )
+        self.users.update_user_api_key_by_id(user.id, "abc")
+        with mock_webui_user(id=user.id):
+            response = self.fast_api_client.delete(self.create_url("/api_key"))
+        assert response.status_code == 200
+        assert response.json() == True
+        db_user = self.users.get_user_by_id(user.id)
+        assert db_user.api_key is None
+
+    def test_get_api_key(self):
+        user = self.auths.insert_new_auth(
+            email="john.doe@openwebui.com",
+            password="password",
+            name="John Doe",
+            profile_image_url="/user.png",
+            role="admin",
+        )
+        self.users.update_user_api_key_by_id(user.id, "abc")
+        with mock_webui_user(id=user.id):
+            response = self.fast_api_client.get(self.create_url("/api_key"))
+        assert response.status_code == 200
+        assert response.json() == {"api_key": "abc"}
diff --git a/backend/open_webui/test/apps/webui/routers/test_chats.py b/backend/open_webui/test/apps/webui/routers/test_chats.py
new file mode 100644
index 0000000000000000000000000000000000000000..935316fd8f5a273d09a9644ef72a9ab1a11d4361
--- /dev/null
+++ b/backend/open_webui/test/apps/webui/routers/test_chats.py
@@ -0,0 +1,236 @@
+import uuid
+
+from test.util.abstract_integration_test import AbstractPostgresTest
+from test.util.mock_user import mock_webui_user
+
+
+class TestChats(AbstractPostgresTest):
+    BASE_PATH = "/api/v1/chats"
+
+    def setup_class(cls):
+        super().setup_class()
+
+    def setup_method(self):
+        super().setup_method()
+        from open_webui.apps.webui.models.chats import ChatForm, Chats
+
+        self.chats = Chats
+        self.chats.insert_new_chat(
+            "2",
+            ChatForm(
+                **{
+                    "chat": {
+                        "name": "chat1",
+                        "description": "chat1 description",
+                        "tags": ["tag1", "tag2"],
+                        "history": {"currentId": "1", "messages": []},
+                    }
+                }
+            ),
+        )
+
+    def test_get_session_user_chat_list(self):
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        first_chat = response.json()[0]
+        assert first_chat["id"] is not None
+        assert first_chat["title"] == "New Chat"
+        assert first_chat["created_at"] is not None
+        assert first_chat["updated_at"] is not None
+
+    def test_delete_all_user_chats(self):
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.delete(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(self.chats.get_chats()) == 0
+
+    def test_get_user_chat_list_by_user_id(self):
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.get(self.create_url("/list/user/2"))
+        assert response.status_code == 200
+        first_chat = response.json()[0]
+        assert first_chat["id"] is not None
+        assert first_chat["title"] == "New Chat"
+        assert first_chat["created_at"] is not None
+        assert first_chat["updated_at"] is not None
+
+    def test_create_new_chat(self):
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url("/new"),
+                json={
+                    "chat": {
+                        "name": "chat2",
+                        "description": "chat2 description",
+                        "tags": ["tag1", "tag2"],
+                    }
+                },
+            )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["archived"] is False
+        assert data["chat"] == {
+            "name": "chat2",
+            "description": "chat2 description",
+            "tags": ["tag1", "tag2"],
+        }
+        assert data["user_id"] == "2"
+        assert data["id"] is not None
+        assert data["share_id"] is None
+        assert data["title"] == "New Chat"
+        assert data["updated_at"] is not None
+        assert data["created_at"] is not None
+        assert len(self.chats.get_chats()) == 2
+
+    def test_get_user_chats(self):
+        self.test_get_session_user_chat_list()
+
+    def test_get_user_archived_chats(self):
+        self.chats.archive_all_chats_by_user_id("2")
+        from open_webui.apps.webui.internal.db import Session
+
+        Session.commit()
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/all/archived"))
+        assert response.status_code == 200
+        first_chat = response.json()[0]
+        assert first_chat["id"] is not None
+        assert first_chat["title"] == "New Chat"
+        assert first_chat["created_at"] is not None
+        assert first_chat["updated_at"] is not None
+
+    def test_get_all_user_chats_in_db(self):
+        with mock_webui_user(id="4"):
+            response = self.fast_api_client.get(self.create_url("/all/db"))
+        assert response.status_code == 200
+        assert len(response.json()) == 1
+
+    def test_get_archived_session_user_chat_list(self):
+        self.test_get_user_archived_chats()
+
+    def test_archive_all_chats(self):
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(self.create_url("/archive/all"))
+        assert response.status_code == 200
+        assert len(self.chats.get_archived_chats_by_user_id("2")) == 1
+
+    def test_get_shared_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        self.chats.update_chat_share_id_by_id(chat_id, chat_id)
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url(f"/share/{chat_id}"))
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] == chat_id
+        assert data["chat"] == {
+            "name": "chat1",
+            "description": "chat1 description",
+            "tags": ["tag1", "tag2"],
+            "history": {"currentId": "1", "messages": []},
+        }
+        assert data["id"] == chat_id
+        assert data["share_id"] == chat_id
+        assert data["title"] == "New Chat"
+
+    def test_get_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url(f"/{chat_id}"))
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] == chat_id
+        assert data["chat"] == {
+            "name": "chat1",
+            "description": "chat1 description",
+            "tags": ["tag1", "tag2"],
+            "history": {"currentId": "1", "messages": []},
+        }
+        assert data["share_id"] is None
+        assert data["title"] == "New Chat"
+        assert data["user_id"] == "2"
+
+    def test_update_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url(f"/{chat_id}"),
+                json={
+                    "chat": {
+                        "name": "chat2",
+                        "description": "chat2 description",
+                        "tags": ["tag2", "tag4"],
+                        "title": "Just another title",
+                    }
+                },
+            )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] == chat_id
+        assert data["chat"] == {
+            "name": "chat2",
+            "title": "Just another title",
+            "description": "chat2 description",
+            "tags": ["tag2", "tag4"],
+            "history": {"currentId": "1", "messages": []},
+        }
+        assert data["share_id"] is None
+        assert data["title"] == "Just another title"
+        assert data["user_id"] == "2"
+
+    def test_delete_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.delete(self.create_url(f"/{chat_id}"))
+        assert response.status_code == 200
+        assert response.json() is True
+
+    def test_clone_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url(f"/{chat_id}/clone"))
+
+        assert response.status_code == 200
+        data = response.json()
+        assert data["id"] != chat_id
+        assert data["chat"] == {
+            "branchPointMessageId": "1",
+            "description": "chat1 description",
+            "history": {"currentId": "1", "messages": []},
+            "name": "chat1",
+            "originalChatId": chat_id,
+            "tags": ["tag1", "tag2"],
+            "title": "Clone of New Chat",
+        }
+        assert data["share_id"] is None
+        assert data["title"] == "Clone of New Chat"
+        assert data["user_id"] == "2"
+
+    def test_archive_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url(f"/{chat_id}/archive"))
+        assert response.status_code == 200
+
+        chat = self.chats.get_chat_by_id(chat_id)
+        assert chat.archived is True
+
+    def test_share_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(self.create_url(f"/{chat_id}/share"))
+        assert response.status_code == 200
+
+        chat = self.chats.get_chat_by_id(chat_id)
+        assert chat.share_id is not None
+
+    def test_delete_shared_chat_by_id(self):
+        chat_id = self.chats.get_chats()[0].id
+        share_id = str(uuid.uuid4())
+        self.chats.update_chat_share_id_by_id(chat_id, share_id)
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.delete(self.create_url(f"/{chat_id}/share"))
+        assert response.status_code
+
+        chat = self.chats.get_chat_by_id(chat_id)
+        assert chat.share_id is None
diff --git a/backend/open_webui/test/apps/webui/routers/test_models.py b/backend/open_webui/test/apps/webui/routers/test_models.py
new file mode 100644
index 0000000000000000000000000000000000000000..1d52658b8f0d2312232f67fb3856f319f1fa7d2d
--- /dev/null
+++ b/backend/open_webui/test/apps/webui/routers/test_models.py
@@ -0,0 +1,61 @@
+from test.util.abstract_integration_test import AbstractPostgresTest
+from test.util.mock_user import mock_webui_user
+
+
+class TestModels(AbstractPostgresTest):
+    BASE_PATH = "/api/v1/models"
+
+    def setup_class(cls):
+        super().setup_class()
+        from open_webui.apps.webui.models.models import Model
+
+        cls.models = Model
+
+    def test_models(self):
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 0
+
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url("/add"),
+                json={
+                    "id": "my-model",
+                    "base_model_id": "base-model-id",
+                    "name": "Hello World",
+                    "meta": {
+                        "profile_image_url": "/static/favicon.png",
+                        "description": "description",
+                        "capabilities": None,
+                        "model_config": {},
+                    },
+                    "params": {},
+                },
+            )
+        assert response.status_code == 200
+
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 1
+
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(
+                self.create_url(query_params={"id": "my-model"})
+            )
+        assert response.status_code == 200
+        data = response.json()[0]
+        assert data["id"] == "my-model"
+        assert data["name"] == "Hello World"
+
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.delete(
+                self.create_url("/delete?id=my-model")
+            )
+        assert response.status_code == 200
+
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 0
diff --git a/backend/open_webui/test/apps/webui/routers/test_prompts.py b/backend/open_webui/test/apps/webui/routers/test_prompts.py
new file mode 100644
index 0000000000000000000000000000000000000000..d91bf77dc5b2602bd433f9dbe01ded8574df3db9
--- /dev/null
+++ b/backend/open_webui/test/apps/webui/routers/test_prompts.py
@@ -0,0 +1,91 @@
+from test.util.abstract_integration_test import AbstractPostgresTest
+from test.util.mock_user import mock_webui_user
+
+
+class TestPrompts(AbstractPostgresTest):
+    BASE_PATH = "/api/v1/prompts"
+
+    def test_prompts(self):
+        # Get all prompts
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 0
+
+        # Create a two new prompts
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url("/create"),
+                json={
+                    "command": "/my-command",
+                    "title": "Hello World",
+                    "content": "description",
+                },
+            )
+        assert response.status_code == 200
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.post(
+                self.create_url("/create"),
+                json={
+                    "command": "/my-command2",
+                    "title": "Hello World 2",
+                    "content": "description 2",
+                },
+            )
+        assert response.status_code == 200
+
+        # Get all prompts
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 2
+
+        # Get prompt by command
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/command/my-command"))
+        assert response.status_code == 200
+        data = response.json()
+        assert data["command"] == "/my-command"
+        assert data["title"] == "Hello World"
+        assert data["content"] == "description"
+        assert data["user_id"] == "2"
+
+        # Update prompt
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url("/command/my-command2/update"),
+                json={
+                    "command": "irrelevant for request",
+                    "title": "Hello World Updated",
+                    "content": "description Updated",
+                },
+            )
+        assert response.status_code == 200
+        data = response.json()
+        assert data["command"] == "/my-command2"
+        assert data["title"] == "Hello World Updated"
+        assert data["content"] == "description Updated"
+        assert data["user_id"] == "3"
+
+        # Get prompt by command
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/command/my-command2"))
+        assert response.status_code == 200
+        data = response.json()
+        assert data["command"] == "/my-command2"
+        assert data["title"] == "Hello World Updated"
+        assert data["content"] == "description Updated"
+        assert data["user_id"] == "3"
+
+        # Delete prompt
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.delete(
+                self.create_url("/command/my-command/delete")
+            )
+        assert response.status_code == 200
+
+        # Get all prompts
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/"))
+        assert response.status_code == 200
+        assert len(response.json()) == 1
diff --git a/backend/open_webui/test/apps/webui/routers/test_users.py b/backend/open_webui/test/apps/webui/routers/test_users.py
new file mode 100644
index 0000000000000000000000000000000000000000..6facf7055a2a3ef5d817ab71200a18a43c060c1b
--- /dev/null
+++ b/backend/open_webui/test/apps/webui/routers/test_users.py
@@ -0,0 +1,167 @@
+from test.util.abstract_integration_test import AbstractPostgresTest
+from test.util.mock_user import mock_webui_user
+
+
+def _get_user_by_id(data, param):
+    return next((item for item in data if item["id"] == param), None)
+
+
+def _assert_user(data, id, **kwargs):
+    user = _get_user_by_id(data, id)
+    assert user is not None
+    comparison_data = {
+        "name": f"user {id}",
+        "email": f"user{id}@openwebui.com",
+        "profile_image_url": f"/user{id}.png",
+        "role": "user",
+        **kwargs,
+    }
+    for key, value in comparison_data.items():
+        assert user[key] == value
+
+
+class TestUsers(AbstractPostgresTest):
+    BASE_PATH = "/api/v1/users"
+
+    def setup_class(cls):
+        super().setup_class()
+        from open_webui.apps.webui.models.users import Users
+
+        cls.users = Users
+
+    def setup_method(self):
+        super().setup_method()
+        self.users.insert_new_user(
+            id="1",
+            name="user 1",
+            email="user1@openwebui.com",
+            profile_image_url="/user1.png",
+            role="user",
+        )
+        self.users.insert_new_user(
+            id="2",
+            name="user 2",
+            email="user2@openwebui.com",
+            profile_image_url="/user2.png",
+            role="user",
+        )
+
+    def test_users(self):
+        # Get all users
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.get(self.create_url(""))
+        assert response.status_code == 200
+        assert len(response.json()) == 2
+        data = response.json()
+        _assert_user(data, "1")
+        _assert_user(data, "2")
+
+        # update role
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.post(
+                self.create_url("/update/role"), json={"id": "2", "role": "admin"}
+            )
+        assert response.status_code == 200
+        _assert_user([response.json()], "2", role="admin")
+
+        # Get all users
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.get(self.create_url(""))
+        assert response.status_code == 200
+        assert len(response.json()) == 2
+        data = response.json()
+        _assert_user(data, "1")
+        _assert_user(data, "2", role="admin")
+
+        # Get (empty) user settings
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/user/settings"))
+        assert response.status_code == 200
+        assert response.json() is None
+
+        # Update user settings
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.post(
+                self.create_url("/user/settings/update"),
+                json={
+                    "ui": {"attr1": "value1", "attr2": "value2"},
+                    "model_config": {"attr3": "value3", "attr4": "value4"},
+                },
+            )
+        assert response.status_code == 200
+
+        # Get user settings
+        with mock_webui_user(id="2"):
+            response = self.fast_api_client.get(self.create_url("/user/settings"))
+        assert response.status_code == 200
+        assert response.json() == {
+            "ui": {"attr1": "value1", "attr2": "value2"},
+            "model_config": {"attr3": "value3", "attr4": "value4"},
+        }
+
+        # Get (empty) user info
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.get(self.create_url("/user/info"))
+        assert response.status_code == 200
+        assert response.json() is None
+
+        # Update user info
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.post(
+                self.create_url("/user/info/update"),
+                json={"attr1": "value1", "attr2": "value2"},
+            )
+        assert response.status_code == 200
+
+        # Get user info
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.get(self.create_url("/user/info"))
+        assert response.status_code == 200
+        assert response.json() == {"attr1": "value1", "attr2": "value2"}
+
+        # Get user by id
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.get(self.create_url("/2"))
+        assert response.status_code == 200
+        assert response.json() == {"name": "user 2", "profile_image_url": "/user2.png"}
+
+        # Update user by id
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.post(
+                self.create_url("/2/update"),
+                json={
+                    "name": "user 2 updated",
+                    "email": "user2-updated@openwebui.com",
+                    "profile_image_url": "/user2-updated.png",
+                },
+            )
+        assert response.status_code == 200
+
+        # Get all users
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.get(self.create_url(""))
+        assert response.status_code == 200
+        assert len(response.json()) == 2
+        data = response.json()
+        _assert_user(data, "1")
+        _assert_user(
+            data,
+            "2",
+            role="admin",
+            name="user 2 updated",
+            email="user2-updated@openwebui.com",
+            profile_image_url="/user2-updated.png",
+        )
+
+        # Delete user by id
+        with mock_webui_user(id="1"):
+            response = self.fast_api_client.delete(self.create_url("/2"))
+        assert response.status_code == 200
+
+        # Get all users
+        with mock_webui_user(id="3"):
+            response = self.fast_api_client.get(self.create_url(""))
+        assert response.status_code == 200
+        assert len(response.json()) == 1
+        data = response.json()
+        _assert_user(data, "1")
diff --git a/backend/open_webui/test/util/abstract_integration_test.py b/backend/open_webui/test/util/abstract_integration_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..2814731e067dc1bc2ac49ca8b48e1a32441d63ff
--- /dev/null
+++ b/backend/open_webui/test/util/abstract_integration_test.py
@@ -0,0 +1,161 @@
+import logging
+import os
+import time
+
+import docker
+import pytest
+from docker import DockerClient
+from pytest_docker.plugin import get_docker_ip
+from fastapi.testclient import TestClient
+from sqlalchemy import text, create_engine
+
+
+log = logging.getLogger(__name__)
+
+
+def get_fast_api_client():
+    from main import app
+
+    with TestClient(app) as c:
+        return c
+
+
+class AbstractIntegrationTest:
+    BASE_PATH = None
+
+    def create_url(self, path="", query_params=None):
+        if self.BASE_PATH is None:
+            raise Exception("BASE_PATH is not set")
+        parts = self.BASE_PATH.split("/")
+        parts = [part.strip() for part in parts if part.strip() != ""]
+        path_parts = path.split("/")
+        path_parts = [part.strip() for part in path_parts if part.strip() != ""]
+        query_parts = ""
+        if query_params:
+            query_parts = "&".join(
+                [f"{key}={value}" for key, value in query_params.items()]
+            )
+            query_parts = f"?{query_parts}"
+        return "/".join(parts + path_parts) + query_parts
+
+    @classmethod
+    def setup_class(cls):
+        pass
+
+    def setup_method(self):
+        pass
+
+    @classmethod
+    def teardown_class(cls):
+        pass
+
+    def teardown_method(self):
+        pass
+
+
+class AbstractPostgresTest(AbstractIntegrationTest):
+    DOCKER_CONTAINER_NAME = "postgres-test-container-will-get-deleted"
+    docker_client: DockerClient
+
+    @classmethod
+    def _create_db_url(cls, env_vars_postgres: dict) -> str:
+        host = get_docker_ip()
+        user = env_vars_postgres["POSTGRES_USER"]
+        pw = env_vars_postgres["POSTGRES_PASSWORD"]
+        port = 8081
+        db = env_vars_postgres["POSTGRES_DB"]
+        return f"postgresql://{user}:{pw}@{host}:{port}/{db}"
+
+    @classmethod
+    def setup_class(cls):
+        super().setup_class()
+        try:
+            env_vars_postgres = {
+                "POSTGRES_USER": "user",
+                "POSTGRES_PASSWORD": "example",
+                "POSTGRES_DB": "openwebui",
+            }
+            cls.docker_client = docker.from_env()
+            cls.docker_client.containers.run(
+                "postgres:16.2",
+                detach=True,
+                environment=env_vars_postgres,
+                name=cls.DOCKER_CONTAINER_NAME,
+                ports={5432: ("0.0.0.0", 8081)},
+                command="postgres -c log_statement=all",
+            )
+            time.sleep(0.5)
+
+            database_url = cls._create_db_url(env_vars_postgres)
+            os.environ["DATABASE_URL"] = database_url
+            retries = 10
+            db = None
+            while retries > 0:
+                try:
+                    from open_webui.config import OPEN_WEBUI_DIR
+
+                    db = create_engine(database_url, pool_pre_ping=True)
+                    db = db.connect()
+                    log.info("postgres is ready!")
+                    break
+                except Exception as e:
+                    log.warning(e)
+                    time.sleep(3)
+                    retries -= 1
+
+            if db:
+                # import must be after setting env!
+                cls.fast_api_client = get_fast_api_client()
+                db.close()
+            else:
+                raise Exception("Could not connect to Postgres")
+        except Exception as ex:
+            log.error(ex)
+            cls.teardown_class()
+            pytest.fail(f"Could not setup test environment: {ex}")
+
+    def _check_db_connection(self):
+        from open_webui.apps.webui.internal.db import Session
+
+        retries = 10
+        while retries > 0:
+            try:
+                Session.execute(text("SELECT 1"))
+                Session.commit()
+                break
+            except Exception as e:
+                Session.rollback()
+                log.warning(e)
+                time.sleep(3)
+                retries -= 1
+
+    def setup_method(self):
+        super().setup_method()
+        self._check_db_connection()
+
+    @classmethod
+    def teardown_class(cls) -> None:
+        super().teardown_class()
+        cls.docker_client.containers.get(cls.DOCKER_CONTAINER_NAME).remove(force=True)
+
+    def teardown_method(self):
+        from open_webui.apps.webui.internal.db import Session
+
+        # rollback everything not yet committed
+        Session.commit()
+
+        # truncate all tables
+        tables = [
+            "auth",
+            "chat",
+            "chatidtag",
+            "document",
+            "memory",
+            "model",
+            "prompt",
+            "tag",
+            '"user"',
+        ]
+        for table in tables:
+            Session.execute(text(f"TRUNCATE TABLE {table}"))
+        Session.commit()
diff --git a/backend/open_webui/test/util/mock_user.py b/backend/open_webui/test/util/mock_user.py
new file mode 100644
index 0000000000000000000000000000000000000000..96456a2c814fdea1a23f2ce41687d3c25a25eda5
--- /dev/null
+++ b/backend/open_webui/test/util/mock_user.py
@@ -0,0 +1,45 @@
+from contextlib import contextmanager
+
+from fastapi import FastAPI
+
+
+@contextmanager
+def mock_webui_user(**kwargs):
+    from open_webui.apps.webui.main import app
+
+    with mock_user(app, **kwargs):
+        yield
+
+
+@contextmanager
+def mock_user(app: FastAPI, **kwargs):
+    from open_webui.utils.utils import (
+        get_current_user,
+        get_verified_user,
+        get_admin_user,
+        get_current_user_by_api_key,
+    )
+    from open_webui.apps.webui.models.users import User
+
+    def create_user():
+        user_parameters = {
+            "id": "1",
+            "name": "John Doe",
+            "email": "john.doe@openwebui.com",
+            "role": "user",
+            "profile_image_url": "/user.png",
+            "last_active_at": 1627351200,
+            "updated_at": 1627351200,
+            "created_at": 162735120,
+            **kwargs,
+        }
+        return User(**user_parameters)
+
+    app.dependency_overrides = {
+        get_current_user: create_user,
+        get_verified_user: create_user,
+        get_admin_user: create_user,
+        get_current_user_by_api_key: create_user,
+    }
+    yield
+    app.dependency_overrides = {}
diff --git a/backend/open_webui/utils/access_control.py b/backend/open_webui/utils/access_control.py
new file mode 100644
index 0000000000000000000000000000000000000000..270b28bcc250b0eb1b88fa8959ca557d47106f18
--- /dev/null
+++ b/backend/open_webui/utils/access_control.py
@@ -0,0 +1,95 @@
+from typing import Optional, Union, List, Dict, Any
+from open_webui.apps.webui.models.groups import Groups
+import json
+
+
+def get_permissions(
+    user_id: str,
+    default_permissions: Dict[str, Any],
+) -> Dict[str, Any]:
+    """
+    Get all permissions for a user by combining the permissions of all groups the user is a member of.
+    If a permission is defined in multiple groups, the most permissive value is used (True > False).
+    Permissions are nested in a dict with the permission key as the key and a boolean as the value.
+    """
+
+    def combine_permissions(
+        permissions: Dict[str, Any], group_permissions: Dict[str, Any]
+    ) -> Dict[str, Any]:
+        """Combine permissions from multiple groups by taking the most permissive value."""
+        for key, value in group_permissions.items():
+            if isinstance(value, dict):
+                if key not in permissions:
+                    permissions[key] = {}
+                permissions[key] = combine_permissions(permissions[key], value)
+            else:
+                if key not in permissions:
+                    permissions[key] = value
+                else:
+                    permissions[key] = permissions[key] or value
+        return permissions
+
+    user_groups = Groups.get_groups_by_member_id(user_id)
+
+    # deep copy default permissions to avoid modifying the original dict
+    permissions = json.loads(json.dumps(default_permissions))
+
+    for group in user_groups:
+        group_permissions = group.permissions
+        permissions = combine_permissions(permissions, group_permissions)
+
+    return permissions
+
+
+def has_permission(
+    user_id: str,
+    permission_key: str,
+    default_permissions: Dict[str, bool] = {},
+) -> bool:
+    """
+    Check if a user has a specific permission by checking the group permissions
+    and falls back to default permissions if not found in any group.
+
+    Permission keys can be hierarchical and separated by dots ('.').
+    """
+
+    def get_permission(permissions: Dict[str, bool], keys: List[str]) -> bool:
+        """Traverse permissions dict using a list of keys (from dot-split permission_key)."""
+        for key in keys:
+            if key not in permissions:
+                return False  # If any part of the hierarchy is missing, deny access
+            permissions = permissions[key]  # Go one level deeper
+
+        return bool(permissions)  # Return the boolean at the final level
+
+    permission_hierarchy = permission_key.split(".")
+
+    # Retrieve user group permissions
+    user_groups = Groups.get_groups_by_member_id(user_id)
+
+    for group in user_groups:
+        group_permissions = group.permissions
+        if get_permission(group_permissions, permission_hierarchy):
+            return True
+
+    # Check default permissions afterwards if the group permissions don't allow it
+    return get_permission(default_permissions, permission_hierarchy)
+
+
+def has_access(
+    user_id: str,
+    type: str = "write",
+    access_control: Optional[dict] = None,
+) -> bool:
+    if access_control is None:
+        return type == "read"
+
+    user_groups = Groups.get_groups_by_member_id(user_id)
+    user_group_ids = [group.id for group in user_groups]
+    permission_access = access_control.get(type, {})
+    permitted_group_ids = permission_access.get("group_ids", [])
+    permitted_user_ids = permission_access.get("user_ids", [])
+
+    return user_id in permitted_user_ids or any(
+        group_id in permitted_group_ids for group_id in user_group_ids
+    )
diff --git a/backend/open_webui/utils/logo.png b/backend/open_webui/utils/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..519af1db620dbf4de3694660dae7abd7392f0b3c
Binary files /dev/null and b/backend/open_webui/utils/logo.png differ
diff --git a/backend/open_webui/utils/misc.py b/backend/open_webui/utils/misc.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5af492ba3198678eba4c148095ab15e215a5201
--- /dev/null
+++ b/backend/open_webui/utils/misc.py
@@ -0,0 +1,336 @@
+import hashlib
+import re
+import time
+import uuid
+from datetime import timedelta
+from pathlib import Path
+from typing import Callable, Optional
+
+
+def get_messages_content(messages: list[dict]) -> str:
+    return "\n".join(
+        [
+            f"{message['role'].upper()}: {get_content_from_message(message)}"
+            for message in messages
+        ]
+    )
+
+
+def get_last_user_message_item(messages: list[dict]) -> Optional[dict]:
+    for message in reversed(messages):
+        if message["role"] == "user":
+            return message
+    return None
+
+
+def get_content_from_message(message: dict) -> Optional[str]:
+    if isinstance(message["content"], list):
+        for item in message["content"]:
+            if item["type"] == "text":
+                return item["text"]
+    else:
+        return message["content"]
+    return None
+
+
+def get_last_user_message(messages: list[dict]) -> Optional[str]:
+    message = get_last_user_message_item(messages)
+    if message is None:
+        return None
+    return get_content_from_message(message)
+
+
+def get_last_assistant_message(messages: list[dict]) -> Optional[str]:
+    for message in reversed(messages):
+        if message["role"] == "assistant":
+            return get_content_from_message(message)
+    return None
+
+
+def get_system_message(messages: list[dict]) -> Optional[dict]:
+    for message in messages:
+        if message["role"] == "system":
+            return message
+    return None
+
+
+def remove_system_message(messages: list[dict]) -> list[dict]:
+    return [message for message in messages if message["role"] != "system"]
+
+
+def pop_system_message(messages: list[dict]) -> tuple[Optional[dict], list[dict]]:
+    return get_system_message(messages), remove_system_message(messages)
+
+
+def prepend_to_first_user_message_content(
+    content: str, messages: list[dict]
+) -> list[dict]:
+    for message in messages:
+        if message["role"] == "user":
+            if isinstance(message["content"], list):
+                for item in message["content"]:
+                    if item["type"] == "text":
+                        item["text"] = f"{content}\n{item['text']}"
+            else:
+                message["content"] = f"{content}\n{message['content']}"
+            break
+    return messages
+
+
+def add_or_update_system_message(content: str, messages: list[dict]):
+    """
+    Adds a new system message at the beginning of the messages list
+    or updates the existing system message at the beginning.
+
+    :param msg: The message to be added or appended.
+    :param messages: The list of message dictionaries.
+    :return: The updated list of message dictionaries.
+    """
+
+    if messages and messages[0].get("role") == "system":
+        messages[0]["content"] = f"{content}\n{messages[0]['content']}"
+    else:
+        # Insert at the beginning
+        messages.insert(0, {"role": "system", "content": content})
+
+    return messages
+
+
+def openai_chat_message_template(model: str):
+    return {
+        "id": f"{model}-{str(uuid.uuid4())}",
+        "created": int(time.time()),
+        "model": model,
+        "choices": [{"index": 0, "logprobs": None, "finish_reason": None}],
+    }
+
+
+def openai_chat_chunk_message_template(
+    model: str, message: Optional[str] = None
+) -> dict:
+    template = openai_chat_message_template(model)
+    template["object"] = "chat.completion.chunk"
+    if message:
+        template["choices"][0]["delta"] = {"content": message}
+    else:
+        template["choices"][0]["finish_reason"] = "stop"
+    return template
+
+
+def openai_chat_completion_message_template(
+    model: str, message: Optional[str] = None
+) -> dict:
+    template = openai_chat_message_template(model)
+    template["object"] = "chat.completion"
+    if message is not None:
+        template["choices"][0]["message"] = {"content": message, "role": "assistant"}
+    template["choices"][0]["finish_reason"] = "stop"
+    return template
+
+
+def get_gravatar_url(email):
+    # Trim leading and trailing whitespace from
+    # an email address and force all characters
+    # to lower case
+    address = str(email).strip().lower()
+
+    # Create a SHA256 hash of the final string
+    hash_object = hashlib.sha256(address.encode())
+    hash_hex = hash_object.hexdigest()
+
+    # Grab the actual image URL
+    return f"https://www.gravatar.com/avatar/{hash_hex}?d=mp"
+
+
+def calculate_sha256(file):
+    sha256 = hashlib.sha256()
+    # Read the file in chunks to efficiently handle large files
+    for chunk in iter(lambda: file.read(8192), b""):
+        sha256.update(chunk)
+    return sha256.hexdigest()
+
+
+def calculate_sha256_string(string):
+    # Create a new SHA-256 hash object
+    sha256_hash = hashlib.sha256()
+    # Update the hash object with the bytes of the input string
+    sha256_hash.update(string.encode("utf-8"))
+    # Get the hexadecimal representation of the hash
+    hashed_string = sha256_hash.hexdigest()
+    return hashed_string
+
+
+def validate_email_format(email: str) -> bool:
+    if email.endswith("@localhost"):
+        return True
+
+    return bool(re.match(r"[^@]+@[^@]+\.[^@]+", email))
+
+
+def sanitize_filename(file_name):
+    # Convert to lowercase
+    lower_case_file_name = file_name.lower()
+
+    # Remove special characters using regular expression
+    sanitized_file_name = re.sub(r"[^\w\s]", "", lower_case_file_name)
+
+    # Replace spaces with dashes
+    final_file_name = re.sub(r"\s+", "-", sanitized_file_name)
+
+    return final_file_name
+
+
+def extract_folders_after_data_docs(path):
+    # Convert the path to a Path object if it's not already
+    path = Path(path)
+
+    # Extract parts of the path
+    parts = path.parts
+
+    # Find the index of '/data/docs' in the path
+    try:
+        index_data_docs = parts.index("data") + 1
+        index_docs = parts.index("docs", index_data_docs) + 1
+    except ValueError:
+        return []
+
+    # Exclude the filename and accumulate folder names
+    tags = []
+
+    folders = parts[index_docs:-1]
+    for idx, _ in enumerate(folders):
+        tags.append("/".join(folders[: idx + 1]))
+
+    return tags
+
+
+def parse_duration(duration: str) -> Optional[timedelta]:
+    if duration == "-1" or duration == "0":
+        return None
+
+    # Regular expression to find number and unit pairs
+    pattern = r"(-?\d+(\.\d+)?)(ms|s|m|h|d|w)"
+    matches = re.findall(pattern, duration)
+
+    if not matches:
+        raise ValueError("Invalid duration string")
+
+    total_duration = timedelta()
+
+    for number, _, unit in matches:
+        number = float(number)
+        if unit == "ms":
+            total_duration += timedelta(milliseconds=number)
+        elif unit == "s":
+            total_duration += timedelta(seconds=number)
+        elif unit == "m":
+            total_duration += timedelta(minutes=number)
+        elif unit == "h":
+            total_duration += timedelta(hours=number)
+        elif unit == "d":
+            total_duration += timedelta(days=number)
+        elif unit == "w":
+            total_duration += timedelta(weeks=number)
+
+    return total_duration
+
+
+def parse_ollama_modelfile(model_text):
+    parameters_meta = {
+        "mirostat": int,
+        "mirostat_eta": float,
+        "mirostat_tau": float,
+        "num_ctx": int,
+        "repeat_last_n": int,
+        "repeat_penalty": float,
+        "temperature": float,
+        "seed": int,
+        "tfs_z": float,
+        "num_predict": int,
+        "top_k": int,
+        "top_p": float,
+        "num_keep": int,
+        "typical_p": float,
+        "presence_penalty": float,
+        "frequency_penalty": float,
+        "penalize_newline": bool,
+        "numa": bool,
+        "num_batch": int,
+        "num_gpu": int,
+        "main_gpu": int,
+        "low_vram": bool,
+        "f16_kv": bool,
+        "vocab_only": bool,
+        "use_mmap": bool,
+        "use_mlock": bool,
+        "num_thread": int,
+    }
+
+    data = {"base_model_id": None, "params": {}}
+
+    # Parse base model
+    base_model_match = re.search(
+        r"^FROM\s+(\w+)", model_text, re.MULTILINE | re.IGNORECASE
+    )
+    if base_model_match:
+        data["base_model_id"] = base_model_match.group(1)
+
+    # Parse template
+    template_match = re.search(
+        r'TEMPLATE\s+"""(.+?)"""', model_text, re.DOTALL | re.IGNORECASE
+    )
+    if template_match:
+        data["params"] = {"template": template_match.group(1).strip()}
+
+    # Parse stops
+    stops = re.findall(r'PARAMETER stop "(.*?)"', model_text, re.IGNORECASE)
+    if stops:
+        data["params"]["stop"] = stops
+
+    # Parse other parameters from the provided list
+    for param, param_type in parameters_meta.items():
+        param_match = re.search(rf"PARAMETER {param} (.+)", model_text, re.IGNORECASE)
+        if param_match:
+            value = param_match.group(1)
+
+            try:
+                if param_type is int:
+                    value = int(value)
+                elif param_type is float:
+                    value = float(value)
+                elif param_type is bool:
+                    value = value.lower() == "true"
+            except Exception as e:
+                print(e)
+                continue
+
+            data["params"][param] = value
+
+    # Parse adapter
+    adapter_match = re.search(r"ADAPTER (.+)", model_text, re.IGNORECASE)
+    if adapter_match:
+        data["params"]["adapter"] = adapter_match.group(1)
+
+    # Parse system description
+    system_desc_match = re.search(
+        r'SYSTEM\s+"""(.+?)"""', model_text, re.DOTALL | re.IGNORECASE
+    )
+    system_desc_match_single = re.search(
+        r"SYSTEM\s+([^\n]+)", model_text, re.IGNORECASE
+    )
+
+    if system_desc_match:
+        data["params"]["system"] = system_desc_match.group(1).strip()
+    elif system_desc_match_single:
+        data["params"]["system"] = system_desc_match_single.group(1).strip()
+
+    # Parse messages
+    messages = []
+    message_matches = re.findall(r"MESSAGE (\w+) (.+)", model_text, re.IGNORECASE)
+    for role, content in message_matches:
+        messages.append({"role": role, "content": content})
+
+    if messages:
+        data["params"]["messages"] = messages
+
+    return data
diff --git a/backend/open_webui/utils/oauth.py b/backend/open_webui/utils/oauth.py
new file mode 100644
index 0000000000000000000000000000000000000000..722b1ea73cb2e1167d4acc5b0905e9b3cd826ae8
--- /dev/null
+++ b/backend/open_webui/utils/oauth.py
@@ -0,0 +1,261 @@
+import base64
+import logging
+import mimetypes
+import uuid
+
+import aiohttp
+from authlib.integrations.starlette_client import OAuth
+from authlib.oidc.core import UserInfo
+from fastapi import (
+    HTTPException,
+    status,
+)
+from starlette.responses import RedirectResponse
+
+from open_webui.apps.webui.models.auths import Auths
+from open_webui.apps.webui.models.users import Users
+from open_webui.config import (
+    DEFAULT_USER_ROLE,
+    ENABLE_OAUTH_SIGNUP,
+    OAUTH_MERGE_ACCOUNTS_BY_EMAIL,
+    OAUTH_PROVIDERS,
+    ENABLE_OAUTH_ROLE_MANAGEMENT,
+    OAUTH_ROLES_CLAIM,
+    OAUTH_EMAIL_CLAIM,
+    OAUTH_PICTURE_CLAIM,
+    OAUTH_USERNAME_CLAIM,
+    OAUTH_ALLOWED_ROLES,
+    OAUTH_ADMIN_ROLES,
+    WEBHOOK_URL,
+    JWT_EXPIRES_IN,
+    AppConfig,
+)
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import WEBUI_SESSION_COOKIE_SAME_SITE, WEBUI_SESSION_COOKIE_SECURE
+from open_webui.utils.misc import parse_duration
+from open_webui.utils.utils import get_password_hash, create_token
+from open_webui.utils.webhook import post_webhook
+
+log = logging.getLogger(__name__)
+
+auth_manager_config = AppConfig()
+auth_manager_config.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
+auth_manager_config.ENABLE_OAUTH_SIGNUP = ENABLE_OAUTH_SIGNUP
+auth_manager_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL = OAUTH_MERGE_ACCOUNTS_BY_EMAIL
+auth_manager_config.ENABLE_OAUTH_ROLE_MANAGEMENT = ENABLE_OAUTH_ROLE_MANAGEMENT
+auth_manager_config.OAUTH_ROLES_CLAIM = OAUTH_ROLES_CLAIM
+auth_manager_config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
+auth_manager_config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
+auth_manager_config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
+auth_manager_config.OAUTH_ALLOWED_ROLES = OAUTH_ALLOWED_ROLES
+auth_manager_config.OAUTH_ADMIN_ROLES = OAUTH_ADMIN_ROLES
+auth_manager_config.WEBHOOK_URL = WEBHOOK_URL
+auth_manager_config.JWT_EXPIRES_IN = JWT_EXPIRES_IN
+
+
+class OAuthManager:
+    def __init__(self):
+        self.oauth = OAuth()
+        for provider_name, provider_config in OAUTH_PROVIDERS.items():
+            self.oauth.register(
+                name=provider_name,
+                client_id=provider_config["client_id"],
+                client_secret=provider_config["client_secret"],
+                server_metadata_url=provider_config["server_metadata_url"],
+                client_kwargs={
+                    "scope": provider_config["scope"],
+                },
+                redirect_uri=provider_config["redirect_uri"],
+            )
+
+    def get_client(self, provider_name):
+        return self.oauth.create_client(provider_name)
+
+    def get_user_role(self, user, user_data):
+        if user and Users.get_num_users() == 1:
+            # If the user is the only user, assign the role "admin" - actually repairs role for single user on login
+            return "admin"
+        if not user and Users.get_num_users() == 0:
+            # If there are no users, assign the role "admin", as the first user will be an admin
+            return "admin"
+
+        if auth_manager_config.ENABLE_OAUTH_ROLE_MANAGEMENT:
+            oauth_claim = auth_manager_config.OAUTH_ROLES_CLAIM
+            oauth_allowed_roles = auth_manager_config.OAUTH_ALLOWED_ROLES
+            oauth_admin_roles = auth_manager_config.OAUTH_ADMIN_ROLES
+            oauth_roles = None
+            role = "pending"  # Default/fallback role if no matching roles are found
+
+            # Next block extracts the roles from the user data, accepting nested claims of any depth
+            if oauth_claim and oauth_allowed_roles and oauth_admin_roles:
+                claim_data = user_data
+                nested_claims = oauth_claim.split(".")
+                for nested_claim in nested_claims:
+                    claim_data = claim_data.get(nested_claim, {})
+                oauth_roles = claim_data if isinstance(claim_data, list) else None
+
+            # If any roles are found, check if they match the allowed or admin roles
+            if oauth_roles:
+                # If role management is enabled, and matching roles are provided, use the roles
+                for allowed_role in oauth_allowed_roles:
+                    # If the user has any of the allowed roles, assign the role "user"
+                    if allowed_role in oauth_roles:
+                        role = "user"
+                        break
+                for admin_role in oauth_admin_roles:
+                    # If the user has any of the admin roles, assign the role "admin"
+                    if admin_role in oauth_roles:
+                        role = "admin"
+                        break
+        else:
+            if not user:
+                # If role management is disabled, use the default role for new users
+                role = auth_manager_config.DEFAULT_USER_ROLE
+            else:
+                # If role management is disabled, use the existing role for existing users
+                role = user.role
+
+        return role
+
+    async def handle_login(self, provider, request):
+        if provider not in OAUTH_PROVIDERS:
+            raise HTTPException(404)
+        # If the provider has a custom redirect URL, use that, otherwise automatically generate one
+        redirect_uri = OAUTH_PROVIDERS[provider].get("redirect_uri") or request.url_for(
+            "oauth_callback", provider=provider
+        )
+        client = self.get_client(provider)
+        if client is None:
+            raise HTTPException(404)
+        return await client.authorize_redirect(request, redirect_uri)
+
+    async def handle_callback(self, provider, request, response):
+        if provider not in OAUTH_PROVIDERS:
+            raise HTTPException(404)
+        client = self.get_client(provider)
+        try:
+            token = await client.authorize_access_token(request)
+        except Exception as e:
+            log.warning(f"OAuth callback error: {e}")
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+        user_data: UserInfo = token["userinfo"]
+        if not user_data:
+            user_data: UserInfo = await client.userinfo(token=token)
+        if not user_data:
+            log.warning(f"OAuth callback failed, user data is missing: {token}")
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+
+        sub = user_data.get("sub")
+        if not sub:
+            log.warning(f"OAuth callback failed, sub is missing: {user_data}")
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+        provider_sub = f"{provider}@{sub}"
+        email_claim = auth_manager_config.OAUTH_EMAIL_CLAIM
+        email = user_data.get(email_claim, "").lower()
+        # We currently mandate that email addresses are provided
+        if not email:
+            log.warning(f"OAuth callback failed, email is missing: {user_data}")
+            raise HTTPException(400, detail=ERROR_MESSAGES.INVALID_CRED)
+
+        # Check if the user exists
+        user = Users.get_user_by_oauth_sub(provider_sub)
+
+        if not user:
+            # If the user does not exist, check if merging is enabled
+            if auth_manager_config.OAUTH_MERGE_ACCOUNTS_BY_EMAIL:
+                # Check if the user exists by email
+                user = Users.get_user_by_email(email)
+                if user:
+                    # Update the user with the new oauth sub
+                    Users.update_user_oauth_sub_by_id(user.id, provider_sub)
+
+        if user:
+            determined_role = self.get_user_role(user, user_data)
+            if user.role != determined_role:
+                Users.update_user_role_by_id(user.id, determined_role)
+
+        if not user:
+            # If the user does not exist, check if signups are enabled
+            if auth_manager_config.ENABLE_OAUTH_SIGNUP:
+                # Check if an existing user with the same email already exists
+                existing_user = Users.get_user_by_email(
+                    user_data.get("email", "").lower()
+                )
+                if existing_user:
+                    raise HTTPException(400, detail=ERROR_MESSAGES.EMAIL_TAKEN)
+
+                picture_claim = auth_manager_config.OAUTH_PICTURE_CLAIM
+                picture_url = user_data.get(picture_claim, "")
+                if picture_url:
+                    # Download the profile image into a base64 string
+                    try:
+                        async with aiohttp.ClientSession() as session:
+                            async with session.get(picture_url) as resp:
+                                picture = await resp.read()
+                                base64_encoded_picture = base64.b64encode(
+                                    picture
+                                ).decode("utf-8")
+                                guessed_mime_type = mimetypes.guess_type(picture_url)[0]
+                                if guessed_mime_type is None:
+                                    # assume JPG, browsers are tolerant enough of image formats
+                                    guessed_mime_type = "image/jpeg"
+                                picture_url = f"data:{guessed_mime_type};base64,{base64_encoded_picture}"
+                    except Exception as e:
+                        log.error(
+                            f"Error downloading profile image '{picture_url}': {e}"
+                        )
+                        picture_url = ""
+                if not picture_url:
+                    picture_url = "/user.png"
+                username_claim = auth_manager_config.OAUTH_USERNAME_CLAIM
+
+                role = self.get_user_role(None, user_data)
+
+                user = Auths.insert_new_auth(
+                    email=email,
+                    password=get_password_hash(
+                        str(uuid.uuid4())
+                    ),  # Random password, not used
+                    name=user_data.get(username_claim, "User"),
+                    profile_image_url=picture_url,
+                    role=role,
+                    oauth_sub=provider_sub,
+                )
+
+                if auth_manager_config.WEBHOOK_URL:
+                    post_webhook(
+                        auth_manager_config.WEBHOOK_URL,
+                        auth_manager_config.WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
+                        {
+                            "action": "signup",
+                            "message": auth_manager_config.WEBHOOK_MESSAGES.USER_SIGNUP(
+                                user.name
+                            ),
+                            "user": user.model_dump_json(exclude_none=True),
+                        },
+                    )
+            else:
+                raise HTTPException(
+                    status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACCESS_PROHIBITED
+                )
+
+        jwt_token = create_token(
+            data={"id": user.id},
+            expires_delta=parse_duration(auth_manager_config.JWT_EXPIRES_IN),
+        )
+
+        # Set the cookie token
+        response.set_cookie(
+            key="token",
+            value=jwt_token,
+            httponly=True,  # Ensures the cookie is not accessible via JavaScript
+            samesite=WEBUI_SESSION_COOKIE_SAME_SITE,
+            secure=WEBUI_SESSION_COOKIE_SECURE,
+        )
+
+        # Redirect back to the frontend with the JWT token
+        redirect_url = f"{request.base_url}auth#token={jwt_token}"
+        return RedirectResponse(url=redirect_url)
+
+
+oauth_manager = OAuthManager()
diff --git a/backend/open_webui/utils/payload.py b/backend/open_webui/utils/payload.py
new file mode 100644
index 0000000000000000000000000000000000000000..04e3a98c408ee52bbd68e3f9778e5851814e31d5
--- /dev/null
+++ b/backend/open_webui/utils/payload.py
@@ -0,0 +1,183 @@
+from open_webui.utils.task import prompt_template
+from open_webui.utils.misc import (
+    add_or_update_system_message,
+)
+
+from typing import Callable, Optional
+
+
+# inplace function: form_data is modified
+def apply_model_system_prompt_to_body(params: dict, form_data: dict, user) -> dict:
+    system = params.get("system", None)
+    if not system:
+        return form_data
+
+    if user:
+        template_params = {
+            "user_name": user.name,
+            "user_location": user.info.get("location") if user.info else None,
+        }
+    else:
+        template_params = {}
+    system = prompt_template(system, **template_params)
+    form_data["messages"] = add_or_update_system_message(
+        system, form_data.get("messages", [])
+    )
+    return form_data
+
+
+# inplace function: form_data is modified
+def apply_model_params_to_body(
+    params: dict, form_data: dict, mappings: dict[str, Callable]
+) -> dict:
+    if not params:
+        return form_data
+
+    for key, cast_func in mappings.items():
+        if (value := params.get(key)) is not None:
+            form_data[key] = cast_func(value)
+
+    return form_data
+
+
+# inplace function: form_data is modified
+def apply_model_params_to_body_openai(params: dict, form_data: dict) -> dict:
+    mappings = {
+        "temperature": float,
+        "top_p": float,
+        "max_tokens": int,
+        "frequency_penalty": float,
+        "seed": lambda x: x,
+        "stop": lambda x: [bytes(s, "utf-8").decode("unicode_escape") for s in x],
+    }
+    return apply_model_params_to_body(params, form_data, mappings)
+
+
+def apply_model_params_to_body_ollama(params: dict, form_data: dict) -> dict:
+    opts = [
+        "temperature",
+        "top_p",
+        "seed",
+        "mirostat",
+        "mirostat_eta",
+        "mirostat_tau",
+        "num_ctx",
+        "num_batch",
+        "num_keep",
+        "repeat_last_n",
+        "tfs_z",
+        "top_k",
+        "min_p",
+        "use_mmap",
+        "use_mlock",
+        "num_thread",
+        "num_gpu",
+    ]
+    mappings = {i: lambda x: x for i in opts}
+    form_data = apply_model_params_to_body(params, form_data, mappings)
+
+    name_differences = {
+        "max_tokens": "num_predict",
+        "frequency_penalty": "repeat_penalty",
+    }
+
+    for key, value in name_differences.items():
+        if (param := params.get(key, None)) is not None:
+            form_data[value] = param
+
+    return form_data
+
+
+def convert_messages_openai_to_ollama(messages: list[dict]) -> list[dict]:
+    ollama_messages = []
+
+    for message in messages:
+        # Initialize the new message structure with the role
+        new_message = {"role": message["role"]}
+
+        content = message.get("content", [])
+
+        # Check if the content is a string (just a simple message)
+        if isinstance(content, str):
+            # If the content is a string, it's pure text
+            new_message["content"] = content
+        else:
+            # Otherwise, assume the content is a list of dicts, e.g., text followed by an image URL
+            content_text = ""
+            images = []
+
+            # Iterate through the list of content items
+            for item in content:
+                # Check if it's a text type
+                if item.get("type") == "text":
+                    content_text += item.get("text", "")
+
+                # Check if it's an image URL type
+                elif item.get("type") == "image_url":
+                    img_url = item.get("image_url", {}).get("url", "")
+                    if img_url:
+                        # If the image url starts with data:, it's a base64 image and should be trimmed
+                        if img_url.startswith("data:"):
+                            img_url = img_url.split(",")[-1]
+                        images.append(img_url)
+
+            # Add content text (if any)
+            if content_text:
+                new_message["content"] = content_text.strip()
+
+            # Add images (if any)
+            if images:
+                new_message["images"] = images
+
+        # Append the new formatted message to the result
+        ollama_messages.append(new_message)
+
+    return ollama_messages
+
+
+def convert_payload_openai_to_ollama(openai_payload: dict) -> dict:
+    """
+    Converts a payload formatted for OpenAI's API to be compatible with Ollama's API endpoint for chat completions.
+
+    Args:
+        openai_payload (dict): The payload originally designed for OpenAI API usage.
+
+    Returns:
+        dict: A modified payload compatible with the Ollama API.
+    """
+    ollama_payload = {}
+
+    # Mapping basic model and message details
+    ollama_payload["model"] = openai_payload.get("model")
+    ollama_payload["messages"] = convert_messages_openai_to_ollama(
+        openai_payload.get("messages")
+    )
+    ollama_payload["stream"] = openai_payload.get("stream", False)
+
+    # If there are advanced parameters in the payload, format them in Ollama's options field
+    ollama_options = {}
+
+    # Handle parameters which map directly
+    for param in ["temperature", "top_p", "seed"]:
+        if param in openai_payload:
+            ollama_options[param] = openai_payload[param]
+
+    # Mapping OpenAI's `max_tokens` -> Ollama's `num_predict`
+    if "max_completion_tokens" in openai_payload:
+        ollama_options["num_predict"] = openai_payload["max_completion_tokens"]
+    elif "max_tokens" in openai_payload:
+        ollama_options["num_predict"] = openai_payload["max_tokens"]
+
+    # Handle frequency / presence_penalty, which needs renaming and checking
+    if "frequency_penalty" in openai_payload:
+        ollama_options["repeat_penalty"] = openai_payload["frequency_penalty"]
+
+    if "presence_penalty" in openai_payload and "penalty" not in ollama_options:
+        # We are assuming presence penalty uses a similar concept in Ollama, which needs custom handling if exists.
+        ollama_options["new_topic_penalty"] = openai_payload["presence_penalty"]
+
+    # Add options to payload if any have been set
+    if ollama_options:
+        ollama_payload["options"] = ollama_options
+
+    return ollama_payload
diff --git a/backend/open_webui/utils/pdf_generator.py b/backend/open_webui/utils/pdf_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb6cd57d532db92e911dbfa593000003c89105cb
--- /dev/null
+++ b/backend/open_webui/utils/pdf_generator.py
@@ -0,0 +1,139 @@
+from datetime import datetime
+from io import BytesIO
+from pathlib import Path
+from typing import Dict, Any, List
+
+from markdown import markdown
+
+import site
+from fpdf import FPDF
+
+from open_webui.env import STATIC_DIR, FONTS_DIR
+from open_webui.apps.webui.models.chats import ChatTitleMessagesForm
+
+
+class PDFGenerator:
+    """
+    Description:
+    The `PDFGenerator` class is designed to create PDF documents from chat messages.
+    The process involves transforming markdown content into HTML and then into a PDF format
+
+    Attributes:
+    - `form_data`: An instance of `ChatTitleMessagesForm` containing title and messages.
+
+    """
+
+    def __init__(self, form_data: ChatTitleMessagesForm):
+        self.html_body = None
+        self.messages_html = None
+        self.form_data = form_data
+
+        self.css = Path(STATIC_DIR / "assets" / "pdf-style.css").read_text()
+
+    def format_timestamp(self, timestamp: float) -> str:
+        """Convert a UNIX timestamp to a formatted date string."""
+        try:
+            date_time = datetime.fromtimestamp(timestamp)
+            return date_time.strftime("%Y-%m-%d, %H:%M:%S")
+        except (ValueError, TypeError) as e:
+            # Log the error if necessary
+            return ""
+
+    def _build_html_message(self, message: Dict[str, Any]) -> str:
+        """Build HTML for a single message."""
+        role = message.get("role", "user")
+        content = message.get("content", "")
+        timestamp = message.get("timestamp")
+
+        model = message.get("model") if role == "assistant" else ""
+
+        date_str = self.format_timestamp(timestamp) if timestamp else ""
+
+        # extends pymdownx extension to convert markdown to html.
+        # - https://facelessuser.github.io/pymdown-extensions/usage_notes/
+        html_content = markdown(content, extensions=["pymdownx.extra"])
+
+        html_message = f"""
+            <div> {date_str} </div>
+            <div class="message">
+                <div>
+                    <h2>
+                        <strong>{role.title()}</strong>
+                        <span style="font-size: 12px; color: #888;">{model}</span>
+                    </h2>
+                </div>
+                <pre class="markdown-section">
+                    {content}
+                </pre>
+            </div>
+          """
+        return html_message
+
+    def _generate_html_body(self) -> str:
+        """Generate the full HTML body for the PDF."""
+        return f"""
+        <html>
+            <head>
+                <meta charset="UTF-8">
+                <meta name="viewport" content="width=device-width, initial-scale=1.0">
+            </head>
+            <body>
+                <div class="container"> 
+                    <div class="text-center">
+                        <h1>{self.form_data.title}</h1>
+                    </div>
+                    <div>
+                        {self.messages_html}
+                    </div>
+                </div>
+            </body>
+        </html>
+        """
+
+    def generate_chat_pdf(self) -> bytes:
+        """
+        Generate a PDF from chat messages.
+        """
+        try:
+            global FONTS_DIR
+
+            pdf = FPDF()
+            pdf.add_page()
+
+            # When running using `pip install` the static directory is in the site packages.
+            if not FONTS_DIR.exists():
+                FONTS_DIR = Path(site.getsitepackages()[0]) / "static/fonts"
+            # When running using `pip install -e .` the static directory is in the site packages.
+            # This path only works if `open-webui serve` is run from the root of this project.
+            if not FONTS_DIR.exists():
+                FONTS_DIR = Path("./backend/static/fonts")
+
+            pdf.add_font("NotoSans", "", f"{FONTS_DIR}/NotoSans-Regular.ttf")
+            pdf.add_font("NotoSans", "b", f"{FONTS_DIR}/NotoSans-Bold.ttf")
+            pdf.add_font("NotoSans", "i", f"{FONTS_DIR}/NotoSans-Italic.ttf")
+            pdf.add_font("NotoSansKR", "", f"{FONTS_DIR}/NotoSansKR-Regular.ttf")
+            pdf.add_font("NotoSansJP", "", f"{FONTS_DIR}/NotoSansJP-Regular.ttf")
+            pdf.add_font("NotoSansSC", "", f"{FONTS_DIR}/NotoSansSC-Regular.ttf")
+
+            pdf.set_font("NotoSans", size=12)
+            pdf.set_fallback_fonts(["NotoSansKR", "NotoSansJP", "NotoSansSC"])
+
+            pdf.set_auto_page_break(auto=True, margin=15)
+
+            # Build HTML messages
+            messages_html_list: List[str] = [
+                self._build_html_message(msg) for msg in self.form_data.messages
+            ]
+            self.messages_html = "<div>" + "".join(messages_html_list) + "</div>"
+
+            # Generate full HTML body
+            self.html_body = self._generate_html_body()
+
+            pdf.write_html(self.html_body)
+
+            # Save the pdf with name .pdf
+            pdf_bytes = pdf.output()
+
+            return bytes(pdf_bytes)
+        except Exception as e:
+            raise e
diff --git a/backend/open_webui/utils/response.py b/backend/open_webui/utils/response.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8501e92cc1b5afe410b5b66adfc5caa35fde9bd
--- /dev/null
+++ b/backend/open_webui/utils/response.py
@@ -0,0 +1,31 @@
+import json
+from open_webui.utils.misc import (
+    openai_chat_chunk_message_template,
+    openai_chat_completion_message_template,
+)
+
+
+def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
+    model = ollama_response.get("model", "ollama")
+    message_content = ollama_response.get("message", {}).get("content", "")
+
+    response = openai_chat_completion_message_template(model, message_content)
+    return response
+
+
+async def convert_streaming_response_ollama_to_openai(ollama_streaming_response):
+    async for data in ollama_streaming_response.body_iterator:
+        data = json.loads(data)
+
+        model = data.get("model", "ollama")
+        message_content = data.get("message", {}).get("content", "")
+        done = data.get("done", False)
+
+        data = openai_chat_chunk_message_template(
+            model, message_content if not done else None
+        )
+
+        line = f"data: {json.dumps(data)}\n\n"
+        yield line
+
+    yield "data: [DONE]\n\n"
diff --git a/backend/open_webui/utils/security_headers.py b/backend/open_webui/utils/security_headers.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbcf7d697799aa576d686d1e1d93f65beb93425d
--- /dev/null
+++ b/backend/open_webui/utils/security_headers.py
@@ -0,0 +1,133 @@
+import re
+import os
+
+from fastapi import Request
+from starlette.middleware.base import BaseHTTPMiddleware
+from typing import Dict
+
+
+class SecurityHeadersMiddleware(BaseHTTPMiddleware):
+    async def dispatch(self, request: Request, call_next):
+        response = await call_next(request)
+        response.headers.update(set_security_headers())
+        return response
+
+
+def set_security_headers() -> Dict[str, str]:
+    """
+    Sets security headers based on environment variables.
+
+    This function reads specific environment variables and uses their values
+    to set corresponding security headers. The headers that can be set are:
+    - cache-control
+    - permissions-policy
+    - strict-transport-security
+    - referrer-policy
+    - x-content-type-options
+    - x-download-options
+    - x-frame-options
+    - x-permitted-cross-domain-policies
+    - content-security-policy
+
+    Each environment variable is associated with a specific setter function
+    that constructs the header. If the environment variable is set, the
+    corresponding header is added to the options dictionary.
+
+    Returns:
+        dict: A dictionary containing the security headers and their values.
+    """
+    options = {}
+    header_setters = {
+        "CACHE_CONTROL": set_cache_control,
+        "HSTS": set_hsts,
+        "PERMISSIONS_POLICY": set_permissions_policy,
+        "REFERRER_POLICY": set_referrer,
+        "XCONTENT_TYPE": set_xcontent_type,
+        "XDOWNLOAD_OPTIONS": set_xdownload_options,
+        "XFRAME_OPTIONS": set_xframe,
+        "XPERMITTED_CROSS_DOMAIN_POLICIES": set_xpermitted_cross_domain_policies,
+        "CONTENT_SECURITY_POLICY": set_content_security_policy,
+    }
+
+    for env_var, setter in header_setters.items():
+        value = os.environ.get(env_var, None)
+        if value:
+            header = setter(value)
+            if header:
+                options.update(header)
+
+    return options
+
+
+# Set HTTP Strict Transport Security(HSTS) response header
+def set_hsts(value: str):
+    pattern = r"^max-age=(\d+)(;includeSubDomains)?(;preload)?$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "max-age=31536000;includeSubDomains"
+    return {"Strict-Transport-Security": value}
+
+
+# Set X-Frame-Options response header
+def set_xframe(value: str):
+    pattern = r"^(DENY|SAMEORIGIN)$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "DENY"
+    return {"X-Frame-Options": value}
+
+
+# Set Permissions-Policy response header
+def set_permissions_policy(value: str):
+    pattern = r"^(?:(accelerometer|autoplay|camera|clipboard-read|clipboard-write|fullscreen|geolocation|gyroscope|magnetometer|microphone|midi|payment|picture-in-picture|sync-xhr|usb|xr-spatial-tracking)=\((self)?\),?)*$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "none"
+    return {"Permissions-Policy": value}
+
+
+# Set Referrer-Policy response header
+def set_referrer(value: str):
+    pattern = r"^(no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|same-origin|strict-origin|strict-origin-when-cross-origin|unsafe-url)$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "no-referrer"
+    return {"Referrer-Policy": value}
+
+
+# Set Cache-Control response header
+def set_cache_control(value: str):
+    pattern = r"^(public|private|no-cache|no-store|must-revalidate|proxy-revalidate|max-age=\d+|s-maxage=\d+|no-transform|immutable)(,\s*(public|private|no-cache|no-store|must-revalidate|proxy-revalidate|max-age=\d+|s-maxage=\d+|no-transform|immutable))*$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "no-store, max-age=0"
+
+    return {"Cache-Control": value}
+
+
+# Set X-Download-Options response header
+def set_xdownload_options(value: str):
+    if value != "noopen":
+        value = "noopen"
+    return {"X-Download-Options": value}
+
+
+# Set X-Content-Type-Options response header
+def set_xcontent_type(value: str):
+    if value != "nosniff":
+        value = "nosniff"
+    return {"X-Content-Type-Options": value}
+
+
+# Set X-Permitted-Cross-Domain-Policies response header
+def set_xpermitted_cross_domain_policies(value: str):
+    pattern = r"^(none|master-only|by-content-type|by-ftp-filename)$"
+    match = re.match(pattern, value, re.IGNORECASE)
+    if not match:
+        value = "none"
+    return {"X-Permitted-Cross-Domain-Policies": value}
+
+
+# Set Content-Security-Policy response header
+def set_content_security_policy(value: str):
+    return {"Content-Security-Policy": value}
diff --git a/backend/open_webui/utils/task.py b/backend/open_webui/utils/task.py
new file mode 100644
index 0000000000000000000000000000000000000000..604161a3188e88e0bdf7bacd67ceaed2bccb3114
--- /dev/null
+++ b/backend/open_webui/utils/task.py
@@ -0,0 +1,299 @@
+import logging
+import math
+import re
+from datetime import datetime
+from typing import Optional
+import uuid
+
+
+from open_webui.utils.misc import get_last_user_message, get_messages_content
+
+from open_webui.env import SRC_LOG_LEVELS
+from open_webui.config import DEFAULT_RAG_TEMPLATE
+
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["RAG"])
+
+
+def prompt_template(
+    template: str, user_name: Optional[str] = None, user_location: Optional[str] = None
+) -> str:
+    # Get the current date
+    current_date = datetime.now()
+
+    # Format the date to YYYY-MM-DD
+    formatted_date = current_date.strftime("%Y-%m-%d")
+    formatted_time = current_date.strftime("%I:%M:%S %p")
+    formatted_weekday = current_date.strftime("%A")
+
+    template = template.replace("{{CURRENT_DATE}}", formatted_date)
+    template = template.replace("{{CURRENT_TIME}}", formatted_time)
+    template = template.replace(
+        "{{CURRENT_DATETIME}}", f"{formatted_date} {formatted_time}"
+    )
+    template = template.replace("{{CURRENT_WEEKDAY}}", formatted_weekday)
+
+    if user_name:
+        # Replace {{USER_NAME}} in the template with the user's name
+        template = template.replace("{{USER_NAME}}", user_name)
+    else:
+        # Replace {{USER_NAME}} in the template with "Unknown"
+        template = template.replace("{{USER_NAME}}", "Unknown")
+
+    if user_location:
+        # Replace {{USER_LOCATION}} in the template with the current location
+        template = template.replace("{{USER_LOCATION}}", user_location)
+    else:
+        # Replace {{USER_LOCATION}} in the template with "Unknown"
+        template = template.replace("{{USER_LOCATION}}", "Unknown")
+
+    return template
+
+
+def replace_prompt_variable(template: str, prompt: str) -> str:
+    def replacement_function(match):
+        full_match = match.group(
+            0
+        ).lower()  # Normalize to lowercase for consistent handling
+        start_length = match.group(1)
+        end_length = match.group(2)
+        middle_length = match.group(3)
+
+        if full_match == "{{prompt}}":
+            return prompt
+        elif start_length is not None:
+            return prompt[: int(start_length)]
+        elif end_length is not None:
+            return prompt[-int(end_length) :]
+        elif middle_length is not None:
+            middle_length = int(middle_length)
+            if len(prompt) <= middle_length:
+                return prompt
+            start = prompt[: math.ceil(middle_length / 2)]
+            end = prompt[-math.floor(middle_length / 2) :]
+            return f"{start}...{end}"
+        return ""
+
+    # Updated regex pattern to make it case-insensitive with the `(?i)` flag
+    pattern = r"(?i){{prompt}}|{{prompt:start:(\d+)}}|{{prompt:end:(\d+)}}|{{prompt:middletruncate:(\d+)}}"
+    template = re.sub(pattern, replacement_function, template)
+    return template
+
+
+def replace_messages_variable(
+    template: str, messages: Optional[list[str]] = None
+) -> str:
+    def replacement_function(match):
+        full_match = match.group(0)
+        start_length = match.group(1)
+        end_length = match.group(2)
+        middle_length = match.group(3)
+        # If messages is None, handle it as an empty list
+        if messages is None:
+            return ""
+
+        # Process messages based on the number of messages required
+        if full_match == "{{MESSAGES}}":
+            return get_messages_content(messages)
+        elif start_length is not None:
+            return get_messages_content(messages[: int(start_length)])
+        elif end_length is not None:
+            return get_messages_content(messages[-int(end_length) :])
+        elif middle_length is not None:
+            mid = int(middle_length)
+
+            if len(messages) <= mid:
+                return get_messages_content(messages)
+            # Handle middle truncation: split to get start and end portions of the messages list
+            half = mid // 2
+            start_msgs = messages[:half]
+            end_msgs = messages[-half:] if mid % 2 == 0 else messages[-(half + 1) :]
+            formatted_start = get_messages_content(start_msgs)
+            formatted_end = get_messages_content(end_msgs)
+            return f"{formatted_start}\n{formatted_end}"
+        return ""
+
+    template = re.sub(
+        r"{{MESSAGES}}|{{MESSAGES:START:(\d+)}}|{{MESSAGES:END:(\d+)}}|{{MESSAGES:MIDDLETRUNCATE:(\d+)}}",
+        replacement_function,
+        template,
+    )
+
+    return template
+
+
+# {{prompt:middletruncate:8000}}
+
+
+def rag_template(template: str, context: str, query: str):
+    if template.strip() == "":
+        template = DEFAULT_RAG_TEMPLATE
+
+    if "[context]" not in template and "{{CONTEXT}}" not in template:
+        log.debug(
+            "WARNING: The RAG template does not contain the '[context]' or '{{CONTEXT}}' placeholder."
+        )
+
+    if "<context>" in context and "</context>" in context:
+        log.debug(
+            "WARNING: Potential prompt injection attack: the RAG "
+            "context contains '<context>' and '</context>'. This might be "
+            "nothing, or the user might be trying to hack something."
+        )
+
+    query_placeholders = []
+    if "[query]" in context:
+        query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
+        template = template.replace("[query]", query_placeholder)
+        query_placeholders.append(query_placeholder)
+
+    if "{{QUERY}}" in context:
+        query_placeholder = "{{QUERY" + str(uuid.uuid4()) + "}}"
+        template = template.replace("{{QUERY}}", query_placeholder)
+        query_placeholders.append(query_placeholder)
+
+    template = template.replace("[context]", context)
+    template = template.replace("{{CONTEXT}}", context)
+    template = template.replace("[query]", query)
+    template = template.replace("{{QUERY}}", query)
+
+    for query_placeholder in query_placeholders:
+        template = template.replace(query_placeholder, query)
+
+    return template
+
+
+def title_generation_template(
+    template: str, messages: list[dict], user: Optional[dict] = None
+) -> str:
+    prompt = get_last_user_message(messages)
+    template = replace_prompt_variable(template, prompt)
+    template = replace_messages_variable(template, messages)
+
+    template = prompt_template(
+        template,
+        **(
+            {"user_name": user.get("name"), "user_location": user.get("location")}
+            if user
+            else {}
+        ),
+    )
+
+    return template
+
+
+def tags_generation_template(
+    template: str, messages: list[dict], user: Optional[dict] = None
+) -> str:
+    prompt = get_last_user_message(messages)
+    template = replace_prompt_variable(template, prompt)
+    template = replace_messages_variable(template, messages)
+
+    template = prompt_template(
+        template,
+        **(
+            {"user_name": user.get("name"), "user_location": user.get("location")}
+            if user
+            else {}
+        ),
+    )
+    return template
+
+
+def emoji_generation_template(
+    template: str, prompt: str, user: Optional[dict] = None
+) -> str:
+    template = replace_prompt_variable(template, prompt)
+    template = prompt_template(
+        template,
+        **(
+            {"user_name": user.get("name"), "user_location": user.get("location")}
+            if user
+            else {}
+        ),
+    )
+
+    return template
+
+
+def autocomplete_generation_template(
+    template: str,
+    prompt: str,
+    messages: Optional[list[dict]] = None,
+    type: Optional[str] = None,
+    user: Optional[dict] = None,
+) -> str:
+    template = template.replace("{{TYPE}}", type if type else "")
+    template = replace_prompt_variable(template, prompt)
+    template = replace_messages_variable(template, messages)
+
+    template = prompt_template(
+        template,
+        **(
+            {"user_name": user.get("name"), "user_location": user.get("location")}
+            if user
+            else {}
+        ),
+    )
+    return template
+
+
+def query_generation_template(
+    template: str, messages: list[dict], user: Optional[dict] = None
+) -> str:
+    prompt = get_last_user_message(messages)
+    template = replace_prompt_variable(template, prompt)
+    template = replace_messages_variable(template, messages)
+
+    template = prompt_template(
+        template,
+        **(
+            {"user_name": user.get("name"), "user_location": user.get("location")}
+            if user
+            else {}
+        ),
+    )
+    return template
+
+
+def moa_response_generation_template(
+    template: str, prompt: str, responses: list[str]
+) -> str:
+    def replacement_function(match):
+        full_match = match.group(0)
+        start_length = match.group(1)
+        end_length = match.group(2)
+        middle_length = match.group(3)
+
+        if full_match == "{{prompt}}":
+            return prompt
+        elif start_length is not None:
+            return prompt[: int(start_length)]
+        elif end_length is not None:
+            return prompt[-int(end_length) :]
+        elif middle_length is not None:
+            middle_length = int(middle_length)
+            if len(prompt) <= middle_length:
+                return prompt
+            start = prompt[: math.ceil(middle_length / 2)]
+            end = prompt[-math.floor(middle_length / 2) :]
+            return f"{start}...{end}"
+        return ""
+
+    template = re.sub(
+        r"{{prompt}}|{{prompt:start:(\d+)}}|{{prompt:end:(\d+)}}|{{prompt:middletruncate:(\d+)}}",
+        replacement_function,
+        template,
+    )
+
+    responses = [f'"""{response}"""' for response in responses]
+    responses = "\n\n".join(responses)
+
+    template = template.replace("{{responses}}", responses)
+    return template
+
+
+def tools_function_calling_generation_template(template: str, tools_specs: str) -> str:
+    template = template.replace("{{TOOLS}}", tools_specs)
+    return template
diff --git a/backend/open_webui/utils/tools.py b/backend/open_webui/utils/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..60a9f942f6472eb197cffe4ed43aff136f9d9c82
--- /dev/null
+++ b/backend/open_webui/utils/tools.py
@@ -0,0 +1,198 @@
+import inspect
+import logging
+import re
+from typing import Any, Awaitable, Callable, get_type_hints
+from functools import update_wrapper, partial
+
+from langchain_core.utils.function_calling import convert_to_openai_function
+from open_webui.apps.webui.models.tools import Tools
+from open_webui.apps.webui.models.users import UserModel
+from open_webui.apps.webui.utils import load_tools_module_by_id
+from pydantic import BaseModel, Field, create_model
+
+log = logging.getLogger(__name__)
+
+
+def apply_extra_params_to_tool_function(
+    function: Callable, extra_params: dict
+) -> Callable[..., Awaitable]:
+    sig = inspect.signature(function)
+    extra_params = {k: v for k, v in extra_params.items() if k in sig.parameters}
+    partial_func = partial(function, **extra_params)
+    if inspect.iscoroutinefunction(function):
+        update_wrapper(partial_func, function)
+        return partial_func
+
+    async def new_function(*args, **kwargs):
+        return partial_func(*args, **kwargs)
+
+    update_wrapper(new_function, function)
+    return new_function
+
+
+# Mutation on extra_params
+def get_tools(
+    webui_app, tool_ids: list[str], user: UserModel, extra_params: dict
+) -> dict[str, dict]:
+    tools_dict = {}
+
+    for tool_id in tool_ids:
+        tools = Tools.get_tool_by_id(tool_id)
+        if tools is None:
+            continue
+
+        module = webui_app.state.TOOLS.get(tool_id, None)
+        if module is None:
+            module, _ = load_tools_module_by_id(tool_id)
+            webui_app.state.TOOLS[tool_id] = module
+
+        extra_params["__id__"] = tool_id
+        if hasattr(module, "valves") and hasattr(module, "Valves"):
+            valves = Tools.get_tool_valves_by_id(tool_id) or {}
+            module.valves = module.Valves(**valves)
+
+        if hasattr(module, "UserValves"):
+            extra_params["__user__"]["valves"] = module.UserValves(  # type: ignore
+                **Tools.get_user_valves_by_id_and_user_id(tool_id, user.id)
+            )
+
+        for spec in tools.specs:
+            # Remove internal parameters
+            spec["parameters"]["properties"] = {
+                key: val
+                for key, val in spec["parameters"]["properties"].items()
+                if not key.startswith("__")
+            }
+
+            function_name = spec["name"]
+
+            # convert to function that takes only model params and inserts custom params
+            original_func = getattr(module, function_name)
+            callable = apply_extra_params_to_tool_function(original_func, extra_params)
+            # TODO: This needs to be a pydantic model
+            tool_dict = {
+                "toolkit_id": tool_id,
+                "callable": callable,
+                "spec": spec,
+                "pydantic_model": function_to_pydantic_model(callable),
+                "file_handler": hasattr(module, "file_handler") and module.file_handler,
+                "citation": hasattr(module, "citation") and module.citation,
+            }
+
+            # TODO: if collision, prepend toolkit name
+            if function_name in tools_dict:
+                log.warning(f"Tool {function_name} already exists in another tools!")
+                log.warning(f"Collision between {tools} and {tool_id}.")
+                log.warning(f"Discarding {tools}.{function_name}")
+            else:
+                tools_dict[function_name] = tool_dict
+
+    return tools_dict
+
+
+def parse_description(docstring: str | None) -> str:
+    """
+    Parse a function's docstring to extract the description.
+
+    Args:
+        docstring (str): The docstring to parse.
+
+    Returns:
+        str: The description.
+    """
+
+    if not docstring:
+        return ""
+
+    lines = [line.strip() for line in docstring.strip().split("\n")]
+    description_lines: list[str] = []
+
+    for line in lines:
+        if re.match(r":param", line) or re.match(r":return", line):
+            break
+
+        description_lines.append(line)
+
+    return "\n".join(description_lines)
+
+
+def parse_docstring(docstring):
+    """
+    Parse a function's docstring to extract parameter descriptions in reST format.
+
+    Args:
+        docstring (str): The docstring to parse.
+
+    Returns:
+        dict: A dictionary where keys are parameter names and values are descriptions.
+    """
+    if not docstring:
+        return {}
+
+    # Regex to match `:param name: description` format
+    param_pattern = re.compile(r":param (\w+):\s*(.+)")
+    param_descriptions = {}
+
+    for line in docstring.splitlines():
+        match = param_pattern.match(line.strip())
+        if not match:
+            continue
+        param_name, param_description = match.groups()
+        if param_name.startswith("__"):
+            continue
+        param_descriptions[param_name] = param_description
+
+    return param_descriptions
+
+
+def function_to_pydantic_model(func: Callable) -> type[BaseModel]:
+    """
+    Converts a Python function's type hints and docstring to a Pydantic model,
+    including support for nested types, default values, and descriptions.
+
+    Args:
+        func: The function whose type hints and docstring should be converted.
+        model_name: The name of the generated Pydantic model.
+
+    Returns:
+        A Pydantic model class.
+    """
+    type_hints = get_type_hints(func)
+    signature = inspect.signature(func)
+    parameters = signature.parameters
+
+    docstring = func.__doc__
+    descriptions = parse_docstring(docstring)
+
+    tool_description = parse_description(docstring)
+
+    field_defs = {}
+    for name, param in parameters.items():
+        type_hint = type_hints.get(name, Any)
+        default_value = param.default if param.default is not param.empty else ...
+        description = descriptions.get(name, None)
+        if not description:
+            field_defs[name] = type_hint, default_value
+            continue
+        field_defs[name] = type_hint, Field(default_value, description=description)
+
+    model = create_model(func.__name__, **field_defs)
+    model.__doc__ = tool_description
+
+    return model
+
+
+def get_callable_attributes(tool: object) -> list[Callable]:
+    return [
+        getattr(tool, func)
+        for func in dir(tool)
+        if callable(getattr(tool, func))
+        and not func.startswith("__")
+        and not inspect.isclass(getattr(tool, func))
+    ]
+
+
+def get_tools_specs(tool_class: object) -> list[dict]:
+    function_list = get_callable_attributes(tool_class)
+    models = map(function_to_pydantic_model, function_list)
+    return [convert_to_openai_function(tool) for tool in models]
diff --git a/backend/open_webui/utils/utils.py b/backend/open_webui/utils/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..cde9531022d8389379ea3ae930e6eee6529a02f5
--- /dev/null
+++ b/backend/open_webui/utils/utils.py
@@ -0,0 +1,155 @@
+import logging
+import uuid
+import jwt
+
+from datetime import UTC, datetime, timedelta
+from typing import Optional, Union, List, Dict
+
+from open_webui.apps.webui.models.users import Users
+
+from open_webui.constants import ERROR_MESSAGES
+from open_webui.env import WEBUI_SECRET_KEY
+
+from fastapi import Depends, HTTPException, Request, Response, status
+from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
+from passlib.context import CryptContext
+
+logging.getLogger("passlib").setLevel(logging.ERROR)
+
+
+SESSION_SECRET = WEBUI_SECRET_KEY
+ALGORITHM = "HS256"
+
+##############
+# Auth Utils
+##############
+
+bearer_security = HTTPBearer(auto_error=False)
+pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
+
+
+def verify_password(plain_password, hashed_password):
+    return (
+        pwd_context.verify(plain_password, hashed_password) if hashed_password else None
+    )
+
+
+def get_password_hash(password):
+    return pwd_context.hash(password)
+
+
+def create_token(data: dict, expires_delta: Union[timedelta, None] = None) -> str:
+    payload = data.copy()
+
+    if expires_delta:
+        expire = datetime.now(UTC) + expires_delta
+        payload.update({"exp": expire})
+
+    encoded_jwt = jwt.encode(payload, SESSION_SECRET, algorithm=ALGORITHM)
+    return encoded_jwt
+
+
+def decode_token(token: str) -> Optional[dict]:
+    try:
+        decoded = jwt.decode(token, SESSION_SECRET, algorithms=[ALGORITHM])
+        return decoded
+    except Exception:
+        return None
+
+
+def extract_token_from_auth_header(auth_header: str):
+    return auth_header[len("Bearer ") :]
+
+
+def create_api_key():
+    key = str(uuid.uuid4()).replace("-", "")
+    return f"sk-{key}"
+
+
+def get_http_authorization_cred(auth_header: str):
+    try:
+        scheme, credentials = auth_header.split(" ")
+        return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
+    except Exception:
+        raise ValueError(ERROR_MESSAGES.INVALID_TOKEN)
+
+
+def get_current_user(
+    request: Request,
+    auth_token: HTTPAuthorizationCredentials = Depends(bearer_security),
+):
+    token = None
+
+    if auth_token is not None:
+        token = auth_token.credentials
+
+    if token is None and "token" in request.cookies:
+        token = request.cookies.get("token")
+
+    if token is None:
+        raise HTTPException(status_code=403, detail="Not authenticated")
+
+    # auth by api key
+    if token.startswith("sk-"):
+        if not request.state.enable_api_key:
+            raise HTTPException(
+                status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.API_KEY_NOT_ALLOWED
+            )
+        return get_current_user_by_api_key(token)
+
+    # auth by jwt token
+    try:
+        data = decode_token(token)
+    except Exception as e:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail="Invalid token",
+        )
+
+    if data is not None and "id" in data:
+        user = Users.get_user_by_id(data["id"])
+        if user is None:
+            raise HTTPException(
+                status_code=status.HTTP_401_UNAUTHORIZED,
+                detail=ERROR_MESSAGES.INVALID_TOKEN,
+            )
+        else:
+            Users.update_user_last_active_by_id(user.id)
+        return user
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.UNAUTHORIZED,
+        )
+
+
+def get_current_user_by_api_key(api_key: str):
+    user = Users.get_user_by_api_key(api_key)
+
+    if user is None:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.INVALID_TOKEN,
+        )
+    else:
+        Users.update_user_last_active_by_id(user.id)
+
+    return user
+
+
+def get_verified_user(user=Depends(get_current_user)):
+    if user.role not in {"user", "admin"}:
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+    return user
+
+
+def get_admin_user(user=Depends(get_current_user)):
+    if user.role != "admin":
+        raise HTTPException(
+            status_code=status.HTTP_401_UNAUTHORIZED,
+            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
+        )
+    return user
diff --git a/backend/open_webui/utils/webhook.py b/backend/open_webui/utils/webhook.py
new file mode 100644
index 0000000000000000000000000000000000000000..234209884fd9e9e1da544ae19cd0cc97db36ef61
--- /dev/null
+++ b/backend/open_webui/utils/webhook.py
@@ -0,0 +1,55 @@
+import json
+import logging
+
+import requests
+from open_webui.config import WEBUI_FAVICON_URL, WEBUI_NAME
+from open_webui.env import SRC_LOG_LEVELS, VERSION
+
+log = logging.getLogger(__name__)
+log.setLevel(SRC_LOG_LEVELS["WEBHOOK"])
+
+
+def post_webhook(url: str, message: str, event_data: dict) -> bool:
+    try:
+        payload = {}
+
+        # Slack and Google Chat Webhooks
+        if "https://hooks.slack.com" in url or "https://chat.googleapis.com" in url:
+            payload["text"] = message
+        # Discord Webhooks
+        elif "https://discord.com/api/webhooks" in url:
+            payload["content"] = message
+        # Microsoft Teams Webhooks
+        elif "webhook.office.com" in url:
+            action = event_data.get("action", "undefined")
+            facts = [
+                {"name": name, "value": value}
+                for name, value in json.loads(event_data.get("user", {})).items()
+            ]
+            payload = {
+                "@type": "MessageCard",
+                "@context": "http://schema.org/extensions",
+                "themeColor": "0076D7",
+                "summary": message,
+                "sections": [
+                    {
+                        "activityTitle": message,
+                        "activitySubtitle": f"{WEBUI_NAME} ({VERSION}) - {action}",
+                        "activityImage": WEBUI_FAVICON_URL,
+                        "facts": facts,
+                        "markdown": True,
+                    }
+                ],
+            }
+        # Default Payload
+        else:
+            payload = {**event_data}
+
+        log.debug(f"payload: {payload}")
+        r = requests.post(url, json=payload)
+        r.raise_for_status()
+        log.debug(f"r.text: {r.text}")
+        return True
+    except Exception as e:
+        log.exception(e)
+        return False
diff --git a/backend/requirements.txt b/backend/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..79e898c6a263e7a08d71b7b3310424dd1c074a1e
--- /dev/null
+++ b/backend/requirements.txt
@@ -0,0 +1,101 @@
+fastapi==0.111.0
+uvicorn[standard]==0.30.6
+pydantic==2.9.2
+python-multipart==0.0.18
+
+Flask==3.0.3
+Flask-Cors==5.0.0
+
+python-socketio==5.11.3
+python-jose==3.3.0
+passlib[bcrypt]==1.7.4
+
+requests==2.32.3
+aiohttp==3.11.8
+async-timeout
+aiocache
+aiofiles
+
+sqlalchemy==2.0.32
+alembic==1.14.0
+peewee==3.17.6
+peewee-migrate==1.12.2
+psycopg2-binary==2.9.9
+pgvector==0.3.5
+PyMySQL==1.1.1
+bcrypt==4.2.0
+
+pymongo
+redis
+boto3==1.35.53
+
+argon2-cffi==23.1.0
+APScheduler==3.10.4
+
+# AI libraries
+openai
+anthropic
+google-generativeai==0.7.2
+tiktoken
+
+langchain==0.3.7
+langchain-community==0.3.7
+langchain-chroma==0.1.4
+
+fake-useragent==1.5.1
+chromadb==0.5.15
+pymilvus==2.5.0
+qdrant-client~=1.12.0
+opensearch-py==2.7.1
+
+sentence-transformers==3.3.1
+colbert-ai==0.2.21
+einops==0.8.0
+
+
+ftfy==6.2.3
+pypdf==4.3.1
+fpdf2==2.7.9
+pymdown-extensions==10.11.2
+docx2txt==0.8
+python-pptx==1.0.0
+unstructured==0.15.9
+nltk==3.9.1
+Markdown==3.7
+pypandoc==1.13
+pandas==2.2.3
+openpyxl==3.1.5
+pyxlsb==1.0.10
+xlrd==2.0.1
+validators==0.33.0
+psutil
+sentencepiece
+soundfile==0.12.1
+
+opencv-python-headless==4.10.0.84
+rapidocr-onnxruntime==1.3.24
+rank-bm25==0.2.2
+
+faster-whisper==1.0.3
+
+PyJWT[crypto]==2.9.0
+authlib==1.3.2
+
+black==24.8.0
+langfuse==2.44.0
+youtube-transcript-api==0.6.3
+pytube==15.0.0
+
+extract_msg
+pydub
+duckduckgo-search~=6.3.5
+
+## Tests
+docker~=7.1.0
+pytest~=8.3.2
+pytest-docker~=3.1.1
+
+googleapis-common-protos==1.63.2
+
+## LDAP
+ldap3==2.9.1
diff --git a/backend/start.sh b/backend/start.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a945acb62e931cf1e0643d7bdcb5a414b4d71704
--- /dev/null
+++ b/backend/start.sh
@@ -0,0 +1,57 @@
+#!/usr/bin/env bash
+
+SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
+cd "$SCRIPT_DIR" || exit
+
+KEY_FILE=.webui_secret_key
+
+PORT="${PORT:-8080}"
+HOST="${HOST:-0.0.0.0}"
+if test "$WEBUI_SECRET_KEY $WEBUI_JWT_SECRET_KEY" = " "; then
+  echo "Loading WEBUI_SECRET_KEY from file, not provided as an environment variable."
+
+  if ! [ -e "$KEY_FILE" ]; then
+    echo "Generating WEBUI_SECRET_KEY"
+    # Generate a random value to use as a WEBUI_SECRET_KEY in case the user didn't provide one.
+    echo $(head -c 12 /dev/random | base64) > "$KEY_FILE"
+  fi
+
+  echo "Loading WEBUI_SECRET_KEY from $KEY_FILE"
+  WEBUI_SECRET_KEY=$(cat "$KEY_FILE")
+fi
+
+if [[ "${USE_OLLAMA_DOCKER,,}" == "true" ]]; then
+    echo "USE_OLLAMA is set to true, starting ollama serve."
+    ollama serve &
+fi
+
+if [[ "${USE_CUDA_DOCKER,,}" == "true" ]]; then
+  echo "CUDA is enabled, appending LD_LIBRARY_PATH to include torch/cudnn & cublas libraries."
+  export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib/python3.11/site-packages/torch/lib:/usr/local/lib/python3.11/site-packages/nvidia/cudnn/lib"
+fi
+
+# Check if SPACE_ID is set, if so, configure for space
+if [ -n "$SPACE_ID" ]; then
+  echo "Configuring for HuggingFace Space deployment"
+  if [ -n "$ADMIN_USER_EMAIL" ] && [ -n "$ADMIN_USER_PASSWORD" ]; then
+    echo "Admin user configured, creating"
+    WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*' &
+    webui_pid=$!
+    echo "Waiting for webui to start..."
+    while ! curl -s http://localhost:8080/health > /dev/null; do
+      sleep 1
+    done
+    echo "Creating admin user..."
+    curl \
+      -X POST "http://localhost:8080/api/v1/auths/signup" \
+      -H "accept: application/json" \
+      -H "Content-Type: application/json" \
+      -d "{ \"email\": \"${ADMIN_USER_EMAIL}\", \"password\": \"${ADMIN_USER_PASSWORD}\", \"name\": \"Admin\" }"
+    echo "Shutting down webui..."
+    kill $webui_pid
+  fi
+
+  export WEBUI_URL=${SPACE_HOST}
+fi
+
+WEBUI_SECRET_KEY="$WEBUI_SECRET_KEY" exec uvicorn open_webui.main:app --host "$HOST" --port "$PORT" --forwarded-allow-ips '*'
diff --git a/backend/start_windows.bat b/backend/start_windows.bat
new file mode 100644
index 0000000000000000000000000000000000000000..3e8c6b97c42d1c0d7d611a61f50b140173cea477
--- /dev/null
+++ b/backend/start_windows.bat
@@ -0,0 +1,33 @@
+:: This method is not recommended, and we recommend you use the `start.sh` file with WSL instead.
+@echo off
+SETLOCAL ENABLEDELAYEDEXPANSION
+
+:: Get the directory of the current script
+SET "SCRIPT_DIR=%~dp0"
+cd /d "%SCRIPT_DIR%" || exit /b
+
+SET "KEY_FILE=.webui_secret_key"
+IF "%PORT%"=="" SET PORT=8080
+IF "%HOST%"=="" SET HOST=0.0.0.0
+SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
+SET "WEBUI_JWT_SECRET_KEY=%WEBUI_JWT_SECRET_KEY%"
+
+:: Check if WEBUI_SECRET_KEY and WEBUI_JWT_SECRET_KEY are not set
+IF "%WEBUI_SECRET_KEY%%WEBUI_JWT_SECRET_KEY%" == " " (
+    echo Loading WEBUI_SECRET_KEY from file, not provided as an environment variable.
+
+    IF NOT EXIST "%KEY_FILE%" (
+        echo Generating WEBUI_SECRET_KEY
+        :: Generate a random value to use as a WEBUI_SECRET_KEY in case the user didn't provide one
+        SET /p WEBUI_SECRET_KEY=<nul
+        FOR /L %%i IN (1,1,12) DO SET /p WEBUI_SECRET_KEY=<!random!>>%KEY_FILE%
+        echo WEBUI_SECRET_KEY generated
+    )
+
+    echo Loading WEBUI_SECRET_KEY from %KEY_FILE%
+    SET /p WEBUI_SECRET_KEY=<%KEY_FILE%
+)
+
+:: Execute uvicorn
+SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
+uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips '*'
diff --git a/confirm_remove.sh b/confirm_remove.sh
new file mode 100755
index 0000000000000000000000000000000000000000..051908e6de668bc47b3edd5b79b70d65094823a2
--- /dev/null
+++ b/confirm_remove.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+echo "Warning: This will remove all containers and volumes, including persistent data. Do you want to continue? [Y/N]"
+read ans
+if [ "$ans" == "Y" ] || [ "$ans" == "y" ]; then
+  command docker-compose 2>/dev/null
+  if [ "$?" == "0" ]; then
+    docker-compose down -v
+  else
+    docker compose down -v
+  fi
+else
+  echo "Operation cancelled."
+fi
diff --git a/cypress.config.ts b/cypress.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dbb538233809b1c26de4c0d33ae45e0c30405c2a
--- /dev/null
+++ b/cypress.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from 'cypress';
+
+export default defineConfig({
+	e2e: {
+		baseUrl: 'http://localhost:8080'
+	},
+	video: true
+});
diff --git a/cypress/data/example-doc.txt b/cypress/data/example-doc.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d4f6f455ed18711baf9ddb8ae814538ea7076e8d
--- /dev/null
+++ b/cypress/data/example-doc.txt
@@ -0,0 +1,9 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pellentesque elit eget gravida cum sociis natoque. Morbi tristique senectus et netus et malesuada. Sapien nec sagittis aliquam malesuada bibendum. Amet consectetur adipiscing elit duis tristique sollicitudin. Non pulvinar neque laoreet suspendisse interdum consectetur libero. Arcu cursus vitae congue mauris rhoncus aenean vel elit scelerisque. Nec feugiat nisl pretium fusce id velit. Imperdiet proin fermentum leo vel. Arcu dui vivamus arcu felis bibendum ut tristique et egestas. Pellentesque sit amet porttitor eget dolor morbi non arcu risus. Egestas tellus rutrum tellus pellentesque eu tincidunt tortor aliquam. Et ultrices neque ornare aenean euismod.
+
+Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac. Viverra nibh cras pulvinar mattis nunc. Lacinia at quis risus sed vulputate. Ac tortor vitae purus faucibus ornare suspendisse sed nisi lacus. Bibendum arcu vitae elementum curabitur vitae nunc. Consectetur adipiscing elit duis tristique sollicitudin nibh sit amet commodo. Velit egestas dui id ornare arcu odio ut. Et malesuada fames ac turpis egestas integer eget aliquet. Lacus suspendisse faucibus interdum posuere lorem ipsum dolor sit. Morbi tristique senectus et netus. Pretium viverra suspendisse potenti nullam ac tortor vitae. Parturient montes nascetur ridiculus mus mauris vitae. Quis viverra nibh cras pulvinar mattis nunc sed blandit libero. Euismod nisi porta lorem mollis aliquam ut porttitor leo. Mauris in aliquam sem fringilla ut morbi. Faucibus pulvinar elementum integer enim neque. Neque sodales ut etiam sit. Consectetur a erat nam at.
+
+Sed nisi lacus sed viverra tellus in hac habitasse. Proin sagittis nisl rhoncus mattis rhoncus. Risus commodo viverra maecenas accumsan lacus. Morbi quis commodo odio aenean sed adipiscing. Mollis nunc sed id semper risus in. Ultricies mi eget mauris pharetra et ultrices neque. Amet luctus venenatis lectus magna fringilla urna porttitor rhoncus. Eget magna fermentum iaculis eu non diam phasellus. Id diam maecenas ultricies mi eget mauris pharetra et ultrices. Id donec ultrices tincidunt arcu non sodales. Sed cras ornare arcu dui vivamus arcu felis bibendum ut. Urna duis convallis convallis tellus id interdum velit. Rhoncus mattis rhoncus urna neque viverra justo nec. Purus semper eget duis at tellus at urna condimentum. Et odio pellentesque diam volutpat commodo sed egestas. Blandit volutpat maecenas volutpat blandit. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Est ullamcorper eget nulla facilisi etiam dignissim.
+
+Justo nec ultrices dui sapien eget mi proin sed. Purus gravida quis blandit turpis cursus in hac. Placerat orci nulla pellentesque dignissim enim sit. Morbi tristique senectus et netus et malesuada fames ac. Consequat mauris nunc congue nisi. Eu lobortis elementum nibh tellus molestie nunc non blandit. Viverra justo nec ultrices dui. Morbi non arcu risus quis. Elementum sagittis vitae et leo duis. Lectus mauris ultrices eros in cursus. Neque laoreet suspendisse interdum consectetur.
+
+Facilisis gravida neque convallis a cras. Nisl rhoncus mattis rhoncus urna neque viverra justo. Faucibus purus in massa tempor. Lacus laoreet non curabitur gravida arcu ac tortor. Tincidunt eget nullam non nisi est sit amet. Ornare lectus sit amet est placerat in egestas. Sollicitudin tempor id eu nisl nunc mi. Scelerisque viverra mauris in aliquam sem fringilla ut. Ullamcorper sit amet risus nullam. Mauris rhoncus aenean vel elit scelerisque mauris pellentesque pulvinar. Velit euismod in pellentesque massa placerat duis ultricies lacus. Pharetra magna ac placerat vestibulum lectus mauris ultrices eros in. Lorem ipsum dolor sit amet. Sit amet mauris commodo quis imperdiet. Quam pellentesque nec nam aliquam sem et tortor. Amet nisl purus in mollis nunc. Sed risus pretium quam vulputate dignissim suspendisse in est. Nisl condimentum id venenatis a condimentum. Velit euismod in pellentesque massa. Quam id leo in vitae turpis massa sed.
diff --git a/cypress/e2e/chat.cy.ts b/cypress/e2e/chat.cy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..17c4d8e735239d8289629bb5056946d9797dd701
--- /dev/null
+++ b/cypress/e2e/chat.cy.ts
@@ -0,0 +1,106 @@
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference path="../support/index.d.ts" />
+
+// These tests run through the chat flow.
+describe('Settings', () => {
+	// Wait for 2 seconds after all tests to fix an issue with Cypress's video recording missing the last few frames
+	after(() => {
+		// eslint-disable-next-line cypress/no-unnecessary-waiting
+		cy.wait(2000);
+	});
+
+	beforeEach(() => {
+		// Login as the admin user
+		cy.loginAdmin();
+		// Visit the home page
+		cy.visit('/');
+	});
+
+	context('Ollama', () => {
+		it('user can select a model', () => {
+			// Click on the model selector
+			cy.get('button[aria-label="Select a model"]').click();
+			// Select the first model
+			cy.get('button[aria-label="model-item"]').first().click();
+		});
+
+		it('user can perform text chat', () => {
+			// Click on the model selector
+			cy.get('button[aria-label="Select a model"]').click();
+			// Select the first model
+			cy.get('button[aria-label="model-item"]').first().click();
+			// Type a message
+			cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', {
+				force: true
+			});
+			// Send the message
+			cy.get('button[type="submit"]').click();
+			// User's message should be visible
+			cy.get('.chat-user').should('exist');
+			// Wait for the response
+			// .chat-assistant is created after the first token is received
+			cy.get('.chat-assistant', { timeout: 10_000 }).should('exist');
+			// Generation Info is created after the stop token is received
+			cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist');
+		});
+
+		it('user can share chat', () => {
+			// Click on the model selector
+			cy.get('button[aria-label="Select a model"]').click();
+			// Select the first model
+			cy.get('button[aria-label="model-item"]').first().click();
+			// Type a message
+			cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', {
+				force: true
+			});
+			// Send the message
+			cy.get('button[type="submit"]').click();
+			// User's message should be visible
+			cy.get('.chat-user').should('exist');
+			// Wait for the response
+			// .chat-assistant is created after the first token is received
+			cy.get('.chat-assistant', { timeout: 10_000 }).should('exist');
+			// Generation Info is created after the stop token is received
+			cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist');
+			// spy on requests
+			const spy = cy.spy();
+			cy.intercept('POST', '/api/v1/chats/**/share', spy);
+			// Open context menu
+			cy.get('#chat-context-menu-button').click();
+			// Click share button
+			cy.get('#chat-share-button').click();
+			// Check if the share dialog is visible
+			cy.get('#copy-and-share-chat-button').should('exist');
+			// Click the copy button
+			cy.get('#copy-and-share-chat-button').click();
+			cy.wrap({}, { timeout: 5_000 }).should(() => {
+				// Check if the share request was made
+				expect(spy).to.be.callCount(1);
+			});
+		});
+
+		it('user can generate image', () => {
+			// Click on the model selector
+			cy.get('button[aria-label="Select a model"]').click();
+			// Select the first model
+			cy.get('button[aria-label="model-item"]').first().click();
+			// Type a message
+			cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', {
+				force: true
+			});
+			// Send the message
+			cy.get('button[type="submit"]').click();
+			// User's message should be visible
+			cy.get('.chat-user').should('exist');
+			// Wait for the response
+			// .chat-assistant is created after the first token is received
+			cy.get('.chat-assistant', { timeout: 10_000 }).should('exist');
+			// Generation Info is created after the stop token is received
+			cy.get('div[aria-label="Generation Info"]', { timeout: 120_000 }).should('exist');
+			// Click on the generate image button
+			cy.get('[aria-label="Generate Image"]').click();
+			// Wait for image to be visible
+			cy.get('img[data-cy="image"]', { timeout: 60_000 }).should('be.visible');
+		});
+	});
+});
diff --git a/cypress/e2e/documents.cy.ts b/cypress/e2e/documents.cy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b14b1de20c916d4b1303a8d304c5abc334e949ea
--- /dev/null
+++ b/cypress/e2e/documents.cy.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference path="../support/index.d.ts" />
diff --git a/cypress/e2e/registration.cy.ts b/cypress/e2e/registration.cy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..232d75e882a93870f318f96f98ffd56efbc4bd8f
--- /dev/null
+++ b/cypress/e2e/registration.cy.ts
@@ -0,0 +1,52 @@
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference path="../support/index.d.ts" />
+import { adminUser } from '../support/e2e';
+
+// These tests assume the following defaults:
+// 1. No users exist in the database or that the test admin user is an admin
+// 2. Language is set to English
+// 3. The default role for new users is 'pending'
+describe('Registration and Login', () => {
+	// Wait for 2 seconds after all tests to fix an issue with Cypress's video recording missing the last few frames
+	after(() => {
+		// eslint-disable-next-line cypress/no-unnecessary-waiting
+		cy.wait(2000);
+	});
+
+	beforeEach(() => {
+		cy.visit('/');
+	});
+
+	it('should register a new user as pending', () => {
+		const userName = `Test User - ${Date.now()}`;
+		const userEmail = `cypress-${Date.now()}@example.com`;
+		// Toggle from sign in to sign up
+		cy.contains('Sign up').click();
+		// Fill out the form
+		cy.get('input[autocomplete="name"]').type(userName);
+		cy.get('input[autocomplete="email"]').type(userEmail);
+		cy.get('input[type="password"]').type('password');
+		// Submit the form
+		cy.get('button[type="submit"]').click();
+		// Wait until the user is redirected to the home page
+		cy.contains(userName);
+		// Expect the user to be pending
+		cy.contains('Check Again');
+	});
+
+	it('can login with the admin user', () => {
+		// Fill out the form
+		cy.get('input[autocomplete="email"]').type(adminUser.email);
+		cy.get('input[type="password"]').type(adminUser.password);
+		// Submit the form
+		cy.get('button[type="submit"]').click();
+		// Wait until the user is redirected to the home page
+		cy.contains(adminUser.name);
+		// Dismiss the changelog dialog if it is visible
+		cy.getAllLocalStorage().then((ls) => {
+			if (!ls['version']) {
+				cy.get('button').contains("Okay, Let's Go!").click();
+			}
+		});
+	});
+});
diff --git a/cypress/e2e/settings.cy.ts b/cypress/e2e/settings.cy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ea91698072fd25cb75a6961ac3d9f0652c303da
--- /dev/null
+++ b/cypress/e2e/settings.cy.ts
@@ -0,0 +1,63 @@
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference path="../support/index.d.ts" />
+import { adminUser } from '../support/e2e';
+
+// These tests run through the various settings pages, ensuring that the user can interact with them as expected
+describe('Settings', () => {
+	// Wait for 2 seconds after all tests to fix an issue with Cypress's video recording missing the last few frames
+	after(() => {
+		// eslint-disable-next-line cypress/no-unnecessary-waiting
+		cy.wait(2000);
+	});
+
+	beforeEach(() => {
+		// Login as the admin user
+		cy.loginAdmin();
+		// Visit the home page
+		cy.visit('/');
+		// Click on the user menu
+		cy.get('button[aria-label="User Menu"]').click();
+		// Click on the settings link
+		cy.get('button').contains('Settings').click();
+	});
+
+	context('General', () => {
+		it('user can open the General modal and hit save', () => {
+			cy.get('button').contains('General').click();
+			cy.get('button').contains('Save').click();
+		});
+	});
+
+	context('Interface', () => {
+		it('user can open the Interface modal and hit save', () => {
+			cy.get('button').contains('Interface').click();
+			cy.get('button').contains('Save').click();
+		});
+	});
+
+	context('Audio', () => {
+		it('user can open the Audio modal and hit save', () => {
+			cy.get('button').contains('Audio').click();
+			cy.get('button').contains('Save').click();
+		});
+	});
+
+	context('Chats', () => {
+		it('user can open the Chats modal', () => {
+			cy.get('button').contains('Chats').click();
+		});
+	});
+
+	context('Account', () => {
+		it('user can open the Account modal and hit save', () => {
+			cy.get('button').contains('Account').click();
+			cy.get('button').contains('Save').click();
+		});
+	});
+
+	context('About', () => {
+		it('user can open the About modal', () => {
+			cy.get('button').contains('About').click();
+		});
+	});
+});
diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b94c47872c87b7ac9aa4ed9752cf46a3ec51c8f
--- /dev/null
+++ b/cypress/support/e2e.ts
@@ -0,0 +1,78 @@
+/// <reference types="cypress" />
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+/// <reference path="../support/index.d.ts" />
+
+export const adminUser = {
+	name: 'Admin User',
+	email: 'admin@example.com',
+	password: 'password'
+};
+
+const login = (email: string, password: string) => {
+	return cy.session(
+		email,
+		() => {
+			// Make sure to test against us english to have stable tests,
+			// regardless on local language preferences
+			localStorage.setItem('locale', 'en-US');
+			// Visit auth page
+			cy.visit('/auth');
+			// Fill out the form
+			cy.get('input[autocomplete="email"]').type(email);
+			cy.get('input[type="password"]').type(password);
+			// Submit the form
+			cy.get('button[type="submit"]').click();
+			// Wait until the user is redirected to the home page
+			cy.get('#chat-search').should('exist');
+			// Get the current version to skip the changelog dialog
+			if (localStorage.getItem('version') === null) {
+				cy.get('button').contains("Okay, Let's Go!").click();
+			}
+		},
+		{
+			validate: () => {
+				cy.request({
+					method: 'GET',
+					url: '/api/v1/auths/',
+					headers: {
+						Authorization: 'Bearer ' + localStorage.getItem('token')
+					}
+				});
+			}
+		}
+	);
+};
+
+const register = (name: string, email: string, password: string) => {
+	return cy
+		.request({
+			method: 'POST',
+			url: '/api/v1/auths/signup',
+			body: {
+				name: name,
+				email: email,
+				password: password
+			},
+			failOnStatusCode: false
+		})
+		.then((response) => {
+			expect(response.status).to.be.oneOf([200, 400]);
+		});
+};
+
+const registerAdmin = () => {
+	return register(adminUser.name, adminUser.email, adminUser.password);
+};
+
+const loginAdmin = () => {
+	return login(adminUser.email, adminUser.password);
+};
+
+Cypress.Commands.add('login', (email, password) => login(email, password));
+Cypress.Commands.add('register', (name, email, password) => register(name, email, password));
+Cypress.Commands.add('registerAdmin', () => registerAdmin());
+Cypress.Commands.add('loginAdmin', () => loginAdmin());
+
+before(() => {
+	cy.registerAdmin();
+});
diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..647db92115a3c6ee9e5b35b90b585a2a0a837306
--- /dev/null
+++ b/cypress/support/index.d.ts
@@ -0,0 +1,13 @@
+// load the global Cypress types
+/// <reference types="cypress" />
+
+declare namespace Cypress {
+	interface Chainable {
+		login(email: string, password: string): Chainable<Element>;
+		register(name: string, email: string, password: string): Chainable<Element>;
+		registerAdmin(): Chainable<Element>;
+		loginAdmin(): Chainable<Element>;
+		uploadTestDocument(suffix: any): Chainable<Element>;
+		deleteTestDocument(suffix: any): Chainable<Element>;
+	}
+}
diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..ff28d946496fa69c603c0ec9287317a7dcc83279
--- /dev/null
+++ b/cypress/tsconfig.json
@@ -0,0 +1,7 @@
+{
+	"extends": "../tsconfig.json",
+	"compilerOptions": {
+		"inlineSourceMap": true,
+		"sourceMap": false
+	}
+}
diff --git a/docker-compose.a1111-test.yaml b/docker-compose.a1111-test.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..e6ab12c07f0995369107943d0b23a68ebac607b3
--- /dev/null
+++ b/docker-compose.a1111-test.yaml
@@ -0,0 +1,31 @@
+# This is an overlay that spins up stable-diffusion-webui for integration testing
+# This is not designed to be used in production
+services:
+  stable-diffusion-webui:
+    # Not built for ARM64
+    platform: linux/amd64
+    image: ghcr.io/neggles/sd-webui-docker:latest
+    restart: unless-stopped
+    environment:
+      CLI_ARGS: "--api --use-cpu all --precision full --no-half --skip-torch-cuda-test --ckpt /empty.pt --do-not-download-clip --disable-nan-check --disable-opt-split-attention"
+      PYTHONUNBUFFERED: "1"
+      TERM: "vt100"
+      SD_WEBUI_VARIANT: "default"
+    # Hack to get container working on Apple Silicon
+    # Rosetta creates a conflict ${HOME}/.cache folder
+    entrypoint: /bin/bash
+    command:
+      - -c
+      - |
+        export HOME=/root-home
+        rm -rf $${HOME}/.cache
+        /docker/entrypoint.sh python -u webui.py --listen --port $${WEBUI_PORT} --skip-version-check $${CLI_ARGS}
+    volumes:
+      - ./test/test_files/image_gen/sd-empty.pt:/empty.pt
+
+  open-webui:
+    environment:
+      ENABLE_IMAGE_GENERATION: "true"
+      AUTOMATIC1111_BASE_URL: http://stable-diffusion-webui:7860
+      IMAGE_SIZE: "64x64"
+      IMAGE_STEPS: "3"
diff --git a/docker-compose.amdgpu.yaml b/docker-compose.amdgpu.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..7a1295d94515c6f32a9c11d913ce603a989f504a
--- /dev/null
+++ b/docker-compose.amdgpu.yaml
@@ -0,0 +1,8 @@
+services:
+  ollama:
+    devices:
+      - /dev/kfd:/dev/kfd
+      - /dev/dri:/dev/dri
+    image: ollama/ollama:${OLLAMA_DOCKER_TAG-rocm}
+    environment:
+      - 'HSA_OVERRIDE_GFX_VERSION=${HSA_OVERRIDE_GFX_VERSION-11.0.0}'
\ No newline at end of file
diff --git a/docker-compose.api.yaml b/docker-compose.api.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8f8fbe59ad03274ef4f6a414cc9333290588e0aa
--- /dev/null
+++ b/docker-compose.api.yaml
@@ -0,0 +1,5 @@
+services:
+  ollama:
+    # Expose Ollama API outside the container stack
+    ports:
+      - ${OLLAMA_WEBAPI_PORT-11434}:11434
diff --git a/docker-compose.data.yaml b/docker-compose.data.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..4b70601f894a1ad3530cc25ea616a3a5ddb691a6
--- /dev/null
+++ b/docker-compose.data.yaml
@@ -0,0 +1,4 @@
+services:
+  ollama:
+    volumes:
+      - ${OLLAMA_DATA_DIR-./ollama-data}:/root/.ollama
diff --git a/docker-compose.gpu.yaml b/docker-compose.gpu.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..de821235da42f5e116315f7407d2c6de0451c99c
--- /dev/null
+++ b/docker-compose.gpu.yaml
@@ -0,0 +1,11 @@
+services:
+  ollama:
+    # GPU support
+    deploy:
+      resources:
+        reservations:
+          devices:
+            - driver: ${OLLAMA_GPU_DRIVER-nvidia}
+              count: ${OLLAMA_GPU_COUNT-1}
+              capabilities:
+                - gpu
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..74249febd9e37e2166fa6c07a73770812e745b7d
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,34 @@
+services:
+  ollama:
+    volumes:
+      - ollama:/root/.ollama
+    container_name: ollama
+    pull_policy: always
+    tty: true
+    restart: unless-stopped
+    image: ollama/ollama:${OLLAMA_DOCKER_TAG-latest}
+
+  open-webui:
+    build:
+      context: .
+      args:
+        OLLAMA_BASE_URL: '/ollama'
+      dockerfile: Dockerfile
+    image: ghcr.io/open-webui/open-webui:${WEBUI_DOCKER_TAG-main}
+    container_name: open-webui
+    volumes:
+      - open-webui:/app/backend/data
+    depends_on:
+      - ollama
+    ports:
+      - ${OPEN_WEBUI_PORT-3000}:8080
+    environment:
+      - 'OLLAMA_BASE_URL=http://ollama:11434'
+      - 'WEBUI_SECRET_KEY='
+    extra_hosts:
+      - host.docker.internal:host-gateway
+    restart: unless-stopped
+
+volumes:
+  ollama: {}
+  open-webui: {}
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..ec8a79bbcea4ed9b62faa312e1ca6041b4426f3c
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,73 @@
+# Contributing to Open WebUI
+
+🚀 **Welcome, Contributors!** 🚀
+
+Your interest in contributing to Open WebUI is greatly appreciated. This document is here to guide you through the process, ensuring your contributions enhance the project effectively. Let's make Open WebUI even better, together!
+
+## 📌 Key Points
+
+### 🦙 Ollama vs. Open WebUI
+
+It's crucial to distinguish between Ollama and Open WebUI:
+
+- **Open WebUI** focuses on providing an intuitive and responsive web interface for chat interactions.
+- **Ollama** is the underlying technology that powers these interactions.
+
+If your issue or contribution pertains directly to the core Ollama technology, please direct it to the appropriate [Ollama project repository](https://ollama.com/). Open WebUI's repository is dedicated to the web interface aspect only.
+
+### 🚨 Reporting Issues
+
+Noticed something off? Have an idea? Check our [Issues tab](https://github.com/open-webui/open-webui/issues) to see if it's already been reported or suggested. If not, feel free to open a new issue. When reporting an issue, please follow our issue templates. These templates are designed to ensure that all necessary details are provided from the start, enabling us to address your concerns more efficiently.
+
+> [!IMPORTANT]
+>
+> - **Template Compliance:** Please be aware that failure to follow the provided issue template, or not providing the requested information at all, will likely result in your issue being closed without further consideration. This approach is critical for maintaining the manageability and integrity of issue tracking.
+> - **Detail is Key:** To ensure your issue is understood and can be effectively addressed, it's imperative to include comprehensive details. Descriptions should be clear, including steps to reproduce, expected outcomes, and actual results. Lack of sufficient detail may hinder our ability to resolve your issue.
+
+### 🧭 Scope of Support
+
+We've noticed an uptick in issues not directly related to Open WebUI but rather to the environment it's run in, especially Docker setups. While we strive to support Docker deployment, understanding Docker fundamentals is crucial for a smooth experience.
+
+- **Docker Deployment Support**: Open WebUI supports Docker deployment. Familiarity with Docker is assumed. For Docker basics, please refer to the [official Docker documentation](https://docs.docker.com/get-started/overview/).
+
+- **Advanced Configurations**: Setting up reverse proxies for HTTPS and managing Docker deployments requires foundational knowledge. There are numerous online resources available to learn these skills. Ensuring you have this knowledge will greatly enhance your experience with Open WebUI and similar projects.
+
+## 💡 Contributing
+
+Looking to contribute? Great! Here's how you can help:
+
+### 🛠 Pull Requests
+
+We welcome pull requests. Before submitting one, please:
+
+1. Open a discussion regarding your ideas [here](https://github.com/open-webui/open-webui/discussions/new/choose).
+2. Follow the project's coding standards and include tests for new features.
+3. Update documentation as necessary.
+4. Write clear, descriptive commit messages.
+5. It's essential to complete your pull request in a timely manner. We move fast, and having PRs hang around too long is not feasible. If you can't get it done within a reasonable time frame, we may have to close it to keep the project moving forward.
+
+### 📚 Documentation & Tutorials
+
+Help us make Open WebUI more accessible by improving documentation, writing tutorials, or creating guides on setting up and optimizing the web UI.
+
+### 🌐 Translations and Internationalization
+
+Help us make Open WebUI available to a wider audience. In this section, we'll guide you through the process of adding new translations to the project.
+
+We use JSON files to store translations. You can find the existing translation files in the `src/lib/i18n/locales` directory. Each directory corresponds to a specific language, for example, `en-US` for English (US), `fr-FR` for French (France) and so on. You can refer to [ISO 639 Language Codes](http://www.lingoes.net/en/translator/langcode.htm) to find the appropriate code for a specific language.
+
+To add a new language:
+
+- Create a new directory in the `src/lib/i18n/locales` path with the appropriate language code as its name. For instance, if you're adding translations for Spanish (Spain), create a new directory named `es-ES`.
+- Copy the American English translation file(s) (from `en-US` directory in `src/lib/i18n/locale`) to this new directory and update the string values in JSON format according to your language. Make sure to preserve the structure of the JSON object.
+- Add the language code and its respective title to languages file at `src/lib/i18n/locales/languages.json`.
+
+### 🤔 Questions & Feedback
+
+Got questions or feedback? Join our [Discord community](https://discord.gg/5rJgQTnV4s) or open an issue. We're here to help!
+
+## 🙏 Thank You!
+
+Your contributions, big or small, make a significant impact on Open WebUI. We're excited to see what you bring to the project!
+
+Together, let's create an even more powerful tool for the community. 🌟
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..4113c35a78f9d9c7d3096cf851f76f495421fc51
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,3 @@
+# Project workflow
+
+[![](https://mermaid.ink/img/pako:eNq1k01rAjEQhv_KkFNLFe1N9iAUevFSRVl6Cci4Gd1ANtlmsmtF_O_N7iqtHxR76ClhMu87zwyZvcicIpEIpo-KbEavGjceC2lL9EFnukQbIGXygNye5y9TY7DAZTpZLsjXXVYXg3dapRM4hh9mu5A7-3hTfSXtAtJK21Tsj8dPl3USmJZkGVbebWNKD2rNOjAYl6HJHYdkNBwNpb3U9aNZvzFNYE6h8tFiSyZzBUGJG4K1dwVwTSYQrCptlLRvLt5dA5i2la5Ruk51Ux0VKQjuxPVbAwuyiuFlNgHfzJ5DoxtgqQf1813gnZRLZ5lAYcD7WT1lpGtiQKug9C4jZrrp-Fd-1-Y1bdzo4dvnZDLz7lPHyj8sOgfg4x84E7RTuEaZt8yRZqtDfgT_rwG2u3Dv_ERPFOQL1Cqu2F5aAClCTgVJkcSrojVWJkgh7SGmYhXcYmczkQRfUU9UZfQ4baRI1miYDl_QqlPg?type=png)](https://mermaid.live/edit#pako:eNq1k01rAjEQhv_KkFNLFe1N9iAUevFSRVl6Cci4Gd1ANtlmsmtF_O_N7iqtHxR76ClhMu87zwyZvcicIpEIpo-KbEavGjceC2lL9EFnukQbIGXygNye5y9TY7DAZTpZLsjXXVYXg3dapRM4hh9mu5A7-3hTfSXtAtJK21Tsj8dPl3USmJZkGVbebWNKD2rNOjAYl6HJHYdkNBwNpb3U9aNZvzFNYE6h8tFiSyZzBUGJG4K1dwVwTSYQrCptlLRvLt5dA5i2la5Ruk51Ux0VKQjuxPVbAwuyiuFlNgHfzJ5DoxtgqQf1813gnZRLZ5lAYcD7WT1lpGtiQKug9C4jZrrp-Fd-1-Y1bdzo4dvnZDLz7lPHyj8sOgfg4x84E7RTuEaZt8yRZqtDfgT_rwG2u3Dv_ERPFOQL1Cqu2F5aAClCTgVJkcSrojVWJkgh7SGmYhXcYmczkQRfUU9UZfQ4baRI1miYDl_QqlPg)
diff --git a/docs/SECURITY.md b/docs/SECURITY.md
new file mode 100644
index 0000000000000000000000000000000000000000..507e3c606955fac816c413d1ab1f8ec5ebd1f2e1
--- /dev/null
+++ b/docs/SECURITY.md
@@ -0,0 +1,44 @@
+# Security Policy
+
+Our primary goal is to ensure the protection and confidentiality of sensitive data stored by users on open-webui.
+
+## Supported Versions
+
+| Version | Supported          |
+| ------- | ------------------ |
+| main    | :white_check_mark: |
+| others  | :x:                |
+
+## Zero Tolerance for External Platforms
+
+Based on a precedent of an unacceptable degree of spamming and unsolicited communications from third-party platforms, we forcefully reaffirm our stance. **We refuse to engage with, join, or monitor any platforms outside of GitHub for vulnerability reporting.** Our reasons are not just procedural but are deep-seated in the ethos of our project, which champions transparency and direct community interaction inherent in the open-source culture. Any attempts to divert our processes to external platforms will be met with outright rejection. This policy is non-negotiable and understands no exceptions.
+
+Any reports or solicitations arriving from sources other than our designated GitHub repository will be dismissed without consideration. We’ve seen how external engagements can dilute and compromise the integrity of community-driven projects, and we’re not here to gamble with the security and privacy of our user community.
+
+## Reporting a Vulnerability
+
+We appreciate the community's interest in identifying potential vulnerabilities. However, effective immediately, we will **not** accept low-effort vulnerability reports. To ensure that submissions are constructive and actionable, please adhere to the following guidelines:
+
+Reports not submitted through our designated GitHub repository will be disregarded, and we will categorically reject invitations to collaborate on external platforms. Our aggressive stance on this matter underscores our commitment to a secure, transparent, and open community where all operations are visible and contributors are accountable.
+
+1. **No Vague Reports**: Submissions such as "I found a vulnerability" without any details will be treated as spam and will not be accepted.
+
+2. **In-Depth Understanding Required**: Reports must reflect a clear understanding of the codebase and provide specific details about the vulnerability, including the affected components and potential impacts.
+
+3. **Proof of Concept (PoC) is Mandatory**: Each submission must include a well-documented proof of concept (PoC) that demonstrates the vulnerability. If confidentiality is a concern, reporters are encouraged to create a private fork of the repository and share access with the maintainers. Reports lacking valid evidence will be disregarded.
+
+4. **Required Patch Submission**: Along with the PoC, reporters must provide a patch or actionable steps to remediate the identified vulnerability. This helps us evaluate and implement fixes rapidly.
+
+5. **Streamlined Merging Process**: When vulnerability reports meet the above criteria, we can consider them for immediate merging, similar to regular pull requests. Well-structured and thorough submissions will expedite the process of enhancing our security.
+
+**Non-compliant submissions will be closed, and repeat violators may be banned.** Our goal is to foster a constructive reporting environment where quality submissions promote better security for all users.
+
+## Product Security
+
+We regularly audit our internal processes and system architecture for vulnerabilities using a combination of automated and manual testing techniques. We are also planning to implement SAST and SCA scans in our project soon.
+
+For immediate concerns or detailed reports that meet our guidelines, please create an issue in our [issue tracker](/open-webui/open-webui/issues) or contact us on [Discord](https://discord.gg/5rJgQTnV4s).
+
+---
+
+_Last updated on **2024-08-19**._
diff --git a/docs/apache.md b/docs/apache.md
new file mode 100644
index 0000000000000000000000000000000000000000..ebbcc17f45777368366d1c99a54b48dd69525558
--- /dev/null
+++ b/docs/apache.md
@@ -0,0 +1,199 @@
+# Hosting UI and Models separately
+
+Sometimes, its beneficial to host Ollama, separate from the UI, but retain the RAG and RBAC support features shared across users:
+
+# Open WebUI Configuration
+
+## UI Configuration
+
+For the UI configuration, you can set up the Apache VirtualHost as follows:
+
+```
+# Assuming you have a website hosting this UI at "server.com"
+<VirtualHost 192.168.1.100:80>
+    ServerName server.com
+    DocumentRoot /home/server/public_html
+
+    ProxyPass / http://server.com:3000/ nocanon
+    ProxyPassReverse / http://server.com:3000/
+
+</VirtualHost>
+```
+
+Enable the site first before you can request SSL:
+
+`a2ensite server.com.conf` # this will enable the site. a2ensite is short for "Apache 2 Enable Site"
+
+```
+# For SSL
+<VirtualHost 192.168.1.100:443>
+    ServerName server.com
+    DocumentRoot /home/server/public_html
+
+    ProxyPass / http://server.com:3000/ nocanon
+    ProxyPassReverse / http://server.com:3000/
+
+    SSLEngine on
+    SSLCertificateFile /etc/ssl/virtualmin/170514456861234/ssl.cert
+    SSLCertificateKeyFile /etc/ssl/virtualmin/170514456861234/ssl.key
+    SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
+
+    SSLProxyEngine on
+    SSLCACertificateFile /etc/ssl/virtualmin/170514456865864/ssl.ca
+</VirtualHost>
+
+```
+
+I'm using virtualmin here for my SSL clusters, but you can also use certbot directly or your preferred SSL method. To use SSL:
+
+### Prerequisites.
+
+Run the following commands:
+
+`snap install certbot --classic`
+`snap apt install python3-certbot-apache` (this will install the apache plugin).
+
+Navigate to the apache sites-available directory:
+
+`cd /etc/apache2/sites-available/`
+
+Create server.com.conf if it is not yet already created, containing the above `<virtualhost>` configuration (it should match your case. Modify as necessary). Use the one without the SSL:
+
+Once it's created, run `certbot --apache -d server.com`, this will request and add/create an SSL keys for you as well as create the server.com.le-ssl.conf
+
+# Configuring Ollama Server
+
+On your latest installation of Ollama, make sure that you have setup your api server from the official Ollama reference:
+
+[Ollama FAQ](https://github.com/jmorganca/ollama/blob/main/docs/faq.md)
+
+### TL;DR
+
+The guide doesn't seem to match the current updated service file on linux. So, we will address it here:
+
+Unless when you're compiling Ollama from source, installing with the standard install `curl https://ollama.com/install.sh | sh` creates a file called `ollama.service` in /etc/systemd/system. You can use nano to edit the file:
+
+```
+sudo nano /etc/systemd/system/ollama.service
+```
+
+Add the following lines:
+
+```
+Environment="OLLAMA_HOST=0.0.0.0:11434" # this line is mandatory. You can also specify
+```
+
+For instance:
+
+```
+[Unit]
+Description=Ollama Service
+After=network-online.target
+
+[Service]
+ExecStart=/usr/local/bin/ollama serve
+Environment="OLLAMA_HOST=0.0.0.0:11434" # this line is mandatory. You can also specify 192.168.254.109:DIFFERENT_PORT, format
+Environment="OLLAMA_ORIGINS=http://192.168.254.106:11434,https://models.server.city" # this line is optional
+User=ollama
+Group=ollama
+Restart=always
+RestartSec=3
+Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/s>
+
+[Install]
+WantedBy=default.target
+```
+
+Save the file by pressing CTRL+S, then press CTRL+X
+
+When your computer restarts, the Ollama server will now be listening on the IP:PORT you specified, in this case 0.0.0.0:11434, or 192.168.254.106:11434 (whatever your local IP address is). Make sure that your router is correctly configured to serve pages from that local IP by forwarding 11434 to your local IP server.
+
+# Ollama Model Configuration
+
+## For the Ollama model configuration, use the following Apache VirtualHost setup:
+
+Navigate to the apache sites-available directory:
+
+`cd /etc/apache2/sites-available/`
+
+`nano models.server.city.conf` # match this with your ollama server domain
+
+Add the following virtualhost containing this example (modify as needed):
+
+```
+
+# Assuming you have a website hosting this UI at "models.server.city"
+<IfModule mod_ssl.c>
+    <VirtualHost 192.168.254.109:443>
+        DocumentRoot "/var/www/html/"
+        ServerName models.server.city
+        <Directory "/var/www/html/">
+            Options None
+            Require all granted
+        </Directory>
+
+        ProxyRequests Off
+        ProxyPreserveHost On
+        ProxyAddHeaders On
+        SSLProxyEngine on
+
+        ProxyPass / http://server.city:1000/ nocanon # or port 11434
+        ProxyPassReverse / http://server.city:1000/ # or port 11434
+
+        SSLCertificateFile /etc/letsencrypt/live/models.server.city/fullchain.pem
+        SSLCertificateKeyFile /etc/letsencrypt/live/models.server.city/privkey.pem
+        Include /etc/letsencrypt/options-ssl-apache.conf
+    </VirtualHost>
+</IfModule>
+```
+
+You may need to enable the site first (if you haven't done so yet) before you can request SSL:
+
+`a2ensite models.server.city.conf`
+
+#### For the SSL part of Ollama server
+
+Run the following commands:
+
+Navigate to the apache sites-available directory:
+
+`cd /etc/apache2/sites-available/`
+`certbot --apache -d server.com`
+
+```
+<VirtualHost 192.168.254.109:80>
+    DocumentRoot "/var/www/html/"
+    ServerName models.server.city
+    <Directory "/var/www/html/">
+        Options None
+        Require all granted
+    </Directory>
+
+    ProxyRequests Off
+    ProxyPreserveHost On
+    ProxyAddHeaders On
+    SSLProxyEngine on
+
+    ProxyPass / http://server.city:1000/ nocanon # or port 11434
+    ProxyPassReverse / http://server.city:1000/ # or port 11434
+
+    RewriteEngine on
+    RewriteCond %{SERVER_NAME} =models.server.city
+    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
+</VirtualHost>
+
+```
+
+Don't forget to restart/reload Apache with `systemctl reload apache2`
+
+Open your site at https://server.com!
+
+**Congratulations**, your _**Open-AI-like Chat-GPT style UI**_ is now serving AI with RAG, RBAC and multimodal features! Download Ollama models if you haven't yet done so!
+
+If you encounter any misconfiguration or errors, please file an issue or engage with our discussion. There are a lot of friendly developers here to assist you.
+
+Let's make this UI much more user friendly for everyone!
+
+Thanks for making open-webui your UI Choice for AI!
+
+This doc is made by **Bob Reyes**, your **Open-WebUI** fan from the Philippines.
diff --git a/hatch_build.py b/hatch_build.py
new file mode 100644
index 0000000000000000000000000000000000000000..8ddaf0749bd6783b785fe63d6ce4b7f3e8a5b5d9
--- /dev/null
+++ b/hatch_build.py
@@ -0,0 +1,23 @@
+# noqa: INP001
+import os
+import shutil
+import subprocess
+from sys import stderr
+
+from hatchling.builders.hooks.plugin.interface import BuildHookInterface
+
+
+class CustomBuildHook(BuildHookInterface):
+    def initialize(self, version, build_data):
+        super().initialize(version, build_data)
+        stderr.write(">>> Building Open Webui frontend\n")
+        npm = shutil.which("npm")
+        if npm is None:
+            raise RuntimeError(
+                "NodeJS `npm` is required for building Open Webui but it was not found"
+            )
+        stderr.write("### npm install\n")
+        subprocess.run([npm, "install"], check=True)  # noqa: S603
+        stderr.write("\n### npm run build\n")
+        os.environ["APP_BUILD_HASH"] = version
+        subprocess.run([npm, "run", "build"], check=True)  # noqa: S603
diff --git a/i18next-parser.config.ts b/i18next-parser.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..37ce57ee1474e49787145f82cd6a64ad65773c4d
--- /dev/null
+++ b/i18next-parser.config.ts
@@ -0,0 +1,38 @@
+// i18next-parser.config.ts
+import { getLanguages } from './src/lib/i18n/index.ts';
+
+const getLangCodes = async () => {
+	const languages = await getLanguages();
+	return languages.map((l) => l.code);
+};
+
+export default {
+	contextSeparator: '_',
+	createOldCatalogs: false,
+	defaultNamespace: 'translation',
+	defaultValue: '',
+	indentation: 2,
+	keepRemoved: false,
+	keySeparator: false,
+	lexers: {
+		svelte: ['JavascriptLexer'],
+		js: ['JavascriptLexer'],
+		ts: ['JavascriptLexer'],
+
+		default: ['JavascriptLexer']
+	},
+	lineEnding: 'auto',
+	locales: await getLangCodes(),
+	namespaceSeparator: false,
+	output: 'src/lib/i18n/locales/$LOCALE/$NAMESPACE.json',
+	pluralSeparator: '_',
+	input: 'src/**/*.{js,svelte}',
+	sort: true,
+	verbose: true,
+	failOnWarnings: false,
+	failOnUpdate: false,
+	customValueTemplate: null,
+	resetDefaultValueLocale: null,
+	i18nextOptions: null,
+	yamlOptions: null
+};
diff --git a/kubernetes/helm/README.md b/kubernetes/helm/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..5737007d964de10301f85357e83a91a3bf635248
--- /dev/null
+++ b/kubernetes/helm/README.md
@@ -0,0 +1,4 @@
+# Helm Charts
+Open WebUI Helm Charts are now hosted in a separate repo, which can be found here: https://github.com/open-webui/helm-charts 
+
+The charts are released at https://helm.openwebui.com. 
\ No newline at end of file
diff --git a/kubernetes/manifest/base/kustomization.yaml b/kubernetes/manifest/base/kustomization.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..61500f87c513a3e53c595422e1ed33169da4a129
--- /dev/null
+++ b/kubernetes/manifest/base/kustomization.yaml
@@ -0,0 +1,8 @@
+resources:
+  - open-webui.yaml
+  - ollama-service.yaml
+  - ollama-statefulset.yaml
+  - webui-deployment.yaml
+  - webui-service.yaml
+  - webui-ingress.yaml
+  - webui-pvc.yaml
diff --git a/kubernetes/manifest/base/ollama-service.yaml b/kubernetes/manifest/base/ollama-service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8bab65b59effa86ab8e80b6b3f8629ccdbf7ca39
--- /dev/null
+++ b/kubernetes/manifest/base/ollama-service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: ollama-service
+  namespace: open-webui
+spec:
+  selector:
+    app: ollama
+  ports:
+  - protocol: TCP
+    port: 11434
+    targetPort: 11434
\ No newline at end of file
diff --git a/kubernetes/manifest/base/ollama-statefulset.yaml b/kubernetes/manifest/base/ollama-statefulset.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..cd1144caf9d506c7cf5922395c9a075621ad0072
--- /dev/null
+++ b/kubernetes/manifest/base/ollama-statefulset.yaml
@@ -0,0 +1,41 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: ollama
+  namespace: open-webui
+spec:
+  serviceName: "ollama"
+  replicas: 1
+  selector:
+    matchLabels:
+      app: ollama
+  template:
+    metadata:
+      labels:
+        app: ollama
+    spec:
+      containers:
+      - name: ollama
+        image: ollama/ollama:latest
+        ports:
+        - containerPort: 11434
+        resources:
+          requests:
+            cpu: "2000m"
+            memory: "2Gi"
+          limits:
+            cpu: "4000m"
+            memory: "4Gi"
+            nvidia.com/gpu: "0"
+        volumeMounts:
+        - name: ollama-volume
+          mountPath: /root/.ollama
+        tty: true
+  volumeClaimTemplates:
+  - metadata:
+      name: ollama-volume
+    spec:
+      accessModes: [ "ReadWriteOnce" ]
+      resources:
+        requests:
+          storage: 30Gi
\ No newline at end of file
diff --git a/kubernetes/manifest/base/open-webui.yaml b/kubernetes/manifest/base/open-webui.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9c1a599f32690ddd8150be77b9bd8a7cd1b14245
--- /dev/null
+++ b/kubernetes/manifest/base/open-webui.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: open-webui
\ No newline at end of file
diff --git a/kubernetes/manifest/base/webui-deployment.yaml b/kubernetes/manifest/base/webui-deployment.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..79a0a9a23c9652bf755273e7801e3002499a5aa6
--- /dev/null
+++ b/kubernetes/manifest/base/webui-deployment.yaml
@@ -0,0 +1,38 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: open-webui-deployment
+  namespace: open-webui
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: open-webui
+  template:
+    metadata:
+      labels:
+        app: open-webui
+    spec:
+      containers:
+      - name: open-webui
+        image: ghcr.io/open-webui/open-webui:main
+        ports:
+        - containerPort: 8080
+        resources:
+          requests:
+            cpu: "500m"
+            memory: "500Mi"
+          limits:
+            cpu: "1000m"
+            memory: "1Gi"
+        env:
+        - name: OLLAMA_BASE_URL
+          value: "http://ollama-service.open-webui.svc.cluster.local:11434"
+        tty: true
+        volumeMounts:
+        - name: webui-volume
+          mountPath: /app/backend/data
+      volumes:
+      - name: webui-volume
+        persistentVolumeClaim:
+          claimName: open-webui-pvc          
\ No newline at end of file
diff --git a/kubernetes/manifest/base/webui-ingress.yaml b/kubernetes/manifest/base/webui-ingress.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..dc0b53ccd456e70910683d28bb117a0272992e1c
--- /dev/null
+++ b/kubernetes/manifest/base/webui-ingress.yaml
@@ -0,0 +1,20 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: open-webui-ingress
+  namespace: open-webui
+  #annotations:
+    # Use appropriate annotations for your Ingress controller, e.g., for NGINX:
+    # nginx.ingress.kubernetes.io/rewrite-target: /
+spec:
+  rules:
+  - host: open-webui.minikube.local
+    http:
+      paths:
+      - path: /
+        pathType: Prefix
+        backend:
+          service:
+            name: open-webui-service
+            port:
+              number: 8080
diff --git a/kubernetes/manifest/base/webui-pvc.yaml b/kubernetes/manifest/base/webui-pvc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..97fb761d422d510a2b725a08c3c52dca53c43ccd
--- /dev/null
+++ b/kubernetes/manifest/base/webui-pvc.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  labels:
+    app: open-webui
+  name: open-webui-pvc
+  namespace: open-webui
+spec:
+  accessModes: ["ReadWriteOnce"]
+  resources:
+    requests:
+      storage: 2Gi
\ No newline at end of file
diff --git a/kubernetes/manifest/base/webui-service.yaml b/kubernetes/manifest/base/webui-service.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d73845f00a8ec45bc39f629b819542e7d2ced84e
--- /dev/null
+++ b/kubernetes/manifest/base/webui-service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: open-webui-service
+  namespace: open-webui
+spec:
+  type: NodePort  # Use LoadBalancer if you're on a cloud that supports it
+  selector:
+    app: open-webui
+  ports:
+    - protocol: TCP
+      port: 8080
+      targetPort: 8080
+      # If using NodePort, you can optionally specify the nodePort:
+      # nodePort: 30000
\ No newline at end of file
diff --git a/kubernetes/manifest/gpu/kustomization.yaml b/kubernetes/manifest/gpu/kustomization.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c0d39fbfaab6be80a5542e238bec20ce98da096d
--- /dev/null
+++ b/kubernetes/manifest/gpu/kustomization.yaml
@@ -0,0 +1,8 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+
+resources:
+  - ../base
+
+patches:
+- path: ollama-statefulset-gpu.yaml
diff --git a/kubernetes/manifest/gpu/ollama-statefulset-gpu.yaml b/kubernetes/manifest/gpu/ollama-statefulset-gpu.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..3e42443656d06c6df25075d094e5c8efb180fda1
--- /dev/null
+++ b/kubernetes/manifest/gpu/ollama-statefulset-gpu.yaml
@@ -0,0 +1,17 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: ollama
+  namespace: open-webui
+spec:
+  selector:
+    matchLabels:
+      app: ollama
+  serviceName: "ollama"
+  template:
+    spec:
+      containers:
+      - name: ollama
+        resources:
+          limits:
+            nvidia.com/gpu: "1"
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..51dcb23eba0f8f07e88a5caf04b4079168c9eacc
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,12570 @@
+{
+	"name": "open-webui",
+	"version": "0.4.7",
+	"lockfileVersion": 3,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "open-webui",
+			"version": "0.4.7",
+			"dependencies": {
+				"@codemirror/lang-javascript": "^6.2.2",
+				"@codemirror/lang-python": "^6.1.6",
+				"@codemirror/language-data": "^6.5.1",
+				"@codemirror/theme-one-dark": "^6.1.2",
+				"@huggingface/transformers": "^3.0.0",
+				"@mediapipe/tasks-vision": "^0.10.17",
+				"@pyscript/core": "^0.4.32",
+				"@sveltejs/adapter-node": "^2.0.0",
+				"@tiptap/core": "^2.10.0",
+				"@tiptap/extension-code-block-lowlight": "^2.10.0",
+				"@tiptap/extension-highlight": "^2.10.0",
+				"@tiptap/extension-placeholder": "^2.10.0",
+				"@tiptap/extension-typography": "^2.10.0",
+				"@tiptap/pm": "^2.10.0",
+				"@tiptap/starter-kit": "^2.10.0",
+				"@xyflow/svelte": "^0.1.19",
+				"async": "^3.2.5",
+				"bits-ui": "^0.19.7",
+				"codemirror": "^6.0.1",
+				"crc-32": "^1.2.2",
+				"dayjs": "^1.11.10",
+				"dompurify": "^3.1.6",
+				"eventsource-parser": "^1.1.2",
+				"file-saver": "^2.0.5",
+				"fuse.js": "^7.0.0",
+				"highlight.js": "^11.9.0",
+				"i18next": "^23.10.0",
+				"i18next-browser-languagedetector": "^7.2.0",
+				"i18next-resources-to-backend": "^1.2.0",
+				"idb": "^7.1.1",
+				"js-sha256": "^0.10.1",
+				"katex": "^0.16.9",
+				"marked": "^9.1.0",
+				"mermaid": "^10.9.3",
+				"paneforge": "^0.0.6",
+				"panzoom": "^9.4.3",
+				"prosemirror-commands": "^1.6.0",
+				"prosemirror-example-setup": "^1.2.3",
+				"prosemirror-history": "^1.4.1",
+				"prosemirror-keymap": "^1.2.2",
+				"prosemirror-markdown": "^1.13.1",
+				"prosemirror-model": "^1.23.0",
+				"prosemirror-schema-basic": "^1.2.3",
+				"prosemirror-schema-list": "^1.4.1",
+				"prosemirror-state": "^1.4.3",
+				"prosemirror-view": "^1.34.3",
+				"pyodide": "^0.26.1",
+				"socket.io-client": "^4.2.0",
+				"sortablejs": "^1.15.2",
+				"svelte-sonner": "^0.3.19",
+				"tippy.js": "^6.3.7",
+				"turndown": "^7.2.0",
+				"uuid": "^9.0.1"
+			},
+			"devDependencies": {
+				"@sveltejs/adapter-auto": "3.2.2",
+				"@sveltejs/adapter-static": "^3.0.2",
+				"@sveltejs/kit": "^2.5.20",
+				"@sveltejs/vite-plugin-svelte": "^3.1.1",
+				"@tailwindcss/typography": "^0.5.13",
+				"@typescript-eslint/eslint-plugin": "^6.17.0",
+				"@typescript-eslint/parser": "^6.17.0",
+				"autoprefixer": "^10.4.16",
+				"cypress": "^13.15.0",
+				"eslint": "^8.56.0",
+				"eslint-config-prettier": "^9.1.0",
+				"eslint-plugin-cypress": "^3.4.0",
+				"eslint-plugin-svelte": "^2.43.0",
+				"i18next-parser": "^9.0.1",
+				"postcss": "^8.4.31",
+				"prettier": "^3.3.3",
+				"prettier-plugin-svelte": "^3.2.6",
+				"sass-embedded": "^1.81.0",
+				"svelte": "^4.2.18",
+				"svelte-check": "^3.8.5",
+				"svelte-confetti": "^1.3.2",
+				"tailwindcss": "^3.3.3",
+				"tslib": "^2.4.1",
+				"typescript": "^5.5.4",
+				"vite": "^5.3.5",
+				"vitest": "^1.6.0"
+			},
+			"engines": {
+				"node": ">=18.13.0 <=22.x.x",
+				"npm": ">=6.0.0"
+			}
+		},
+		"node_modules/@aashutoshrathi/word-wrap": {
+			"version": "1.2.6",
+			"resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+			"integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/@alloc/quick-lru": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+			"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/@ampproject/remapping": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+			"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+			"dependencies": {
+				"@jridgewell/gen-mapping": "^0.3.5",
+				"@jridgewell/trace-mapping": "^0.3.24"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@babel/runtime": {
+			"version": "7.24.1",
+			"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
+			"integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
+			"dependencies": {
+				"regenerator-runtime": "^0.14.0"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/@braintree/sanitize-url": {
+			"version": "6.0.4",
+			"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz",
+			"integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A=="
+		},
+		"node_modules/@bufbuild/protobuf": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.2.tgz",
+			"integrity": "sha512-UNtPCbrwrenpmrXuRwn9jYpPoweNXj8X5sMvYgsqYyaH8jQ6LfUJSk3dJLnBK+6sfYPrF4iAIo5sd5HQ+tg75A==",
+			"devOptional": true,
+			"license": "(Apache-2.0 AND BSD-3-Clause)"
+		},
+		"node_modules/@codemirror/autocomplete": {
+			"version": "6.16.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.2.tgz",
+			"integrity": "sha512-MjfDrHy0gHKlPWsvSsikhO1+BOh+eBHNgfH1OXs1+DAf30IonQldgMM3kxLDTG9ktE7kDLaA1j/l7KMPA4KNfw==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0"
+			},
+			"peerDependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/commands": {
+			"version": "6.6.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
+			"integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.4.0",
+				"@codemirror/view": "^6.27.0",
+				"@lezer/common": "^1.1.0"
+			}
+		},
+		"node_modules/@codemirror/lang-angular": {
+			"version": "0.1.3",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-angular/-/lang-angular-0.1.3.tgz",
+			"integrity": "sha512-xgeWGJQQl1LyStvndWtruUvb4SnBZDAu/gvFH/ZU+c0W25tQR8e5hq7WTwiIY2dNxnf+49mRiGI/9yxIwB6f5w==",
+			"dependencies": {
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/lang-javascript": "^6.1.2",
+				"@codemirror/language": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.3.3"
+			}
+		},
+		"node_modules/@codemirror/lang-cpp": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz",
+			"integrity": "sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/cpp": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-css": {
+			"version": "6.3.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.0.tgz",
+			"integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.0.2",
+				"@lezer/css": "^1.1.7"
+			}
+		},
+		"node_modules/@codemirror/lang-go": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.1.tgz",
+			"integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.6.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/go": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-html": {
+			"version": "6.4.9",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz",
+			"integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/lang-css": "^6.0.0",
+				"@codemirror/lang-javascript": "^6.0.0",
+				"@codemirror/language": "^6.4.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/css": "^1.1.0",
+				"@lezer/html": "^1.3.0"
+			}
+		},
+		"node_modules/@codemirror/lang-java": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.1.tgz",
+			"integrity": "sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/java": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-javascript": {
+			"version": "6.2.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
+			"integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.6.0",
+				"@codemirror/lint": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.17.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/javascript": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-json": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
+			"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/json": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-less": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-less/-/lang-less-6.0.2.tgz",
+			"integrity": "sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==",
+			"dependencies": {
+				"@codemirror/lang-css": "^6.2.0",
+				"@codemirror/language": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-liquid": {
+			"version": "6.2.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-liquid/-/lang-liquid-6.2.1.tgz",
+			"integrity": "sha512-J1Mratcm6JLNEiX+U2OlCDTysGuwbHD76XwuL5o5bo9soJtSbz2g6RU3vGHFyS5DC8rgVmFSzi7i6oBftm7tnA==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.3.1"
+			}
+		},
+		"node_modules/@codemirror/lang-markdown": {
+			"version": "6.3.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.3.0.tgz",
+			"integrity": "sha512-lYrI8SdL/vhd0w0aHIEvIRLRecLF7MiiRfzXFZY94dFwHqC9HtgxgagJ8fyYNBldijGatf9wkms60d8SrAj6Nw==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.7.1",
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/language": "^6.3.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/common": "^1.2.1",
+				"@lezer/markdown": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-php": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.1.tgz",
+			"integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==",
+			"dependencies": {
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/php": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-python": {
+			"version": "6.1.6",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.6.tgz",
+			"integrity": "sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.3.2",
+				"@codemirror/language": "^6.8.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.2.1",
+				"@lezer/python": "^1.1.4"
+			}
+		},
+		"node_modules/@codemirror/lang-rust": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz",
+			"integrity": "sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/rust": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-sass": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz",
+			"integrity": "sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==",
+			"dependencies": {
+				"@codemirror/lang-css": "^6.2.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.0.2",
+				"@lezer/sass": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-sql": {
+			"version": "6.8.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.8.0.tgz",
+			"integrity": "sha512-aGLmY4OwGqN3TdSx3h6QeA1NrvaYtF7kkoWR/+W7/JzB0gQtJ+VJxewlnE3+VImhA4WVlhmkJr109PefOOhjLg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-vue": {
+			"version": "0.1.3",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz",
+			"integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==",
+			"dependencies": {
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/lang-javascript": "^6.1.2",
+				"@codemirror/language": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.3.1"
+			}
+		},
+		"node_modules/@codemirror/lang-wast": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz",
+			"integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-xml": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz",
+			"integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.4.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/common": "^1.0.0",
+				"@lezer/xml": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/lang-yaml": {
+			"version": "6.1.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz",
+			"integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.2.0",
+				"@lezer/yaml": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/language": {
+			"version": "6.10.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
+			"integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==",
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.23.0",
+				"@lezer/common": "^1.1.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0",
+				"style-mod": "^4.0.0"
+			}
+		},
+		"node_modules/@codemirror/language-data": {
+			"version": "6.5.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/language-data/-/language-data-6.5.1.tgz",
+			"integrity": "sha512-0sWxeUSNlBr6OmkqybUTImADFUP0M3P0IiSde4nc24bz/6jIYzqYSgkOSLS+CBIoW1vU8Q9KUWXscBXeoMVC9w==",
+			"dependencies": {
+				"@codemirror/lang-angular": "^0.1.0",
+				"@codemirror/lang-cpp": "^6.0.0",
+				"@codemirror/lang-css": "^6.0.0",
+				"@codemirror/lang-go": "^6.0.0",
+				"@codemirror/lang-html": "^6.0.0",
+				"@codemirror/lang-java": "^6.0.0",
+				"@codemirror/lang-javascript": "^6.0.0",
+				"@codemirror/lang-json": "^6.0.0",
+				"@codemirror/lang-less": "^6.0.0",
+				"@codemirror/lang-liquid": "^6.0.0",
+				"@codemirror/lang-markdown": "^6.0.0",
+				"@codemirror/lang-php": "^6.0.0",
+				"@codemirror/lang-python": "^6.0.0",
+				"@codemirror/lang-rust": "^6.0.0",
+				"@codemirror/lang-sass": "^6.0.0",
+				"@codemirror/lang-sql": "^6.0.0",
+				"@codemirror/lang-vue": "^0.1.1",
+				"@codemirror/lang-wast": "^6.0.0",
+				"@codemirror/lang-xml": "^6.0.0",
+				"@codemirror/lang-yaml": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/legacy-modes": "^6.4.0"
+			}
+		},
+		"node_modules/@codemirror/legacy-modes": {
+			"version": "6.4.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.1.tgz",
+			"integrity": "sha512-vdg3XY7OAs5uLDx2Iw+cGfnwtd7kM+Et/eMsqAGTfT/JKiVBQZXosTzjEbWAi/FrY6DcQIz8mQjBozFHZEUWQA==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0"
+			}
+		},
+		"node_modules/@codemirror/lint": {
+			"version": "6.8.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz",
+			"integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==",
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/search": {
+			"version": "6.5.6",
+			"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
+			"integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
+			"dependencies": {
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"crelt": "^1.0.5"
+			}
+		},
+		"node_modules/@codemirror/state": {
+			"version": "6.4.1",
+			"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
+			"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A=="
+		},
+		"node_modules/@codemirror/theme-one-dark": {
+			"version": "6.1.2",
+			"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
+			"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
+			"dependencies": {
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0",
+				"@lezer/highlight": "^1.0.0"
+			}
+		},
+		"node_modules/@codemirror/view": {
+			"version": "6.28.0",
+			"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.0.tgz",
+			"integrity": "sha512-fo7CelaUDKWIyemw4b+J57cWuRkOu4SWCCPfNDkPvfWkGjM9D5racHQXr4EQeYCD6zEBIBxGCeaKkQo+ysl0gA==",
+			"dependencies": {
+				"@codemirror/state": "^6.4.0",
+				"style-mod": "^4.1.0",
+				"w3c-keyname": "^2.2.4"
+			}
+		},
+		"node_modules/@colors/colors": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+			"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+			"dev": true,
+			"optional": true,
+			"engines": {
+				"node": ">=0.1.90"
+			}
+		},
+		"node_modules/@cypress/request": {
+			"version": "3.0.5",
+			"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz",
+			"integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==",
+			"dev": true,
+			"dependencies": {
+				"aws-sign2": "~0.7.0",
+				"aws4": "^1.8.0",
+				"caseless": "~0.12.0",
+				"combined-stream": "~1.0.6",
+				"extend": "~3.0.2",
+				"forever-agent": "~0.6.1",
+				"form-data": "~4.0.0",
+				"http-signature": "~1.4.0",
+				"is-typedarray": "~1.0.0",
+				"isstream": "~0.1.2",
+				"json-stringify-safe": "~5.0.1",
+				"mime-types": "~2.1.19",
+				"performance-now": "^2.1.0",
+				"qs": "6.13.0",
+				"safe-buffer": "^5.1.2",
+				"tough-cookie": "^4.1.3",
+				"tunnel-agent": "^0.6.0",
+				"uuid": "^8.3.2"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/@cypress/request/node_modules/uuid": {
+			"version": "8.3.2",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+			"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+			"dev": true,
+			"bin": {
+				"uuid": "dist/bin/uuid"
+			}
+		},
+		"node_modules/@cypress/xvfb": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
+			"integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==",
+			"dev": true,
+			"dependencies": {
+				"debug": "^3.1.0",
+				"lodash.once": "^4.1.1"
+			}
+		},
+		"node_modules/@cypress/xvfb/node_modules/debug": {
+			"version": "3.2.7",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+			"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+			"dev": true,
+			"dependencies": {
+				"ms": "^2.1.1"
+			}
+		},
+		"node_modules/@emnapi/runtime": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
+			"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
+			"optional": true,
+			"dependencies": {
+				"tslib": "^2.4.0"
+			}
+		},
+		"node_modules/@esbuild/aix-ppc64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+			"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"aix"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-arm": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+			"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-arm64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+			"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/android-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+			"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-arm64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
+			"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/darwin-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+			"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+			"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/freebsd-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+			"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+			"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+			"cpu": [
+				"arm"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-arm64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+			"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ia32": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+			"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-loong64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+			"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+			"cpu": [
+				"loong64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-mips64el": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+			"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+			"cpu": [
+				"mips64el"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-ppc64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+			"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+			"cpu": [
+				"ppc64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-riscv64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+			"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+			"cpu": [
+				"riscv64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-s390x": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+			"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+			"cpu": [
+				"s390x"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/linux-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+			"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/netbsd-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+			"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/openbsd-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+			"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/sunos-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+			"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-arm64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+			"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+			"cpu": [
+				"arm64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-ia32": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+			"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+			"cpu": [
+				"ia32"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@esbuild/win32-x64": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+			"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+			"cpu": [
+				"x64"
+			],
+			"dev": true,
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@eslint-community/eslint-utils": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+			"integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+			"dev": true,
+			"dependencies": {
+				"eslint-visitor-keys": "^3.3.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"peerDependencies": {
+				"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+			}
+		},
+		"node_modules/@eslint-community/regexpp": {
+			"version": "4.10.0",
+			"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+			"integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+			"dev": true,
+			"engines": {
+				"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+			}
+		},
+		"node_modules/@eslint/eslintrc": {
+			"version": "2.1.4",
+			"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+			"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+			"dev": true,
+			"dependencies": {
+				"ajv": "^6.12.4",
+				"debug": "^4.3.2",
+				"espree": "^9.6.0",
+				"globals": "^13.19.0",
+				"ignore": "^5.2.0",
+				"import-fresh": "^3.2.1",
+				"js-yaml": "^4.1.0",
+				"minimatch": "^3.1.2",
+				"strip-json-comments": "^3.1.1"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/@eslint/eslintrc/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/@eslint/js": {
+			"version": "8.57.0",
+			"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+			"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+			"dev": true,
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			}
+		},
+		"node_modules/@floating-ui/core": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz",
+			"integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==",
+			"dependencies": {
+				"@floating-ui/utils": "^0.2.1"
+			}
+		},
+		"node_modules/@floating-ui/dom": {
+			"version": "1.6.3",
+			"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz",
+			"integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==",
+			"dependencies": {
+				"@floating-ui/core": "^1.0.0",
+				"@floating-ui/utils": "^0.2.0"
+			}
+		},
+		"node_modules/@floating-ui/utils": {
+			"version": "0.2.1",
+			"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
+			"integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
+		},
+		"node_modules/@gulpjs/to-absolute-glob": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz",
+			"integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==",
+			"dev": true,
+			"dependencies": {
+				"is-negated-glob": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/@huggingface/jinja": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.3.1.tgz",
+			"integrity": "sha512-SbcBWUKDQ76lzlVYOloscUk0SJjuL1LcbZsfQv/Bxxc7dwJMYuS+DAQ+HhVw6ZkTFXArejaX5HQRuCuleYwYdA==",
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/@huggingface/transformers": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/@huggingface/transformers/-/transformers-3.0.0.tgz",
+			"integrity": "sha512-OWIPnTijAw4DQ+IFHBOrej2SDdYyykYlTtpTLCEt5MZq/e9Cb65RS2YVhdGcgbaW/6JAL3i8ZA5UhDeWGm4iRQ==",
+			"dependencies": {
+				"@huggingface/jinja": "^0.3.0",
+				"onnxruntime-node": "1.19.2",
+				"onnxruntime-web": "1.20.0-dev.20241016-2b8fc5529b",
+				"sharp": "^0.33.5"
+			}
+		},
+		"node_modules/@humanwhocodes/config-array": {
+			"version": "0.11.14",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+			"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+			"dev": true,
+			"dependencies": {
+				"@humanwhocodes/object-schema": "^2.0.2",
+				"debug": "^4.3.1",
+				"minimatch": "^3.0.5"
+			},
+			"engines": {
+				"node": ">=10.10.0"
+			}
+		},
+		"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/@humanwhocodes/module-importer": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+			"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+			"dev": true,
+			"engines": {
+				"node": ">=12.22"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/nzakas"
+			}
+		},
+		"node_modules/@humanwhocodes/object-schema": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
+			"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+			"dev": true
+		},
+		"node_modules/@img/sharp-darwin-arm64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
+			"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-darwin-arm64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-darwin-x64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
+			"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-darwin-x64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-libvips-darwin-arm64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
+			"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-darwin-x64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
+			"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linux-arm": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
+			"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linux-arm64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
+			"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linux-s390x": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
+			"integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+			"cpu": [
+				"s390x"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linux-x64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
+			"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
+			"integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-libvips-linuxmusl-x64": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
+			"integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-linux-arm": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
+			"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linux-arm": "1.0.5"
+			}
+		},
+		"node_modules/@img/sharp-linux-arm64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
+			"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linux-arm64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-linux-s390x": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
+			"integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+			"cpu": [
+				"s390x"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linux-s390x": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-linux-x64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
+			"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linux-x64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-linuxmusl-arm64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
+			"integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-linuxmusl-x64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
+			"integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+			}
+		},
+		"node_modules/@img/sharp-wasm32": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
+			"integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+			"cpu": [
+				"wasm32"
+			],
+			"optional": true,
+			"dependencies": {
+				"@emnapi/runtime": "^1.2.0"
+			},
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-win32-ia32": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
+			"integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+			"cpu": [
+				"ia32"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@img/sharp-win32-x64": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
+			"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			}
+		},
+		"node_modules/@internationalized/date": {
+			"version": "3.5.2",
+			"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.5.2.tgz",
+			"integrity": "sha512-vo1yOMUt2hzp63IutEaTUxROdvQg1qlMRsbCvbay2AK2Gai7wIgCyK5weEX3nHkiLgo4qCXHijFNC/ILhlRpOQ==",
+			"dependencies": {
+				"@swc/helpers": "^0.5.0"
+			}
+		},
+		"node_modules/@isaacs/cliui": {
+			"version": "8.0.2",
+			"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+			"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+			"dependencies": {
+				"string-width": "^5.1.2",
+				"string-width-cjs": "npm:string-width@^4.2.0",
+				"strip-ansi": "^7.0.1",
+				"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+				"wrap-ansi": "^8.1.0",
+				"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+			"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-regex?sponsor=1"
+			}
+		},
+		"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+			"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+			"dependencies": {
+				"ansi-regex": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/strip-ansi?sponsor=1"
+			}
+		},
+		"node_modules/@isaacs/fs-minipass": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+			"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+			"dependencies": {
+				"minipass": "^7.0.4"
+			},
+			"engines": {
+				"node": ">=18.0.0"
+			}
+		},
+		"node_modules/@jest/schemas": {
+			"version": "29.6.3",
+			"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+			"integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
+			"dev": true,
+			"dependencies": {
+				"@sinclair/typebox": "^0.27.8"
+			},
+			"engines": {
+				"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+			}
+		},
+		"node_modules/@jridgewell/gen-mapping": {
+			"version": "0.3.5",
+			"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+			"integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+			"dependencies": {
+				"@jridgewell/set-array": "^1.2.1",
+				"@jridgewell/sourcemap-codec": "^1.4.10",
+				"@jridgewell/trace-mapping": "^0.3.24"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@jridgewell/resolve-uri": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+			"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@jridgewell/set-array": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+			"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/@jridgewell/sourcemap-codec": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+			"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+		},
+		"node_modules/@jridgewell/trace-mapping": {
+			"version": "0.3.25",
+			"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+			"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+			"dependencies": {
+				"@jridgewell/resolve-uri": "^3.1.0",
+				"@jridgewell/sourcemap-codec": "^1.4.14"
+			}
+		},
+		"node_modules/@lezer/common": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
+			"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ=="
+		},
+		"node_modules/@lezer/cpp": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.2.tgz",
+			"integrity": "sha512-macwKtyeUO0EW86r3xWQCzOV9/CF8imJLpJlPv3sDY57cPGeUZ8gXWOWNlJr52TVByMV3PayFQCA5SHEERDmVQ==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/css": {
+			"version": "1.1.9",
+			"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz",
+			"integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/go": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.0.tgz",
+			"integrity": "sha512-co9JfT3QqX1YkrMmourYw2Z8meGC50Ko4d54QEcQbEYpvdUvN4yb0NBZdn/9ertgvjsySxHsKzH3lbm3vqJ4Jw==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/highlight": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
+			"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
+			"dependencies": {
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/html": {
+			"version": "1.3.10",
+			"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz",
+			"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/java": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.2.tgz",
+			"integrity": "sha512-3j8X70JvYf0BZt8iSRLXLkt0Ry1hVUgH6wT32yBxH/Xi55nW2VMhc1Az4SKwu4YGSmxCm1fsqDDcHTuFjC8pmg==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/javascript": {
+			"version": "1.4.16",
+			"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.16.tgz",
+			"integrity": "sha512-84UXR3N7s11MPQHWgMnjb9571fr19MmXnr5zTv2XX0gHXXUvW3uPJ8GCjKrfTXmSdfktjRK0ayKklw+A13rk4g==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.1.3",
+				"@lezer/lr": "^1.3.0"
+			}
+		},
+		"node_modules/@lezer/json": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz",
+			"integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/lr": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz",
+			"integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==",
+			"dependencies": {
+				"@lezer/common": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/markdown": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.3.1.tgz",
+			"integrity": "sha512-DGlzU/i8DC8k0uz1F+jeePrkATl0jWakauTzftMQOcbaMkHbNSRki/4E2tOzJWsVpoKYhe7iTJ03aepdwVUXUA==",
+			"dependencies": {
+				"@lezer/common": "^1.0.0",
+				"@lezer/highlight": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/php": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.2.tgz",
+			"integrity": "sha512-GN7BnqtGRpFyeoKSEqxvGvhJQiI4zkgmYnDk/JIyc7H7Ifc1tkPnUn/R2R8meH3h/aBf5rzjvU8ZQoyiNDtDrA==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.1.0"
+			}
+		},
+		"node_modules/@lezer/python": {
+			"version": "1.1.14",
+			"resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.14.tgz",
+			"integrity": "sha512-ykDOb2Ti24n76PJsSa4ZoDF0zH12BSw1LGfQXCYJhJyOGiFTfGaX0Du66Ze72R+u/P35U+O6I9m8TFXov1JzsA==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/rust": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz",
+			"integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/sass": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.0.7.tgz",
+			"integrity": "sha512-8HLlOkuX/SMHOggI2DAsXUw38TuURe+3eQ5hiuk9QmYOUyC55B1dYEIMkav5A4IELVaW4e1T4P9WRiI5ka4mdw==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/xml": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.5.tgz",
+			"integrity": "sha512-VFouqOzmUWfIg+tfmpcdV33ewtK+NSwd4ngSe1aG7HFb4BN0ExyY1b8msp+ndFrnlG4V4iC8yXacjFtrwERnaw==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.0.0"
+			}
+		},
+		"node_modules/@lezer/yaml": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz",
+			"integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==",
+			"dependencies": {
+				"@lezer/common": "^1.2.0",
+				"@lezer/highlight": "^1.0.0",
+				"@lezer/lr": "^1.4.0"
+			}
+		},
+		"node_modules/@mediapipe/tasks-vision": {
+			"version": "0.10.17",
+			"resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz",
+			"integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg=="
+		},
+		"node_modules/@melt-ui/svelte": {
+			"version": "0.76.0",
+			"resolved": "https://registry.npmjs.org/@melt-ui/svelte/-/svelte-0.76.0.tgz",
+			"integrity": "sha512-X1ktxKujjLjOBt8LBvfckHGDMrkHWceRt1jdsUTf0EH76ikNPP1ofSoiV0IhlduDoCBV+2YchJ8kXCDfDXfC9Q==",
+			"dependencies": {
+				"@floating-ui/core": "^1.3.1",
+				"@floating-ui/dom": "^1.4.5",
+				"@internationalized/date": "^3.5.0",
+				"dequal": "^2.0.3",
+				"focus-trap": "^7.5.2",
+				"nanoid": "^5.0.4"
+			},
+			"peerDependencies": {
+				"svelte": ">=3 <5"
+			}
+		},
+		"node_modules/@mixmark-io/domino": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
+			"integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="
+		},
+		"node_modules/@nodelib/fs.scandir": {
+			"version": "2.1.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+			"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.stat": "2.0.5",
+				"run-parallel": "^1.1.9"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.stat": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+			"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+			"dev": true,
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@nodelib/fs.walk": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+			"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.scandir": "2.1.5",
+				"fastq": "^1.6.0"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/@pkgjs/parseargs": {
+			"version": "0.11.0",
+			"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+			"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+			"optional": true,
+			"engines": {
+				"node": ">=14"
+			}
+		},
+		"node_modules/@polka/url": {
+			"version": "1.0.0-next.28",
+			"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz",
+			"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
+			"license": "MIT"
+		},
+		"node_modules/@popperjs/core": {
+			"version": "2.11.8",
+			"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+			"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/popperjs"
+			}
+		},
+		"node_modules/@protobufjs/aspromise": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+			"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+		},
+		"node_modules/@protobufjs/base64": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+			"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+		},
+		"node_modules/@protobufjs/codegen": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+			"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+		},
+		"node_modules/@protobufjs/eventemitter": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+			"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+		},
+		"node_modules/@protobufjs/fetch": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+			"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+			"dependencies": {
+				"@protobufjs/aspromise": "^1.1.1",
+				"@protobufjs/inquire": "^1.1.0"
+			}
+		},
+		"node_modules/@protobufjs/float": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+			"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+		},
+		"node_modules/@protobufjs/inquire": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+			"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+		},
+		"node_modules/@protobufjs/path": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+			"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+		},
+		"node_modules/@protobufjs/pool": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+			"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+		},
+		"node_modules/@protobufjs/utf8": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+			"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+		},
+		"node_modules/@pyscript/core": {
+			"version": "0.4.32",
+			"resolved": "https://registry.npmjs.org/@pyscript/core/-/core-0.4.32.tgz",
+			"integrity": "sha512-WQATzPp1ggf871+PukCmTypzScXkEB1EWD/vg5GNxpM96N6rDPqQ13msuA5XvwU01ZVhL8HHSFDLk4IfaXNGWg==",
+			"dependencies": {
+				"@ungap/with-resolvers": "^0.1.0",
+				"basic-devtools": "^0.1.6",
+				"polyscript": "^0.12.8",
+				"sticky-module": "^0.1.1",
+				"to-json-callback": "^0.1.1",
+				"type-checked-collections": "^0.1.7"
+			}
+		},
+		"node_modules/@remirror/core-constants": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
+			"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
+			"license": "MIT"
+		},
+		"node_modules/@rollup/plugin-commonjs": {
+			"version": "25.0.7",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz",
+			"integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==",
+			"dependencies": {
+				"@rollup/pluginutils": "^5.0.1",
+				"commondir": "^1.0.1",
+				"estree-walker": "^2.0.2",
+				"glob": "^8.0.3",
+				"is-reference": "1.2.1",
+				"magic-string": "^0.30.3"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^2.68.0||^3.0.0||^4.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/plugin-json": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
+			"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
+			"dependencies": {
+				"@rollup/pluginutils": "^5.1.0"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/plugin-node-resolve": {
+			"version": "15.2.3",
+			"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz",
+			"integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==",
+			"dependencies": {
+				"@rollup/pluginutils": "^5.0.1",
+				"@types/resolve": "1.20.2",
+				"deepmerge": "^4.2.2",
+				"is-builtin-module": "^3.2.1",
+				"is-module": "^1.0.0",
+				"resolve": "^1.22.1"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^2.78.0||^3.0.0||^4.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/pluginutils": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz",
+			"integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==",
+			"dependencies": {
+				"@types/estree": "^1.0.0",
+				"estree-walker": "^2.0.2",
+				"picomatch": "^2.3.1"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+			},
+			"peerDependenciesMeta": {
+				"rollup": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@rollup/rollup-android-arm-eabi": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz",
+			"integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"android"
+			]
+		},
+		"node_modules/@rollup/rollup-android-arm64": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz",
+			"integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"android"
+			]
+		},
+		"node_modules/@rollup/rollup-darwin-arm64": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz",
+			"integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			]
+		},
+		"node_modules/@rollup/rollup-darwin-x64": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz",
+			"integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz",
+			"integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm-musleabihf": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz",
+			"integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm64-gnu": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz",
+			"integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-arm64-musl": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz",
+			"integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz",
+			"integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==",
+			"cpu": [
+				"ppc64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-riscv64-gnu": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz",
+			"integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==",
+			"cpu": [
+				"riscv64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-s390x-gnu": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz",
+			"integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==",
+			"cpu": [
+				"s390x"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-x64-gnu": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz",
+			"integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-linux-x64-musl": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz",
+			"integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-arm64-msvc": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz",
+			"integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-ia32-msvc": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz",
+			"integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==",
+			"cpu": [
+				"ia32"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@rollup/rollup-win32-x64-msvc": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz",
+			"integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			]
+		},
+		"node_modules/@sinclair/typebox": {
+			"version": "0.27.8",
+			"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
+			"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
+			"dev": true
+		},
+		"node_modules/@socket.io/component-emitter": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+			"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
+		},
+		"node_modules/@svelte-put/shortcut": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.1.1.tgz",
+			"integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==",
+			"peerDependencies": {
+				"svelte": "^3.55.0 || ^4.0.0 || ^5.0.0"
+			}
+		},
+		"node_modules/@sveltejs/adapter-auto": {
+			"version": "3.2.2",
+			"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.2.tgz",
+			"integrity": "sha512-Mso5xPCA8zgcKrv+QioVlqMZkyUQ5MjDJiEPuG/Z7cV/5tmwV7LmcVWk5tZ+H0NCOV1x12AsoSpt/CwFwuVXMA==",
+			"dev": true,
+			"dependencies": {
+				"import-meta-resolve": "^4.1.0"
+			},
+			"peerDependencies": {
+				"@sveltejs/kit": "^2.0.0"
+			}
+		},
+		"node_modules/@sveltejs/adapter-node": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-2.1.2.tgz",
+			"integrity": "sha512-ZfVY5buBclWHoBT+RbkMUViJGEIZ3IfT/0Hvhlgp+qC3LRZwp+wS1Zsw5dgkB2sFDZXctbLNXJtwlkjSp1mw0g==",
+			"dependencies": {
+				"@rollup/plugin-commonjs": "^25.0.7",
+				"@rollup/plugin-json": "^6.1.0",
+				"@rollup/plugin-node-resolve": "^15.2.3",
+				"rollup": "^4.8.0"
+			},
+			"peerDependencies": {
+				"@sveltejs/kit": "^2.0.0"
+			}
+		},
+		"node_modules/@sveltejs/adapter-static": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.2.tgz",
+			"integrity": "sha512-/EBFydZDwfwFfFEuF1vzUseBoRziwKP7AoHAwv+Ot3M084sE/HTVBHf9mCmXfdM9ijprY5YEugZjleflncX5fQ==",
+			"dev": true,
+			"peerDependencies": {
+				"@sveltejs/kit": "^2.0.0"
+			}
+		},
+		"node_modules/@sveltejs/kit": {
+			"version": "2.9.0",
+			"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz",
+			"integrity": "sha512-W3E7ed3ChB6kPqRs2H7tcHp+Z7oiTFC6m+lLyAQQuyXeqw6LdNuuwEUla+5VM0OGgqQD+cYD6+7Xq80vVm17Vg==",
+			"hasInstallScript": true,
+			"license": "MIT",
+			"dependencies": {
+				"@types/cookie": "^0.6.0",
+				"cookie": "^0.6.0",
+				"devalue": "^5.1.0",
+				"esm-env": "^1.2.1",
+				"import-meta-resolve": "^4.1.0",
+				"kleur": "^4.1.5",
+				"magic-string": "^0.30.5",
+				"mrmime": "^2.0.0",
+				"sade": "^1.8.1",
+				"set-cookie-parser": "^2.6.0",
+				"sirv": "^3.0.0",
+				"tiny-glob": "^0.2.9"
+			},
+			"bin": {
+				"svelte-kit": "svelte-kit.js"
+			},
+			"engines": {
+				"node": ">=18.13"
+			},
+			"peerDependencies": {
+				"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0",
+				"svelte": "^4.0.0 || ^5.0.0-next.0",
+				"vite": "^5.0.3 || ^6.0.0"
+			}
+		},
+		"node_modules/@sveltejs/vite-plugin-svelte": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.1.tgz",
+			"integrity": "sha512-rimpFEAboBBHIlzISibg94iP09k/KYdHgVhJlcsTfn7KMBhc70jFX/GRWkRdFCc2fdnk+4+Bdfej23cMDnJS6A==",
+			"dependencies": {
+				"@sveltejs/vite-plugin-svelte-inspector": "^2.1.0",
+				"debug": "^4.3.4",
+				"deepmerge": "^4.3.1",
+				"kleur": "^4.1.5",
+				"magic-string": "^0.30.10",
+				"svelte-hmr": "^0.16.0",
+				"vitefu": "^0.2.5"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20"
+			},
+			"peerDependencies": {
+				"svelte": "^4.0.0 || ^5.0.0-next.0",
+				"vite": "^5.0.0"
+			}
+		},
+		"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz",
+			"integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==",
+			"dependencies": {
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20"
+			},
+			"peerDependencies": {
+				"@sveltejs/vite-plugin-svelte": "^3.0.0",
+				"svelte": "^4.0.0 || ^5.0.0-next.0",
+				"vite": "^5.0.0"
+			}
+		},
+		"node_modules/@swc/helpers": {
+			"version": "0.5.7",
+			"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.7.tgz",
+			"integrity": "sha512-BVvNZhx362+l2tSwSuyEUV4h7+jk9raNdoTSdLfwTshXJSaGmYKluGRJznziCI3KX02Z19DdsQrdfrpXAU3Hfg==",
+			"dependencies": {
+				"tslib": "^2.4.0"
+			}
+		},
+		"node_modules/@tailwindcss/typography": {
+			"version": "0.5.13",
+			"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.13.tgz",
+			"integrity": "sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw==",
+			"dev": true,
+			"dependencies": {
+				"lodash.castarray": "^4.4.0",
+				"lodash.isplainobject": "^4.0.6",
+				"lodash.merge": "^4.6.2",
+				"postcss-selector-parser": "6.0.10"
+			},
+			"peerDependencies": {
+				"tailwindcss": ">=3.0.0 || insiders"
+			}
+		},
+		"node_modules/@tiptap/core": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.10.0.tgz",
+			"integrity": "sha512-58nAjPxLRFcXepdDqQRC1mhrw6E8Sanqr6bbO4Tz0+FWgDJMZvHG+dOK5wHaDVNSgK2iJDz08ETvQayfOOgDvg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-blockquote": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.10.0.tgz",
+			"integrity": "sha512-6Xmfo2lpfIRcbfkLD/NGX4YgQqfgAbu6XaZQZf5oGtHLPTrz4D7Mw20GgNBHzae2XwUCwLMt6zXOkBgU/LnlZg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-bold": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.10.0.tgz",
+			"integrity": "sha512-1wL8UI1Aii0u2cbDEvwyqsZb2pgBt8HLJdsIax/ELoF2tKCD5821nElqTGLBBg4pUGPa0ru9ZemuL8GdXZp3Qg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-bullet-list": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.10.0.tgz",
+			"integrity": "sha512-Cl+DGu6D3SgF/hlKUDNet3gaZFy6cPEonOOkHwzXoybDXXdddFbaTvt9MLkBRUR3ldksXuVRP2/LwZsK5WyxJQ==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-code": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.10.0.tgz",
+			"integrity": "sha512-8JznKG1Jmv8gJezZGPoka8oRmfrcAAnMEOeMpKXjwMrIbQ6QynTZpqMGGVL1kfkZlLV84PYm+CGjGgjSsT4iZw==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-code-block": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.10.0.tgz",
+			"integrity": "sha512-QH+LP7L1s1EJlrDFnfgOP0q+Siqt0Zbkx4ICMcUGvEsycl53Ti8P0DRW7fAjRISdTCItuWJYvtmiYY7O3rYb+Q==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-code-block-lowlight": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.10.0.tgz",
+			"integrity": "sha512-dAv03XIHT5h+sdFmJzvx2FfpfFOOK9SBKHflRUdqTa8eA+0VZNAcPRjvJWVEWqts1fKZDJj774mO28NlhFzk9Q==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/extension-code-block": "^2.7.0",
+				"@tiptap/pm": "^2.7.0",
+				"highlight.js": "^11",
+				"lowlight": "^2 || ^3"
+			}
+		},
+		"node_modules/@tiptap/extension-document": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.10.0.tgz",
+			"integrity": "sha512-vseMW3EKiQAPgdbN48Y8F0nRqWhhrAo9DLacAfP7tu0x3uv44uotNjDBtAgp5QmJmqQVyrEdkLSZaU5vFzduhQ==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-dropcursor": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.10.0.tgz",
+			"integrity": "sha512-tifxp/a3NxTjLAuYBx9XAwVo4MSDoY/mQ8E18QtuXj0vuieCFxd8Bkyre0otubIAAQePXLTVGQoxPrKmMAa+Jg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-gapcursor": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.10.0.tgz",
+			"integrity": "sha512-GViEnSnEBE74k7SYdXrQ4aXlKmWkrd9awdj/TgDSORgpZ4Dfyqtn+ENIWWby4NhL+BPM9P5hGCjkQXZsi6JKOw==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-hard-break": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.10.0.tgz",
+			"integrity": "sha512-NL/xPYUhhvQyCnOO5Yn+BlBOMLC1ru32nw7ox12TShGmaeKBrnV0DhzBRkyJU0MqCS26oWjieNPxfu0lR3oMSA==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-heading": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.10.0.tgz",
+			"integrity": "sha512-x2Uj5wrAHFaUdlChwLoQVmWtzZCuNyJpBRA19kA4idWL5z+6cIrUWepvwVBxA8ou6ictbzWW15o+blKtW7DlqA==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-highlight": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.10.0.tgz",
+			"integrity": "sha512-HU8UuKU7ljlzNn7jg29pM8QtIX7QvePcBjcWAt6K3qVwF1cbBNguIjKRY2rmoonU2nu8I6GknQNgV847kZifCQ==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-history": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.10.0.tgz",
+			"integrity": "sha512-5aYOmxqaCnw7e7wmWqFZmkpYCxxDjEzFbgVI6WknqNwqeOizR4+YJf3aAt/lTbksLJe47XF+NBX51gOm/ZBCiw==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-horizontal-rule": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.10.0.tgz",
+			"integrity": "sha512-el1SzI/x/h4HW8UltxJlyMSrRsO55ypKPLQHJC9h7F6kTTR31fJUzQa3AeTFrZvXS0kNHIFRpAMstw+N0L5TYg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-italic": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.10.0.tgz",
+			"integrity": "sha512-MqPYbHAEeO8QBvZRIkF4J2OTf/uiUPzUiXGLJ50w1ozfMBIw1txMvfR3g2cpwfvZlcOgYTgy7M0Oq00nQz5eXg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-list-item": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.10.0.tgz",
+			"integrity": "sha512-BxC6NNHd2xcC+mk5hpYWURUdj/mRz6TGFwH5CsyrUXPxApx0+V+EPHaAgdpu8dr+jtTEzjXF62V6e2JmOAPimg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-ordered-list": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.10.0.tgz",
+			"integrity": "sha512-jsK+mvzs7HmxQuQOU3HgIga+v7zUbQlmSP4/danusqUihJ+lc1n0frDCIkVvJrnSB3FChvNgT6ZEA14HOhdJzg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-paragraph": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.10.0.tgz",
+			"integrity": "sha512-4LUkVaJYjNdNZ7QOX6TRcA+m7oCtyrLGk49G22wl7XcPBkQPILP1mCUCU4f41bhjfhCgK5PPWP63kMtD+cEACg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-placeholder": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.10.0.tgz",
+			"integrity": "sha512-1o6azk2plgYAFgMrV3prnBb1NZjl2V1T3wwnH4n3/h9z9lJ0v5BBAk9r+TRYSrcdXknwwHAWFYnQe6dc9buG2g==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0",
+				"@tiptap/pm": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-strike": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.10.0.tgz",
+			"integrity": "sha512-SxApLJMQkxnmPGR3lwaskvLK61yI+Bu9hGZGdwMZqNh6o3LoDOxDaXjHD5joeMYQiqQrBE9zg46506MsXtrU7Q==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-text": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.10.0.tgz",
+			"integrity": "sha512-SSnNncADS1KucdEcJlF6WGCs5+1pAhPrD68vlw34oj3NDT3Zh05KiyXsCV3Nw4wpHOnbWahV+z3uT2SnR+xgoQ==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-text-style": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.10.0.tgz",
+			"integrity": "sha512-VZtH1dp64wg1UcFtUPpRQK+kOm4JHBIv+WXuKX7EnpIEKjHKnyfV94BBVmaqY5UE4n3kbkkmIRB2Cmix/10AMg==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/extension-typography": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/extension-typography/-/extension-typography-2.10.0.tgz",
+			"integrity": "sha512-03IOfJm4bk2hZ4SsSfxgBOVzcDxMRBlFD7ZY12H2EGNf1TKxj/0ANWhAH54FtquuOMoY5aWg5LZf0lk++8UDAw==",
+			"license": "MIT",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			},
+			"peerDependencies": {
+				"@tiptap/core": "^2.7.0"
+			}
+		},
+		"node_modules/@tiptap/pm": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.10.0.tgz",
+			"integrity": "sha512-ohshlWf4MlW6D3rQkNQnhmiQ2w4pwRoQcJmTPt8UJoIDGkeKmZh494fQp4Aeh80XuGd81SsCv//1HJeyaeHJYQ==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-changeset": "^2.2.1",
+				"prosemirror-collab": "^1.3.1",
+				"prosemirror-commands": "^1.6.2",
+				"prosemirror-dropcursor": "^1.8.1",
+				"prosemirror-gapcursor": "^1.3.2",
+				"prosemirror-history": "^1.4.1",
+				"prosemirror-inputrules": "^1.4.0",
+				"prosemirror-keymap": "^1.2.2",
+				"prosemirror-markdown": "^1.13.1",
+				"prosemirror-menu": "^1.2.4",
+				"prosemirror-model": "^1.23.0",
+				"prosemirror-schema-basic": "^1.2.3",
+				"prosemirror-schema-list": "^1.4.1",
+				"prosemirror-state": "^1.4.3",
+				"prosemirror-tables": "^1.6.1",
+				"prosemirror-trailing-node": "^3.0.0",
+				"prosemirror-transform": "^1.10.2",
+				"prosemirror-view": "^1.36.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			}
+		},
+		"node_modules/@tiptap/starter-kit": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.10.0.tgz",
+			"integrity": "sha512-hMIM9a6HjYZo25EzhZHlKEIR7CFi0grRSOltEyggiyBuQqKFkI7iwCpZVVtviDV1FwV0EPANpIAxPS7aBRgFdg==",
+			"license": "MIT",
+			"dependencies": {
+				"@tiptap/core": "^2.10.0",
+				"@tiptap/extension-blockquote": "^2.10.0",
+				"@tiptap/extension-bold": "^2.10.0",
+				"@tiptap/extension-bullet-list": "^2.10.0",
+				"@tiptap/extension-code": "^2.10.0",
+				"@tiptap/extension-code-block": "^2.10.0",
+				"@tiptap/extension-document": "^2.10.0",
+				"@tiptap/extension-dropcursor": "^2.10.0",
+				"@tiptap/extension-gapcursor": "^2.10.0",
+				"@tiptap/extension-hard-break": "^2.10.0",
+				"@tiptap/extension-heading": "^2.10.0",
+				"@tiptap/extension-history": "^2.10.0",
+				"@tiptap/extension-horizontal-rule": "^2.10.0",
+				"@tiptap/extension-italic": "^2.10.0",
+				"@tiptap/extension-list-item": "^2.10.0",
+				"@tiptap/extension-ordered-list": "^2.10.0",
+				"@tiptap/extension-paragraph": "^2.10.0",
+				"@tiptap/extension-strike": "^2.10.0",
+				"@tiptap/extension-text": "^2.10.0",
+				"@tiptap/extension-text-style": "^2.10.0",
+				"@tiptap/pm": "^2.10.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/ueberdosis"
+			}
+		},
+		"node_modules/@types/cookie": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+			"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
+		},
+		"node_modules/@types/d3-color": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+			"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
+		},
+		"node_modules/@types/d3-drag": {
+			"version": "3.0.7",
+			"resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+			"integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+			"dependencies": {
+				"@types/d3-selection": "*"
+			}
+		},
+		"node_modules/@types/d3-interpolate": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+			"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+			"dependencies": {
+				"@types/d3-color": "*"
+			}
+		},
+		"node_modules/@types/d3-scale": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz",
+			"integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==",
+			"dependencies": {
+				"@types/d3-time": "*"
+			}
+		},
+		"node_modules/@types/d3-scale-chromatic": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz",
+			"integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw=="
+		},
+		"node_modules/@types/d3-selection": {
+			"version": "3.0.10",
+			"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz",
+			"integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg=="
+		},
+		"node_modules/@types/d3-time": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz",
+			"integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw=="
+		},
+		"node_modules/@types/d3-transition": {
+			"version": "3.0.8",
+			"resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz",
+			"integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==",
+			"dependencies": {
+				"@types/d3-selection": "*"
+			}
+		},
+		"node_modules/@types/d3-zoom": {
+			"version": "3.0.8",
+			"resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+			"integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+			"dependencies": {
+				"@types/d3-interpolate": "*",
+				"@types/d3-selection": "*"
+			}
+		},
+		"node_modules/@types/debug": {
+			"version": "4.1.12",
+			"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+			"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+			"dependencies": {
+				"@types/ms": "*"
+			}
+		},
+		"node_modules/@types/estree": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+			"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+		},
+		"node_modules/@types/hast": {
+			"version": "3.0.4",
+			"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+			"integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@types/unist": "*"
+			}
+		},
+		"node_modules/@types/json-schema": {
+			"version": "7.0.15",
+			"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+			"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+			"dev": true
+		},
+		"node_modules/@types/linkify-it": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+			"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="
+		},
+		"node_modules/@types/markdown-it": {
+			"version": "14.1.2",
+			"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+			"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+			"dependencies": {
+				"@types/linkify-it": "^5",
+				"@types/mdurl": "^2"
+			}
+		},
+		"node_modules/@types/mdast": {
+			"version": "3.0.15",
+			"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz",
+			"integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==",
+			"dependencies": {
+				"@types/unist": "^2"
+			}
+		},
+		"node_modules/@types/mdurl": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+			"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="
+		},
+		"node_modules/@types/minimatch": {
+			"version": "3.0.5",
+			"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+			"integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+			"dev": true
+		},
+		"node_modules/@types/ms": {
+			"version": "0.7.34",
+			"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
+			"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
+		},
+		"node_modules/@types/node": {
+			"version": "20.11.30",
+			"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
+			"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
+			"dependencies": {
+				"undici-types": "~5.26.4"
+			}
+		},
+		"node_modules/@types/pug": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
+			"integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==",
+			"dev": true
+		},
+		"node_modules/@types/resolve": {
+			"version": "1.20.2",
+			"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+			"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
+		},
+		"node_modules/@types/semver": {
+			"version": "7.5.8",
+			"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+			"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+			"dev": true
+		},
+		"node_modules/@types/sinonjs__fake-timers": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz",
+			"integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==",
+			"dev": true
+		},
+		"node_modules/@types/sizzle": {
+			"version": "2.3.8",
+			"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz",
+			"integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==",
+			"dev": true
+		},
+		"node_modules/@types/symlink-or-copy": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz",
+			"integrity": "sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA==",
+			"dev": true
+		},
+		"node_modules/@types/unist": {
+			"version": "2.0.10",
+			"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz",
+			"integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="
+		},
+		"node_modules/@types/yauzl": {
+			"version": "2.10.3",
+			"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+			"integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+			"dev": true,
+			"optional": true,
+			"dependencies": {
+				"@types/node": "*"
+			}
+		},
+		"node_modules/@typescript-eslint/eslint-plugin": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
+			"integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/regexpp": "^4.5.1",
+				"@typescript-eslint/scope-manager": "6.21.0",
+				"@typescript-eslint/type-utils": "6.21.0",
+				"@typescript-eslint/utils": "6.21.0",
+				"@typescript-eslint/visitor-keys": "6.21.0",
+				"debug": "^4.3.4",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.2.4",
+				"natural-compare": "^1.4.0",
+				"semver": "^7.5.4",
+				"ts-api-utils": "^1.0.1"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
+				"eslint": "^7.0.0 || ^8.0.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/parser": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
+			"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/scope-manager": "6.21.0",
+				"@typescript-eslint/types": "6.21.0",
+				"@typescript-eslint/typescript-estree": "6.21.0",
+				"@typescript-eslint/visitor-keys": "6.21.0",
+				"debug": "^4.3.4"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^7.0.0 || ^8.0.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/scope-manager": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
+			"integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "6.21.0",
+				"@typescript-eslint/visitor-keys": "6.21.0"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@typescript-eslint/type-utils": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
+			"integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/typescript-estree": "6.21.0",
+				"@typescript-eslint/utils": "6.21.0",
+				"debug": "^4.3.4",
+				"ts-api-utils": "^1.0.1"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^7.0.0 || ^8.0.0"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/types": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
+			"integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
+			"dev": true,
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@typescript-eslint/typescript-estree": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
+			"integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "6.21.0",
+				"@typescript-eslint/visitor-keys": "6.21.0",
+				"debug": "^4.3.4",
+				"globby": "^11.1.0",
+				"is-glob": "^4.0.3",
+				"minimatch": "9.0.3",
+				"semver": "^7.5.4",
+				"ts-api-utils": "^1.0.1"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependenciesMeta": {
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/@typescript-eslint/utils": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
+			"integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.4.0",
+				"@types/json-schema": "^7.0.12",
+				"@types/semver": "^7.5.0",
+				"@typescript-eslint/scope-manager": "6.21.0",
+				"@typescript-eslint/types": "6.21.0",
+				"@typescript-eslint/typescript-estree": "6.21.0",
+				"semver": "^7.5.4"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			},
+			"peerDependencies": {
+				"eslint": "^7.0.0 || ^8.0.0"
+			}
+		},
+		"node_modules/@typescript-eslint/visitor-keys": {
+			"version": "6.21.0",
+			"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
+			"integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
+			"dev": true,
+			"dependencies": {
+				"@typescript-eslint/types": "6.21.0",
+				"eslint-visitor-keys": "^3.4.1"
+			},
+			"engines": {
+				"node": "^16.0.0 || >=18.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/typescript-eslint"
+			}
+		},
+		"node_modules/@ungap/structured-clone": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+			"integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+		},
+		"node_modules/@ungap/with-resolvers": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/@ungap/with-resolvers/-/with-resolvers-0.1.0.tgz",
+			"integrity": "sha512-g7f0IkJdPW2xhY7H4iE72DAsIyfuwEFc6JWc2tYFwKDMWWAF699vGjrM348cwQuOXgHpe1gWFe+Eiyjx/ewvvw=="
+		},
+		"node_modules/@vitest/expect": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz",
+			"integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==",
+			"dev": true,
+			"dependencies": {
+				"@vitest/spy": "1.6.0",
+				"@vitest/utils": "1.6.0",
+				"chai": "^4.3.10"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/@vitest/runner": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz",
+			"integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==",
+			"dev": true,
+			"dependencies": {
+				"@vitest/utils": "1.6.0",
+				"p-limit": "^5.0.0",
+				"pathe": "^1.1.1"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/@vitest/runner/node_modules/p-limit": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz",
+			"integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==",
+			"dev": true,
+			"dependencies": {
+				"yocto-queue": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=18"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/@vitest/runner/node_modules/yocto-queue": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
+			"integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
+			"dev": true,
+			"engines": {
+				"node": ">=12.20"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/@vitest/snapshot": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz",
+			"integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==",
+			"dev": true,
+			"dependencies": {
+				"magic-string": "^0.30.5",
+				"pathe": "^1.1.1",
+				"pretty-format": "^29.7.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/@vitest/spy": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz",
+			"integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==",
+			"dev": true,
+			"dependencies": {
+				"tinyspy": "^2.2.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/@vitest/utils": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz",
+			"integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==",
+			"dev": true,
+			"dependencies": {
+				"diff-sequences": "^29.6.3",
+				"estree-walker": "^3.0.3",
+				"loupe": "^2.3.7",
+				"pretty-format": "^29.7.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/@vitest/utils/node_modules/estree-walker": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+			"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+			"dev": true,
+			"dependencies": {
+				"@types/estree": "^1.0.0"
+			}
+		},
+		"node_modules/@webreflection/fetch": {
+			"version": "0.1.5",
+			"resolved": "https://registry.npmjs.org/@webreflection/fetch/-/fetch-0.1.5.tgz",
+			"integrity": "sha512-zCcqCJoNLvdeF41asAK71XPlwSPieeRDsE09albBunJEksuYPYNillKNQjf8p5BqSoTKTuKrW3lUm3MNodUC4g=="
+		},
+		"node_modules/@xyflow/svelte": {
+			"version": "0.1.19",
+			"resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-0.1.19.tgz",
+			"integrity": "sha512-yW5w5aI+Yqkob4kLQpVDo/ZmX+E9Pw7459kqwLfv4YG4N1NYXrsDRh9cyph/rapbuDnPi6zqK5E8LKrgaCQC0w==",
+			"dependencies": {
+				"@svelte-put/shortcut": "^3.1.0",
+				"@xyflow/system": "0.0.42",
+				"classcat": "^5.0.4"
+			},
+			"peerDependencies": {
+				"svelte": "^3.0.0 || ^4.0.0"
+			}
+		},
+		"node_modules/@xyflow/system": {
+			"version": "0.0.42",
+			"resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.42.tgz",
+			"integrity": "sha512-kWYj+Y0GOct0jKYTdyRMNOLPxGNbb2TYvPg2gTmJnZ31DOOMkL5uRBLX825DR2gOACDu+i5FHLxPJUPf/eGOJw==",
+			"dependencies": {
+				"@types/d3-drag": "^3.0.7",
+				"@types/d3-selection": "^3.0.10",
+				"@types/d3-transition": "^3.0.8",
+				"@types/d3-zoom": "^3.0.8",
+				"d3-drag": "^3.0.0",
+				"d3-selection": "^3.0.0",
+				"d3-zoom": "^3.0.0"
+			}
+		},
+		"node_modules/acorn": {
+			"version": "8.11.3",
+			"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+			"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+			"bin": {
+				"acorn": "bin/acorn"
+			},
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/acorn-jsx": {
+			"version": "5.3.2",
+			"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+			"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+			"dev": true,
+			"peerDependencies": {
+				"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+			}
+		},
+		"node_modules/acorn-walk": {
+			"version": "8.3.2",
+			"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
+			"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/aggregate-error": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+			"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+			"dev": true,
+			"dependencies": {
+				"clean-stack": "^2.0.0",
+				"indent-string": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/ajv": {
+			"version": "6.12.6",
+			"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+			"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+			"dev": true,
+			"dependencies": {
+				"fast-deep-equal": "^3.1.1",
+				"fast-json-stable-stringify": "^2.0.0",
+				"json-schema-traverse": "^0.4.1",
+				"uri-js": "^4.2.2"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/epoberezkin"
+			}
+		},
+		"node_modules/amator": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/amator/-/amator-1.1.0.tgz",
+			"integrity": "sha512-V5+aH8pe+Z3u/UG3L3pG3BaFQGXAyXHVQDroRwjPHdh08bcUEchAVsU1MCuJSCaU5o60wTK6KaE6te5memzgYw==",
+			"dependencies": {
+				"bezier-easing": "^2.0.3"
+			}
+		},
+		"node_modules/ansi-colors": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+			"integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/ansi-escapes": {
+			"version": "4.3.2",
+			"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+			"integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+			"dev": true,
+			"dependencies": {
+				"type-fest": "^0.21.3"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ansi-escapes/node_modules/type-fest": {
+			"version": "0.21.3",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+			"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/ansi-regex": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+			"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/ansi-styles": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+			"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+			"dependencies": {
+				"color-convert": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/any-promise": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+			"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+			"dev": true
+		},
+		"node_modules/anymatch": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+			"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+			"dev": true,
+			"dependencies": {
+				"normalize-path": "^3.0.0",
+				"picomatch": "^2.0.4"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/arch": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+			"integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/arg": {
+			"version": "5.0.2",
+			"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+			"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+			"dev": true
+		},
+		"node_modules/argparse": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+			"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+		},
+		"node_modules/aria-query": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+			"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+			"dependencies": {
+				"dequal": "^2.0.3"
+			}
+		},
+		"node_modules/array-union": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+			"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/asn1": {
+			"version": "0.2.6",
+			"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+			"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+			"dev": true,
+			"dependencies": {
+				"safer-buffer": "~2.1.0"
+			}
+		},
+		"node_modules/assert-plus": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+			"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/assertion-error": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+			"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/astral-regex": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+			"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/async": {
+			"version": "3.2.5",
+			"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
+			"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
+		},
+		"node_modules/asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+			"dev": true
+		},
+		"node_modules/at-least-node": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+			"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4.0.0"
+			}
+		},
+		"node_modules/autoprefixer": {
+			"version": "10.4.19",
+			"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
+			"integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/autoprefixer"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"dependencies": {
+				"browserslist": "^4.23.0",
+				"caniuse-lite": "^1.0.30001599",
+				"fraction.js": "^4.3.7",
+				"normalize-range": "^0.1.2",
+				"picocolors": "^1.0.0",
+				"postcss-value-parser": "^4.2.0"
+			},
+			"bin": {
+				"autoprefixer": "bin/autoprefixer"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			},
+			"peerDependencies": {
+				"postcss": "^8.1.0"
+			}
+		},
+		"node_modules/aws-sign2": {
+			"version": "0.7.0",
+			"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+			"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/aws4": {
+			"version": "1.13.2",
+			"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
+			"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
+			"dev": true
+		},
+		"node_modules/axobject-query": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
+			"integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
+			"dependencies": {
+				"dequal": "^2.0.3"
+			}
+		},
+		"node_modules/balanced-match": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+			"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+		},
+		"node_modules/bare-events": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz",
+			"integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==",
+			"dev": true,
+			"optional": true
+		},
+		"node_modules/base64-js": {
+			"version": "1.5.1",
+			"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+			"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/basic-devtools": {
+			"version": "0.1.6",
+			"resolved": "https://registry.npmjs.org/basic-devtools/-/basic-devtools-0.1.6.tgz",
+			"integrity": "sha512-g9zJ63GmdUesS3/Fwv0B5SYX6nR56TQXmGr+wE5PRTNCnGQMYWhUx/nZB/mMWnQJVLPPAp89oxDNlasdtNkW5Q=="
+		},
+		"node_modules/bcrypt-pbkdf": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+			"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+			"dev": true,
+			"dependencies": {
+				"tweetnacl": "^0.14.3"
+			}
+		},
+		"node_modules/bezier-easing": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
+			"integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig=="
+		},
+		"node_modules/binary-extensions": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+			"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/bits-ui": {
+			"version": "0.19.7",
+			"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.19.7.tgz",
+			"integrity": "sha512-GHUpKvN7QyazhnZNkUy0lxg6W1M6KJHWSZ4a/UGCjPE6nQgk6vKbGysY67PkDtQMknZTZAzVoMj1Eic4IKeCRQ==",
+			"dependencies": {
+				"@internationalized/date": "^3.5.1",
+				"@melt-ui/svelte": "0.76.0",
+				"nanoid": "^5.0.5"
+			},
+			"peerDependencies": {
+				"svelte": "^4.0.0"
+			}
+		},
+		"node_modules/bl": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz",
+			"integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==",
+			"dev": true,
+			"dependencies": {
+				"buffer": "^6.0.3",
+				"inherits": "^2.0.4",
+				"readable-stream": "^3.4.0"
+			}
+		},
+		"node_modules/bl/node_modules/readable-stream": {
+			"version": "3.6.2",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+			"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+			"dev": true,
+			"dependencies": {
+				"inherits": "^2.0.3",
+				"string_decoder": "^1.1.1",
+				"util-deprecate": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/blob-util": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz",
+			"integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==",
+			"dev": true
+		},
+		"node_modules/bluebird": {
+			"version": "3.7.2",
+			"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+			"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+			"dev": true
+		},
+		"node_modules/boolbase": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+			"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+			"dev": true
+		},
+		"node_modules/brace-expansion": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+			"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+			"dependencies": {
+				"balanced-match": "^1.0.0"
+			}
+		},
+		"node_modules/braces": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+			"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+			"dev": true,
+			"dependencies": {
+				"fill-range": "^7.1.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/broccoli-node-api": {
+			"version": "1.7.0",
+			"resolved": "https://registry.npmjs.org/broccoli-node-api/-/broccoli-node-api-1.7.0.tgz",
+			"integrity": "sha512-QIqLSVJWJUVOhclmkmypJJH9u9s/aWH4+FH6Q6Ju5l+Io4dtwqdPUNmDfw40o6sxhbZHhqGujDJuHTML1wG8Yw==",
+			"dev": true
+		},
+		"node_modules/broccoli-node-info": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/broccoli-node-info/-/broccoli-node-info-2.2.0.tgz",
+			"integrity": "sha512-VabSGRpKIzpmC+r+tJueCE5h8k6vON7EIMMWu6d/FyPdtijwLQ7QvzShEw+m3mHoDzUaj/kiZsDYrS8X2adsBg==",
+			"dev": true,
+			"engines": {
+				"node": "8.* || >= 10.*"
+			}
+		},
+		"node_modules/broccoli-output-wrapper": {
+			"version": "3.2.5",
+			"resolved": "https://registry.npmjs.org/broccoli-output-wrapper/-/broccoli-output-wrapper-3.2.5.tgz",
+			"integrity": "sha512-bQAtwjSrF4Nu0CK0JOy5OZqw9t5U0zzv2555EA/cF8/a8SLDTIetk9UgrtMVw7qKLKdSpOZ2liZNeZZDaKgayw==",
+			"dev": true,
+			"dependencies": {
+				"fs-extra": "^8.1.0",
+				"heimdalljs-logger": "^0.1.10",
+				"symlink-or-copy": "^1.2.0"
+			},
+			"engines": {
+				"node": "10.* || >= 12.*"
+			}
+		},
+		"node_modules/broccoli-output-wrapper/node_modules/fs-extra": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+			"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+			"dev": true,
+			"dependencies": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^4.0.0",
+				"universalify": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=6 <7 || >=8"
+			}
+		},
+		"node_modules/broccoli-output-wrapper/node_modules/jsonfile": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+			"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+			"dev": true,
+			"optionalDependencies": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"node_modules/broccoli-output-wrapper/node_modules/universalify": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4.0.0"
+			}
+		},
+		"node_modules/broccoli-plugin": {
+			"version": "4.0.7",
+			"resolved": "https://registry.npmjs.org/broccoli-plugin/-/broccoli-plugin-4.0.7.tgz",
+			"integrity": "sha512-a4zUsWtA1uns1K7p9rExYVYG99rdKeGRymW0qOCNkvDPHQxVi3yVyJHhQbM3EZwdt2E0mnhr5e0c/bPpJ7p3Wg==",
+			"dev": true,
+			"dependencies": {
+				"broccoli-node-api": "^1.7.0",
+				"broccoli-output-wrapper": "^3.2.5",
+				"fs-merger": "^3.2.1",
+				"promise-map-series": "^0.3.0",
+				"quick-temp": "^0.1.8",
+				"rimraf": "^3.0.2",
+				"symlink-or-copy": "^1.3.1"
+			},
+			"engines": {
+				"node": "10.* || >= 12.*"
+			}
+		},
+		"node_modules/browserslist": {
+			"version": "4.23.0",
+			"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+			"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/browserslist"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"dependencies": {
+				"caniuse-lite": "^1.0.30001587",
+				"electron-to-chromium": "^1.4.668",
+				"node-releases": "^2.0.14",
+				"update-browserslist-db": "^1.0.13"
+			},
+			"bin": {
+				"browserslist": "cli.js"
+			},
+			"engines": {
+				"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+			}
+		},
+		"node_modules/buffer": {
+			"version": "6.0.3",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+			"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.2.1"
+			}
+		},
+		"node_modules/buffer-builder": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
+			"integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==",
+			"devOptional": true,
+			"license": "MIT/X11"
+		},
+		"node_modules/buffer-crc32": {
+			"version": "0.2.13",
+			"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+			"integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/builtin-modules": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+			"integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/cac": {
+			"version": "6.7.14",
+			"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+			"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cachedir": {
+			"version": "2.4.0",
+			"resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz",
+			"integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/call-bind": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+			"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
+			"dev": true,
+			"dependencies": {
+				"es-define-property": "^1.0.0",
+				"es-errors": "^1.3.0",
+				"function-bind": "^1.1.2",
+				"get-intrinsic": "^1.2.4",
+				"set-function-length": "^1.2.1"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/callsites": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+			"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/camelcase-css": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+			"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/caniuse-lite": {
+			"version": "1.0.30001600",
+			"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
+			"integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			]
+		},
+		"node_modules/caseless": {
+			"version": "0.12.0",
+			"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+			"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+			"dev": true
+		},
+		"node_modules/chai": {
+			"version": "4.4.1",
+			"resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
+			"integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==",
+			"dev": true,
+			"dependencies": {
+				"assertion-error": "^1.1.0",
+				"check-error": "^1.0.3",
+				"deep-eql": "^4.1.3",
+				"get-func-name": "^2.0.2",
+				"loupe": "^2.3.6",
+				"pathval": "^1.1.1",
+				"type-detect": "^4.0.8"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/chalk": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+			"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.1.0",
+				"supports-color": "^7.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/chalk?sponsor=1"
+			}
+		},
+		"node_modules/character-entities": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+			"integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/check-error": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+			"integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+			"dev": true,
+			"dependencies": {
+				"get-func-name": "^2.0.2"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/check-more-types": {
+			"version": "2.24.0",
+			"resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
+			"integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/cheerio": {
+			"version": "1.0.0-rc.12",
+			"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+			"integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
+			"dev": true,
+			"dependencies": {
+				"cheerio-select": "^2.1.0",
+				"dom-serializer": "^2.0.0",
+				"domhandler": "^5.0.3",
+				"domutils": "^3.0.1",
+				"htmlparser2": "^8.0.1",
+				"parse5": "^7.0.0",
+				"parse5-htmlparser2-tree-adapter": "^7.0.0"
+			},
+			"engines": {
+				"node": ">= 6"
+			},
+			"funding": {
+				"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+			}
+		},
+		"node_modules/cheerio-select": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+			"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+			"dev": true,
+			"dependencies": {
+				"boolbase": "^1.0.0",
+				"css-select": "^5.1.0",
+				"css-what": "^6.1.0",
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.3",
+				"domutils": "^3.0.1"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/chokidar": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+			"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+			"dev": true,
+			"dependencies": {
+				"anymatch": "~3.1.2",
+				"braces": "~3.0.2",
+				"glob-parent": "~5.1.2",
+				"is-binary-path": "~2.1.0",
+				"is-glob": "~4.0.1",
+				"normalize-path": "~3.0.0",
+				"readdirp": "~3.6.0"
+			},
+			"engines": {
+				"node": ">= 8.10.0"
+			},
+			"funding": {
+				"url": "https://paulmillr.com/funding/"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/chokidar/node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/chownr": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+			"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/ci-info": {
+			"version": "3.9.0",
+			"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+			"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/sibiraj-s"
+				}
+			],
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/classcat": {
+			"version": "5.0.5",
+			"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
+			"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
+		},
+		"node_modules/clean-stack": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+			"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/cli-cursor": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+			"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+			"dev": true,
+			"dependencies": {
+				"restore-cursor": "^3.1.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cli-table3": {
+			"version": "0.6.4",
+			"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz",
+			"integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==",
+			"dev": true,
+			"dependencies": {
+				"string-width": "^4.2.0"
+			},
+			"engines": {
+				"node": "10.* || >= 12.*"
+			},
+			"optionalDependencies": {
+				"@colors/colors": "1.5.0"
+			}
+		},
+		"node_modules/cli-table3/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true
+		},
+		"node_modules/cli-table3/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/cli-truncate": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+			"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+			"dev": true,
+			"dependencies": {
+				"slice-ansi": "^3.0.0",
+				"string-width": "^4.2.0"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/cli-truncate/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true
+		},
+		"node_modules/cli-truncate/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/clone": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+			"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/clone-stats": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
+			"integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==",
+			"dev": true
+		},
+		"node_modules/code-red": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
+			"integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.4.15",
+				"@types/estree": "^1.0.1",
+				"acorn": "^8.10.0",
+				"estree-walker": "^3.0.3",
+				"periscopic": "^3.1.0"
+			}
+		},
+		"node_modules/code-red/node_modules/estree-walker": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+			"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+			"dependencies": {
+				"@types/estree": "^1.0.0"
+			}
+		},
+		"node_modules/codedent": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/codedent/-/codedent-0.1.2.tgz",
+			"integrity": "sha512-qEqzcy5viM3UoCN0jYHZeXZoyd4NZQzYFg0kOBj8O1CgoGG9WYYTF+VeQRsN0OSKFjF3G1u4WDUOtOsWEx6N2w==",
+			"dependencies": {
+				"plain-tag": "^0.1.3"
+			}
+		},
+		"node_modules/codemirror": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
+			"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
+			"dependencies": {
+				"@codemirror/autocomplete": "^6.0.0",
+				"@codemirror/commands": "^6.0.0",
+				"@codemirror/language": "^6.0.0",
+				"@codemirror/lint": "^6.0.0",
+				"@codemirror/search": "^6.0.0",
+				"@codemirror/state": "^6.0.0",
+				"@codemirror/view": "^6.0.0"
+			}
+		},
+		"node_modules/coincident": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/coincident/-/coincident-1.2.3.tgz",
+			"integrity": "sha512-Uxz3BMTWIslzeWjuQnizGWVg0j6khbvHUQ8+5BdM7WuJEm4ALXwq3wluYoB+uF68uPBz/oUOeJnYURKyfjexlA==",
+			"dependencies": {
+				"@ungap/structured-clone": "^1.2.0",
+				"@ungap/with-resolvers": "^0.1.0",
+				"gc-hook": "^0.3.1",
+				"proxy-target": "^3.0.2"
+			},
+			"optionalDependencies": {
+				"ws": "^8.16.0"
+			}
+		},
+		"node_modules/color": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+			"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+			"dependencies": {
+				"color-convert": "^2.0.1",
+				"color-string": "^1.9.0"
+			},
+			"engines": {
+				"node": ">=12.5.0"
+			}
+		},
+		"node_modules/color-convert": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+			"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+			"dependencies": {
+				"color-name": "~1.1.4"
+			},
+			"engines": {
+				"node": ">=7.0.0"
+			}
+		},
+		"node_modules/color-name": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+			"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+		},
+		"node_modules/color-string": {
+			"version": "1.9.1",
+			"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+			"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+			"dependencies": {
+				"color-name": "^1.0.0",
+				"simple-swizzle": "^0.2.2"
+			}
+		},
+		"node_modules/colorette": {
+			"version": "2.0.20",
+			"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+			"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+			"dev": true
+		},
+		"node_modules/colorjs.io": {
+			"version": "0.5.2",
+			"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
+			"integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
+			"devOptional": true,
+			"license": "MIT"
+		},
+		"node_modules/colors": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+			"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.1.90"
+			}
+		},
+		"node_modules/combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"dev": true,
+			"dependencies": {
+				"delayed-stream": "~1.0.0"
+			},
+			"engines": {
+				"node": ">= 0.8"
+			}
+		},
+		"node_modules/commander": {
+			"version": "12.1.0",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+			"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+			"dev": true,
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/common-tags": {
+			"version": "1.8.2",
+			"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+			"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0.0"
+			}
+		},
+		"node_modules/commondir": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+			"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
+		},
+		"node_modules/concat-map": {
+			"version": "0.0.1",
+			"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+			"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+			"dev": true
+		},
+		"node_modules/confbox": {
+			"version": "0.1.7",
+			"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz",
+			"integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==",
+			"dev": true
+		},
+		"node_modules/convert-source-map": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+			"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+			"dev": true
+		},
+		"node_modules/cookie": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+			"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/core-util-is": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+			"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+			"dev": true
+		},
+		"node_modules/cose-base": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
+			"integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+			"dependencies": {
+				"layout-base": "^1.0.0"
+			}
+		},
+		"node_modules/crc-32": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+			"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+			"bin": {
+				"crc32": "bin/crc32.njs"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/crelt": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
+			"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="
+		},
+		"node_modules/cross-spawn": {
+			"version": "7.0.6",
+			"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+			"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+			"license": "MIT",
+			"dependencies": {
+				"path-key": "^3.1.0",
+				"shebang-command": "^2.0.0",
+				"which": "^2.0.1"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/css-select": {
+			"version": "5.1.0",
+			"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+			"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+			"dev": true,
+			"dependencies": {
+				"boolbase": "^1.0.0",
+				"css-what": "^6.1.0",
+				"domhandler": "^5.0.2",
+				"domutils": "^3.0.1",
+				"nth-check": "^2.0.1"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/css-tree": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+			"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+			"dependencies": {
+				"mdn-data": "2.0.30",
+				"source-map-js": "^1.0.1"
+			},
+			"engines": {
+				"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+			}
+		},
+		"node_modules/css-what": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+			"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/fb55"
+			}
+		},
+		"node_modules/cssesc": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+			"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+			"dev": true,
+			"bin": {
+				"cssesc": "bin/cssesc"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/cypress": {
+			"version": "13.15.0",
+			"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz",
+			"integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==",
+			"dev": true,
+			"hasInstallScript": true,
+			"dependencies": {
+				"@cypress/request": "^3.0.4",
+				"@cypress/xvfb": "^1.2.4",
+				"@types/sinonjs__fake-timers": "8.1.1",
+				"@types/sizzle": "^2.3.2",
+				"arch": "^2.2.0",
+				"blob-util": "^2.0.2",
+				"bluebird": "^3.7.2",
+				"buffer": "^5.7.1",
+				"cachedir": "^2.3.0",
+				"chalk": "^4.1.0",
+				"check-more-types": "^2.24.0",
+				"cli-cursor": "^3.1.0",
+				"cli-table3": "~0.6.1",
+				"commander": "^6.2.1",
+				"common-tags": "^1.8.0",
+				"dayjs": "^1.10.4",
+				"debug": "^4.3.4",
+				"enquirer": "^2.3.6",
+				"eventemitter2": "6.4.7",
+				"execa": "4.1.0",
+				"executable": "^4.1.1",
+				"extract-zip": "2.0.1",
+				"figures": "^3.2.0",
+				"fs-extra": "^9.1.0",
+				"getos": "^3.2.1",
+				"is-ci": "^3.0.1",
+				"is-installed-globally": "~0.4.0",
+				"lazy-ass": "^1.6.0",
+				"listr2": "^3.8.3",
+				"lodash": "^4.17.21",
+				"log-symbols": "^4.0.0",
+				"minimist": "^1.2.8",
+				"ospath": "^1.2.2",
+				"pretty-bytes": "^5.6.0",
+				"process": "^0.11.10",
+				"proxy-from-env": "1.0.0",
+				"request-progress": "^3.0.0",
+				"semver": "^7.5.3",
+				"supports-color": "^8.1.1",
+				"tmp": "~0.2.3",
+				"untildify": "^4.0.0",
+				"yauzl": "^2.10.0"
+			},
+			"bin": {
+				"cypress": "bin/cypress"
+			},
+			"engines": {
+				"node": "^16.0.0 || ^18.0.0 || >=20.0.0"
+			}
+		},
+		"node_modules/cypress/node_modules/buffer": {
+			"version": "5.7.1",
+			"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+			"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"base64-js": "^1.3.1",
+				"ieee754": "^1.1.13"
+			}
+		},
+		"node_modules/cypress/node_modules/commander": {
+			"version": "6.2.1",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+			"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/cypress/node_modules/fs-extra": {
+			"version": "9.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+			"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+			"dev": true,
+			"dependencies": {
+				"at-least-node": "^1.0.0",
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^6.0.1",
+				"universalify": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/cypress/node_modules/supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"dev": true,
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/supports-color?sponsor=1"
+			}
+		},
+		"node_modules/cytoscape": {
+			"version": "3.29.2",
+			"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.29.2.tgz",
+			"integrity": "sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==",
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/cytoscape-cose-bilkent": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+			"integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+			"dependencies": {
+				"cose-base": "^1.0.0"
+			},
+			"peerDependencies": {
+				"cytoscape": "^3.2.0"
+			}
+		},
+		"node_modules/d3": {
+			"version": "7.9.0",
+			"resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+			"integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+			"dependencies": {
+				"d3-array": "3",
+				"d3-axis": "3",
+				"d3-brush": "3",
+				"d3-chord": "3",
+				"d3-color": "3",
+				"d3-contour": "4",
+				"d3-delaunay": "6",
+				"d3-dispatch": "3",
+				"d3-drag": "3",
+				"d3-dsv": "3",
+				"d3-ease": "3",
+				"d3-fetch": "3",
+				"d3-force": "3",
+				"d3-format": "3",
+				"d3-geo": "3",
+				"d3-hierarchy": "3",
+				"d3-interpolate": "3",
+				"d3-path": "3",
+				"d3-polygon": "3",
+				"d3-quadtree": "3",
+				"d3-random": "3",
+				"d3-scale": "4",
+				"d3-scale-chromatic": "3",
+				"d3-selection": "3",
+				"d3-shape": "3",
+				"d3-time": "3",
+				"d3-time-format": "4",
+				"d3-timer": "3",
+				"d3-transition": "3",
+				"d3-zoom": "3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-array": {
+			"version": "3.2.4",
+			"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+			"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+			"dependencies": {
+				"internmap": "1 - 2"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-axis": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+			"integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-brush": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+			"integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+			"dependencies": {
+				"d3-dispatch": "1 - 3",
+				"d3-drag": "2 - 3",
+				"d3-interpolate": "1 - 3",
+				"d3-selection": "3",
+				"d3-transition": "3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-chord": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+			"integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+			"dependencies": {
+				"d3-path": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-color": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+			"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-contour": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+			"integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+			"dependencies": {
+				"d3-array": "^3.2.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-delaunay": {
+			"version": "6.0.4",
+			"resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+			"integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+			"dependencies": {
+				"delaunator": "5"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-dispatch": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+			"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-drag": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+			"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+			"dependencies": {
+				"d3-dispatch": "1 - 3",
+				"d3-selection": "3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-dsv": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+			"integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+			"dependencies": {
+				"commander": "7",
+				"iconv-lite": "0.6",
+				"rw": "1"
+			},
+			"bin": {
+				"csv2json": "bin/dsv2json.js",
+				"csv2tsv": "bin/dsv2dsv.js",
+				"dsv2dsv": "bin/dsv2dsv.js",
+				"dsv2json": "bin/dsv2json.js",
+				"json2csv": "bin/json2dsv.js",
+				"json2dsv": "bin/json2dsv.js",
+				"json2tsv": "bin/json2dsv.js",
+				"tsv2csv": "bin/dsv2dsv.js",
+				"tsv2json": "bin/dsv2json.js"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-dsv/node_modules/commander": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+			"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/d3-ease": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+			"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-fetch": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+			"integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+			"dependencies": {
+				"d3-dsv": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-force": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+			"integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+			"dependencies": {
+				"d3-dispatch": "1 - 3",
+				"d3-quadtree": "1 - 3",
+				"d3-timer": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-format": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+			"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-geo": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+			"integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+			"dependencies": {
+				"d3-array": "2.5.0 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-hierarchy": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+			"integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-interpolate": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+			"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+			"dependencies": {
+				"d3-color": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-path": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+			"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-polygon": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+			"integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-quadtree": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+			"integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-random": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+			"integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-sankey": {
+			"version": "0.12.3",
+			"resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+			"integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+			"dependencies": {
+				"d3-array": "1 - 2",
+				"d3-shape": "^1.2.0"
+			}
+		},
+		"node_modules/d3-sankey/node_modules/d3-array": {
+			"version": "2.12.1",
+			"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+			"integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+			"dependencies": {
+				"internmap": "^1.0.0"
+			}
+		},
+		"node_modules/d3-sankey/node_modules/d3-path": {
+			"version": "1.0.9",
+			"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+			"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
+		},
+		"node_modules/d3-sankey/node_modules/d3-shape": {
+			"version": "1.3.7",
+			"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+			"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+			"dependencies": {
+				"d3-path": "1"
+			}
+		},
+		"node_modules/d3-sankey/node_modules/internmap": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+			"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
+		},
+		"node_modules/d3-scale": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+			"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+			"dependencies": {
+				"d3-array": "2.10.0 - 3",
+				"d3-format": "1 - 3",
+				"d3-interpolate": "1.2.0 - 3",
+				"d3-time": "2.1.1 - 3",
+				"d3-time-format": "2 - 4"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-scale-chromatic": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+			"integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+			"dependencies": {
+				"d3-color": "1 - 3",
+				"d3-interpolate": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-selection": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+			"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-shape": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+			"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+			"dependencies": {
+				"d3-path": "^3.1.0"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-time": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+			"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+			"dependencies": {
+				"d3-array": "2 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-time-format": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+			"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+			"dependencies": {
+				"d3-time": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-timer": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+			"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/d3-transition": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+			"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+			"dependencies": {
+				"d3-color": "1 - 3",
+				"d3-dispatch": "1 - 3",
+				"d3-ease": "1 - 3",
+				"d3-interpolate": "1 - 3",
+				"d3-timer": "1 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"peerDependencies": {
+				"d3-selection": "2 - 3"
+			}
+		},
+		"node_modules/d3-zoom": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+			"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+			"dependencies": {
+				"d3-dispatch": "1 - 3",
+				"d3-drag": "2 - 3",
+				"d3-interpolate": "1 - 3",
+				"d3-selection": "2 - 3",
+				"d3-transition": "2 - 3"
+			},
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/dagre-d3-es": {
+			"version": "7.0.10",
+			"resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz",
+			"integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==",
+			"dependencies": {
+				"d3": "^7.8.2",
+				"lodash-es": "^4.17.21"
+			}
+		},
+		"node_modules/dashdash": {
+			"version": "1.14.1",
+			"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+			"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+			"dev": true,
+			"dependencies": {
+				"assert-plus": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/dayjs": {
+			"version": "1.11.10",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+			"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
+		},
+		"node_modules/debug": {
+			"version": "4.3.4",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+			"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+			"dependencies": {
+				"ms": "2.1.2"
+			},
+			"engines": {
+				"node": ">=6.0"
+			},
+			"peerDependenciesMeta": {
+				"supports-color": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/decode-named-character-reference": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz",
+			"integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==",
+			"dependencies": {
+				"character-entities": "^2.0.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/deep-eql": {
+			"version": "4.1.3",
+			"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+			"integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+			"dev": true,
+			"dependencies": {
+				"type-detect": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/deep-is": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+			"dev": true
+		},
+		"node_modules/deepmerge": {
+			"version": "4.3.1",
+			"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+			"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/define-data-property": {
+			"version": "1.1.4",
+			"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+			"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+			"dev": true,
+			"dependencies": {
+				"es-define-property": "^1.0.0",
+				"es-errors": "^1.3.0",
+				"gopd": "^1.0.1"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/delaunator": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+			"integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+			"dependencies": {
+				"robust-predicates": "^3.0.2"
+			}
+		},
+		"node_modules/delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/dequal": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+			"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/detect-indent": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
+			"integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/detect-libc": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+			"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/devalue": {
+			"version": "5.1.1",
+			"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
+			"integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="
+		},
+		"node_modules/devlop": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+			"integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"dequal": "^2.0.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/didyoumean": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+			"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+			"dev": true
+		},
+		"node_modules/diff": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+			"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
+			"engines": {
+				"node": ">=0.3.1"
+			}
+		},
+		"node_modules/diff-sequences": {
+			"version": "29.6.3",
+			"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+			"integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
+			"dev": true,
+			"engines": {
+				"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+			}
+		},
+		"node_modules/dir-glob": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+			"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+			"dev": true,
+			"dependencies": {
+				"path-type": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/dlv": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+			"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+			"dev": true
+		},
+		"node_modules/doctrine": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+			"integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+			"dev": true,
+			"dependencies": {
+				"esutils": "^2.0.2"
+			},
+			"engines": {
+				"node": ">=6.0.0"
+			}
+		},
+		"node_modules/dom-serializer": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+			"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+			"dev": true,
+			"dependencies": {
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.2",
+				"entities": "^4.2.0"
+			},
+			"funding": {
+				"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+			}
+		},
+		"node_modules/domelementtype": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+			"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/fb55"
+				}
+			]
+		},
+		"node_modules/domhandler": {
+			"version": "5.0.3",
+			"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+			"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+			"dev": true,
+			"dependencies": {
+				"domelementtype": "^2.3.0"
+			},
+			"engines": {
+				"node": ">= 4"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domhandler?sponsor=1"
+			}
+		},
+		"node_modules/dompurify": {
+			"version": "3.1.6",
+			"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
+			"integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ=="
+		},
+		"node_modules/domutils": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
+			"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+			"dev": true,
+			"dependencies": {
+				"dom-serializer": "^2.0.0",
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.3"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/domutils?sponsor=1"
+			}
+		},
+		"node_modules/eastasianwidth": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+			"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+		},
+		"node_modules/ecc-jsbn": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+			"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+			"dev": true,
+			"dependencies": {
+				"jsbn": "~0.1.0",
+				"safer-buffer": "^2.1.0"
+			}
+		},
+		"node_modules/electron-to-chromium": {
+			"version": "1.4.715",
+			"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz",
+			"integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==",
+			"dev": true
+		},
+		"node_modules/elkjs": {
+			"version": "0.9.3",
+			"resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz",
+			"integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ=="
+		},
+		"node_modules/emoji-regex": {
+			"version": "9.2.2",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+			"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+		},
+		"node_modules/end-of-stream": {
+			"version": "1.4.4",
+			"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+			"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+			"dev": true,
+			"dependencies": {
+				"once": "^1.4.0"
+			}
+		},
+		"node_modules/engine.io-client": {
+			"version": "6.5.4",
+			"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
+			"integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
+			"dependencies": {
+				"@socket.io/component-emitter": "~3.1.0",
+				"debug": "~4.3.1",
+				"engine.io-parser": "~5.2.1",
+				"ws": "~8.17.1",
+				"xmlhttprequest-ssl": "~2.0.0"
+			}
+		},
+		"node_modules/engine.io-parser": {
+			"version": "5.2.2",
+			"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz",
+			"integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==",
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/enquirer": {
+			"version": "2.4.1",
+			"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz",
+			"integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==",
+			"dev": true,
+			"dependencies": {
+				"ansi-colors": "^4.1.1",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/ensure-posix-path": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz",
+			"integrity": "sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==",
+			"dev": true
+		},
+		"node_modules/entities": {
+			"version": "4.5.0",
+			"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+			"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+			"engines": {
+				"node": ">=0.12"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/entities?sponsor=1"
+			}
+		},
+		"node_modules/eol": {
+			"version": "0.9.1",
+			"resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz",
+			"integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==",
+			"dev": true
+		},
+		"node_modules/es-define-property": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+			"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+			"dev": true,
+			"dependencies": {
+				"get-intrinsic": "^1.2.4"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/es-errors": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+			"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/es6-promise": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+			"integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==",
+			"dev": true
+		},
+		"node_modules/esbuild": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
+			"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
+			"dev": true,
+			"hasInstallScript": true,
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"@esbuild/aix-ppc64": "0.20.2",
+				"@esbuild/android-arm": "0.20.2",
+				"@esbuild/android-arm64": "0.20.2",
+				"@esbuild/android-x64": "0.20.2",
+				"@esbuild/darwin-arm64": "0.20.2",
+				"@esbuild/darwin-x64": "0.20.2",
+				"@esbuild/freebsd-arm64": "0.20.2",
+				"@esbuild/freebsd-x64": "0.20.2",
+				"@esbuild/linux-arm": "0.20.2",
+				"@esbuild/linux-arm64": "0.20.2",
+				"@esbuild/linux-ia32": "0.20.2",
+				"@esbuild/linux-loong64": "0.20.2",
+				"@esbuild/linux-mips64el": "0.20.2",
+				"@esbuild/linux-ppc64": "0.20.2",
+				"@esbuild/linux-riscv64": "0.20.2",
+				"@esbuild/linux-s390x": "0.20.2",
+				"@esbuild/linux-x64": "0.20.2",
+				"@esbuild/netbsd-x64": "0.20.2",
+				"@esbuild/openbsd-x64": "0.20.2",
+				"@esbuild/sunos-x64": "0.20.2",
+				"@esbuild/win32-arm64": "0.20.2",
+				"@esbuild/win32-ia32": "0.20.2",
+				"@esbuild/win32-x64": "0.20.2"
+			}
+		},
+		"node_modules/escalade": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+			"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/escape-string-regexp": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+			"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/eslint": {
+			"version": "8.57.0",
+			"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+			"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.2.0",
+				"@eslint-community/regexpp": "^4.6.1",
+				"@eslint/eslintrc": "^2.1.4",
+				"@eslint/js": "8.57.0",
+				"@humanwhocodes/config-array": "^0.11.14",
+				"@humanwhocodes/module-importer": "^1.0.1",
+				"@nodelib/fs.walk": "^1.2.8",
+				"@ungap/structured-clone": "^1.2.0",
+				"ajv": "^6.12.4",
+				"chalk": "^4.0.0",
+				"cross-spawn": "^7.0.2",
+				"debug": "^4.3.2",
+				"doctrine": "^3.0.0",
+				"escape-string-regexp": "^4.0.0",
+				"eslint-scope": "^7.2.2",
+				"eslint-visitor-keys": "^3.4.3",
+				"espree": "^9.6.1",
+				"esquery": "^1.4.2",
+				"esutils": "^2.0.2",
+				"fast-deep-equal": "^3.1.3",
+				"file-entry-cache": "^6.0.1",
+				"find-up": "^5.0.0",
+				"glob-parent": "^6.0.2",
+				"globals": "^13.19.0",
+				"graphemer": "^1.4.0",
+				"ignore": "^5.2.0",
+				"imurmurhash": "^0.1.4",
+				"is-glob": "^4.0.0",
+				"is-path-inside": "^3.0.3",
+				"js-yaml": "^4.1.0",
+				"json-stable-stringify-without-jsonify": "^1.0.1",
+				"levn": "^0.4.1",
+				"lodash.merge": "^4.6.2",
+				"minimatch": "^3.1.2",
+				"natural-compare": "^1.4.0",
+				"optionator": "^0.9.3",
+				"strip-ansi": "^6.0.1",
+				"text-table": "^0.2.0"
+			},
+			"bin": {
+				"eslint": "bin/eslint.js"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint-compat-utils": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
+			"integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
+			"dev": true,
+			"dependencies": {
+				"semver": "^7.5.4"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"peerDependencies": {
+				"eslint": ">=6.0.0"
+			}
+		},
+		"node_modules/eslint-config-prettier": {
+			"version": "9.1.0",
+			"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
+			"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
+			"dev": true,
+			"bin": {
+				"eslint-config-prettier": "bin/cli.js"
+			},
+			"peerDependencies": {
+				"eslint": ">=7.0.0"
+			}
+		},
+		"node_modules/eslint-plugin-cypress": {
+			"version": "3.4.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-3.4.0.tgz",
+			"integrity": "sha512-Rrrr3Ri6wHqzrRr+TyUV7bDS4UnMMrFY1R1PP2F7XdGfe9txDC6lQEshyoNOWqGoPkbbeDm1x1XPc/adxemsnA==",
+			"dev": true,
+			"dependencies": {
+				"globals": "^13.20.0"
+			},
+			"peerDependencies": {
+				"eslint": ">=7"
+			}
+		},
+		"node_modules/eslint-plugin-svelte": {
+			"version": "2.43.0",
+			"resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.43.0.tgz",
+			"integrity": "sha512-REkxQWvg2pp7QVLxQNa+dJ97xUqRe7Y2JJbSWkHSuszu0VcblZtXkPBPckkivk99y5CdLw4slqfPylL2d/X4jQ==",
+			"dev": true,
+			"dependencies": {
+				"@eslint-community/eslint-utils": "^4.4.0",
+				"@jridgewell/sourcemap-codec": "^1.4.15",
+				"eslint-compat-utils": "^0.5.1",
+				"esutils": "^2.0.3",
+				"known-css-properties": "^0.34.0",
+				"postcss": "^8.4.38",
+				"postcss-load-config": "^3.1.4",
+				"postcss-safe-parser": "^6.0.0",
+				"postcss-selector-parser": "^6.1.0",
+				"semver": "^7.6.2",
+				"svelte-eslint-parser": "^0.41.0"
+			},
+			"engines": {
+				"node": "^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ota-meshi"
+			},
+			"peerDependencies": {
+				"eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0",
+				"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.191"
+			},
+			"peerDependenciesMeta": {
+				"svelte": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/eslint-plugin-svelte/node_modules/postcss-selector-parser": {
+			"version": "6.1.1",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz",
+			"integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/eslint-scope": {
+			"version": "7.2.2",
+			"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+			"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+			"dev": true,
+			"dependencies": {
+				"esrecurse": "^4.3.0",
+				"estraverse": "^5.2.0"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint-visitor-keys": {
+			"version": "3.4.3",
+			"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+			"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+			"dev": true,
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/eslint/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/eslint/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/esm-env": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz",
+			"integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==",
+			"license": "MIT"
+		},
+		"node_modules/espree": {
+			"version": "9.6.1",
+			"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+			"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.9.0",
+				"acorn-jsx": "^5.3.2",
+				"eslint-visitor-keys": "^3.4.1"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/eslint"
+			}
+		},
+		"node_modules/esquery": {
+			"version": "1.5.0",
+			"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+			"integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+			"dev": true,
+			"dependencies": {
+				"estraverse": "^5.1.0"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/esrecurse": {
+			"version": "4.3.0",
+			"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+			"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+			"dev": true,
+			"dependencies": {
+				"estraverse": "^5.2.0"
+			},
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/estraverse": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+			"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+			"dev": true,
+			"engines": {
+				"node": ">=4.0"
+			}
+		},
+		"node_modules/estree-walker": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+			"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+		},
+		"node_modules/esutils": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+			"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/eventemitter2": {
+			"version": "6.4.7",
+			"resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz",
+			"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
+			"dev": true
+		},
+		"node_modules/eventsource-parser": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
+			"integrity": "sha512-v0eOBUbiaFojBu2s2NPBfYUoRR9GjcDNvCXVaqEf5vVfpIAh9f8RCo4vXTP8c63QRKCFwoLpMpTdPwwhEKVgzA==",
+			"engines": {
+				"node": ">=14.18"
+			}
+		},
+		"node_modules/execa": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+			"integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+			"dev": true,
+			"dependencies": {
+				"cross-spawn": "^7.0.0",
+				"get-stream": "^5.0.0",
+				"human-signals": "^1.1.1",
+				"is-stream": "^2.0.0",
+				"merge-stream": "^2.0.0",
+				"npm-run-path": "^4.0.0",
+				"onetime": "^5.1.0",
+				"signal-exit": "^3.0.2",
+				"strip-final-newline": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sindresorhus/execa?sponsor=1"
+			}
+		},
+		"node_modules/execa/node_modules/signal-exit": {
+			"version": "3.0.7",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+			"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+			"dev": true
+		},
+		"node_modules/executable": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz",
+			"integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==",
+			"dev": true,
+			"dependencies": {
+				"pify": "^2.2.0"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/extend": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+			"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+			"dev": true
+		},
+		"node_modules/extract-zip": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+			"integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+			"dev": true,
+			"dependencies": {
+				"debug": "^4.1.1",
+				"get-stream": "^5.1.0",
+				"yauzl": "^2.10.0"
+			},
+			"bin": {
+				"extract-zip": "cli.js"
+			},
+			"engines": {
+				"node": ">= 10.17.0"
+			},
+			"optionalDependencies": {
+				"@types/yauzl": "^2.9.1"
+			}
+		},
+		"node_modules/extsprintf": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+			"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+			"dev": true,
+			"engines": [
+				"node >=0.6.0"
+			]
+		},
+		"node_modules/fast-deep-equal": {
+			"version": "3.1.3",
+			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+			"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+			"dev": true
+		},
+		"node_modules/fast-fifo": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+			"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+			"dev": true
+		},
+		"node_modules/fast-glob": {
+			"version": "3.3.2",
+			"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+			"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+			"dev": true,
+			"dependencies": {
+				"@nodelib/fs.stat": "^2.0.2",
+				"@nodelib/fs.walk": "^1.2.3",
+				"glob-parent": "^5.1.2",
+				"merge2": "^1.3.0",
+				"micromatch": "^4.0.4"
+			},
+			"engines": {
+				"node": ">=8.6.0"
+			}
+		},
+		"node_modules/fast-glob/node_modules/glob-parent": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.1"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fast-json-stable-stringify": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+			"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+			"dev": true
+		},
+		"node_modules/fast-levenshtein": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+			"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+			"dev": true
+		},
+		"node_modules/fastq": {
+			"version": "1.17.1",
+			"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+			"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+			"dev": true,
+			"dependencies": {
+				"reusify": "^1.0.4"
+			}
+		},
+		"node_modules/fd-slicer": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+			"integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+			"dev": true,
+			"dependencies": {
+				"pend": "~1.2.0"
+			}
+		},
+		"node_modules/figures": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+			"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+			"dev": true,
+			"dependencies": {
+				"escape-string-regexp": "^1.0.5"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/figures/node_modules/escape-string-regexp": {
+			"version": "1.0.5",
+			"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+			"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.8.0"
+			}
+		},
+		"node_modules/file-entry-cache": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+			"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+			"dev": true,
+			"dependencies": {
+				"flat-cache": "^3.0.4"
+			},
+			"engines": {
+				"node": "^10.12.0 || >=12.0.0"
+			}
+		},
+		"node_modules/file-saver": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+			"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+		},
+		"node_modules/fill-range": {
+			"version": "7.1.1",
+			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+			"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+			"dev": true,
+			"dependencies": {
+				"to-regex-range": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/find-up": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+			"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+			"dev": true,
+			"dependencies": {
+				"locate-path": "^6.0.0",
+				"path-exists": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/flat-cache": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+			"integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+			"dev": true,
+			"dependencies": {
+				"flatted": "^3.2.9",
+				"keyv": "^4.5.3",
+				"rimraf": "^3.0.2"
+			},
+			"engines": {
+				"node": "^10.12.0 || >=12.0.0"
+			}
+		},
+		"node_modules/flatbuffers": {
+			"version": "1.12.0",
+			"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz",
+			"integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ=="
+		},
+		"node_modules/flatted": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+			"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+			"dev": true
+		},
+		"node_modules/focus-trap": {
+			"version": "7.5.4",
+			"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
+			"integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==",
+			"dependencies": {
+				"tabbable": "^6.2.0"
+			}
+		},
+		"node_modules/foreground-child": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+			"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+			"dependencies": {
+				"cross-spawn": "^7.0.0",
+				"signal-exit": "^4.0.1"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/forever-agent": {
+			"version": "0.6.1",
+			"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+			"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/form-data": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"dev": true,
+			"dependencies": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			},
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/fraction.js": {
+			"version": "4.3.7",
+			"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+			"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"type": "patreon",
+				"url": "https://github.com/sponsors/rawify"
+			}
+		},
+		"node_modules/fs-extra": {
+			"version": "11.2.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+			"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+			"dev": true,
+			"dependencies": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^6.0.1",
+				"universalify": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=14.14"
+			}
+		},
+		"node_modules/fs-merger": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/fs-merger/-/fs-merger-3.2.1.tgz",
+			"integrity": "sha512-AN6sX12liy0JE7C2evclwoo0aCG3PFulLjrTLsJpWh/2mM+DinhpSGqYLbHBBbIW1PLRNcFhJG8Axtz8mQW3ug==",
+			"dev": true,
+			"dependencies": {
+				"broccoli-node-api": "^1.7.0",
+				"broccoli-node-info": "^2.1.0",
+				"fs-extra": "^8.0.1",
+				"fs-tree-diff": "^2.0.1",
+				"walk-sync": "^2.2.0"
+			}
+		},
+		"node_modules/fs-merger/node_modules/fs-extra": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+			"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+			"dev": true,
+			"dependencies": {
+				"graceful-fs": "^4.2.0",
+				"jsonfile": "^4.0.0",
+				"universalify": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=6 <7 || >=8"
+			}
+		},
+		"node_modules/fs-merger/node_modules/jsonfile": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+			"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+			"dev": true,
+			"optionalDependencies": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"node_modules/fs-merger/node_modules/universalify": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+			"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4.0.0"
+			}
+		},
+		"node_modules/fs-mkdirp-stream": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz",
+			"integrity": "sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==",
+			"dev": true,
+			"dependencies": {
+				"graceful-fs": "^4.2.8",
+				"streamx": "^2.12.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/fs-tree-diff": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz",
+			"integrity": "sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==",
+			"dev": true,
+			"dependencies": {
+				"@types/symlink-or-copy": "^1.2.0",
+				"heimdalljs-logger": "^0.1.7",
+				"object-assign": "^4.1.0",
+				"path-posix": "^1.0.0",
+				"symlink-or-copy": "^1.1.8"
+			},
+			"engines": {
+				"node": "6.* || 8.* || >= 10.*"
+			}
+		},
+		"node_modules/fs.realpath": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+			"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+		},
+		"node_modules/fsevents": {
+			"version": "2.3.3",
+			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+			"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+			"hasInstallScript": true,
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+			}
+		},
+		"node_modules/function-bind": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+			"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/fuse.js": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
+			"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/gc-hook": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/gc-hook/-/gc-hook-0.3.1.tgz",
+			"integrity": "sha512-E5M+O/h2o7eZzGhzRZGex6hbB3k4NWqO0eA+OzLRLXxhdbYPajZnynPwAtphnh+cRHPwsj5Z80dqZlfI4eK55A=="
+		},
+		"node_modules/get-func-name": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+			"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/get-intrinsic": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+			"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
+			"dev": true,
+			"dependencies": {
+				"es-errors": "^1.3.0",
+				"function-bind": "^1.1.2",
+				"has-proto": "^1.0.1",
+				"has-symbols": "^1.0.3",
+				"hasown": "^2.0.0"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/get-stream": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+			"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+			"dev": true,
+			"dependencies": {
+				"pump": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/getos": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz",
+			"integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==",
+			"dev": true,
+			"dependencies": {
+				"async": "^3.2.0"
+			}
+		},
+		"node_modules/getpass": {
+			"version": "0.1.7",
+			"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+			"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+			"dev": true,
+			"dependencies": {
+				"assert-plus": "^1.0.0"
+			}
+		},
+		"node_modules/glob": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+			"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^5.0.1",
+				"once": "^1.3.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/glob-parent": {
+			"version": "6.0.2",
+			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+			"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+			"dev": true,
+			"dependencies": {
+				"is-glob": "^4.0.3"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/glob-stream": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.0.tgz",
+			"integrity": "sha512-CdIUuwOkYNv9ZadR3jJvap8CMooKziQZ/QCSPhEb7zqfsEI5YnPmvca7IvbaVE3z58ZdUYD2JsU6AUWjL8WZJA==",
+			"dev": true,
+			"dependencies": {
+				"@gulpjs/to-absolute-glob": "^4.0.0",
+				"anymatch": "^3.1.3",
+				"fastq": "^1.13.0",
+				"glob-parent": "^6.0.2",
+				"is-glob": "^4.0.3",
+				"is-negated-glob": "^1.0.0",
+				"normalize-path": "^3.0.0",
+				"streamx": "^2.12.5"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/glob/node_modules/minimatch": {
+			"version": "5.1.6",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+			"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/global-dirs": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz",
+			"integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==",
+			"dev": true,
+			"dependencies": {
+				"ini": "2.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/globals": {
+			"version": "13.24.0",
+			"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+			"integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+			"dev": true,
+			"dependencies": {
+				"type-fest": "^0.20.2"
+			},
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/globalyzer": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
+			"integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q=="
+		},
+		"node_modules/globby": {
+			"version": "11.1.0",
+			"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+			"integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+			"dev": true,
+			"dependencies": {
+				"array-union": "^2.1.0",
+				"dir-glob": "^3.0.1",
+				"fast-glob": "^3.2.9",
+				"ignore": "^5.2.0",
+				"merge2": "^1.4.1",
+				"slash": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/globrex": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+			"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="
+		},
+		"node_modules/gopd": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+			"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+			"dev": true,
+			"dependencies": {
+				"get-intrinsic": "^1.1.3"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/graceful-fs": {
+			"version": "4.2.11",
+			"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+			"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+			"dev": true
+		},
+		"node_modules/graphemer": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+			"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+			"dev": true
+		},
+		"node_modules/guid-typescript": {
+			"version": "1.0.9",
+			"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
+			"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ=="
+		},
+		"node_modules/gulp-sort": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/gulp-sort/-/gulp-sort-2.0.0.tgz",
+			"integrity": "sha512-MyTel3FXOdh1qhw1yKhpimQrAmur9q1X0ZigLmCOxouQD+BD3za9/89O+HfbgBQvvh4igEbp0/PUWO+VqGYG1g==",
+			"dev": true,
+			"dependencies": {
+				"through2": "^2.0.1"
+			}
+		},
+		"node_modules/has-flag": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+			"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+			"devOptional": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/has-property-descriptors": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+			"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+			"dev": true,
+			"dependencies": {
+				"es-define-property": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/has-proto": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+			"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/has-symbols": {
+			"version": "1.0.3",
+			"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+			"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/hasown": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+			"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+			"dependencies": {
+				"function-bind": "^1.1.2"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/heimdalljs": {
+			"version": "0.2.6",
+			"resolved": "https://registry.npmjs.org/heimdalljs/-/heimdalljs-0.2.6.tgz",
+			"integrity": "sha512-o9bd30+5vLBvBtzCPwwGqpry2+n0Hi6H1+qwt6y+0kwRHGGF8TFIhJPmnuM0xO97zaKrDZMwO/V56fAnn8m/tA==",
+			"dev": true,
+			"dependencies": {
+				"rsvp": "~3.2.1"
+			}
+		},
+		"node_modules/heimdalljs-logger": {
+			"version": "0.1.10",
+			"resolved": "https://registry.npmjs.org/heimdalljs-logger/-/heimdalljs-logger-0.1.10.tgz",
+			"integrity": "sha512-pO++cJbhIufVI/fmB/u2Yty3KJD0TqNPecehFae0/eps0hkZ3b4Zc/PezUMOpYuHFQbA7FxHZxa305EhmjLj4g==",
+			"dev": true,
+			"dependencies": {
+				"debug": "^2.2.0",
+				"heimdalljs": "^0.2.6"
+			}
+		},
+		"node_modules/heimdalljs-logger/node_modules/debug": {
+			"version": "2.6.9",
+			"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+			"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+			"dev": true,
+			"dependencies": {
+				"ms": "2.0.0"
+			}
+		},
+		"node_modules/heimdalljs-logger/node_modules/ms": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+			"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+			"dev": true
+		},
+		"node_modules/heimdalljs/node_modules/rsvp": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.2.1.tgz",
+			"integrity": "sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==",
+			"dev": true
+		},
+		"node_modules/highlight.js": {
+			"version": "11.9.0",
+			"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
+			"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
+			"engines": {
+				"node": ">=12.0.0"
+			}
+		},
+		"node_modules/html-escaper": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+			"integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
+		},
+		"node_modules/htmlparser2": {
+			"version": "8.0.2",
+			"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
+			"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+			"dev": true,
+			"funding": [
+				"https://github.com/fb55/htmlparser2?sponsor=1",
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/fb55"
+				}
+			],
+			"dependencies": {
+				"domelementtype": "^2.3.0",
+				"domhandler": "^5.0.3",
+				"domutils": "^3.0.1",
+				"entities": "^4.4.0"
+			}
+		},
+		"node_modules/http-signature": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz",
+			"integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==",
+			"dev": true,
+			"dependencies": {
+				"assert-plus": "^1.0.0",
+				"jsprim": "^2.0.2",
+				"sshpk": "^1.18.0"
+			},
+			"engines": {
+				"node": ">=0.10"
+			}
+		},
+		"node_modules/human-signals": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+			"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8.12.0"
+			}
+		},
+		"node_modules/i18next": {
+			"version": "23.10.1",
+			"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.1.tgz",
+			"integrity": "sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==",
+			"funding": [
+				{
+					"type": "individual",
+					"url": "https://locize.com"
+				},
+				{
+					"type": "individual",
+					"url": "https://locize.com/i18next.html"
+				},
+				{
+					"type": "individual",
+					"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+				}
+			],
+			"dependencies": {
+				"@babel/runtime": "^7.23.2"
+			}
+		},
+		"node_modules/i18next-browser-languagedetector": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.0.tgz",
+			"integrity": "sha512-U00DbDtFIYD3wkWsr2aVGfXGAj2TgnELzOX9qv8bT0aJtvPV9CRO77h+vgmHFBMe7LAxdwvT/7VkCWGya6L3tA==",
+			"dependencies": {
+				"@babel/runtime": "^7.23.2"
+			}
+		},
+		"node_modules/i18next-parser": {
+			"version": "9.0.1",
+			"resolved": "https://registry.npmjs.org/i18next-parser/-/i18next-parser-9.0.1.tgz",
+			"integrity": "sha512-/Pr93/yEBdwsMKRsk4Zn63K368ALhzh8BRVrM6JNGOHy86ZKpiNJI6m8l1S/4T4Ofy1J4dlwkD7N98M70GP4aA==",
+			"dev": true,
+			"dependencies": {
+				"@babel/runtime": "^7.23.2",
+				"broccoli-plugin": "^4.0.7",
+				"cheerio": "^1.0.0-rc.2",
+				"colors": "1.4.0",
+				"commander": "~12.1.0",
+				"eol": "^0.9.1",
+				"esbuild": "^0.20.1",
+				"fs-extra": "^11.1.0",
+				"gulp-sort": "^2.0.0",
+				"i18next": "^23.5.1",
+				"js-yaml": "4.1.0",
+				"lilconfig": "^3.0.0",
+				"rsvp": "^4.8.2",
+				"sort-keys": "^5.0.0",
+				"typescript": "^5.0.4",
+				"vinyl": "~3.0.0",
+				"vinyl-fs": "^4.0.0"
+			},
+			"bin": {
+				"i18next": "bin/cli.js"
+			},
+			"engines": {
+				"node": ">=18.0.0 || >=20.0.0 || >=22.0.0",
+				"npm": ">=6",
+				"yarn": ">=1"
+			}
+		},
+		"node_modules/i18next-resources-to-backend": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/i18next-resources-to-backend/-/i18next-resources-to-backend-1.2.0.tgz",
+			"integrity": "sha512-8f1l03s+QxDmCfpSXCh9V+AFcxAwIp0UaroWuyOx+hmmv8484GcELHs+lnu54FrNij8cDBEXvEwhzZoXsKcVpg==",
+			"dependencies": {
+				"@babel/runtime": "^7.23.2"
+			}
+		},
+		"node_modules/iconv-lite": {
+			"version": "0.6.3",
+			"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+			"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+			"dependencies": {
+				"safer-buffer": ">= 2.1.2 < 3.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/idb": {
+			"version": "7.1.1",
+			"resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz",
+			"integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="
+		},
+		"node_modules/ieee754": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+			"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/ignore": {
+			"version": "5.3.1",
+			"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+			"integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4"
+			}
+		},
+		"node_modules/immutable": {
+			"version": "5.0.3",
+			"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
+			"integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==",
+			"devOptional": true,
+			"license": "MIT"
+		},
+		"node_modules/import-fresh": {
+			"version": "3.3.0",
+			"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+			"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+			"dev": true,
+			"dependencies": {
+				"parent-module": "^1.0.0",
+				"resolve-from": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/import-meta-resolve": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
+			"integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==",
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/imurmurhash": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+			"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.8.19"
+			}
+		},
+		"node_modules/indent-string": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+			"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/inflight": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+			"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+			"dependencies": {
+				"once": "^1.3.0",
+				"wrappy": "1"
+			}
+		},
+		"node_modules/inherits": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+			"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+		},
+		"node_modules/ini": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz",
+			"integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/internmap": {
+			"version": "2.0.3",
+			"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+			"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/is-arrayish": {
+			"version": "0.3.2",
+			"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+			"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+		},
+		"node_modules/is-binary-path": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+			"dev": true,
+			"dependencies": {
+				"binary-extensions": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-builtin-module": {
+			"version": "3.2.1",
+			"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+			"integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+			"dependencies": {
+				"builtin-modules": "^3.3.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-ci": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+			"integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+			"dev": true,
+			"dependencies": {
+				"ci-info": "^3.2.0"
+			},
+			"bin": {
+				"is-ci": "bin.js"
+			}
+		},
+		"node_modules/is-core-module": {
+			"version": "2.13.1",
+			"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
+			"integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
+			"dependencies": {
+				"hasown": "^2.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/is-extglob": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+			"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-fullwidth-code-point": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+			"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-glob": {
+			"version": "4.0.3",
+			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+			"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+			"dev": true,
+			"dependencies": {
+				"is-extglob": "^2.1.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-installed-globally": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz",
+			"integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==",
+			"dev": true,
+			"dependencies": {
+				"global-dirs": "^3.0.0",
+				"is-path-inside": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-module": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+			"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="
+		},
+		"node_modules/is-negated-glob": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+			"integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/is-number": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.12.0"
+			}
+		},
+		"node_modules/is-path-inside": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+			"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/is-plain-obj": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+			"integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-reference": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+			"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+			"dependencies": {
+				"@types/estree": "*"
+			}
+		},
+		"node_modules/is-stream": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+			"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-typedarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+			"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+			"dev": true
+		},
+		"node_modules/is-unicode-supported": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+			"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/is-valid-glob": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+			"integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/isarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+			"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+			"dev": true
+		},
+		"node_modules/isexe": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+		},
+		"node_modules/isstream": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+			"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+			"dev": true
+		},
+		"node_modules/jackspeak": {
+			"version": "2.3.6",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+			"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+			"dev": true,
+			"dependencies": {
+				"@isaacs/cliui": "^8.0.2"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			},
+			"optionalDependencies": {
+				"@pkgjs/parseargs": "^0.11.0"
+			}
+		},
+		"node_modules/jiti": {
+			"version": "1.21.0",
+			"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
+			"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+			"dev": true,
+			"bin": {
+				"jiti": "bin/jiti.js"
+			}
+		},
+		"node_modules/js-sha256": {
+			"version": "0.10.1",
+			"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.10.1.tgz",
+			"integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw=="
+		},
+		"node_modules/js-tokens": {
+			"version": "9.0.0",
+			"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz",
+			"integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
+			"dev": true
+		},
+		"node_modules/js-yaml": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+			"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+			"dev": true,
+			"dependencies": {
+				"argparse": "^2.0.1"
+			},
+			"bin": {
+				"js-yaml": "bin/js-yaml.js"
+			}
+		},
+		"node_modules/jsbn": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+			"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+			"dev": true
+		},
+		"node_modules/json-buffer": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+			"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+			"dev": true
+		},
+		"node_modules/json-schema": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+			"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+			"dev": true
+		},
+		"node_modules/json-schema-traverse": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+			"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+			"dev": true
+		},
+		"node_modules/json-stable-stringify-without-jsonify": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+			"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+			"dev": true
+		},
+		"node_modules/json-stringify-safe": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+			"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+			"dev": true
+		},
+		"node_modules/jsonfile": {
+			"version": "6.1.0",
+			"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+			"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+			"dev": true,
+			"dependencies": {
+				"universalify": "^2.0.0"
+			},
+			"optionalDependencies": {
+				"graceful-fs": "^4.1.6"
+			}
+		},
+		"node_modules/jsprim": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
+			"integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==",
+			"dev": true,
+			"engines": [
+				"node >=0.6.0"
+			],
+			"dependencies": {
+				"assert-plus": "1.0.0",
+				"extsprintf": "1.3.0",
+				"json-schema": "0.4.0",
+				"verror": "1.10.0"
+			}
+		},
+		"node_modules/katex": {
+			"version": "0.16.10",
+			"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz",
+			"integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==",
+			"funding": [
+				"https://opencollective.com/katex",
+				"https://github.com/sponsors/katex"
+			],
+			"dependencies": {
+				"commander": "^8.3.0"
+			},
+			"bin": {
+				"katex": "cli.js"
+			}
+		},
+		"node_modules/katex/node_modules/commander": {
+			"version": "8.3.0",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+			"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+			"engines": {
+				"node": ">= 12"
+			}
+		},
+		"node_modules/keyv": {
+			"version": "4.5.4",
+			"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+			"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+			"dev": true,
+			"dependencies": {
+				"json-buffer": "3.0.1"
+			}
+		},
+		"node_modules/khroma": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
+			"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
+		},
+		"node_modules/kleur": {
+			"version": "4.1.5",
+			"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+			"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/known-css-properties": {
+			"version": "0.34.0",
+			"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz",
+			"integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==",
+			"dev": true
+		},
+		"node_modules/layout-base": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+			"integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="
+		},
+		"node_modules/lazy-ass": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
+			"integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
+			"dev": true,
+			"engines": {
+				"node": "> 0.8"
+			}
+		},
+		"node_modules/lead": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz",
+			"integrity": "sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg==",
+			"dev": true,
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/levn": {
+			"version": "0.4.1",
+			"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+			"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+			"dev": true,
+			"dependencies": {
+				"prelude-ls": "^1.2.1",
+				"type-check": "~0.4.0"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/lilconfig": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+			"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antonk52"
+			}
+		},
+		"node_modules/lines-and-columns": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+			"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+			"dev": true
+		},
+		"node_modules/linkify-it": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+			"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+			"dependencies": {
+				"uc.micro": "^2.0.0"
+			}
+		},
+		"node_modules/listr2": {
+			"version": "3.14.0",
+			"resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
+			"integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==",
+			"dev": true,
+			"dependencies": {
+				"cli-truncate": "^2.1.0",
+				"colorette": "^2.0.16",
+				"log-update": "^4.0.0",
+				"p-map": "^4.0.0",
+				"rfdc": "^1.3.0",
+				"rxjs": "^7.5.1",
+				"through": "^2.3.8",
+				"wrap-ansi": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			},
+			"peerDependencies": {
+				"enquirer": ">= 2.3.0 < 3"
+			},
+			"peerDependenciesMeta": {
+				"enquirer": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/listr2/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true
+		},
+		"node_modules/listr2/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/listr2/node_modules/wrap-ansi": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/local-pkg": {
+			"version": "0.5.0",
+			"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
+			"integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
+			"dev": true,
+			"dependencies": {
+				"mlly": "^1.4.2",
+				"pkg-types": "^1.0.3"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/locate-character": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
+			"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
+		},
+		"node_modules/locate-path": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+			"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+			"dev": true,
+			"dependencies": {
+				"p-locate": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+			"dev": true
+		},
+		"node_modules/lodash-es": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+			"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+		},
+		"node_modules/lodash.castarray": {
+			"version": "4.4.0",
+			"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+			"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+			"dev": true
+		},
+		"node_modules/lodash.isplainobject": {
+			"version": "4.0.6",
+			"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+			"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+			"dev": true
+		},
+		"node_modules/lodash.merge": {
+			"version": "4.6.2",
+			"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+			"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+			"dev": true
+		},
+		"node_modules/lodash.once": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+			"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+			"dev": true
+		},
+		"node_modules/log-symbols": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+			"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+			"dev": true,
+			"dependencies": {
+				"chalk": "^4.1.0",
+				"is-unicode-supported": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/log-update": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+			"integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
+			"dev": true,
+			"dependencies": {
+				"ansi-escapes": "^4.3.0",
+				"cli-cursor": "^3.1.0",
+				"slice-ansi": "^4.0.0",
+				"wrap-ansi": "^6.2.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/log-update/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+			"dev": true
+		},
+		"node_modules/log-update/node_modules/slice-ansi": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+			"integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"astral-regex": "^2.0.0",
+				"is-fullwidth-code-point": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/slice-ansi?sponsor=1"
+			}
+		},
+		"node_modules/log-update/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dev": true,
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/log-update/node_modules/wrap-ansi": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+			"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/long": {
+			"version": "5.2.3",
+			"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+			"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+		},
+		"node_modules/loupe": {
+			"version": "2.3.7",
+			"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
+			"integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
+			"dev": true,
+			"dependencies": {
+				"get-func-name": "^2.0.1"
+			}
+		},
+		"node_modules/lowlight": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.1.0.tgz",
+			"integrity": "sha512-CEbNVoSikAxwDMDPjXlqlFYiZLkDJHwyGu/MfOsJnF3d7f3tds5J3z8s/l9TMXhzfsJCCJEAsD78842mwmg0PQ==",
+			"license": "MIT",
+			"peer": true,
+			"dependencies": {
+				"@types/hast": "^3.0.0",
+				"devlop": "^1.0.0",
+				"highlight.js": "~11.9.0"
+			},
+			"funding": {
+				"type": "github",
+				"url": "https://github.com/sponsors/wooorm"
+			}
+		},
+		"node_modules/magic-string": {
+			"version": "0.30.11",
+			"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+			"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.5.0"
+			}
+		},
+		"node_modules/markdown-it": {
+			"version": "14.1.0",
+			"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
+			"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+			"dependencies": {
+				"argparse": "^2.0.1",
+				"entities": "^4.4.0",
+				"linkify-it": "^5.0.0",
+				"mdurl": "^2.0.0",
+				"punycode.js": "^2.3.1",
+				"uc.micro": "^2.1.0"
+			},
+			"bin": {
+				"markdown-it": "bin/markdown-it.mjs"
+			}
+		},
+		"node_modules/marked": {
+			"version": "9.1.6",
+			"resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz",
+			"integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==",
+			"bin": {
+				"marked": "bin/marked.js"
+			},
+			"engines": {
+				"node": ">= 16"
+			}
+		},
+		"node_modules/matcher-collection": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/matcher-collection/-/matcher-collection-2.0.1.tgz",
+			"integrity": "sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==",
+			"dev": true,
+			"dependencies": {
+				"@types/minimatch": "^3.0.3",
+				"minimatch": "^3.0.2"
+			},
+			"engines": {
+				"node": "6.* || 8.* || >= 10.*"
+			}
+		},
+		"node_modules/matcher-collection/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/matcher-collection/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/mdast-util-from-markdown": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz",
+			"integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==",
+			"dependencies": {
+				"@types/mdast": "^3.0.0",
+				"@types/unist": "^2.0.0",
+				"decode-named-character-reference": "^1.0.0",
+				"mdast-util-to-string": "^3.1.0",
+				"micromark": "^3.0.0",
+				"micromark-util-decode-numeric-character-reference": "^1.0.0",
+				"micromark-util-decode-string": "^1.0.0",
+				"micromark-util-normalize-identifier": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0",
+				"unist-util-stringify-position": "^3.0.0",
+				"uvu": "^0.5.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/unified"
+			}
+		},
+		"node_modules/mdast-util-to-string": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz",
+			"integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==",
+			"dependencies": {
+				"@types/mdast": "^3.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/unified"
+			}
+		},
+		"node_modules/mdn-data": {
+			"version": "2.0.30",
+			"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+			"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="
+		},
+		"node_modules/mdurl": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+			"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="
+		},
+		"node_modules/merge-stream": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+			"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+			"dev": true
+		},
+		"node_modules/merge2": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+			"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/mermaid": {
+			"version": "10.9.3",
+			"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.3.tgz",
+			"integrity": "sha512-V80X1isSEvAewIL3xhmz/rVmc27CVljcsbWxkxlWJWY/1kQa4XOABqpDl2qQLGKzpKm6WbTfUEKImBlUfFYArw==",
+			"dependencies": {
+				"@braintree/sanitize-url": "^6.0.1",
+				"@types/d3-scale": "^4.0.3",
+				"@types/d3-scale-chromatic": "^3.0.0",
+				"cytoscape": "^3.28.1",
+				"cytoscape-cose-bilkent": "^4.1.0",
+				"d3": "^7.4.0",
+				"d3-sankey": "^0.12.3",
+				"dagre-d3-es": "7.0.10",
+				"dayjs": "^1.11.7",
+				"dompurify": "^3.0.5 <3.1.7",
+				"elkjs": "^0.9.0",
+				"katex": "^0.16.9",
+				"khroma": "^2.0.0",
+				"lodash-es": "^4.17.21",
+				"mdast-util-from-markdown": "^1.3.0",
+				"non-layered-tidy-tree-layout": "^2.0.2",
+				"stylis": "^4.1.3",
+				"ts-dedent": "^2.2.0",
+				"uuid": "^9.0.0",
+				"web-worker": "^1.2.0"
+			}
+		},
+		"node_modules/micromark": {
+			"version": "3.2.0",
+			"resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz",
+			"integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"@types/debug": "^4.0.0",
+				"debug": "^4.0.0",
+				"decode-named-character-reference": "^1.0.0",
+				"micromark-core-commonmark": "^1.0.1",
+				"micromark-factory-space": "^1.0.0",
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-chunked": "^1.0.0",
+				"micromark-util-combine-extensions": "^1.0.0",
+				"micromark-util-decode-numeric-character-reference": "^1.0.0",
+				"micromark-util-encode": "^1.0.0",
+				"micromark-util-normalize-identifier": "^1.0.0",
+				"micromark-util-resolve-all": "^1.0.0",
+				"micromark-util-sanitize-uri": "^1.0.0",
+				"micromark-util-subtokenize": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.1",
+				"uvu": "^0.5.0"
+			}
+		},
+		"node_modules/micromark-core-commonmark": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz",
+			"integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"decode-named-character-reference": "^1.0.0",
+				"micromark-factory-destination": "^1.0.0",
+				"micromark-factory-label": "^1.0.0",
+				"micromark-factory-space": "^1.0.0",
+				"micromark-factory-title": "^1.0.0",
+				"micromark-factory-whitespace": "^1.0.0",
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-chunked": "^1.0.0",
+				"micromark-util-classify-character": "^1.0.0",
+				"micromark-util-html-tag-name": "^1.0.0",
+				"micromark-util-normalize-identifier": "^1.0.0",
+				"micromark-util-resolve-all": "^1.0.0",
+				"micromark-util-subtokenize": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.1",
+				"uvu": "^0.5.0"
+			}
+		},
+		"node_modules/micromark-factory-destination": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz",
+			"integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-factory-label": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz",
+			"integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0",
+				"uvu": "^0.5.0"
+			}
+		},
+		"node_modules/micromark-factory-space": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz",
+			"integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-factory-title": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz",
+			"integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-factory-space": "^1.0.0",
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-factory-whitespace": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz",
+			"integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-factory-space": "^1.0.0",
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-character": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz",
+			"integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-chunked": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz",
+			"integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-symbol": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-classify-character": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz",
+			"integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-combine-extensions": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz",
+			"integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-chunked": "^1.0.0",
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-decode-numeric-character-reference": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz",
+			"integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-symbol": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-decode-string": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz",
+			"integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"decode-named-character-reference": "^1.0.0",
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-decode-numeric-character-reference": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-encode": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz",
+			"integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			]
+		},
+		"node_modules/micromark-util-html-tag-name": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz",
+			"integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			]
+		},
+		"node_modules/micromark-util-normalize-identifier": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz",
+			"integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-symbol": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-resolve-all": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz",
+			"integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-types": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-sanitize-uri": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz",
+			"integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-character": "^1.0.0",
+				"micromark-util-encode": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0"
+			}
+		},
+		"node_modules/micromark-util-subtokenize": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz",
+			"integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			],
+			"dependencies": {
+				"micromark-util-chunked": "^1.0.0",
+				"micromark-util-symbol": "^1.0.0",
+				"micromark-util-types": "^1.0.0",
+				"uvu": "^0.5.0"
+			}
+		},
+		"node_modules/micromark-util-symbol": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz",
+			"integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			]
+		},
+		"node_modules/micromark-util-types": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz",
+			"integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==",
+			"funding": [
+				{
+					"type": "GitHub Sponsors",
+					"url": "https://github.com/sponsors/unifiedjs"
+				},
+				{
+					"type": "OpenCollective",
+					"url": "https://opencollective.com/unified"
+				}
+			]
+		},
+		"node_modules/micromatch": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+			"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+			"dev": true,
+			"dependencies": {
+				"braces": "^3.0.3",
+				"picomatch": "^2.3.1"
+			},
+			"engines": {
+				"node": ">=8.6"
+			}
+		},
+		"node_modules/mime-db": {
+			"version": "1.52.0",
+			"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+			"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mime-types": {
+			"version": "2.1.35",
+			"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+			"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+			"dev": true,
+			"dependencies": {
+				"mime-db": "1.52.0"
+			},
+			"engines": {
+				"node": ">= 0.6"
+			}
+		},
+		"node_modules/mimic-fn": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+			"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/min-indent": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+			"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+			"dev": true,
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/minimatch": {
+			"version": "9.0.3",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+			"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/minimist": {
+			"version": "1.2.8",
+			"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+			"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+			"dev": true,
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/minipass": {
+			"version": "7.1.2",
+			"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+			"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			}
+		},
+		"node_modules/minizlib": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz",
+			"integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==",
+			"dependencies": {
+				"minipass": "^7.0.4",
+				"rimraf": "^5.0.5"
+			},
+			"engines": {
+				"node": ">= 18"
+			}
+		},
+		"node_modules/minizlib/node_modules/glob": {
+			"version": "10.4.5",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+			"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+			"dependencies": {
+				"foreground-child": "^3.1.0",
+				"jackspeak": "^3.1.2",
+				"minimatch": "^9.0.4",
+				"minipass": "^7.1.2",
+				"package-json-from-dist": "^1.0.0",
+				"path-scurry": "^1.11.1"
+			},
+			"bin": {
+				"glob": "dist/esm/bin.mjs"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/minizlib/node_modules/jackspeak": {
+			"version": "3.4.3",
+			"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+			"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+			"dependencies": {
+				"@isaacs/cliui": "^8.0.2"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			},
+			"optionalDependencies": {
+				"@pkgjs/parseargs": "^0.11.0"
+			}
+		},
+		"node_modules/minizlib/node_modules/minimatch": {
+			"version": "9.0.5",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+			"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+			"dependencies": {
+				"brace-expansion": "^2.0.1"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/minizlib/node_modules/rimraf": {
+			"version": "5.0.10",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
+			"integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+			"dependencies": {
+				"glob": "^10.3.7"
+			},
+			"bin": {
+				"rimraf": "dist/esm/bin.mjs"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/mkdirp": {
+			"version": "0.5.6",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+			"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+			"dev": true,
+			"dependencies": {
+				"minimist": "^1.2.6"
+			},
+			"bin": {
+				"mkdirp": "bin/cmd.js"
+			}
+		},
+		"node_modules/mktemp": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/mktemp/-/mktemp-0.4.0.tgz",
+			"integrity": "sha512-IXnMcJ6ZyTuhRmJSjzvHSRhlVPiN9Jwc6e59V0bEJ0ba6OBeX2L0E+mRN1QseeOF4mM+F1Rit6Nh7o+rl2Yn/A==",
+			"dev": true,
+			"engines": {
+				"node": ">0.9"
+			}
+		},
+		"node_modules/mlly": {
+			"version": "1.7.0",
+			"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz",
+			"integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==",
+			"dev": true,
+			"dependencies": {
+				"acorn": "^8.11.3",
+				"pathe": "^1.1.2",
+				"pkg-types": "^1.1.0",
+				"ufo": "^1.5.3"
+			}
+		},
+		"node_modules/mri": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
+			"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/mrmime": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
+			"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/ms": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+			"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+		},
+		"node_modules/mz": {
+			"version": "2.7.0",
+			"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+			"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+			"dev": true,
+			"dependencies": {
+				"any-promise": "^1.0.0",
+				"object-assign": "^4.0.1",
+				"thenify-all": "^1.0.0"
+			}
+		},
+		"node_modules/nanoid": {
+			"version": "5.0.6",
+			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.6.tgz",
+			"integrity": "sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"bin": {
+				"nanoid": "bin/nanoid.js"
+			},
+			"engines": {
+				"node": "^18 || >=20"
+			}
+		},
+		"node_modules/natural-compare": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+			"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+			"dev": true
+		},
+		"node_modules/ngraph.events": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.2.2.tgz",
+			"integrity": "sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ=="
+		},
+		"node_modules/node-releases": {
+			"version": "2.0.14",
+			"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+			"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+			"dev": true
+		},
+		"node_modules/non-layered-tidy-tree-layout": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz",
+			"integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw=="
+		},
+		"node_modules/normalize-path": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/normalize-range": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+			"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/now-and-later": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz",
+			"integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==",
+			"dev": true,
+			"dependencies": {
+				"once": "^1.4.0"
+			},
+			"engines": {
+				"node": ">= 10.13.0"
+			}
+		},
+		"node_modules/npm-run-path": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+			"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+			"dev": true,
+			"dependencies": {
+				"path-key": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/nth-check": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+			"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+			"dev": true,
+			"dependencies": {
+				"boolbase": "^1.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/fb55/nth-check?sponsor=1"
+			}
+		},
+		"node_modules/object-assign": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+			"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/object-hash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+			"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/object-inspect": {
+			"version": "1.13.2",
+			"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
+			"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/once": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+			"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+			"dependencies": {
+				"wrappy": "1"
+			}
+		},
+		"node_modules/onetime": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+			"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+			"dev": true,
+			"dependencies": {
+				"mimic-fn": "^2.1.0"
+			},
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/onnxruntime-common": {
+			"version": "1.19.2",
+			"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.19.2.tgz",
+			"integrity": "sha512-a4R7wYEVFbZBlp0BfhpbFWqe4opCor3KM+5Wm22Az3NGDcQMiU2hfG/0MfnBs+1ZrlSGmlgWeMcXQkDk1UFb8Q=="
+		},
+		"node_modules/onnxruntime-node": {
+			"version": "1.19.2",
+			"resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.19.2.tgz",
+			"integrity": "sha512-9eHMP/HKbbeUcqte1JYzaaRC8JPn7ojWeCeoyShO86TOR97OCyIyAIOGX3V95ErjslVhJRXY8Em/caIUc0hm1Q==",
+			"hasInstallScript": true,
+			"os": [
+				"win32",
+				"darwin",
+				"linux"
+			],
+			"dependencies": {
+				"onnxruntime-common": "1.19.2",
+				"tar": "^7.0.1"
+			}
+		},
+		"node_modules/onnxruntime-web": {
+			"version": "1.20.0-dev.20241016-2b8fc5529b",
+			"resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.20.0-dev.20241016-2b8fc5529b.tgz",
+			"integrity": "sha512-1XovqtgqeEFtupuyzdDQo7Tqj4GRyNHzOoXjapCEo4rfH3JrXok5VtqucWfRXHPsOI5qoNxMQ9VE+drDIp6woQ==",
+			"dependencies": {
+				"flatbuffers": "^1.12.0",
+				"guid-typescript": "^1.0.9",
+				"long": "^5.2.3",
+				"onnxruntime-common": "1.20.0-dev.20241016-2b8fc5529b",
+				"platform": "^1.3.6",
+				"protobufjs": "^7.2.4"
+			}
+		},
+		"node_modules/onnxruntime-web/node_modules/onnxruntime-common": {
+			"version": "1.20.0-dev.20241016-2b8fc5529b",
+			"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.20.0-dev.20241016-2b8fc5529b.tgz",
+			"integrity": "sha512-KZK8b6zCYGZFjd4ANze0pqBnqnFTS3GIVeclQpa2qseDpXrCQJfkWBixRcrZShNhm3LpFOZ8qJYFC5/qsJK9WQ=="
+		},
+		"node_modules/optionator": {
+			"version": "0.9.3",
+			"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+			"integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+			"dev": true,
+			"dependencies": {
+				"@aashutoshrathi/word-wrap": "^1.2.3",
+				"deep-is": "^0.1.3",
+				"fast-levenshtein": "^2.0.6",
+				"levn": "^0.4.1",
+				"prelude-ls": "^1.2.1",
+				"type-check": "^0.4.0"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/orderedmap": {
+			"version": "2.1.1",
+			"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+			"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="
+		},
+		"node_modules/ospath": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
+			"integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==",
+			"dev": true
+		},
+		"node_modules/p-limit": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+			"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+			"dev": true,
+			"dependencies": {
+				"yocto-queue": "^0.1.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/p-locate": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+			"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+			"dev": true,
+			"dependencies": {
+				"p-limit": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/p-map": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+			"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+			"dev": true,
+			"dependencies": {
+				"aggregate-error": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/package-json-from-dist": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+			"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
+		},
+		"node_modules/paneforge": {
+			"version": "0.0.6",
+			"resolved": "https://registry.npmjs.org/paneforge/-/paneforge-0.0.6.tgz",
+			"integrity": "sha512-jYeN/wdREihja5c6nK3S5jritDQ+EbCqC5NrDo97qCZzZ9GkmEcN5C0ZCjF4nmhBwkDKr6tLIgz4QUKWxLXjAw==",
+			"dependencies": {
+				"nanoid": "^5.0.4"
+			},
+			"peerDependencies": {
+				"svelte": "^4.0.0 || ^5.0.0-next.1"
+			}
+		},
+		"node_modules/panzoom": {
+			"version": "9.4.3",
+			"resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz",
+			"integrity": "sha512-xaxCpElcRbQsUtIdwlrZA90P90+BHip4Vda2BC8MEb4tkI05PmR6cKECdqUCZ85ZvBHjpI9htJrZBxV5Gp/q/w==",
+			"dependencies": {
+				"amator": "^1.1.0",
+				"ngraph.events": "^1.2.2",
+				"wheel": "^1.0.0"
+			}
+		},
+		"node_modules/parent-module": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+			"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+			"dev": true,
+			"dependencies": {
+				"callsites": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/parse5": {
+			"version": "7.1.2",
+			"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+			"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+			"dev": true,
+			"dependencies": {
+				"entities": "^4.4.0"
+			},
+			"funding": {
+				"url": "https://github.com/inikulin/parse5?sponsor=1"
+			}
+		},
+		"node_modules/parse5-htmlparser2-tree-adapter": {
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+			"integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+			"dev": true,
+			"dependencies": {
+				"domhandler": "^5.0.2",
+				"parse5": "^7.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/inikulin/parse5?sponsor=1"
+			}
+		},
+		"node_modules/path-exists": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+			"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-is-absolute": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+			"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/path-key": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+			"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/path-parse": {
+			"version": "1.0.7",
+			"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+			"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+		},
+		"node_modules/path-posix": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/path-posix/-/path-posix-1.0.0.tgz",
+			"integrity": "sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==",
+			"dev": true
+		},
+		"node_modules/path-scurry": {
+			"version": "1.11.1",
+			"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+			"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+			"dependencies": {
+				"lru-cache": "^10.2.0",
+				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.18"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/path-scurry/node_modules/lru-cache": {
+			"version": "10.2.0",
+			"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+			"integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+			"engines": {
+				"node": "14 || >=16.14"
+			}
+		},
+		"node_modules/path-type": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+			"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/pathe": {
+			"version": "1.1.2",
+			"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+			"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+			"dev": true
+		},
+		"node_modules/pathval": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+			"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+			"dev": true,
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/pend": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+			"integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+			"dev": true
+		},
+		"node_modules/performance-now": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+			"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+			"dev": true
+		},
+		"node_modules/periscopic": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
+			"integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
+			"dependencies": {
+				"@types/estree": "^1.0.0",
+				"estree-walker": "^3.0.0",
+				"is-reference": "^3.0.0"
+			}
+		},
+		"node_modules/periscopic/node_modules/estree-walker": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+			"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+			"dependencies": {
+				"@types/estree": "^1.0.0"
+			}
+		},
+		"node_modules/periscopic/node_modules/is-reference": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
+			"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
+			"dependencies": {
+				"@types/estree": "*"
+			}
+		},
+		"node_modules/picocolors": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
+			"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
+		},
+		"node_modules/picomatch": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+			"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+			"engines": {
+				"node": ">=8.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/jonschlinkert"
+			}
+		},
+		"node_modules/pify": {
+			"version": "2.3.0",
+			"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+			"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/pirates": {
+			"version": "4.0.6",
+			"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+			"integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/pkg-types": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz",
+			"integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==",
+			"dev": true,
+			"dependencies": {
+				"confbox": "^0.1.7",
+				"mlly": "^1.7.0",
+				"pathe": "^1.1.2"
+			}
+		},
+		"node_modules/plain-tag": {
+			"version": "0.1.3",
+			"resolved": "https://registry.npmjs.org/plain-tag/-/plain-tag-0.1.3.tgz",
+			"integrity": "sha512-yyVAOFKTAElc7KdLt2+UKGExNYwYb/Y/WE9i+1ezCQsJE8gbKSjewfpRqK2nQgZ4d4hhAAGgDCOcIZVilqE5UA=="
+		},
+		"node_modules/platform": {
+			"version": "1.3.6",
+			"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
+			"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="
+		},
+		"node_modules/polyscript": {
+			"version": "0.12.8",
+			"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.12.8.tgz",
+			"integrity": "sha512-kcG3W9jU/s1sYjWOTAa2jAh5D2jm3zJRi+glSTsC+lA3D1b/Sd67pEIGpyL9bWNKYSimqAx4se6jAhQjJZ7+jQ==",
+			"dependencies": {
+				"@ungap/structured-clone": "^1.2.0",
+				"@ungap/with-resolvers": "^0.1.0",
+				"@webreflection/fetch": "^0.1.5",
+				"basic-devtools": "^0.1.6",
+				"codedent": "^0.1.2",
+				"coincident": "^1.2.3",
+				"gc-hook": "^0.3.1",
+				"html-escaper": "^3.0.3",
+				"proxy-target": "^3.0.2",
+				"sticky-module": "^0.1.1",
+				"to-json-callback": "^0.1.1"
+			}
+		},
+		"node_modules/postcss": {
+			"version": "8.4.47",
+			"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+			"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/postcss"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"dependencies": {
+				"nanoid": "^3.3.7",
+				"picocolors": "^1.1.0",
+				"source-map-js": "^1.2.1"
+			},
+			"engines": {
+				"node": "^10 || ^12 || >=14"
+			}
+		},
+		"node_modules/postcss-import": {
+			"version": "15.1.0",
+			"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+			"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+			"dev": true,
+			"dependencies": {
+				"postcss-value-parser": "^4.0.0",
+				"read-cache": "^1.0.0",
+				"resolve": "^1.1.7"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.0.0"
+			}
+		},
+		"node_modules/postcss-js": {
+			"version": "4.0.1",
+			"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+			"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+			"dev": true,
+			"dependencies": {
+				"camelcase-css": "^2.0.1"
+			},
+			"engines": {
+				"node": "^12 || ^14 || >= 16"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/postcss/"
+			},
+			"peerDependencies": {
+				"postcss": "^8.4.21"
+			}
+		},
+		"node_modules/postcss-load-config": {
+			"version": "3.1.4",
+			"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
+			"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
+			"dev": true,
+			"dependencies": {
+				"lilconfig": "^2.0.5",
+				"yaml": "^1.10.2"
+			},
+			"engines": {
+				"node": ">= 10"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/postcss/"
+			},
+			"peerDependencies": {
+				"postcss": ">=8.0.9",
+				"ts-node": ">=9.0.0"
+			},
+			"peerDependenciesMeta": {
+				"postcss": {
+					"optional": true
+				},
+				"ts-node": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/postcss-load-config/node_modules/lilconfig": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+			"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/postcss-nested": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
+			"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+			"dev": true,
+			"dependencies": {
+				"postcss-selector-parser": "^6.0.11"
+			},
+			"engines": {
+				"node": ">=12.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/postcss/"
+			},
+			"peerDependencies": {
+				"postcss": "^8.2.14"
+			}
+		},
+		"node_modules/postcss-nested/node_modules/postcss-selector-parser": {
+			"version": "6.0.16",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+			"integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-safe-parser": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz",
+			"integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=12.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/postcss/"
+			},
+			"peerDependencies": {
+				"postcss": "^8.3.3"
+			}
+		},
+		"node_modules/postcss-scss": {
+			"version": "4.0.9",
+			"resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz",
+			"integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/postcss-scss"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"engines": {
+				"node": ">=12.0"
+			},
+			"peerDependencies": {
+				"postcss": "^8.4.29"
+			}
+		},
+		"node_modules/postcss-selector-parser": {
+			"version": "6.0.10",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+			"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/postcss-value-parser": {
+			"version": "4.2.0",
+			"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+			"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+			"dev": true
+		},
+		"node_modules/postcss/node_modules/nanoid": {
+			"version": "3.3.7",
+			"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+			"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"bin": {
+				"nanoid": "bin/nanoid.cjs"
+			},
+			"engines": {
+				"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+			}
+		},
+		"node_modules/prelude-ls": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+			"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/prettier": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+			"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+			"dev": true,
+			"bin": {
+				"prettier": "bin/prettier.cjs"
+			},
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/prettier/prettier?sponsor=1"
+			}
+		},
+		"node_modules/prettier-plugin-svelte": {
+			"version": "3.2.6",
+			"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.6.tgz",
+			"integrity": "sha512-Y1XWLw7vXUQQZmgv1JAEiLcErqUniAF2wO7QJsw8BVMvpLET2dI5WpEIEJx1r11iHVdSMzQxivyfrH9On9t2IQ==",
+			"dev": true,
+			"peerDependencies": {
+				"prettier": "^3.0.0",
+				"svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
+			}
+		},
+		"node_modules/pretty-bytes": {
+			"version": "5.6.0",
+			"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+			"integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/pretty-format": {
+			"version": "29.7.0",
+			"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+			"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
+			"dev": true,
+			"dependencies": {
+				"@jest/schemas": "^29.6.3",
+				"ansi-styles": "^5.0.0",
+				"react-is": "^18.0.0"
+			},
+			"engines": {
+				"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+			}
+		},
+		"node_modules/pretty-format/node_modules/ansi-styles": {
+			"version": "5.2.0",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+			"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/process": {
+			"version": "0.11.10",
+			"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+			"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+			"dev": true,
+			"engines": {
+				"node": ">= 0.6.0"
+			}
+		},
+		"node_modules/process-nextick-args": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+			"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+			"dev": true
+		},
+		"node_modules/promise-map-series": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/promise-map-series/-/promise-map-series-0.3.0.tgz",
+			"integrity": "sha512-3npG2NGhTc8BWBolLLf8l/92OxMGaRLbqvIh9wjCHhDXNvk4zsxaTaCpiCunW09qWPrN2zeNSNwRLVBrQQtutA==",
+			"dev": true,
+			"engines": {
+				"node": "10.* || >= 12.*"
+			}
+		},
+		"node_modules/prosemirror-changeset": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz",
+			"integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-transform": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-collab": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
+			"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-state": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-commands": {
+			"version": "1.6.2",
+			"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.6.2.tgz",
+			"integrity": "sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-model": "^1.0.0",
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-transform": "^1.10.2"
+			}
+		},
+		"node_modules/prosemirror-dropcursor": {
+			"version": "1.8.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz",
+			"integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==",
+			"dependencies": {
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-transform": "^1.1.0",
+				"prosemirror-view": "^1.1.0"
+			}
+		},
+		"node_modules/prosemirror-example-setup": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/prosemirror-example-setup/-/prosemirror-example-setup-1.2.3.tgz",
+			"integrity": "sha512-+hXZi8+xbFvYM465zZH3rdZ9w7EguVKmUYwYLZjIJIjPK+I0nPTwn8j0ByW2avchVczRwZmOJGNvehblyIerSQ==",
+			"dependencies": {
+				"prosemirror-commands": "^1.0.0",
+				"prosemirror-dropcursor": "^1.0.0",
+				"prosemirror-gapcursor": "^1.0.0",
+				"prosemirror-history": "^1.0.0",
+				"prosemirror-inputrules": "^1.0.0",
+				"prosemirror-keymap": "^1.0.0",
+				"prosemirror-menu": "^1.0.0",
+				"prosemirror-schema-list": "^1.0.0",
+				"prosemirror-state": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-gapcursor": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
+			"integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
+			"dependencies": {
+				"prosemirror-keymap": "^1.0.0",
+				"prosemirror-model": "^1.0.0",
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-view": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-history": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz",
+			"integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==",
+			"dependencies": {
+				"prosemirror-state": "^1.2.2",
+				"prosemirror-transform": "^1.0.0",
+				"prosemirror-view": "^1.31.0",
+				"rope-sequence": "^1.3.0"
+			}
+		},
+		"node_modules/prosemirror-inputrules": {
+			"version": "1.4.0",
+			"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz",
+			"integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==",
+			"dependencies": {
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-transform": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-keymap": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz",
+			"integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==",
+			"dependencies": {
+				"prosemirror-state": "^1.0.0",
+				"w3c-keyname": "^2.2.0"
+			}
+		},
+		"node_modules/prosemirror-markdown": {
+			"version": "1.13.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.1.tgz",
+			"integrity": "sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==",
+			"dependencies": {
+				"@types/markdown-it": "^14.0.0",
+				"markdown-it": "^14.0.0",
+				"prosemirror-model": "^1.20.0"
+			}
+		},
+		"node_modules/prosemirror-menu": {
+			"version": "1.2.4",
+			"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz",
+			"integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==",
+			"dependencies": {
+				"crelt": "^1.0.0",
+				"prosemirror-commands": "^1.0.0",
+				"prosemirror-history": "^1.0.0",
+				"prosemirror-state": "^1.0.0"
+			}
+		},
+		"node_modules/prosemirror-model": {
+			"version": "1.23.0",
+			"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.23.0.tgz",
+			"integrity": "sha512-Q/fgsgl/dlOAW9ILu4OOhYWQbc7TQd4BwKH/RwmUjyVf8682Be4zj3rOYdLnYEcGzyg8LL9Q5IWYKD8tdToreQ==",
+			"dependencies": {
+				"orderedmap": "^2.0.0"
+			}
+		},
+		"node_modules/prosemirror-schema-basic": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz",
+			"integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==",
+			"dependencies": {
+				"prosemirror-model": "^1.19.0"
+			}
+		},
+		"node_modules/prosemirror-schema-list": {
+			"version": "1.4.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.4.1.tgz",
+			"integrity": "sha512-jbDyaP/6AFfDfu70VzySsD75Om2t3sXTOdl5+31Wlxlg62td1haUpty/ybajSfJ1pkGadlOfwQq9kgW5IMo1Rg==",
+			"dependencies": {
+				"prosemirror-model": "^1.0.0",
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-transform": "^1.7.3"
+			}
+		},
+		"node_modules/prosemirror-state": {
+			"version": "1.4.3",
+			"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
+			"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
+			"dependencies": {
+				"prosemirror-model": "^1.0.0",
+				"prosemirror-transform": "^1.0.0",
+				"prosemirror-view": "^1.27.0"
+			}
+		},
+		"node_modules/prosemirror-tables": {
+			"version": "1.6.1",
+			"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.6.1.tgz",
+			"integrity": "sha512-p8WRJNA96jaNQjhJolmbxTzd6M4huRE5xQ8OxjvMhQUP0Nzpo4zz6TztEiwk6aoqGBhz9lxRWR1yRZLlpQN98w==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-keymap": "^1.1.2",
+				"prosemirror-model": "^1.8.1",
+				"prosemirror-state": "^1.3.1",
+				"prosemirror-transform": "^1.2.1",
+				"prosemirror-view": "^1.13.3"
+			}
+		},
+		"node_modules/prosemirror-trailing-node": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
+			"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
+			"license": "MIT",
+			"dependencies": {
+				"@remirror/core-constants": "3.0.0",
+				"escape-string-regexp": "^4.0.0"
+			},
+			"peerDependencies": {
+				"prosemirror-model": "^1.22.1",
+				"prosemirror-state": "^1.4.2",
+				"prosemirror-view": "^1.33.8"
+			}
+		},
+		"node_modules/prosemirror-transform": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz",
+			"integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-model": "^1.21.0"
+			}
+		},
+		"node_modules/prosemirror-view": {
+			"version": "1.36.0",
+			"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.36.0.tgz",
+			"integrity": "sha512-U0GQd5yFvV5qUtT41X1zCQfbw14vkbbKwLlQXhdylEmgpYVHkefXYcC4HHwWOfZa3x6Y8wxDLUBv7dxN5XQ3nA==",
+			"license": "MIT",
+			"dependencies": {
+				"prosemirror-model": "^1.20.0",
+				"prosemirror-state": "^1.0.0",
+				"prosemirror-transform": "^1.1.0"
+			}
+		},
+		"node_modules/protobufjs": {
+			"version": "7.4.0",
+			"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+			"integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+			"hasInstallScript": true,
+			"dependencies": {
+				"@protobufjs/aspromise": "^1.1.2",
+				"@protobufjs/base64": "^1.1.2",
+				"@protobufjs/codegen": "^2.0.4",
+				"@protobufjs/eventemitter": "^1.1.0",
+				"@protobufjs/fetch": "^1.1.0",
+				"@protobufjs/float": "^1.0.2",
+				"@protobufjs/inquire": "^1.1.0",
+				"@protobufjs/path": "^1.1.2",
+				"@protobufjs/pool": "^1.1.0",
+				"@protobufjs/utf8": "^1.1.0",
+				"@types/node": ">=13.7.0",
+				"long": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=12.0.0"
+			}
+		},
+		"node_modules/proxy-from-env": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz",
+			"integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==",
+			"dev": true
+		},
+		"node_modules/proxy-target": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/proxy-target/-/proxy-target-3.0.2.tgz",
+			"integrity": "sha512-FFE1XNwXX/FNC3/P8HiKaJSy/Qk68RitG/QEcLy/bVnTAPlgTAWPZKh0pARLAnpfXQPKyalBhk009NRTgsk8vQ=="
+		},
+		"node_modules/psl": {
+			"version": "1.9.0",
+			"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+			"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+			"dev": true
+		},
+		"node_modules/pump": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+			"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+			"dev": true,
+			"dependencies": {
+				"end-of-stream": "^1.1.0",
+				"once": "^1.3.1"
+			}
+		},
+		"node_modules/punycode": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+			"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/punycode.js": {
+			"version": "2.3.1",
+			"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+			"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/pyodide": {
+			"version": "0.26.1",
+			"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.26.1.tgz",
+			"integrity": "sha512-P+Gm88nwZqY7uBgjbQH8CqqU6Ei/rDn7pS1t02sNZsbyLJMyE2OVXjgNuqVT3KqYWnyGREUN0DbBUCJqk8R0ew==",
+			"dependencies": {
+				"ws": "^8.5.0"
+			},
+			"engines": {
+				"node": ">=18.0.0"
+			}
+		},
+		"node_modules/qs": {
+			"version": "6.13.0",
+			"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+			"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+			"dev": true,
+			"dependencies": {
+				"side-channel": "^1.0.6"
+			},
+			"engines": {
+				"node": ">=0.6"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/querystringify": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+			"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+			"dev": true
+		},
+		"node_modules/queue-microtask": {
+			"version": "1.2.3",
+			"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+			"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			]
+		},
+		"node_modules/queue-tick": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
+			"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
+			"dev": true
+		},
+		"node_modules/quick-temp": {
+			"version": "0.1.8",
+			"resolved": "https://registry.npmjs.org/quick-temp/-/quick-temp-0.1.8.tgz",
+			"integrity": "sha512-YsmIFfD9j2zaFwJkzI6eMG7y0lQP7YeWzgtFgNl38pGWZBSXJooZbOWwkcRot7Vt0Fg9L23pX0tqWU3VvLDsiA==",
+			"dev": true,
+			"dependencies": {
+				"mktemp": "~0.4.0",
+				"rimraf": "^2.5.4",
+				"underscore.string": "~3.3.4"
+			}
+		},
+		"node_modules/quick-temp/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/quick-temp/node_modules/glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.1.1",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/quick-temp/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/quick-temp/node_modules/rimraf": {
+			"version": "2.7.1",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+			"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+			"dev": true,
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			}
+		},
+		"node_modules/react-is": {
+			"version": "18.3.1",
+			"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+			"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+			"dev": true
+		},
+		"node_modules/read-cache": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+			"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+			"dev": true,
+			"dependencies": {
+				"pify": "^2.3.0"
+			}
+		},
+		"node_modules/readable-stream": {
+			"version": "2.3.8",
+			"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+			"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+			"dev": true,
+			"dependencies": {
+				"core-util-is": "~1.0.0",
+				"inherits": "~2.0.3",
+				"isarray": "~1.0.0",
+				"process-nextick-args": "~2.0.0",
+				"safe-buffer": "~5.1.1",
+				"string_decoder": "~1.1.1",
+				"util-deprecate": "~1.0.1"
+			}
+		},
+		"node_modules/readdirp": {
+			"version": "3.6.0",
+			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+			"dev": true,
+			"dependencies": {
+				"picomatch": "^2.2.1"
+			},
+			"engines": {
+				"node": ">=8.10.0"
+			}
+		},
+		"node_modules/regenerator-runtime": {
+			"version": "0.14.1",
+			"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+			"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+		},
+		"node_modules/remove-trailing-separator": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+			"integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+			"dev": true
+		},
+		"node_modules/replace-ext": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz",
+			"integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==",
+			"dev": true,
+			"engines": {
+				"node": ">= 10"
+			}
+		},
+		"node_modules/request-progress": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+			"integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==",
+			"dev": true,
+			"dependencies": {
+				"throttleit": "^1.0.0"
+			}
+		},
+		"node_modules/requires-port": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+			"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+			"dev": true
+		},
+		"node_modules/resolve": {
+			"version": "1.22.8",
+			"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+			"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+			"dependencies": {
+				"is-core-module": "^2.13.0",
+				"path-parse": "^1.0.7",
+				"supports-preserve-symlinks-flag": "^1.0.0"
+			},
+			"bin": {
+				"resolve": "bin/resolve"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/resolve-from": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+			"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+			"dev": true,
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/resolve-options": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz",
+			"integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==",
+			"dev": true,
+			"dependencies": {
+				"value-or-function": "^4.0.0"
+			},
+			"engines": {
+				"node": ">= 10.13.0"
+			}
+		},
+		"node_modules/restore-cursor": {
+			"version": "3.1.0",
+			"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+			"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+			"dev": true,
+			"dependencies": {
+				"onetime": "^5.1.0",
+				"signal-exit": "^3.0.2"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/restore-cursor/node_modules/signal-exit": {
+			"version": "3.0.7",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+			"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+			"dev": true
+		},
+		"node_modules/reusify": {
+			"version": "1.0.4",
+			"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+			"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+			"dev": true,
+			"engines": {
+				"iojs": ">=1.0.0",
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/rfdc": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
+			"integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==",
+			"dev": true
+		},
+		"node_modules/rimraf": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+			"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+			"dev": true,
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/rimraf/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/rimraf/node_modules/glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.1.1",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/rimraf/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/robust-predicates": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+			"integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
+		},
+		"node_modules/rollup": {
+			"version": "4.22.4",
+			"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz",
+			"integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==",
+			"dependencies": {
+				"@types/estree": "1.0.5"
+			},
+			"bin": {
+				"rollup": "dist/bin/rollup"
+			},
+			"engines": {
+				"node": ">=18.0.0",
+				"npm": ">=8.0.0"
+			},
+			"optionalDependencies": {
+				"@rollup/rollup-android-arm-eabi": "4.22.4",
+				"@rollup/rollup-android-arm64": "4.22.4",
+				"@rollup/rollup-darwin-arm64": "4.22.4",
+				"@rollup/rollup-darwin-x64": "4.22.4",
+				"@rollup/rollup-linux-arm-gnueabihf": "4.22.4",
+				"@rollup/rollup-linux-arm-musleabihf": "4.22.4",
+				"@rollup/rollup-linux-arm64-gnu": "4.22.4",
+				"@rollup/rollup-linux-arm64-musl": "4.22.4",
+				"@rollup/rollup-linux-powerpc64le-gnu": "4.22.4",
+				"@rollup/rollup-linux-riscv64-gnu": "4.22.4",
+				"@rollup/rollup-linux-s390x-gnu": "4.22.4",
+				"@rollup/rollup-linux-x64-gnu": "4.22.4",
+				"@rollup/rollup-linux-x64-musl": "4.22.4",
+				"@rollup/rollup-win32-arm64-msvc": "4.22.4",
+				"@rollup/rollup-win32-ia32-msvc": "4.22.4",
+				"@rollup/rollup-win32-x64-msvc": "4.22.4",
+				"fsevents": "~2.3.2"
+			}
+		},
+		"node_modules/rope-sequence": {
+			"version": "1.3.4",
+			"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
+			"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="
+		},
+		"node_modules/rsvp": {
+			"version": "4.8.5",
+			"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
+			"integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==",
+			"dev": true,
+			"engines": {
+				"node": "6.* || >= 7.*"
+			}
+		},
+		"node_modules/run-parallel": {
+			"version": "1.2.0",
+			"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+			"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/feross"
+				},
+				{
+					"type": "patreon",
+					"url": "https://www.patreon.com/feross"
+				},
+				{
+					"type": "consulting",
+					"url": "https://feross.org/support"
+				}
+			],
+			"dependencies": {
+				"queue-microtask": "^1.2.2"
+			}
+		},
+		"node_modules/rw": {
+			"version": "1.3.3",
+			"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+			"integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
+		},
+		"node_modules/rxjs": {
+			"version": "7.8.1",
+			"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+			"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+			"devOptional": true,
+			"dependencies": {
+				"tslib": "^2.1.0"
+			}
+		},
+		"node_modules/sade": {
+			"version": "1.8.1",
+			"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
+			"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
+			"dependencies": {
+				"mri": "^1.1.0"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/safe-buffer": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+			"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+			"dev": true
+		},
+		"node_modules/safer-buffer": {
+			"version": "2.1.2",
+			"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+			"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+		},
+		"node_modules/sander": {
+			"version": "0.5.1",
+			"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
+			"integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==",
+			"dev": true,
+			"dependencies": {
+				"es6-promise": "^3.1.2",
+				"graceful-fs": "^4.1.3",
+				"mkdirp": "^0.5.1",
+				"rimraf": "^2.5.2"
+			}
+		},
+		"node_modules/sander/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/sander/node_modules/glob": {
+			"version": "7.2.3",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+			"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+			"dev": true,
+			"dependencies": {
+				"fs.realpath": "^1.0.0",
+				"inflight": "^1.0.4",
+				"inherits": "2",
+				"minimatch": "^3.1.1",
+				"once": "^1.3.0",
+				"path-is-absolute": "^1.0.0"
+			},
+			"engines": {
+				"node": "*"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/sander/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/sander/node_modules/rimraf": {
+			"version": "2.7.1",
+			"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+			"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+			"dev": true,
+			"dependencies": {
+				"glob": "^7.1.3"
+			},
+			"bin": {
+				"rimraf": "bin.js"
+			}
+		},
+		"node_modules/sass-embedded": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.81.0.tgz",
+			"integrity": "sha512-uZQ2Faxb1oWBHpeSSzjxnhClbMb3QadN0ql0ZFNuqWOLUxwaVhrMlMhPq6TDPbbfDUjihuwrMCuy695Bgna5RA==",
+			"devOptional": true,
+			"license": "MIT",
+			"dependencies": {
+				"@bufbuild/protobuf": "^2.0.0",
+				"buffer-builder": "^0.2.0",
+				"colorjs.io": "^0.5.0",
+				"immutable": "^5.0.2",
+				"rxjs": "^7.4.0",
+				"supports-color": "^8.1.1",
+				"sync-child-process": "^1.0.2",
+				"varint": "^6.0.0"
+			},
+			"bin": {
+				"sass": "dist/bin/sass.js"
+			},
+			"engines": {
+				"node": ">=16.0.0"
+			},
+			"optionalDependencies": {
+				"sass-embedded-android-arm": "1.81.0",
+				"sass-embedded-android-arm64": "1.81.0",
+				"sass-embedded-android-ia32": "1.81.0",
+				"sass-embedded-android-riscv64": "1.81.0",
+				"sass-embedded-android-x64": "1.81.0",
+				"sass-embedded-darwin-arm64": "1.81.0",
+				"sass-embedded-darwin-x64": "1.81.0",
+				"sass-embedded-linux-arm": "1.81.0",
+				"sass-embedded-linux-arm64": "1.81.0",
+				"sass-embedded-linux-ia32": "1.81.0",
+				"sass-embedded-linux-musl-arm": "1.81.0",
+				"sass-embedded-linux-musl-arm64": "1.81.0",
+				"sass-embedded-linux-musl-ia32": "1.81.0",
+				"sass-embedded-linux-musl-riscv64": "1.81.0",
+				"sass-embedded-linux-musl-x64": "1.81.0",
+				"sass-embedded-linux-riscv64": "1.81.0",
+				"sass-embedded-linux-x64": "1.81.0",
+				"sass-embedded-win32-arm64": "1.81.0",
+				"sass-embedded-win32-ia32": "1.81.0",
+				"sass-embedded-win32-x64": "1.81.0"
+			}
+		},
+		"node_modules/sass-embedded-android-arm": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.81.0.tgz",
+			"integrity": "sha512-NWEmIuaIEsGFNsIRa+5JpIpPJyZ32H15E85CNZqEIhhwWlk9UNw7vlOCmTH8MtabtnACwC/2NG8VyNa3nxKzUQ==",
+			"cpu": [
+				"arm"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-android-arm64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.81.0.tgz",
+			"integrity": "sha512-I36P77/PKAHx6sqOmexO2iEY5kpsmQ1VxcgITZSOxPMQhdB6m4t3bTabfDuWQQmCrqqiNFtLQHeytB65bUqwiw==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-android-ia32": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-android-ia32/-/sass-embedded-android-ia32-1.81.0.tgz",
+			"integrity": "sha512-k8V1usXw30w1GVxvrteG1RzgYJzYQ9PfL2aeOqGdroBN7zYTD9VGJXTGcxA4IeeRxmRd7szVW2mKXXS472fh8g==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-android-riscv64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.81.0.tgz",
+			"integrity": "sha512-RXlanyLXEpN/DEehXgLuKPsqT//GYlsGFxKXgRiCc8hIPAueFLQXKJmLWlL3BEtHgmFdbsStIu4aZCcb1hOFlQ==",
+			"cpu": [
+				"riscv64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-android-x64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.81.0.tgz",
+			"integrity": "sha512-RQG0FxGQ1DERNyUDED8+BDVaLIjI+BNg8lVcyqlLZUrWY6NhzjwYEeiN/DNZmMmHtqDucAPNDcsdVUNQqsBy2A==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-darwin-arm64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.81.0.tgz",
+			"integrity": "sha512-gLKbsfII9Ppua76N41ODFnKGutla9qv0OGAas8gxe0jYBeAQFi/1iKQYdNtQtKi4mA9n5TQTqz+HHCKszZCoyA==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-darwin-x64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.81.0.tgz",
+			"integrity": "sha512-7uMOlT9hD2KUJCbTN2XcfghDxt/rc50ujjfSjSHjX1SYj7mGplkINUXvVbbvvaV2wt6t9vkGkCo5qNbeBhfwBg==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-arm": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.81.0.tgz",
+			"integrity": "sha512-REqR9qM4RchCE3cKqzRy9Q4zigIV82SbSpCi/O4O3oK3pg2I1z7vkb3TiJsivusG/li7aqKZGmYOtAXjruGQDA==",
+			"cpu": [
+				"arm"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-arm64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.81.0.tgz",
+			"integrity": "sha512-jy4bvhdUmqbyw1jv1f3Uxl+MF8EU/Y/GDx4w6XPJm4Ds+mwH/TwnyAwsxxoBhWfnBnW8q2ADy039DlS5p+9csQ==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-ia32": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-ia32/-/sass-embedded-linux-ia32-1.81.0.tgz",
+			"integrity": "sha512-ga/Jk4q5Bn1aC+iHJteDZuLSKnmBUiS3dEg1fnl/Z7GaHIChceKDJOw0zNaILRXI0qT2E1at9MwzoRaRA5Nn/g==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-musl-arm": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.81.0.tgz",
+			"integrity": "sha512-oWVUvQ4d5Kx1Md75YXZl5z1WBjc+uOhfRRqzkJ3nWc8tjszxJN+y/5EOJavhsNI3/2yoTt6eMXRTqDD9b0tWSQ==",
+			"cpu": [
+				"arm"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-musl-arm64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.81.0.tgz",
+			"integrity": "sha512-hpntWf5kjkoxncA1Vh8vhsUOquZ8AROZKx0rQh7ZjSRs4JrYZASz1cfevPKaEM3wIim/nYa6TJqm0VqWsrERlA==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-musl-ia32": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-ia32/-/sass-embedded-linux-musl-ia32-1.81.0.tgz",
+			"integrity": "sha512-UEXUYkBuqTSwg5JNWiNlfMZ1Jx6SJkaEdx+fsL3Tk099L8cKSoJWH2EPz4ZJjNbyIMymrSdVfymheTeZ8u24xA==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-musl-riscv64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.81.0.tgz",
+			"integrity": "sha512-1D7OznytbIhx2XDHWi1nuQ8d/uCVR7FGGzELgaU//T8A9DapVTUgPKvB70AF1k4GzChR9IXU/WvFZs2hDTbaJg==",
+			"cpu": [
+				"riscv64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-musl-x64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.81.0.tgz",
+			"integrity": "sha512-ia6VCTeVDQtBSMktXRFza1AZCt8/6aUoujot6Ugf4KmdytQqPJIHxkHaGftm5xwi9WdrMGYS7zgolToPijR11A==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-riscv64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.81.0.tgz",
+			"integrity": "sha512-KbxSsqu4tT1XbhZfJV/5NfW0VtJIGlD58RjqJqJBi8Rnjrx29/upBsuwoDWtsPV/LhoGwwU1XkSa9Q1ifCz4fQ==",
+			"cpu": [
+				"riscv64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-linux-x64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.81.0.tgz",
+			"integrity": "sha512-AMDeVY2T9WAnSFkuQcsOn5c29GRs/TuqnCiblKeXfxCSKym5uKdBl/N7GnTV6OjzoxiJBbkYKdVIaS5By7Gj4g==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-win32-arm64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.81.0.tgz",
+			"integrity": "sha512-YOmBRYnygwWUmCoH14QbMRHjcvCJufeJBAp0m61tOJXIQh64ziwV4mjdqjS/Rx3zhTT4T+nulDUw4d3kLiMncA==",
+			"cpu": [
+				"arm64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-win32-ia32": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-win32-ia32/-/sass-embedded-win32-ia32-1.81.0.tgz",
+			"integrity": "sha512-HFfr/C+uLJGGTENdnssuNTmXI/xnIasUuEHEKqI+2J0FHCWT5cpz3PGAOHymPyJcZVYGUG/7gIxIx/d7t0LFYw==",
+			"cpu": [
+				"ia32"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded-win32-x64": {
+			"version": "1.81.0",
+			"resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.81.0.tgz",
+			"integrity": "sha512-wxj52jDcIAwWcXb7ShZ7vQYKcVUkJ+04YM9l46jDY+qwHzliGuorAUyujLyKTE9heGD3gShJ3wPPC1lXzq6v9A==",
+			"cpu": [
+				"x64"
+			],
+			"license": "MIT",
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/sass-embedded/node_modules/supports-color": {
+			"version": "8.1.1",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+			"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+			"devOptional": true,
+			"license": "MIT",
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/supports-color?sponsor=1"
+			}
+		},
+		"node_modules/semver": {
+			"version": "7.6.3",
+			"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+			"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+			"bin": {
+				"semver": "bin/semver.js"
+			},
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/set-cookie-parser": {
+			"version": "2.6.0",
+			"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
+			"integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ=="
+		},
+		"node_modules/set-function-length": {
+			"version": "1.2.2",
+			"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+			"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+			"dev": true,
+			"dependencies": {
+				"define-data-property": "^1.1.4",
+				"es-errors": "^1.3.0",
+				"function-bind": "^1.1.2",
+				"get-intrinsic": "^1.2.4",
+				"gopd": "^1.0.1",
+				"has-property-descriptors": "^1.0.2"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			}
+		},
+		"node_modules/sharp": {
+			"version": "0.33.5",
+			"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
+			"integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+			"hasInstallScript": true,
+			"dependencies": {
+				"color": "^4.2.3",
+				"detect-libc": "^2.0.3",
+				"semver": "^7.6.3"
+			},
+			"engines": {
+				"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/libvips"
+			},
+			"optionalDependencies": {
+				"@img/sharp-darwin-arm64": "0.33.5",
+				"@img/sharp-darwin-x64": "0.33.5",
+				"@img/sharp-libvips-darwin-arm64": "1.0.4",
+				"@img/sharp-libvips-darwin-x64": "1.0.4",
+				"@img/sharp-libvips-linux-arm": "1.0.5",
+				"@img/sharp-libvips-linux-arm64": "1.0.4",
+				"@img/sharp-libvips-linux-s390x": "1.0.4",
+				"@img/sharp-libvips-linux-x64": "1.0.4",
+				"@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+				"@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+				"@img/sharp-linux-arm": "0.33.5",
+				"@img/sharp-linux-arm64": "0.33.5",
+				"@img/sharp-linux-s390x": "0.33.5",
+				"@img/sharp-linux-x64": "0.33.5",
+				"@img/sharp-linuxmusl-arm64": "0.33.5",
+				"@img/sharp-linuxmusl-x64": "0.33.5",
+				"@img/sharp-wasm32": "0.33.5",
+				"@img/sharp-win32-ia32": "0.33.5",
+				"@img/sharp-win32-x64": "0.33.5"
+			}
+		},
+		"node_modules/shebang-command": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+			"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+			"dependencies": {
+				"shebang-regex": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/shebang-regex": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+			"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/side-channel": {
+			"version": "1.0.6",
+			"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+			"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
+			"dev": true,
+			"dependencies": {
+				"call-bind": "^1.0.7",
+				"es-errors": "^1.3.0",
+				"get-intrinsic": "^1.2.4",
+				"object-inspect": "^1.13.1"
+			},
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/siginfo": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+			"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+			"dev": true
+		},
+		"node_modules/signal-exit": {
+			"version": "4.1.0",
+			"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+			"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/simple-swizzle": {
+			"version": "0.2.2",
+			"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+			"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+			"dependencies": {
+				"is-arrayish": "^0.3.1"
+			}
+		},
+		"node_modules/sirv": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz",
+			"integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==",
+			"license": "MIT",
+			"dependencies": {
+				"@polka/url": "^1.0.0-next.24",
+				"mrmime": "^2.0.0",
+				"totalist": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/slash": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+			"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/slice-ansi": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+			"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+			"dev": true,
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"astral-regex": "^2.0.0",
+				"is-fullwidth-code-point": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/socket.io-client": {
+			"version": "4.7.5",
+			"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz",
+			"integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==",
+			"dependencies": {
+				"@socket.io/component-emitter": "~3.1.0",
+				"debug": "~4.3.2",
+				"engine.io-client": "~6.5.2",
+				"socket.io-parser": "~4.2.4"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/socket.io-parser": {
+			"version": "4.2.4",
+			"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+			"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+			"dependencies": {
+				"@socket.io/component-emitter": "~3.1.0",
+				"debug": "~4.3.1"
+			},
+			"engines": {
+				"node": ">=10.0.0"
+			}
+		},
+		"node_modules/sorcery": {
+			"version": "0.11.0",
+			"resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz",
+			"integrity": "sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==",
+			"dev": true,
+			"dependencies": {
+				"@jridgewell/sourcemap-codec": "^1.4.14",
+				"buffer-crc32": "^0.2.5",
+				"minimist": "^1.2.0",
+				"sander": "^0.5.0"
+			},
+			"bin": {
+				"sorcery": "bin/sorcery"
+			}
+		},
+		"node_modules/sort-keys": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.0.0.tgz",
+			"integrity": "sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==",
+			"dev": true,
+			"dependencies": {
+				"is-plain-obj": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/sortablejs": {
+			"version": "1.15.2",
+			"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz",
+			"integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA=="
+		},
+		"node_modules/source-map-js": {
+			"version": "1.2.1",
+			"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+			"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/sprintf-js": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+			"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+			"dev": true
+		},
+		"node_modules/sshpk": {
+			"version": "1.18.0",
+			"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
+			"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
+			"dev": true,
+			"dependencies": {
+				"asn1": "~0.2.3",
+				"assert-plus": "^1.0.0",
+				"bcrypt-pbkdf": "^1.0.0",
+				"dashdash": "^1.12.0",
+				"ecc-jsbn": "~0.1.1",
+				"getpass": "^0.1.1",
+				"jsbn": "~0.1.0",
+				"safer-buffer": "^2.0.2",
+				"tweetnacl": "~0.14.0"
+			},
+			"bin": {
+				"sshpk-conv": "bin/sshpk-conv",
+				"sshpk-sign": "bin/sshpk-sign",
+				"sshpk-verify": "bin/sshpk-verify"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/stackback": {
+			"version": "0.0.2",
+			"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+			"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+			"dev": true
+		},
+		"node_modules/std-env": {
+			"version": "3.7.0",
+			"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
+			"integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
+			"dev": true
+		},
+		"node_modules/sticky-module": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/sticky-module/-/sticky-module-0.1.1.tgz",
+			"integrity": "sha512-IuYgnyIMUx/m6rtu14l/LR2MaqOLtpXcWkxPmtPsiScRHEo+S4Tojk+DWFHOncSdFX/OsoLOM4+T92yOmI1AMw=="
+		},
+		"node_modules/stream-composer": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz",
+			"integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==",
+			"dev": true,
+			"dependencies": {
+				"streamx": "^2.13.2"
+			}
+		},
+		"node_modules/streamx": {
+			"version": "2.16.1",
+			"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz",
+			"integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==",
+			"dev": true,
+			"dependencies": {
+				"fast-fifo": "^1.1.0",
+				"queue-tick": "^1.0.1"
+			},
+			"optionalDependencies": {
+				"bare-events": "^2.2.0"
+			}
+		},
+		"node_modules/string_decoder": {
+			"version": "1.1.1",
+			"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+			"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+			"dev": true,
+			"dependencies": {
+				"safe-buffer": "~5.1.0"
+			}
+		},
+		"node_modules/string-width": {
+			"version": "5.1.2",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+			"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+			"dependencies": {
+				"eastasianwidth": "^0.2.0",
+				"emoji-regex": "^9.2.2",
+				"strip-ansi": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/string-width-cjs": {
+			"name": "string-width",
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/string-width-cjs/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+		},
+		"node_modules/string-width/node_modules/ansi-regex": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+			"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-regex?sponsor=1"
+			}
+		},
+		"node_modules/string-width/node_modules/strip-ansi": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+			"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+			"dependencies": {
+				"ansi-regex": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/strip-ansi?sponsor=1"
+			}
+		},
+		"node_modules/strip-ansi": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-ansi-cjs": {
+			"name": "strip-ansi",
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+			"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+			"dependencies": {
+				"ansi-regex": "^5.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-final-newline": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+			"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+			"dev": true,
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/strip-indent": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
+			"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
+			"dev": true,
+			"dependencies": {
+				"min-indent": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/strip-json-comments": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+			"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/strip-literal": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz",
+			"integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==",
+			"dev": true,
+			"dependencies": {
+				"js-tokens": "^9.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antfu"
+			}
+		},
+		"node_modules/style-mod": {
+			"version": "4.1.2",
+			"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
+			"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="
+		},
+		"node_modules/stylis": {
+			"version": "4.3.2",
+			"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz",
+			"integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg=="
+		},
+		"node_modules/sucrase": {
+			"version": "3.35.0",
+			"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+			"integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+			"dev": true,
+			"dependencies": {
+				"@jridgewell/gen-mapping": "^0.3.2",
+				"commander": "^4.0.0",
+				"glob": "^10.3.10",
+				"lines-and-columns": "^1.1.6",
+				"mz": "^2.7.0",
+				"pirates": "^4.0.1",
+				"ts-interface-checker": "^0.1.9"
+			},
+			"bin": {
+				"sucrase": "bin/sucrase",
+				"sucrase-node": "bin/sucrase-node"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			}
+		},
+		"node_modules/sucrase/node_modules/commander": {
+			"version": "4.1.1",
+			"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+			"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/sucrase/node_modules/glob": {
+			"version": "10.3.10",
+			"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
+			"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+			"dev": true,
+			"dependencies": {
+				"foreground-child": "^3.1.0",
+				"jackspeak": "^2.3.5",
+				"minimatch": "^9.0.1",
+				"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
+				"path-scurry": "^1.10.1"
+			},
+			"bin": {
+				"glob": "dist/esm/bin.mjs"
+			},
+			"engines": {
+				"node": ">=16 || 14 >=14.17"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/supports-color": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+			"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+			"dev": true,
+			"dependencies": {
+				"has-flag": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/supports-preserve-symlinks-flag": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+			"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+			"engines": {
+				"node": ">= 0.4"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ljharb"
+			}
+		},
+		"node_modules/svelte": {
+			"version": "4.2.19",
+			"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz",
+			"integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==",
+			"dependencies": {
+				"@ampproject/remapping": "^2.2.1",
+				"@jridgewell/sourcemap-codec": "^1.4.15",
+				"@jridgewell/trace-mapping": "^0.3.18",
+				"@types/estree": "^1.0.1",
+				"acorn": "^8.9.0",
+				"aria-query": "^5.3.0",
+				"axobject-query": "^4.0.0",
+				"code-red": "^1.0.3",
+				"css-tree": "^2.3.1",
+				"estree-walker": "^3.0.3",
+				"is-reference": "^3.0.1",
+				"locate-character": "^3.0.0",
+				"magic-string": "^0.30.4",
+				"periscopic": "^3.1.0"
+			},
+			"engines": {
+				"node": ">=16"
+			}
+		},
+		"node_modules/svelte-check": {
+			"version": "3.8.5",
+			"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.5.tgz",
+			"integrity": "sha512-3OGGgr9+bJ/+1nbPgsvulkLC48xBsqsgtc8Wam281H4G9F5v3mYGa2bHRsPuwHC5brKl4AxJH95QF73kmfihGQ==",
+			"dev": true,
+			"dependencies": {
+				"@jridgewell/trace-mapping": "^0.3.17",
+				"chokidar": "^3.4.1",
+				"picocolors": "^1.0.0",
+				"sade": "^1.7.4",
+				"svelte-preprocess": "^5.1.3",
+				"typescript": "^5.0.3"
+			},
+			"bin": {
+				"svelte-check": "bin/svelte-check"
+			},
+			"peerDependencies": {
+				"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
+			}
+		},
+		"node_modules/svelte-confetti": {
+			"version": "1.3.2",
+			"resolved": "https://registry.npmjs.org/svelte-confetti/-/svelte-confetti-1.3.2.tgz",
+			"integrity": "sha512-R+JwFTC7hIgWVA/OuXrkj384B7CMoceb0t9VacyW6dORTQg0pWojVBB8Bo3tM30cLEQE48Fekzqgx+XSzHESMA==",
+			"dev": true,
+			"peerDependencies": {
+				"svelte": "^4.0.0"
+			}
+		},
+		"node_modules/svelte-eslint-parser": {
+			"version": "0.41.0",
+			"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.41.0.tgz",
+			"integrity": "sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==",
+			"dev": true,
+			"dependencies": {
+				"eslint-scope": "^7.2.2",
+				"eslint-visitor-keys": "^3.4.3",
+				"espree": "^9.6.1",
+				"postcss": "^8.4.39",
+				"postcss-scss": "^4.0.9"
+			},
+			"engines": {
+				"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/ota-meshi"
+			},
+			"peerDependencies": {
+				"svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.191"
+			},
+			"peerDependenciesMeta": {
+				"svelte": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/svelte-hmr": {
+			"version": "0.16.0",
+			"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
+			"integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==",
+			"engines": {
+				"node": "^12.20 || ^14.13.1 || >= 16"
+			},
+			"peerDependencies": {
+				"svelte": "^3.19.0 || ^4.0.0"
+			}
+		},
+		"node_modules/svelte-preprocess": {
+			"version": "5.1.3",
+			"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.3.tgz",
+			"integrity": "sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==",
+			"dev": true,
+			"hasInstallScript": true,
+			"dependencies": {
+				"@types/pug": "^2.0.6",
+				"detect-indent": "^6.1.0",
+				"magic-string": "^0.30.5",
+				"sorcery": "^0.11.0",
+				"strip-indent": "^3.0.0"
+			},
+			"engines": {
+				"node": ">= 16.0.0",
+				"pnpm": "^8.0.0"
+			},
+			"peerDependencies": {
+				"@babel/core": "^7.10.2",
+				"coffeescript": "^2.5.1",
+				"less": "^3.11.3 || ^4.0.0",
+				"postcss": "^7 || ^8",
+				"postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
+				"pug": "^3.0.0",
+				"sass": "^1.26.8",
+				"stylus": "^0.55.0",
+				"sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
+				"svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
+				"typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
+			},
+			"peerDependenciesMeta": {
+				"@babel/core": {
+					"optional": true
+				},
+				"coffeescript": {
+					"optional": true
+				},
+				"less": {
+					"optional": true
+				},
+				"postcss": {
+					"optional": true
+				},
+				"postcss-load-config": {
+					"optional": true
+				},
+				"pug": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"stylus": {
+					"optional": true
+				},
+				"sugarss": {
+					"optional": true
+				},
+				"typescript": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/svelte-sonner": {
+			"version": "0.3.28",
+			"resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-0.3.28.tgz",
+			"integrity": "sha512-K3AmlySeFifF/cKgsYNv5uXqMVNln0NBAacOYgmkQStLa/UoU0LhfAACU6Gr+YYC8bOCHdVmFNoKuDbMEsppJg==",
+			"peerDependencies": {
+				"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0-next.1"
+			}
+		},
+		"node_modules/svelte/node_modules/estree-walker": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+			"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+			"dependencies": {
+				"@types/estree": "^1.0.0"
+			}
+		},
+		"node_modules/svelte/node_modules/is-reference": {
+			"version": "3.0.2",
+			"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
+			"integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
+			"dependencies": {
+				"@types/estree": "*"
+			}
+		},
+		"node_modules/symlink-or-copy": {
+			"version": "1.3.1",
+			"resolved": "https://registry.npmjs.org/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz",
+			"integrity": "sha512-0K91MEXFpBUaywiwSSkmKjnGcasG/rVBXFLJz5DrgGabpYD6N+3yZrfD6uUIfpuTu65DZLHi7N8CizHc07BPZA==",
+			"dev": true
+		},
+		"node_modules/sync-child-process": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
+			"integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
+			"devOptional": true,
+			"license": "MIT",
+			"dependencies": {
+				"sync-message-port": "^1.0.0"
+			},
+			"engines": {
+				"node": ">=16.0.0"
+			}
+		},
+		"node_modules/sync-message-port": {
+			"version": "1.1.3",
+			"resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz",
+			"integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==",
+			"devOptional": true,
+			"license": "MIT",
+			"engines": {
+				"node": ">=16.0.0"
+			}
+		},
+		"node_modules/tabbable": {
+			"version": "6.2.0",
+			"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
+			"integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+		},
+		"node_modules/tailwindcss": {
+			"version": "3.4.1",
+			"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
+			"integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
+			"dev": true,
+			"dependencies": {
+				"@alloc/quick-lru": "^5.2.0",
+				"arg": "^5.0.2",
+				"chokidar": "^3.5.3",
+				"didyoumean": "^1.2.2",
+				"dlv": "^1.1.3",
+				"fast-glob": "^3.3.0",
+				"glob-parent": "^6.0.2",
+				"is-glob": "^4.0.3",
+				"jiti": "^1.19.1",
+				"lilconfig": "^2.1.0",
+				"micromatch": "^4.0.5",
+				"normalize-path": "^3.0.0",
+				"object-hash": "^3.0.0",
+				"picocolors": "^1.0.0",
+				"postcss": "^8.4.23",
+				"postcss-import": "^15.1.0",
+				"postcss-js": "^4.0.1",
+				"postcss-load-config": "^4.0.1",
+				"postcss-nested": "^6.0.1",
+				"postcss-selector-parser": "^6.0.11",
+				"resolve": "^1.22.2",
+				"sucrase": "^3.32.0"
+			},
+			"bin": {
+				"tailwind": "lib/cli.js",
+				"tailwindcss": "lib/cli.js"
+			},
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/tailwindcss/node_modules/lilconfig": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+			"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			}
+		},
+		"node_modules/tailwindcss/node_modules/postcss-load-config": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+			"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/postcss/"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"dependencies": {
+				"lilconfig": "^3.0.0",
+				"yaml": "^2.3.4"
+			},
+			"engines": {
+				"node": ">= 14"
+			},
+			"peerDependencies": {
+				"postcss": ">=8.0.9",
+				"ts-node": ">=9.0.0"
+			},
+			"peerDependenciesMeta": {
+				"postcss": {
+					"optional": true
+				},
+				"ts-node": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
+			"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=14"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/antonk52"
+			}
+		},
+		"node_modules/tailwindcss/node_modules/postcss-selector-parser": {
+			"version": "6.0.16",
+			"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
+			"integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
+			"dev": true,
+			"dependencies": {
+				"cssesc": "^3.0.0",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/tailwindcss/node_modules/yaml": {
+			"version": "2.4.1",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
+			"integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
+			"dev": true,
+			"bin": {
+				"yaml": "bin.mjs"
+			},
+			"engines": {
+				"node": ">= 14"
+			}
+		},
+		"node_modules/tar": {
+			"version": "7.4.3",
+			"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+			"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+			"dependencies": {
+				"@isaacs/fs-minipass": "^4.0.0",
+				"chownr": "^3.0.0",
+				"minipass": "^7.1.2",
+				"minizlib": "^3.0.1",
+				"mkdirp": "^3.0.1",
+				"yallist": "^5.0.0"
+			},
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/tar/node_modules/mkdirp": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+			"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+			"bin": {
+				"mkdirp": "dist/cjs/src/bin.js"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/isaacs"
+			}
+		},
+		"node_modules/teex": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz",
+			"integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==",
+			"dev": true,
+			"dependencies": {
+				"streamx": "^2.12.5"
+			}
+		},
+		"node_modules/text-table": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+			"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+			"dev": true
+		},
+		"node_modules/thenify": {
+			"version": "3.3.1",
+			"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+			"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+			"dev": true,
+			"dependencies": {
+				"any-promise": "^1.0.0"
+			}
+		},
+		"node_modules/thenify-all": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+			"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+			"dev": true,
+			"dependencies": {
+				"thenify": ">= 3.1.0 < 4"
+			},
+			"engines": {
+				"node": ">=0.8"
+			}
+		},
+		"node_modules/throttleit": {
+			"version": "1.0.1",
+			"resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz",
+			"integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==",
+			"dev": true,
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/through": {
+			"version": "2.3.8",
+			"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+			"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+			"dev": true
+		},
+		"node_modules/through2": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+			"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+			"dev": true,
+			"dependencies": {
+				"readable-stream": "~2.3.6",
+				"xtend": "~4.0.1"
+			}
+		},
+		"node_modules/tiny-glob": {
+			"version": "0.2.9",
+			"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
+			"integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==",
+			"dependencies": {
+				"globalyzer": "0.1.0",
+				"globrex": "^0.1.2"
+			}
+		},
+		"node_modules/tinybench": {
+			"version": "2.8.0",
+			"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz",
+			"integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==",
+			"dev": true
+		},
+		"node_modules/tinypool": {
+			"version": "0.8.4",
+			"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz",
+			"integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/tinyspy": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz",
+			"integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==",
+			"dev": true,
+			"engines": {
+				"node": ">=14.0.0"
+			}
+		},
+		"node_modules/tippy.js": {
+			"version": "6.3.7",
+			"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
+			"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+			"dependencies": {
+				"@popperjs/core": "^2.9.0"
+			}
+		},
+		"node_modules/tmp": {
+			"version": "0.2.3",
+			"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+			"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+			"dev": true,
+			"engines": {
+				"node": ">=14.14"
+			}
+		},
+		"node_modules/to-json-callback": {
+			"version": "0.1.1",
+			"resolved": "https://registry.npmjs.org/to-json-callback/-/to-json-callback-0.1.1.tgz",
+			"integrity": "sha512-BzOeinTT3NjE+FJ2iCvWB8HvyuyBzoH3WlSnJ+AYVC4tlePyZWSYdkQIFOARWiq0t35/XhmI0uQsFiUsRksRqg=="
+		},
+		"node_modules/to-regex-range": {
+			"version": "5.0.1",
+			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+			"dev": true,
+			"dependencies": {
+				"is-number": "^7.0.0"
+			},
+			"engines": {
+				"node": ">=8.0"
+			}
+		},
+		"node_modules/to-through": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz",
+			"integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==",
+			"dev": true,
+			"dependencies": {
+				"streamx": "^2.12.5"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/totalist": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+			"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+			"license": "MIT",
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/tough-cookie": {
+			"version": "4.1.4",
+			"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+			"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+			"dev": true,
+			"dependencies": {
+				"psl": "^1.1.33",
+				"punycode": "^2.1.1",
+				"universalify": "^0.2.0",
+				"url-parse": "^1.5.3"
+			},
+			"engines": {
+				"node": ">=6"
+			}
+		},
+		"node_modules/tough-cookie/node_modules/universalify": {
+			"version": "0.2.0",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+			"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 4.0.0"
+			}
+		},
+		"node_modules/ts-api-utils": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+			"integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=16"
+			},
+			"peerDependencies": {
+				"typescript": ">=4.2.0"
+			}
+		},
+		"node_modules/ts-dedent": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
+			"integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
+			"engines": {
+				"node": ">=6.10"
+			}
+		},
+		"node_modules/ts-interface-checker": {
+			"version": "0.1.13",
+			"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+			"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+			"dev": true
+		},
+		"node_modules/tslib": {
+			"version": "2.6.2",
+			"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+			"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+		},
+		"node_modules/tunnel-agent": {
+			"version": "0.6.0",
+			"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+			"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+			"dev": true,
+			"dependencies": {
+				"safe-buffer": "^5.0.1"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/turndown": {
+			"version": "7.2.0",
+			"resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz",
+			"integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==",
+			"license": "MIT",
+			"dependencies": {
+				"@mixmark-io/domino": "^2.2.0"
+			}
+		},
+		"node_modules/tweetnacl": {
+			"version": "0.14.5",
+			"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+			"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+			"dev": true
+		},
+		"node_modules/type-check": {
+			"version": "0.4.0",
+			"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+			"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+			"dev": true,
+			"dependencies": {
+				"prelude-ls": "^1.2.1"
+			},
+			"engines": {
+				"node": ">= 0.8.0"
+			}
+		},
+		"node_modules/type-checked-collections": {
+			"version": "0.1.7",
+			"resolved": "https://registry.npmjs.org/type-checked-collections/-/type-checked-collections-0.1.7.tgz",
+			"integrity": "sha512-fLIydlJy7IG9XL4wjRwEcKhxx/ekLXiWiMvcGo01cOMF+TN+5ZqajM1mRNRz2bNNi1bzou2yofhjZEQi7kgl9A=="
+		},
+		"node_modules/type-detect": {
+			"version": "4.0.8",
+			"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+			"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+			"dev": true,
+			"engines": {
+				"node": ">=4"
+			}
+		},
+		"node_modules/type-fest": {
+			"version": "0.20.2",
+			"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+			"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/typescript": {
+			"version": "5.5.4",
+			"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
+			"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
+			"dev": true,
+			"bin": {
+				"tsc": "bin/tsc",
+				"tsserver": "bin/tsserver"
+			},
+			"engines": {
+				"node": ">=14.17"
+			}
+		},
+		"node_modules/uc.micro": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+			"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="
+		},
+		"node_modules/ufo": {
+			"version": "1.5.3",
+			"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz",
+			"integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==",
+			"dev": true
+		},
+		"node_modules/underscore.string": {
+			"version": "3.3.6",
+			"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz",
+			"integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==",
+			"dev": true,
+			"dependencies": {
+				"sprintf-js": "^1.1.1",
+				"util-deprecate": "^1.0.2"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/undici-types": {
+			"version": "5.26.5",
+			"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+			"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+		},
+		"node_modules/unist-util-stringify-position": {
+			"version": "3.0.3",
+			"resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz",
+			"integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==",
+			"dependencies": {
+				"@types/unist": "^2.0.0"
+			},
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/unified"
+			}
+		},
+		"node_modules/universalify": {
+			"version": "2.0.1",
+			"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+			"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+			"dev": true,
+			"engines": {
+				"node": ">= 10.0.0"
+			}
+		},
+		"node_modules/untildify": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+			"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+			"dev": true,
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/update-browserslist-db": {
+			"version": "1.0.13",
+			"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+			"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+			"dev": true,
+			"funding": [
+				{
+					"type": "opencollective",
+					"url": "https://opencollective.com/browserslist"
+				},
+				{
+					"type": "tidelift",
+					"url": "https://tidelift.com/funding/github/npm/browserslist"
+				},
+				{
+					"type": "github",
+					"url": "https://github.com/sponsors/ai"
+				}
+			],
+			"dependencies": {
+				"escalade": "^3.1.1",
+				"picocolors": "^1.0.0"
+			},
+			"bin": {
+				"update-browserslist-db": "cli.js"
+			},
+			"peerDependencies": {
+				"browserslist": ">= 4.21.0"
+			}
+		},
+		"node_modules/uri-js": {
+			"version": "4.4.1",
+			"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+			"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+			"dev": true,
+			"dependencies": {
+				"punycode": "^2.1.0"
+			}
+		},
+		"node_modules/url-parse": {
+			"version": "1.5.10",
+			"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+			"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+			"dev": true,
+			"dependencies": {
+				"querystringify": "^2.1.1",
+				"requires-port": "^1.0.0"
+			}
+		},
+		"node_modules/util-deprecate": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+			"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+			"dev": true
+		},
+		"node_modules/uuid": {
+			"version": "9.0.1",
+			"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+			"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+			"funding": [
+				"https://github.com/sponsors/broofa",
+				"https://github.com/sponsors/ctavan"
+			],
+			"bin": {
+				"uuid": "dist/bin/uuid"
+			}
+		},
+		"node_modules/uvu": {
+			"version": "0.5.6",
+			"resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz",
+			"integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==",
+			"dependencies": {
+				"dequal": "^2.0.0",
+				"diff": "^5.0.0",
+				"kleur": "^4.0.3",
+				"sade": "^1.7.3"
+			},
+			"bin": {
+				"uvu": "bin.js"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/value-or-function": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz",
+			"integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 10.13.0"
+			}
+		},
+		"node_modules/varint": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+			"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+			"devOptional": true,
+			"license": "MIT"
+		},
+		"node_modules/verror": {
+			"version": "1.10.0",
+			"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+			"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+			"dev": true,
+			"engines": [
+				"node >=0.6.0"
+			],
+			"dependencies": {
+				"assert-plus": "^1.0.0",
+				"core-util-is": "1.0.2",
+				"extsprintf": "^1.2.0"
+			}
+		},
+		"node_modules/verror/node_modules/core-util-is": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+			"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+			"dev": true
+		},
+		"node_modules/vinyl": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz",
+			"integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==",
+			"dev": true,
+			"dependencies": {
+				"clone": "^2.1.2",
+				"clone-stats": "^1.0.0",
+				"remove-trailing-separator": "^1.1.0",
+				"replace-ext": "^2.0.0",
+				"teex": "^1.0.1"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/vinyl-contents": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz",
+			"integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==",
+			"dev": true,
+			"dependencies": {
+				"bl": "^5.0.0",
+				"vinyl": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/vinyl-fs": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.0.tgz",
+			"integrity": "sha512-7GbgBnYfaquMk3Qu9g22x000vbYkOex32930rBnc3qByw6HfMEAoELjCjoJv4HuEQxHAurT+nvMHm6MnJllFLw==",
+			"dev": true,
+			"dependencies": {
+				"fs-mkdirp-stream": "^2.0.1",
+				"glob-stream": "^8.0.0",
+				"graceful-fs": "^4.2.11",
+				"iconv-lite": "^0.6.3",
+				"is-valid-glob": "^1.0.0",
+				"lead": "^4.0.0",
+				"normalize-path": "3.0.0",
+				"resolve-options": "^2.0.0",
+				"stream-composer": "^1.0.2",
+				"streamx": "^2.14.0",
+				"to-through": "^3.0.0",
+				"value-or-function": "^4.0.0",
+				"vinyl": "^3.0.0",
+				"vinyl-sourcemap": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/vinyl-sourcemap": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz",
+			"integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==",
+			"dev": true,
+			"dependencies": {
+				"convert-source-map": "^2.0.0",
+				"graceful-fs": "^4.2.10",
+				"now-and-later": "^3.0.0",
+				"streamx": "^2.12.5",
+				"vinyl": "^3.0.0",
+				"vinyl-contents": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=10.13.0"
+			}
+		},
+		"node_modules/vite": {
+			"version": "5.4.6",
+			"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz",
+			"integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==",
+			"dependencies": {
+				"esbuild": "^0.21.3",
+				"postcss": "^8.4.43",
+				"rollup": "^4.20.0"
+			},
+			"bin": {
+				"vite": "bin/vite.js"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/vitejs/vite?sponsor=1"
+			},
+			"optionalDependencies": {
+				"fsevents": "~2.3.3"
+			},
+			"peerDependencies": {
+				"@types/node": "^18.0.0 || >=20.0.0",
+				"less": "*",
+				"lightningcss": "^1.21.0",
+				"sass": "*",
+				"sass-embedded": "*",
+				"stylus": "*",
+				"sugarss": "*",
+				"terser": "^5.4.0"
+			},
+			"peerDependenciesMeta": {
+				"@types/node": {
+					"optional": true
+				},
+				"less": {
+					"optional": true
+				},
+				"lightningcss": {
+					"optional": true
+				},
+				"sass": {
+					"optional": true
+				},
+				"sass-embedded": {
+					"optional": true
+				},
+				"stylus": {
+					"optional": true
+				},
+				"sugarss": {
+					"optional": true
+				},
+				"terser": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vite-node": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz",
+			"integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==",
+			"dev": true,
+			"dependencies": {
+				"cac": "^6.7.14",
+				"debug": "^4.3.4",
+				"pathe": "^1.1.1",
+				"picocolors": "^1.0.0",
+				"vite": "^5.0.0"
+			},
+			"bin": {
+				"vite-node": "vite-node.mjs"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+			"integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+			"cpu": [
+				"ppc64"
+			],
+			"optional": true,
+			"os": [
+				"aix"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-arm": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+			"integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+			"integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/android-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+			"integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"android"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+			"integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/darwin-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+			"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"darwin"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+			"integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+			"integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"freebsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-arm": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+			"integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+			"cpu": [
+				"arm"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+			"integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-ia32": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+			"integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+			"cpu": [
+				"ia32"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-loong64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+			"integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+			"cpu": [
+				"loong64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+			"integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+			"cpu": [
+				"mips64el"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+			"integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+			"cpu": [
+				"ppc64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+			"integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+			"cpu": [
+				"riscv64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-s390x": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+			"integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+			"cpu": [
+				"s390x"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/linux-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+			"integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"linux"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+			"integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"netbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+			"integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"openbsd"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/sunos-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+			"integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"sunos"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-arm64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+			"integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+			"cpu": [
+				"arm64"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-ia32": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+			"integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+			"cpu": [
+				"ia32"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/@esbuild/win32-x64": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+			"integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+			"cpu": [
+				"x64"
+			],
+			"optional": true,
+			"os": [
+				"win32"
+			],
+			"engines": {
+				"node": ">=12"
+			}
+		},
+		"node_modules/vite/node_modules/esbuild": {
+			"version": "0.21.5",
+			"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+			"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+			"hasInstallScript": true,
+			"bin": {
+				"esbuild": "bin/esbuild"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"optionalDependencies": {
+				"@esbuild/aix-ppc64": "0.21.5",
+				"@esbuild/android-arm": "0.21.5",
+				"@esbuild/android-arm64": "0.21.5",
+				"@esbuild/android-x64": "0.21.5",
+				"@esbuild/darwin-arm64": "0.21.5",
+				"@esbuild/darwin-x64": "0.21.5",
+				"@esbuild/freebsd-arm64": "0.21.5",
+				"@esbuild/freebsd-x64": "0.21.5",
+				"@esbuild/linux-arm": "0.21.5",
+				"@esbuild/linux-arm64": "0.21.5",
+				"@esbuild/linux-ia32": "0.21.5",
+				"@esbuild/linux-loong64": "0.21.5",
+				"@esbuild/linux-mips64el": "0.21.5",
+				"@esbuild/linux-ppc64": "0.21.5",
+				"@esbuild/linux-riscv64": "0.21.5",
+				"@esbuild/linux-s390x": "0.21.5",
+				"@esbuild/linux-x64": "0.21.5",
+				"@esbuild/netbsd-x64": "0.21.5",
+				"@esbuild/openbsd-x64": "0.21.5",
+				"@esbuild/sunos-x64": "0.21.5",
+				"@esbuild/win32-arm64": "0.21.5",
+				"@esbuild/win32-ia32": "0.21.5",
+				"@esbuild/win32-x64": "0.21.5"
+			}
+		},
+		"node_modules/vitefu": {
+			"version": "0.2.5",
+			"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz",
+			"integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==",
+			"peerDependencies": {
+				"vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
+			},
+			"peerDependenciesMeta": {
+				"vite": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vitest": {
+			"version": "1.6.0",
+			"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz",
+			"integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==",
+			"dev": true,
+			"dependencies": {
+				"@vitest/expect": "1.6.0",
+				"@vitest/runner": "1.6.0",
+				"@vitest/snapshot": "1.6.0",
+				"@vitest/spy": "1.6.0",
+				"@vitest/utils": "1.6.0",
+				"acorn-walk": "^8.3.2",
+				"chai": "^4.3.10",
+				"debug": "^4.3.4",
+				"execa": "^8.0.1",
+				"local-pkg": "^0.5.0",
+				"magic-string": "^0.30.5",
+				"pathe": "^1.1.1",
+				"picocolors": "^1.0.0",
+				"std-env": "^3.5.0",
+				"strip-literal": "^2.0.0",
+				"tinybench": "^2.5.1",
+				"tinypool": "^0.8.3",
+				"vite": "^5.0.0",
+				"vite-node": "1.6.0",
+				"why-is-node-running": "^2.2.2"
+			},
+			"bin": {
+				"vitest": "vitest.mjs"
+			},
+			"engines": {
+				"node": "^18.0.0 || >=20.0.0"
+			},
+			"funding": {
+				"url": "https://opencollective.com/vitest"
+			},
+			"peerDependencies": {
+				"@edge-runtime/vm": "*",
+				"@types/node": "^18.0.0 || >=20.0.0",
+				"@vitest/browser": "1.6.0",
+				"@vitest/ui": "1.6.0",
+				"happy-dom": "*",
+				"jsdom": "*"
+			},
+			"peerDependenciesMeta": {
+				"@edge-runtime/vm": {
+					"optional": true
+				},
+				"@types/node": {
+					"optional": true
+				},
+				"@vitest/browser": {
+					"optional": true
+				},
+				"@vitest/ui": {
+					"optional": true
+				},
+				"happy-dom": {
+					"optional": true
+				},
+				"jsdom": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/vitest/node_modules/execa": {
+			"version": "8.0.1",
+			"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+			"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+			"dev": true,
+			"dependencies": {
+				"cross-spawn": "^7.0.3",
+				"get-stream": "^8.0.1",
+				"human-signals": "^5.0.0",
+				"is-stream": "^3.0.0",
+				"merge-stream": "^2.0.0",
+				"npm-run-path": "^5.1.0",
+				"onetime": "^6.0.0",
+				"signal-exit": "^4.1.0",
+				"strip-final-newline": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=16.17"
+			},
+			"funding": {
+				"url": "https://github.com/sindresorhus/execa?sponsor=1"
+			}
+		},
+		"node_modules/vitest/node_modules/get-stream": {
+			"version": "8.0.1",
+			"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+			"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+			"dev": true,
+			"engines": {
+				"node": ">=16"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/human-signals": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+			"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=16.17.0"
+			}
+		},
+		"node_modules/vitest/node_modules/is-stream": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+			"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+			"dev": true,
+			"engines": {
+				"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/mimic-fn": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+			"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/npm-run-path": {
+			"version": "5.3.0",
+			"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+			"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+			"dev": true,
+			"dependencies": {
+				"path-key": "^4.0.0"
+			},
+			"engines": {
+				"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/onetime": {
+			"version": "6.0.0",
+			"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+			"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+			"dev": true,
+			"dependencies": {
+				"mimic-fn": "^4.0.0"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/path-key": {
+			"version": "4.0.0",
+			"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+			"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/vitest/node_modules/strip-final-newline": {
+			"version": "3.0.0",
+			"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+			"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+			"dev": true,
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/w3c-keyname": {
+			"version": "2.2.8",
+			"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+			"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="
+		},
+		"node_modules/walk-sync": {
+			"version": "2.2.0",
+			"resolved": "https://registry.npmjs.org/walk-sync/-/walk-sync-2.2.0.tgz",
+			"integrity": "sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==",
+			"dev": true,
+			"dependencies": {
+				"@types/minimatch": "^3.0.3",
+				"ensure-posix-path": "^1.1.0",
+				"matcher-collection": "^2.0.0",
+				"minimatch": "^3.0.4"
+			},
+			"engines": {
+				"node": "8.* || >= 10.*"
+			}
+		},
+		"node_modules/walk-sync/node_modules/brace-expansion": {
+			"version": "1.1.11",
+			"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+			"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+			"dev": true,
+			"dependencies": {
+				"balanced-match": "^1.0.0",
+				"concat-map": "0.0.1"
+			}
+		},
+		"node_modules/walk-sync/node_modules/minimatch": {
+			"version": "3.1.2",
+			"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+			"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+			"dev": true,
+			"dependencies": {
+				"brace-expansion": "^1.1.7"
+			},
+			"engines": {
+				"node": "*"
+			}
+		},
+		"node_modules/web-worker": {
+			"version": "1.3.0",
+			"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz",
+			"integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA=="
+		},
+		"node_modules/wheel": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/wheel/-/wheel-1.0.0.tgz",
+			"integrity": "sha512-XiCMHibOiqalCQ+BaNSwRoZ9FDTAvOsXxGHXChBugewDj7HC8VBIER71dEOiRH1fSdLbRCQzngKTSiZ06ZQzeA=="
+		},
+		"node_modules/which": {
+			"version": "2.0.2",
+			"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+			"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+			"dependencies": {
+				"isexe": "^2.0.0"
+			},
+			"bin": {
+				"node-which": "bin/node-which"
+			},
+			"engines": {
+				"node": ">= 8"
+			}
+		},
+		"node_modules/why-is-node-running": {
+			"version": "2.2.2",
+			"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz",
+			"integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==",
+			"dev": true,
+			"dependencies": {
+				"siginfo": "^2.0.0",
+				"stackback": "0.0.2"
+			},
+			"bin": {
+				"why-is-node-running": "cli.js"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi": {
+			"version": "8.1.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+			"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+			"dependencies": {
+				"ansi-styles": "^6.1.0",
+				"string-width": "^5.0.1",
+				"strip-ansi": "^7.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi-cjs": {
+			"name": "wrap-ansi",
+			"version": "7.0.0",
+			"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+			"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+			"dependencies": {
+				"ansi-styles": "^4.0.0",
+				"string-width": "^4.1.0",
+				"strip-ansi": "^6.0.0"
+			},
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+			"version": "8.0.0",
+			"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+			"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+		},
+		"node_modules/wrap-ansi-cjs/node_modules/string-width": {
+			"version": "4.2.3",
+			"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+			"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+			"dependencies": {
+				"emoji-regex": "^8.0.0",
+				"is-fullwidth-code-point": "^3.0.0",
+				"strip-ansi": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=8"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/ansi-regex": {
+			"version": "6.0.1",
+			"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+			"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-regex?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/ansi-styles": {
+			"version": "6.2.1",
+			"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+			"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/ansi-styles?sponsor=1"
+			}
+		},
+		"node_modules/wrap-ansi/node_modules/strip-ansi": {
+			"version": "7.1.0",
+			"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+			"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+			"dependencies": {
+				"ansi-regex": "^6.0.1"
+			},
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/chalk/strip-ansi?sponsor=1"
+			}
+		},
+		"node_modules/wrappy": {
+			"version": "1.0.2",
+			"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+			"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+		},
+		"node_modules/ws": {
+			"version": "8.17.1",
+			"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+			"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+			"engines": {
+				"node": ">=10.0.0"
+			},
+			"peerDependencies": {
+				"bufferutil": "^4.0.1",
+				"utf-8-validate": ">=5.0.2"
+			},
+			"peerDependenciesMeta": {
+				"bufferutil": {
+					"optional": true
+				},
+				"utf-8-validate": {
+					"optional": true
+				}
+			}
+		},
+		"node_modules/xmlhttprequest-ssl": {
+			"version": "2.0.0",
+			"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+			"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
+			"engines": {
+				"node": ">=0.4.0"
+			}
+		},
+		"node_modules/xtend": {
+			"version": "4.0.2",
+			"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+			"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+			"dev": true,
+			"engines": {
+				"node": ">=0.4"
+			}
+		},
+		"node_modules/yallist": {
+			"version": "5.0.0",
+			"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+			"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+			"engines": {
+				"node": ">=18"
+			}
+		},
+		"node_modules/yaml": {
+			"version": "1.10.2",
+			"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+			"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+			"dev": true,
+			"engines": {
+				"node": ">= 6"
+			}
+		},
+		"node_modules/yauzl": {
+			"version": "2.10.0",
+			"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+			"integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+			"dev": true,
+			"dependencies": {
+				"buffer-crc32": "~0.2.3",
+				"fd-slicer": "~1.1.0"
+			}
+		},
+		"node_modules/yocto-queue": {
+			"version": "0.1.0",
+			"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+			"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+			"dev": true,
+			"engines": {
+				"node": ">=10"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		}
+	}
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..220f5c6919060f936233685f5c2e467d6132be22
--- /dev/null
+++ b/package.json
@@ -0,0 +1,110 @@
+{
+	"name": "open-webui",
+	"version": "0.4.7",
+	"private": true,
+	"scripts": {
+		"dev": "npm run pyodide:fetch && vite dev --host",
+		"build": "npm run pyodide:fetch && vite build",
+		"preview": "vite preview",
+		"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
+		"lint": "npm run lint:frontend ; npm run lint:types ; npm run lint:backend",
+		"lint:frontend": "eslint . --fix",
+		"lint:types": "npm run check",
+		"lint:backend": "pylint backend/",
+		"format": "prettier --plugin-search-dir --write \"**/*.{js,ts,svelte,css,md,html,json}\"",
+		"format:backend": "black . --exclude \".venv/|/venv/\"",
+		"i18n:parse": "i18next --config i18next-parser.config.ts && prettier --write \"src/lib/i18n/**/*.{js,json}\"",
+		"cy:open": "cypress open",
+		"test:frontend": "vitest --passWithNoTests",
+		"pyodide:fetch": "node scripts/prepare-pyodide.js"
+	},
+	"devDependencies": {
+		"@sveltejs/adapter-auto": "3.2.2",
+		"@sveltejs/adapter-static": "^3.0.2",
+		"@sveltejs/kit": "^2.5.20",
+		"@sveltejs/vite-plugin-svelte": "^3.1.1",
+		"@tailwindcss/typography": "^0.5.13",
+		"@typescript-eslint/eslint-plugin": "^6.17.0",
+		"@typescript-eslint/parser": "^6.17.0",
+		"autoprefixer": "^10.4.16",
+		"cypress": "^13.15.0",
+		"eslint": "^8.56.0",
+		"eslint-config-prettier": "^9.1.0",
+		"eslint-plugin-cypress": "^3.4.0",
+		"eslint-plugin-svelte": "^2.43.0",
+		"i18next-parser": "^9.0.1",
+		"postcss": "^8.4.31",
+		"prettier": "^3.3.3",
+		"prettier-plugin-svelte": "^3.2.6",
+		"sass-embedded": "^1.81.0",
+		"svelte": "^4.2.18",
+		"svelte-check": "^3.8.5",
+		"svelte-confetti": "^1.3.2",
+		"tailwindcss": "^3.3.3",
+		"tslib": "^2.4.1",
+		"typescript": "^5.5.4",
+		"vite": "^5.3.5",
+		"vitest": "^1.6.0"
+	},
+	"type": "module",
+	"dependencies": {
+		"@codemirror/lang-javascript": "^6.2.2",
+		"@codemirror/lang-python": "^6.1.6",
+		"@codemirror/language-data": "^6.5.1",
+		"@codemirror/theme-one-dark": "^6.1.2",
+		"@huggingface/transformers": "^3.0.0",
+		"@mediapipe/tasks-vision": "^0.10.17",
+		"@pyscript/core": "^0.4.32",
+		"@sveltejs/adapter-node": "^2.0.0",
+		"@tiptap/core": "^2.10.0",
+		"@tiptap/extension-code-block-lowlight": "^2.10.0",
+		"@tiptap/extension-highlight": "^2.10.0",
+		"@tiptap/extension-placeholder": "^2.10.0",
+		"@tiptap/extension-typography": "^2.10.0",
+		"@tiptap/pm": "^2.10.0",
+		"@tiptap/starter-kit": "^2.10.0",
+		"@xyflow/svelte": "^0.1.19",
+		"async": "^3.2.5",
+		"bits-ui": "^0.19.7",
+		"codemirror": "^6.0.1",
+		"crc-32": "^1.2.2",
+		"dayjs": "^1.11.10",
+		"dompurify": "^3.1.6",
+		"eventsource-parser": "^1.1.2",
+		"file-saver": "^2.0.5",
+		"fuse.js": "^7.0.0",
+		"highlight.js": "^11.9.0",
+		"i18next": "^23.10.0",
+		"i18next-browser-languagedetector": "^7.2.0",
+		"i18next-resources-to-backend": "^1.2.0",
+		"idb": "^7.1.1",
+		"js-sha256": "^0.10.1",
+		"katex": "^0.16.9",
+		"marked": "^9.1.0",
+		"mermaid": "^10.9.3",
+		"paneforge": "^0.0.6",
+		"panzoom": "^9.4.3",
+		"prosemirror-commands": "^1.6.0",
+		"prosemirror-example-setup": "^1.2.3",
+		"prosemirror-history": "^1.4.1",
+		"prosemirror-keymap": "^1.2.2",
+		"prosemirror-markdown": "^1.13.1",
+		"prosemirror-model": "^1.23.0",
+		"prosemirror-schema-basic": "^1.2.3",
+		"prosemirror-schema-list": "^1.4.1",
+		"prosemirror-state": "^1.4.3",
+		"prosemirror-view": "^1.34.3",
+		"pyodide": "^0.26.1",
+		"socket.io-client": "^4.2.0",
+		"sortablejs": "^1.15.2",
+		"svelte-sonner": "^0.3.19",
+		"tippy.js": "^6.3.7",
+		"turndown": "^7.2.0",
+		"uuid": "^9.0.1"
+	},
+	"engines": {
+		"node": ">=18.13.0 <=22.x.x",
+		"npm": ">=6.0.0"
+	}
+}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f7721681d725ddea512a5ed734891cf6545ca3c
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+	plugins: {
+		tailwindcss: {},
+		autoprefixer: {}
+	}
+};
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0554baa9e10baa4acd1b28e97728fda6bf0b9bb4
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,152 @@
+[project]
+name = "open-webui"
+description = "Open WebUI"
+authors = [
+    { name = "Timothy Jaeryang Baek", email = "tim@openwebui.com" }
+]
+license = { file = "LICENSE" }
+dependencies = [
+    "fastapi==0.111.0",
+    "uvicorn[standard]==0.30.6",
+    "pydantic==2.9.2",
+    "python-multipart==0.0.18",
+
+    "Flask==3.0.3",
+    "Flask-Cors==5.0.0",
+
+    "python-socketio==5.11.3",
+    "python-jose==3.3.0",
+    "passlib[bcrypt]==1.7.4",
+
+    "requests==2.32.3",
+    "aiohttp==3.11.8",
+    "async-timeout",
+    "aiocache",
+    "aiofiles",
+
+    "sqlalchemy==2.0.32",
+    "alembic==1.14.0",
+    "peewee==3.17.6",
+    "peewee-migrate==1.12.2",
+    "psycopg2-binary==2.9.9",
+    "pgvector==0.3.5",
+    "PyMySQL==1.1.1",
+    "bcrypt==4.2.0",
+
+    "pymongo",
+    "redis",
+    "boto3==1.35.53",
+
+    "argon2-cffi==23.1.0",
+    "APScheduler==3.10.4",
+
+    "openai",
+    "anthropic",
+    "google-generativeai==0.7.2",
+    "tiktoken",
+
+    "langchain==0.3.7",
+    "langchain-community==0.3.7",
+    "langchain-chroma==0.1.4",
+
+    "fake-useragent==1.5.1",
+    "chromadb==0.5.15",
+    "pymilvus==2.5.0",
+    "qdrant-client~=1.12.0",
+    "opensearch-py==2.7.1",
+
+    "sentence-transformers==3.3.1",
+    "colbert-ai==0.2.21",
+    "einops==0.8.0",
+
+    "ftfy==6.2.3",
+    "pypdf==4.3.1",
+    "fpdf2==2.7.9",
+    "pymdown-extensions==10.11.2",
+    "docx2txt==0.8",
+    "python-pptx==1.0.0",
+    "unstructured==0.15.9",
+    "nltk==3.9.1",
+    "Markdown==3.7",
+    "pypandoc==1.13",
+    "pandas==2.2.3",
+    "openpyxl==3.1.5",
+    "pyxlsb==1.0.10",
+    "xlrd==2.0.1",
+    "validators==0.33.0",
+    "psutil",
+    "sentencepiece",
+    "soundfile==0.12.1",
+
+    "opencv-python-headless==4.10.0.84",
+    "rapidocr-onnxruntime==1.3.24",
+    "rank-bm25==0.2.2",
+
+    "faster-whisper==1.0.3",
+
+    "PyJWT[crypto]==2.9.0",
+    "authlib==1.3.2",
+
+    "black==24.8.0",
+    "langfuse==2.44.0",
+    "youtube-transcript-api==0.6.3",
+    "pytube==15.0.0",
+
+    "extract_msg",
+    "pydub",
+    "duckduckgo-search~=6.3.5",
+
+    "docker~=7.1.0",
+    "pytest~=8.3.2",
+    "pytest-docker~=3.1.1",
+
+    "googleapis-common-protos==1.63.2",
+
+    "ldap3==2.9.1"
+]
+readme = "README.md"
+requires-python = ">= 3.11, < 3.12.0a1"
+dynamic = ["version"]
+classifiers = [
+    "Development Status :: 4 - Beta",
+    "License :: OSI Approved :: MIT License",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.11",
+    "Topic :: Communications :: Chat",
+    "Topic :: Multimedia",
+]
+
+[project.scripts]
+open-webui = "open_webui:app"
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.rye]
+managed = true
+dev-dependencies = []
+
+[tool.hatch.metadata]
+allow-direct-references = true
+
+[tool.hatch.version]
+path = "package.json"
+pattern = '"version":\s*"(?P<version>[^"]+)"'
+
+[tool.hatch.build.hooks.custom]  # keep this for reading hooks from `hatch_build.py`
+
+[tool.hatch.build.targets.wheel]
+sources = ["backend"]
+exclude = [
+    ".dockerignore",
+    ".gitignore",
+    ".webui_secret_key",
+    "dev.sh",
+    "requirements.txt",
+    "start.sh",
+    "start_windows.bat",
+    "webui.db",
+    "chroma.sqlite3",
+]
+force-include = { "CHANGELOG.md" = "open_webui/CHANGELOG.md", build = "open_webui/frontend" }
diff --git a/run-compose.sh b/run-compose.sh
new file mode 100755
index 0000000000000000000000000000000000000000..21574e95997fd512bc243adf0fa77456e570010d
--- /dev/null
+++ b/run-compose.sh
@@ -0,0 +1,241 @@
+#!/bin/bash
+
+# Define color and formatting codes
+BOLD='\033[1m'
+GREEN='\033[1;32m'
+WHITE='\033[1;37m'
+RED='\033[0;31m'
+NC='\033[0m' # No Color
+# Unicode character for tick mark
+TICK='\u2713'
+
+# Detect GPU driver
+get_gpu_driver() {
+    # Detect NVIDIA GPUs using lspci or nvidia-smi
+    if lspci | grep -i nvidia >/dev/null || nvidia-smi >/dev/null 2>&1; then
+        echo "nvidia"
+        return
+    fi
+
+    # Detect AMD GPUs (including GCN architecture check for amdgpu vs radeon)
+    if lspci | grep -i amd >/dev/null; then
+        # List of known GCN and later architecture cards
+        # This is a simplified list, and in a real-world scenario, you'd want a more comprehensive one
+        local gcn_and_later=("Radeon HD 7000" "Radeon HD 8000" "Radeon R5" "Radeon R7" "Radeon R9" "Radeon RX")
+
+        # Get GPU information
+        local gpu_info=$(lspci | grep -i 'vga.*amd')
+
+        for model in "${gcn_and_later[@]}"; do
+            if echo "$gpu_info" | grep -iq "$model"; then
+                echo "amdgpu"
+                return
+            fi
+        done
+
+        # Default to radeon if no GCN or later architecture is detected
+        echo "radeon"
+        return
+    fi
+
+    # Detect Intel GPUs
+    if lspci | grep -i intel >/dev/null; then
+        echo "i915"
+        return
+    fi
+
+    # If no known GPU is detected
+    echo "Unknown or unsupported GPU driver"
+    exit 1
+}
+
+# Function for rolling animation
+show_loading() {
+    local spin='-\|/'
+    local i=0
+
+    printf " "
+
+    while kill -0 $1 2>/dev/null; do
+        i=$(( (i+1) %4 ))
+        printf "\b${spin:$i:1}"
+        sleep .1
+    done
+
+    # Replace the spinner with a tick
+    printf "\b${GREEN}${TICK}${NC}"
+}
+
+# Usage information
+usage() {
+    echo "Usage: $0 [OPTIONS]"
+    echo "Options:"
+    echo "  --enable-gpu[count=COUNT]  Enable GPU support with the specified count."
+    echo "  --enable-api[port=PORT]    Enable API and expose it on the specified port."
+    echo "  --webui[port=PORT]         Set the port for the web user interface."
+    echo "  --data[folder=PATH]        Bind mount for ollama data folder (by default will create the 'ollama' volume)."
+    echo "  --build                    Build the docker image before running the compose project."
+    echo "  --drop                     Drop the compose project."
+    echo "  -q, --quiet                Run script in headless mode."
+    echo "  -h, --help                 Show this help message."
+    echo ""
+    echo "Examples:"
+    echo "  $0 --drop"
+    echo "  $0 --enable-gpu[count=1]"
+    echo "  $0 --enable-gpu[count=all]"
+    echo "  $0 --enable-api[port=11435]"
+    echo "  $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000]"
+    echo "  $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data]"
+    echo "  $0 --enable-gpu[count=1] --enable-api[port=12345] --webui[port=3000] --data[folder=./ollama-data] --build"
+    echo ""
+    echo "This script configures and runs a docker-compose setup with optional GPU support, API exposure, and web UI configuration."
+    echo "About the gpu to use, the script automatically detects it using the "lspci" command."
+    echo "In this case the gpu detected is: $(get_gpu_driver)"
+}
+
+# Default values
+gpu_count=1
+api_port=11435
+webui_port=3000
+headless=false
+build_image=false
+kill_compose=false
+
+# Function to extract value from the parameter
+extract_value() {
+    echo "$1" | sed -E 's/.*\[.*=(.*)\].*/\1/; t; s/.*//'
+}
+
+# Parse arguments
+while [[ $# -gt 0 ]]; do
+    key="$1"
+
+    case $key in
+        --enable-gpu*)
+            enable_gpu=true
+            value=$(extract_value "$key")
+            gpu_count=${value:-1}
+            ;;
+        --enable-api*)
+            enable_api=true
+            value=$(extract_value "$key")
+            api_port=${value:-11435}
+            ;;
+        --webui*)
+            value=$(extract_value "$key")
+            webui_port=${value:-3000}
+            ;;
+        --data*)
+            value=$(extract_value "$key")
+            data_dir=${value:-"./ollama-data"}
+            ;;
+        --drop)
+            kill_compose=true
+            ;;
+        --build)
+            build_image=true
+            ;;
+        -q|--quiet)
+            headless=true
+            ;;
+        -h|--help)
+            usage
+            exit
+            ;;
+        *)
+            # Unknown option
+            echo "Unknown option: $key"
+            usage
+            exit 1
+            ;;
+    esac
+    shift # past argument or value
+done
+
+if [[ $kill_compose == true ]]; then
+    docker compose down --remove-orphans
+    echo -e "${GREEN}${BOLD}Compose project dropped successfully.${NC}"
+    exit
+else
+    DEFAULT_COMPOSE_COMMAND="docker compose -f docker-compose.yaml"
+    if [[ $enable_gpu == true ]]; then
+        # Validate and process command-line arguments
+        if [[ -n $gpu_count ]]; then
+            if ! [[ $gpu_count =~ ^([0-9]+|all)$ ]]; then
+                echo "Invalid GPU count: $gpu_count"
+                exit 1
+            fi
+            echo "Enabling GPU with $gpu_count GPUs"
+            # Add your GPU allocation logic here
+            export OLLAMA_GPU_DRIVER=$(get_gpu_driver)
+            export OLLAMA_GPU_COUNT=$gpu_count # Set OLLAMA_GPU_COUNT environment variable
+        fi
+        DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.gpu.yaml"
+    fi
+    if [[ $enable_api == true ]]; then
+        DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.api.yaml"
+        if [[ -n $api_port ]]; then
+            export OLLAMA_WEBAPI_PORT=$api_port # Set OLLAMA_WEBAPI_PORT environment variable
+        fi
+    fi
+    if [[ -n $data_dir ]]; then
+        DEFAULT_COMPOSE_COMMAND+=" -f docker-compose.data.yaml"
+        export OLLAMA_DATA_DIR=$data_dir # Set OLLAMA_DATA_DIR environment variable
+    fi
+    if [[ -n $webui_port ]]; then
+        export OPEN_WEBUI_PORT=$webui_port # Set OPEN_WEBUI_PORT environment variable
+    fi
+    DEFAULT_COMPOSE_COMMAND+=" up -d"
+    DEFAULT_COMPOSE_COMMAND+=" --remove-orphans"
+    DEFAULT_COMPOSE_COMMAND+=" --force-recreate"
+    if [[ $build_image == true ]]; then
+        DEFAULT_COMPOSE_COMMAND+=" --build"
+    fi
+fi
+
+# Recap of environment variables
+echo
+echo -e "${WHITE}${BOLD}Current Setup:${NC}"
+echo -e "   ${GREEN}${BOLD}GPU Driver:${NC} ${OLLAMA_GPU_DRIVER:-Not Enabled}"
+echo -e "   ${GREEN}${BOLD}GPU Count:${NC} ${OLLAMA_GPU_COUNT:-Not Enabled}"
+echo -e "   ${GREEN}${BOLD}WebAPI Port:${NC} ${OLLAMA_WEBAPI_PORT:-Not Enabled}"
+echo -e "   ${GREEN}${BOLD}Data Folder:${NC} ${data_dir:-Using ollama volume}"
+echo -e "   ${GREEN}${BOLD}WebUI Port:${NC} $webui_port"
+echo
+
+if [[ $headless == true ]]; then
+    echo -ne "${WHITE}${BOLD}Running in headless mode... ${NC}"
+    choice="y"
+else
+    # Ask for user acceptance
+    echo -ne "${WHITE}${BOLD}Do you want to proceed with current setup? (Y/n): ${NC}"
+    read -n1 -s choice
+fi
+
+echo
+
+if [[ $choice == "" || $choice == "y" ]]; then
+    # Execute the command with the current user
+    eval "$DEFAULT_COMPOSE_COMMAND" &
+
+    # Capture the background process PID
+    PID=$!
+
+    # Display the loading animation
+    #show_loading $PID
+
+    # Wait for the command to finish
+    wait $PID
+
+    echo
+    # Check exit status
+    if [ $? -eq 0 ]; then
+        echo -e "${GREEN}${BOLD}Compose project started successfully.${NC}"
+    else
+        echo -e "${RED}${BOLD}There was an error starting the compose project.${NC}"
+    fi
+else
+    echo "Aborted."
+fi
+
+echo
diff --git a/run-ollama-docker.sh b/run-ollama-docker.sh
new file mode 100644
index 0000000000000000000000000000000000000000..c2a025bea3fa88beab7c0e6640de682dc8169c02
--- /dev/null
+++ b/run-ollama-docker.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+host_port=11434
+container_port=11434
+
+read -r -p "Do you want ollama in Docker with GPU support? (y/n): " use_gpu
+
+docker rm -f ollama || true
+docker pull ollama/ollama:latest
+
+docker_args="-d -v ollama:/root/.ollama -p $host_port:$container_port --name ollama ollama/ollama"
+
+if [ "$use_gpu" = "y" ]; then
+    docker_args="--gpus=all $docker_args"
+fi
+
+docker run $docker_args
+
+docker image prune -f
\ No newline at end of file
diff --git a/run.sh b/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..6793fe16271c0b56b9ad1cc409d533bfe1e6ca83
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+image_name="open-webui"
+container_name="open-webui"
+host_port=3000
+container_port=8080
+
+docker build -t "$image_name" .
+docker stop "$container_name" &>/dev/null || true
+docker rm "$container_name" &>/dev/null || true
+
+docker run -d -p "$host_port":"$container_port" \
+    --add-host=host.docker.internal:host-gateway \
+    -v "${image_name}:/app/backend/data" \
+    --name "$container_name" \
+    --restart always \
+    "$image_name"
+
+docker image prune -f
diff --git a/scripts/prepare-pyodide.js b/scripts/prepare-pyodide.js
new file mode 100644
index 0000000000000000000000000000000000000000..5aaac5927e7dbf5674bd3dc4839b9ed1548bf7b4
--- /dev/null
+++ b/scripts/prepare-pyodide.js
@@ -0,0 +1,85 @@
+const packages = [
+	'micropip',
+	'packaging',
+	'requests',
+	'beautifulsoup4',
+	'numpy',
+	'pandas',
+	'matplotlib',
+	'scikit-learn',
+	'scipy',
+	'regex',
+	'seaborn'
+];
+
+import { loadPyodide } from 'pyodide';
+import { writeFile, readFile, copyFile, readdir, rmdir } from 'fs/promises';
+
+async function downloadPackages() {
+	console.log('Setting up pyodide + micropip');
+
+	let pyodide;
+	try {
+		pyodide = await loadPyodide({
+			packageCacheDir: 'static/pyodide'
+		});
+	} catch (err) {
+		console.error('Failed to load Pyodide:', err);
+		return;
+	}
+
+	const packageJson = JSON.parse(await readFile('package.json'));
+	const pyodideVersion = packageJson.dependencies.pyodide.replace('^', '');
+
+	try {
+		const pyodidePackageJson = JSON.parse(await readFile('static/pyodide/package.json'));
+		const pyodidePackageVersion = pyodidePackageJson.version.replace('^', '');
+
+		if (pyodideVersion !== pyodidePackageVersion) {
+			console.log('Pyodide version mismatch, removing static/pyodide directory');
+			await rmdir('static/pyodide', { recursive: true });
+		}
+	} catch (e) {
+		console.log('Pyodide package not found, proceeding with download.');
+	}
+
+	try {
+		console.log('Loading micropip package');
+		await pyodide.loadPackage('micropip');
+
+		const micropip = pyodide.pyimport('micropip');
+		console.log('Downloading Pyodide packages:', packages);
+
+		try {
+			for (const pkg of packages) {
+				console.log(`Installing package: ${pkg}`);
+				await micropip.install(pkg);
+			}
+		} catch (err) {
+			console.error('Package installation failed:', err);
+			return;
+		}
+
+		console.log('Pyodide packages downloaded, freezing into lock file');
+
+		try {
+			const lockFile = await micropip.freeze();
+			await writeFile('static/pyodide/pyodide-lock.json', lockFile);
+		} catch (err) {
+			console.error('Failed to write lock file:', err);
+		}
+	} catch (err) {
+		console.error('Failed to load or install micropip:', err);
+	}
+}
+
+async function copyPyodide() {
+	console.log('Copying Pyodide files into static directory');
+	// Copy all files from node_modules/pyodide to static/pyodide
+	for await (const entry of await readdir('node_modules/pyodide')) {
+		await copyFile(`node_modules/pyodide/${entry}`, `static/pyodide/${entry}`);
+	}
+}
+
+await downloadPackages();
+await copyPyodide();
diff --git a/src/app.css b/src/app.css
new file mode 100644
index 0000000000000000000000000000000000000000..a9fffdb21055312c7e8baa92d9aac10b225ea429
--- /dev/null
+++ b/src/app.css
@@ -0,0 +1,301 @@
+@font-face {
+	font-family: 'Inter';
+	src: url('/assets/fonts/Inter-Variable.ttf');
+	font-display: swap;
+}
+
+@font-face {
+	font-family: 'Archivo';
+	src: url('/assets/fonts/Archivo-Variable.ttf');
+	font-display: swap;
+}
+
+@font-face {
+	font-family: 'Mona Sans';
+	src: url('/assets/fonts/Mona-Sans.woff2');
+	font-display: swap;
+}
+
+@font-face {
+	font-family: 'InstrumentSerif';
+	src: url('/assets/fonts/InstrumentSerif-Regular.ttf');
+	font-display: swap;
+}
+
+html {
+	word-break: break-word;
+}
+
+code {
+	/* white-space-collapse: preserve !important; */
+	overflow-x: auto;
+	width: auto;
+}
+
+.font-secondary {
+	font-family: 'InstrumentSerif', sans-serif;
+}
+
+math {
+	margin-top: 1rem;
+}
+
+.hljs {
+	@apply rounded-lg;
+}
+
+.input-prose {
+	@apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line;
+}
+
+.input-prose-sm {
+	@apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line text-sm;
+}
+
+.markdown-prose {
+	@apply prose dark:prose-invert prose-headings:font-semibold prose-hr:my-4 prose-p:my-0 prose-img:my-1 prose-headings:my-1 prose-pre:my-0 prose-table:my-0 prose-blockquote:my-0 prose-ul:-my-0 prose-ol:-my-0 prose-li:-my-0 whitespace-pre-line;
+}
+
+.markdown a {
+	@apply underline;
+}
+
+.font-primary {
+	font-family: 'Archivo', sans-serif;
+}
+
+iframe {
+	@apply rounded-lg;
+}
+
+li p {
+	display: inline;
+}
+
+::-webkit-scrollbar-thumb {
+	--tw-border-opacity: 1;
+	background-color: rgba(236, 236, 236, 0.8);
+	border-color: rgba(255, 255, 255, var(--tw-border-opacity));
+	border-radius: 9999px;
+	border-width: 1px;
+}
+
+/* Dark theme scrollbar styles */
+.dark ::-webkit-scrollbar-thumb {
+	background-color: rgba(33, 33, 33, 0.8); /* Darker color for dark theme */
+	border-color: rgba(0, 0, 0, var(--tw-border-opacity));
+}
+
+::-webkit-scrollbar {
+	height: 0.4rem;
+	width: 0.4rem;
+}
+
+::-webkit-scrollbar-track {
+	background-color: transparent;
+	border-radius: 9999px;
+}
+
+select {
+	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");
+	background-position: right 0.5rem center;
+	background-repeat: no-repeat;
+	background-size: 1.5em 1.5em;
+	padding-right: 2.5rem;
+	-webkit-print-color-adjust: exact;
+	print-color-adjust: exact;
+	/* for Firefox */
+	-moz-appearance: none;
+	/* for Chrome */
+	-webkit-appearance: none;
+}
+
+.katex-mathml {
+	display: none;
+}
+
+.scrollbar-hidden:active::-webkit-scrollbar-thumb,
+.scrollbar-hidden:focus::-webkit-scrollbar-thumb,
+.scrollbar-hidden:hover::-webkit-scrollbar-thumb {
+	visibility: visible;
+}
+.scrollbar-hidden::-webkit-scrollbar-thumb {
+	visibility: hidden;
+}
+
+.scrollbar-hidden::-webkit-scrollbar-corner {
+	display: none;
+}
+
+.scrollbar-none::-webkit-scrollbar {
+	display: none; /* for Chrome, Safari and Opera */
+}
+
+.scrollbar-none::-webkit-scrollbar-corner {
+	display: none;
+}
+
+.scrollbar-none {
+	-ms-overflow-style: none; /* IE and Edge */
+	scrollbar-width: none; /* Firefox */
+}
+
+input::-webkit-outer-spin-button,
+input::-webkit-inner-spin-button {
+	/* display: none; <- Crashes Chrome on hover */
+	-webkit-appearance: none;
+	margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+}
+
+input[type='number'] {
+	-moz-appearance: textfield; /* Firefox */
+}
+
+.cm-editor {
+	height: 100%;
+	width: 100%;
+}
+
+.cm-scroller {
+	@apply scrollbar-hidden;
+}
+
+.cm-editor.cm-focused {
+	outline: none;
+}
+
+.tippy-box[data-theme~='dark'] {
+	@apply rounded-lg bg-gray-950 text-xs border border-gray-900 shadow-xl;
+}
+
+.password {
+	-webkit-text-security: disc;
+}
+
+.codespan {
+	color: #eb5757;
+	border-width: 0px;
+	padding: 3px 8px;
+	font-size: 0.8em;
+	font-weight: 600;
+	@apply rounded-md dark:bg-gray-800 bg-gray-100 mx-0.5;
+}
+
+.svelte-flow {
+	background-color: transparent !important;
+}
+
+.svelte-flow__edge > path {
+	stroke-width: 0.5;
+}
+
+.svelte-flow__edge.animated > path {
+	stroke-width: 2;
+	@apply stroke-gray-600 dark:stroke-gray-500;
+}
+
+.bg-gray-950-90 {
+	background-color: rgba(var(--color-gray-950, #0d0d0d), 0.9);
+}
+
+.ProseMirror {
+	@apply h-full min-h-fit max-h-full whitespace-pre-wrap;
+}
+
+.ProseMirror:focus {
+	outline: none;
+}
+
+.ProseMirror p.is-editor-empty:first-child::before {
+	content: attr(data-placeholder);
+	float: left;
+	color: #adb5bd;
+	pointer-events: none;
+	
+	@apply line-clamp-1 absolute
+}
+
+.ai-autocompletion::after {
+	color: #a0a0a0;
+
+	content: attr(data-suggestion);
+	pointer-events: none;
+}
+
+.tiptap > pre > code {
+	border-radius: 0.4rem;
+	font-size: 0.85rem;
+	padding: 0.25em 0.3em;
+
+	@apply dark:bg-gray-800 bg-gray-100;
+}
+
+.tiptap > pre {
+	border-radius: 0.5rem;
+	font-family: 'JetBrainsMono', monospace;
+	margin: 1.5rem 0;
+	padding: 0.75rem 1rem;
+
+	@apply dark:bg-gray-800 bg-gray-100;
+}
+
+.tiptap p code {
+	color: #eb5757;
+	border-width: 0px;
+	padding: 3px 8px;
+	font-size: 0.8em;
+	font-weight: 600;
+	@apply rounded-md dark:bg-gray-800 bg-gray-100 mx-0.5;
+}
+
+/* Code styling */
+.hljs-comment,
+.hljs-quote {
+	color: #616161;
+}
+
+.hljs-variable,
+.hljs-template-variable,
+.hljs-attribute,
+.hljs-tag,
+.hljs-regexp,
+.hljs-link,
+.hljs-name,
+.hljs-selector-id,
+.hljs-selector-class {
+	color: #f98181;
+}
+
+.hljs-number,
+.hljs-meta,
+.hljs-built_in,
+.hljs-builtin-name,
+.hljs-literal,
+.hljs-type,
+.hljs-params {
+	color: #fbbc88;
+}
+
+.hljs-string,
+.hljs-symbol,
+.hljs-bullet {
+	color: #b9f18d;
+}
+
+.hljs-title,
+.hljs-section {
+	color: #faf594;
+}
+
+.hljs-keyword,
+.hljs-selector-tag {
+	color: #70cff8;
+}
+
+.hljs-emphasis {
+	font-style: italic;
+}
+
+.hljs-strong {
+	font-weight: 700;
+}
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f59b884c51ed3c31fc0738fd38d0d75b580df5e4
--- /dev/null
+++ b/src/app.d.ts
@@ -0,0 +1,12 @@
+// See https://kit.svelte.dev/docs/types#app
+// for information about these interfaces
+declare global {
+	namespace App {
+		// interface Error {}
+		// interface Locals {}
+		// interface PageData {}
+		// interface Platform {}
+	}
+}
+
+export {};
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000000000000000000000000000000000000..537e28dbe7af4cbedaf628e89896e790c908be0a
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,234 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96" />
+		<link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg" />
+		<link rel="shortcut icon" href="/favicon/favicon.ico" />
+		<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png" />
+		<meta name="apple-mobile-web-app-title" content="Open WebUI" />
+		<link rel="manifest" href="/favicon/site.webmanifest" />
+		<meta
+			name="viewport"
+			content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover"
+		/>
+		<meta name="theme-color" content="#171717" />
+		<meta name="robots" content="noindex,nofollow" />
+		<meta name="description" content="Open WebUI" />
+		<link
+			rel="search"
+			type="application/opensearchdescription+xml"
+			title="Open WebUI"
+			href="/opensearch.xml"
+		/>
+
+		<script>
+			function resizeIframe(obj) {
+				obj.style.height = obj.contentWindow.document.documentElement.scrollHeight + 'px';
+			}
+		</script>
+
+		<script>
+			// On page load or when changing themes, best to add inline in `head` to avoid FOUC
+			(() => {
+				const metaThemeColorTag = document.querySelector('meta[name="theme-color"]');
+				const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
+
+				if (!localStorage?.theme) {
+					localStorage.theme = 'system';
+				}
+
+				if (localStorage.theme === 'system') {
+					document.documentElement.classList.add(prefersDarkTheme ? 'dark' : 'light');
+					metaThemeColorTag.setAttribute('content', prefersDarkTheme ? '#171717' : '#ffffff');
+				} else if (localStorage.theme === 'oled-dark') {
+					document.documentElement.style.setProperty('--color-gray-800', '#101010');
+					document.documentElement.style.setProperty('--color-gray-850', '#050505');
+					document.documentElement.style.setProperty('--color-gray-900', '#000000');
+					document.documentElement.style.setProperty('--color-gray-950', '#000000');
+					document.documentElement.classList.add('dark');
+					metaThemeColorTag.setAttribute('content', '#000000');
+				} else if (localStorage.theme === 'light') {
+					document.documentElement.classList.add('light');
+					metaThemeColorTag.setAttribute('content', '#ffffff');
+				} else if (localStorage.theme === 'her') {
+					document.documentElement.classList.add('dark');
+					document.documentElement.classList.add('her');
+					metaThemeColorTag.setAttribute('content', '#983724');
+				} else {
+					document.documentElement.classList.add('dark');
+					metaThemeColorTag.setAttribute('content', '#171717');
+				}
+
+				window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
+					if (localStorage.theme === 'system') {
+						if (e.matches) {
+							document.documentElement.classList.add('dark');
+							document.documentElement.classList.remove('light');
+							metaThemeColorTag.setAttribute('content', '#171717');
+						} else {
+							document.documentElement.classList.add('light');
+							document.documentElement.classList.remove('dark');
+							metaThemeColorTag.setAttribute('content', '#ffffff');
+						}
+					}
+				});
+			})();
+		</script>
+
+		<title>Open WebUI</title>
+
+		%sveltekit.head%
+	</head>
+
+	<body data-sveltekit-preload-data="hover">
+		<div style="display: contents">%sveltekit.body%</div>
+
+		<div
+			id="splash-screen"
+			style="position: fixed; z-index: 100; top: 0; left: 0; width: 100%; height: 100%"
+		>
+			<style type="text/css" nonce="">
+				html {
+					overflow-y: scroll !important;
+				}
+			</style>
+
+			<img
+				id="logo"
+				style="
+					position: absolute;
+					width: auto;
+					height: 6rem;
+					top: 44%;
+					left: 50%;
+					transform: translateX(-50%);
+				"
+				src="/static/splash.png"
+			/>
+
+			<div
+				style="
+					position: absolute;
+					top: 33%;
+					left: 50%;
+
+					width: 24rem;
+					transform: translateX(-50%);
+
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+				"
+			>
+				<img
+					id="logo-her"
+					style="width: auto; height: 13rem"
+					src="/static/splash.png"
+					class="animate-pulse-fast"
+				/>
+
+				<div style="position: relative; width: 24rem; margin-top: 0.5rem">
+					<div
+						id="progress-background"
+						style="
+							position: absolute;
+							width: 100%;
+							height: 0.75rem;
+
+							border-radius: 9999px;
+							background-color: #fafafa9a;
+						"
+					></div>
+
+					<div
+						id="progress-bar"
+						style="
+							position: absolute;
+							width: 0%;
+							height: 0.75rem;
+							border-radius: 9999px;
+							background-color: #fff;
+						"
+						class="bg-white"
+					></div>
+				</div>
+			</div>
+
+			<!-- <span style="position: absolute; bottom: 32px; left: 50%; margin: -36px 0 0 -36px">
+				Footer content
+			</span> -->
+		</div>
+	</body>
+</html>
+
+<style type="text/css" nonce="">
+	html {
+		overflow-y: hidden !important;
+	}
+
+	#splash-screen {
+		background: #fff;
+	}
+
+	html.dark #splash-screen {
+		background: #000;
+	}
+
+	html.dark #splash-screen img {
+		filter: invert(1);
+	}
+
+	html.her #splash-screen {
+		background: #983724;
+	}
+
+	#logo-her {
+		display: none;
+	}
+
+	#progress-background {
+		display: none;
+	}
+
+	#progress-bar {
+		display: none;
+	}
+
+	html.her #logo {
+		display: none;
+	}
+
+	html.her #logo-her {
+		display: block;
+		filter: invert(1);
+	}
+
+	html.her #progress-background {
+		display: block;
+	}
+
+	html.her #progress-bar {
+		display: block;
+	}
+
+	@media (max-width: 24rem) {
+		html.her #progress-background {
+			display: none;
+		}
+
+		html.her #progress-bar {
+			display: none;
+		}
+	}
+
+	@keyframes pulse {
+		50% {
+			opacity: 0.65;
+		}
+	}
+
+	.animate-pulse-fast {
+		animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+	}
+</style>
diff --git a/src/lib/apis/audio/index.ts b/src/lib/apis/audio/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5cd6ab949ca469f54dbec0cc43a7d7e784340306
--- /dev/null
+++ b/src/lib/apis/audio/index.ts
@@ -0,0 +1,193 @@
+import { AUDIO_API_BASE_URL } from '$lib/constants';
+
+export const getAudioConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${AUDIO_API_BASE_URL}/config`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type OpenAIConfigForm = {
+	url: string;
+	key: string;
+	model: string;
+	speaker: string;
+};
+
+export const updateAudioConfig = async (token: string, payload: OpenAIConfigForm) => {
+	let error = null;
+
+	const res = await fetch(`${AUDIO_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...payload
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const transcribeAudio = async (token: string, file: File) => {
+	const data = new FormData();
+	data.append('file', file);
+
+	let error = null;
+	const res = await fetch(`${AUDIO_API_BASE_URL}/transcriptions`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: data
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const synthesizeOpenAISpeech = async (
+	token: string = '',
+	speaker: string = 'alloy',
+	text: string = '',
+	model?: string
+) => {
+	let error = null;
+
+	const res = await fetch(`${AUDIO_API_BASE_URL}/speech`, {
+		method: 'POST',
+		headers: {
+			Authorization: `Bearer ${token}`,
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			input: text,
+			voice: speaker,
+			...(model && { model })
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+interface AvailableModelsResponse {
+	models: { name: string; id: string }[] | { id: string }[];
+}
+
+export const getModels = async (token: string = ''): Promise<AvailableModelsResponse> => {
+	let error = null;
+
+	const res = await fetch(`${AUDIO_API_BASE_URL}/models`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getVoices = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${AUDIO_API_BASE_URL}/voices`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/auths/index.ts b/src/lib/apis/auths/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40caebf5daf63394917056cd0ce59d6dda15dbdf
--- /dev/null
+++ b/src/lib/apis/auths/index.ts
@@ -0,0 +1,694 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const getAdminDetails = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/details`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAdminConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateAdminConfig = async (token: string, body: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify(body)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getSessionUser = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		credentials: 'include'
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const ldapUserSignIn = async (user: string, password: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/ldap`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		credentials: 'include',
+		body: JSON.stringify({
+			user: user,
+			password: password
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getLdapConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config/ldap`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateLdapConfig = async (token: string = '', enable_ldap: boolean) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config/ldap`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			enable_ldap: enable_ldap
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getLdapServer = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config/ldap/server`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateLdapServer = async (token: string = '', body: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/admin/config/ldap/server`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify(body)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const userSignIn = async (email: string, password: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signin`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		credentials: 'include',
+		body: JSON.stringify({
+			email: email,
+			password: password
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const userSignUp = async (
+	name: string,
+	email: string,
+	password: string,
+	profile_image_url: string
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		credentials: 'include',
+		body: JSON.stringify({
+			name: name,
+			email: email,
+			password: password,
+			profile_image_url: profile_image_url
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const userSignOut = async () => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signout`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		credentials: 'include'
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res;
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+};
+
+export const addUser = async (
+	token: string,
+	name: string,
+	email: string,
+	password: string,
+	role: string = 'pending'
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/add`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			name: name,
+			email: email,
+			password: password,
+			role: role
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserProfile = async (token: string, name: string, profileImageUrl: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/update/profile`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			name: name,
+			profile_image_url: profileImageUrl
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserPassword = async (token: string, password: string, newPassword: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/update/password`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			password: password,
+			new_password: newPassword
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getSignUpEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getDefaultUserRole = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/user/role`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateDefaultUserRole = async (token: string, role: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/user/role`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			role: role
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleSignUpEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup/enabled/toggle`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getJWTExpiresDuration = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/token/expires`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateJWTExpiresDuration = async (token: string, duration: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/token/expires/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			duration: duration
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const createAPIKey = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/api_key`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+	if (error) {
+		throw error;
+	}
+	return res.api_key;
+};
+
+export const getAPIKey = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/api_key`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+	if (error) {
+		throw error;
+	}
+	return res.api_key;
+};
+
+export const deleteAPIKey = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/auths/api_key`, {
+		method: 'DELETE',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+	if (error) {
+		throw error;
+	}
+	return res;
+};
diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d93d21c73abf4d2726dbc38f67ea5d8398c1217b
--- /dev/null
+++ b/src/lib/apis/chats/index.ts
@@ -0,0 +1,1013 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+import { getTimeRange } from '$lib/utils';
+
+export const createNewChat = async (token: string, chat: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/new`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			chat: chat
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const importChat = async (
+	token: string,
+	chat: object,
+	meta: object | null,
+	pinned?: boolean,
+	folderId?: string | null
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/import`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			chat: chat,
+			meta: meta ?? {},
+			pinned: pinned,
+			folder_id: folderId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getChatList = async (token: string = '', page: number | null = null) => {
+	let error = null;
+	const searchParams = new URLSearchParams();
+
+	if (page !== null) {
+		searchParams.append('page', `${page}`);
+	}
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/?${searchParams.toString()}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.map((chat) => ({
+		...chat,
+		time_range: getTimeRange(chat.updated_at)
+	}));
+};
+
+export const getChatListByUserId = async (token: string = '', userId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/list/user/${userId}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.map((chat) => ({
+		...chat,
+		time_range: getTimeRange(chat.updated_at)
+	}));
+};
+
+export const getArchivedChatList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/archived`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAllChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getChatListBySearchText = async (token: string, text: string, page: number = 1) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	searchParams.append('text', text);
+	searchParams.append('page', `${page}`);
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/search?${searchParams.toString()}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.map((chat) => ({
+		...chat,
+		time_range: getTimeRange(chat.updated_at)
+	}));
+};
+
+export const getChatsByFolderId = async (token: string, folderId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/folder/${folderId}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAllArchivedChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all/archived`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAllUserChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all/db`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAllTags = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/all/tags`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPinnedChatList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/pinned`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.map((chat) => ({
+		...chat,
+		time_range: getTimeRange(chat.updated_at)
+	}));
+};
+
+export const getChatListByTagName = async (token: string = '', tagName: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/tags`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			name: tagName
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.map((chat) => ({
+		...chat,
+		time_range: getTimeRange(chat.updated_at)
+	}));
+};
+
+export const getChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getChatByShareId = async (token: string, share_id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/share/${share_id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getChatPinnedStatusById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/pinned`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleChatPinnedStatusById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/pin`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const cloneChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/clone`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const shareChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/share`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateChatFolderIdById = async (token: string, id: string, folderId?: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/folder`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			folder_id: folderId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const archiveChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/archive`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteSharedChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/share`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateChatById = async (token: string, id: string, chat: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			chat: chat
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteChatById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getTagsById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const addTagById = async (token: string, id: string, tagName: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			name: tagName
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteTagById = async (token: string, id: string, tagName: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			name: tagName
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+export const deleteTagsById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}/tags/all`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteAllChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const archiveAllChats = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/chats/archive/all`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/configs/index.ts b/src/lib/apis/configs/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9faf346bcf42228418d8ec31e5489b2abe1430c
--- /dev/null
+++ b/src/lib/apis/configs/index.ts
@@ -0,0 +1,203 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+import type { Banner } from '$lib/types';
+
+export const importConfig = async (token: string, config) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/import`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			config: config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const exportConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/export`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getModelsConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const setModelsConfig = async (token: string, config: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/models`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const setDefaultPromptSuggestions = async (token: string, promptSuggestions: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/suggestions`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			suggestions: promptSuggestions
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getBanners = async (token: string): Promise<Banner[]> => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/banners`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const setBanners = async (token: string, banners: Banner[]) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/configs/banners`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			banners: banners
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/evaluations/index.ts b/src/lib/apis/evaluations/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6f35f7c18de93af0d188ff61c417698094e5dee
--- /dev/null
+++ b/src/lib/apis/evaluations/index.ts
@@ -0,0 +1,246 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const getConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateConfig = async (token: string, config: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/config`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAllFeedbacks = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedbacks/all`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const exportAllFeedbacks = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedbacks/all/export`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const createNewFeedback = async (token: string, feedback: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedback`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...feedback
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFeedbackById = async (token: string, feedbackId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedback/${feedbackId}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFeedbackById = async (token: string, feedbackId: string, feedback: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedback/${feedbackId}`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...feedback
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteFeedbackById = async (token: string, feedbackId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedback/${feedbackId}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/files/index.ts b/src/lib/apis/files/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6a42ec6147e87315d27d815528e98632401896ac
--- /dev/null
+++ b/src/lib/apis/files/index.ts
@@ -0,0 +1,243 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const uploadFile = async (token: string, file: File) => {
+	const data = new FormData();
+	data.append('file', file);
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: data
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const uploadDir = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/upload/dir`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFiles = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFileById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFileDataContentById = async (token: string, id: string, content: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}/data/content/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			content: content
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFileContentById = async (id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}/content`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json'
+		},
+		credentials: 'include'
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return await res.blob();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteFileById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/${id}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteAllFiles = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/files/all`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/folders/index.ts b/src/lib/apis/folders/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1a1f5b4832ba0a193fca92a84ef86ee3e3399f6
--- /dev/null
+++ b/src/lib/apis/folders/index.ts
@@ -0,0 +1,269 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const createNewFolder = async (token: string, name: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			name: name
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFolders = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFolderById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFolderNameById = async (token: string, id: string, name: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			name: name
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFolderIsExpandedById = async (
+	token: string,
+	id: string,
+	isExpanded: boolean
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}/update/expanded`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			is_expanded: isExpanded
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFolderParentIdById = async (token: string, id: string, parentId?: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}/update/parent`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			parent_id: parentId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type FolderItems = {
+	chat_ids: string[];
+	file_ids: string[];
+};
+
+export const updateFolderItemsById = async (token: string, id: string, items: FolderItems) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}/update/items`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			items: items
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteFolderById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/folders/${id}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/functions/index.ts b/src/lib/apis/functions/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ed3306b3214967e98dee60ab0cf14042bc0bf141
--- /dev/null
+++ b/src/lib/apis/functions/index.ts
@@ -0,0 +1,455 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const createNewFunction = async (token: string, func: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...func
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctions = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const exportFunctions = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/export`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctionById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFunctionById = async (token: string, id: string, func: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...func
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteFunctionById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleFunctionById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/toggle`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleGlobalById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/toggle/global`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctionValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getFunctionValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFunctionValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/functions/id/${id}/valves/user/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/groups/index.ts b/src/lib/apis/groups/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b7d4f8ef9f803f54a36a2d9b6fe15307e7390816
--- /dev/null
+++ b/src/lib/apis/groups/index.ts
@@ -0,0 +1,162 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const createNewGroup = async (token: string, group: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/groups/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...group
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getGroups = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/groups/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getGroupById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/groups/id/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateGroupById = async (token: string, id: string, group: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/groups/id/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...group
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteGroupById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/groups/id/${id}/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/images/index.ts b/src/lib/apis/images/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e6510437b5176c8b9aad7a571b07628d0bee7a5
--- /dev/null
+++ b/src/lib/apis/images/index.ts
@@ -0,0 +1,232 @@
+import { IMAGES_API_BASE_URL } from '$lib/constants';
+
+export const getConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateConfig = async (token: string = '', config: object) => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			...config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const verifyConfigUrl = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/config/url/verify`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getImageGenerationConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/image/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateImageGenerationConfig = async (token: string = '', config: object) => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/image/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({ ...config })
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getImageGenerationModels = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/models`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const imageGenerations = async (token: string = '', prompt: string) => {
+	let error = null;
+
+	const res = await fetch(`${IMAGES_API_BASE_URL}/generations`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			prompt: prompt
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/index.ts b/src/lib/apis/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e76aa3c99e17a9607100e60fc4f42c48ccd0c0ac
--- /dev/null
+++ b/src/lib/apis/index.ts
@@ -0,0 +1,1123 @@
+import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
+
+export const getModels = async (token: string = '', base: boolean = false) => {
+	let error = null;
+	const res = await fetch(`${WEBUI_BASE_URL}/api/models${base ? '/base' : ''}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	let models = res?.data ?? [];
+	return models;
+};
+
+type ChatCompletedForm = {
+	model: string;
+	messages: string[];
+	chat_id: string;
+	session_id: string;
+};
+
+export const chatCompleted = async (token: string, body: ChatCompletedForm) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/chat/completed`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify(body)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type ChatActionForm = {
+	model: string;
+	messages: string[];
+	chat_id: string;
+};
+
+export const chatAction = async (token: string, action_id: string, body: ChatActionForm) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/chat/actions/${action_id}`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify(body)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getTaskConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateTaskConfig = async (token: string, config: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify(config)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const generateTitle = async (
+	token: string = '',
+	model: string,
+	messages: string[],
+	chat_id?: string
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/title/completions`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			messages: messages,
+			...(chat_id && { chat_id: chat_id })
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res?.choices[0]?.message?.content.replace(/["']/g, '') ?? 'New Chat';
+};
+
+export const generateTags = async (
+	token: string = '',
+	model: string,
+	messages: string,
+	chat_id?: string
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/tags/completions`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			messages: messages,
+			...(chat_id && { chat_id: chat_id })
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	try {
+		// Step 1: Safely extract the response string
+		const response = res?.choices[0]?.message?.content ?? '';
+
+		// Step 2: Attempt to fix common JSON format issues like single quotes
+		const sanitizedResponse = response.replace(/['‘’`]/g, '"'); // Convert single quotes to double quotes for valid JSON
+
+		// Step 3: Find the relevant JSON block within the response
+		const jsonStartIndex = sanitizedResponse.indexOf('{');
+		const jsonEndIndex = sanitizedResponse.lastIndexOf('}');
+
+		// Step 4: Check if we found a valid JSON block (with both `{` and `}`)
+		if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
+			const jsonResponse = sanitizedResponse.substring(jsonStartIndex, jsonEndIndex + 1);
+
+			// Step 5: Parse the JSON block
+			const parsed = JSON.parse(jsonResponse);
+
+			// Step 6: If there's a "tags" key, return the tags array; otherwise, return an empty array
+			if (parsed && parsed.tags) {
+				return Array.isArray(parsed.tags) ? parsed.tags : [];
+			} else {
+				return [];
+			}
+		}
+
+		// If no valid JSON block found, return an empty array
+		return [];
+	} catch (e) {
+		// Catch and safely return empty array on any parsing errors
+		console.error('Failed to parse response: ', e);
+		return [];
+	}
+};
+
+export const generateEmoji = async (
+	token: string = '',
+	model: string,
+	prompt: string,
+	chat_id?: string
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/emoji/completions`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: prompt,
+			...(chat_id && { chat_id: chat_id })
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	const response = res?.choices[0]?.message?.content.replace(/["']/g, '') ?? null;
+
+	if (response) {
+		if (/\p{Extended_Pictographic}/u.test(response)) {
+			return response.match(/\p{Extended_Pictographic}/gu)[0];
+		}
+	}
+
+	return null;
+};
+
+export const generateQueries = async (
+	token: string = '',
+	model: string,
+	messages: object[],
+	prompt: string,
+	type?: string = 'web_search'
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/queries/completions`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			messages: messages,
+			prompt: prompt,
+			type: type
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	// Step 1: Safely extract the response string
+	const response = res?.choices[0]?.message?.content ?? '';
+
+	try {
+		const jsonStartIndex = response.indexOf('{');
+		const jsonEndIndex = response.lastIndexOf('}');
+
+		if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
+			const jsonResponse = response.substring(jsonStartIndex, jsonEndIndex + 1);
+
+			// Step 5: Parse the JSON block
+			const parsed = JSON.parse(jsonResponse);
+
+			// Step 6: If there's a "queries" key, return the queries array; otherwise, return an empty array
+			if (parsed && parsed.queries) {
+				return Array.isArray(parsed.queries) ? parsed.queries : [];
+			} else {
+				return [];
+			}
+		}
+
+		// If no valid JSON block found, return response as is
+		return [response];
+	} catch (e) {
+		// Catch and safely return empty array on any parsing errors
+		console.error('Failed to parse response: ', e);
+		return [response];
+	}
+};
+
+export const generateAutoCompletion = async (
+	token: string = '',
+	model: string,
+	prompt: string,
+	messages?: object[],
+	type: string = 'search query'
+) => {
+	const controller = new AbortController();
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/auto/completions`, {
+		signal: controller.signal,
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: prompt,
+			...(messages && { messages: messages }),
+			type: type,
+			stream: false
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	const response = res?.choices[0]?.message?.content ?? '';
+
+	try {
+		const jsonStartIndex = response.indexOf('{');
+		const jsonEndIndex = response.lastIndexOf('}');
+
+		if (jsonStartIndex !== -1 && jsonEndIndex !== -1) {
+			const jsonResponse = response.substring(jsonStartIndex, jsonEndIndex + 1);
+
+			// Step 5: Parse the JSON block
+			const parsed = JSON.parse(jsonResponse);
+
+			// Step 6: If there's a "queries" key, return the queries array; otherwise, return an empty array
+			if (parsed && parsed.text) {
+				return parsed.text;
+			} else {
+				return '';
+			}
+		}
+
+		// If no valid JSON block found, return response as is
+		return response;
+	} catch (e) {
+		// Catch and safely return empty array on any parsing errors
+		console.error('Failed to parse response: ', e);
+		return response;
+	}
+};
+
+export const generateMoACompletion = async (
+	token: string = '',
+	model: string,
+	prompt: string,
+	responses: string[]
+) => {
+	const controller = new AbortController();
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/task/moa/completions`, {
+		signal: controller.signal,
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: prompt,
+			responses: responses,
+			stream: true
+		})
+	}).catch((err) => {
+		console.log(err);
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return [res, controller];
+};
+
+export const getPipelinesList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/list`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	let pipelines = res?.data ?? [];
+	return pipelines;
+};
+
+export const uploadPipeline = async (token: string, file: File, urlIdx: string) => {
+	let error = null;
+
+	// Create a new FormData object to handle the file upload
+	const formData = new FormData();
+	formData.append('file', file);
+	formData.append('urlIdx', urlIdx);
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/upload`, {
+		method: 'POST',
+		headers: {
+			...(token && { authorization: `Bearer ${token}` })
+			// 'Content-Type': 'multipart/form-data' is not needed as Fetch API will set it automatically
+		},
+		body: formData
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const downloadPipeline = async (token: string, url: string, urlIdx: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/add`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			url: url,
+			urlIdx: urlIdx
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deletePipeline = async (token: string, id: string, urlIdx: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			id: id,
+			urlIdx: urlIdx
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPipelines = async (token: string, urlIdx?: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	if (urlIdx !== undefined) {
+		searchParams.append('urlIdx', urlIdx);
+	}
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/pipelines?${searchParams.toString()}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	let pipelines = res?.data ?? [];
+	return pipelines;
+};
+
+export const getPipelineValves = async (token: string, pipeline_id: string, urlIdx: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	if (urlIdx !== undefined) {
+		searchParams.append('urlIdx', urlIdx);
+	}
+
+	const res = await fetch(
+		`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves?${searchParams.toString()}`,
+		{
+			method: 'GET',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				...(token && { authorization: `Bearer ${token}` })
+			}
+		}
+	)
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPipelineValvesSpec = async (token: string, pipeline_id: string, urlIdx: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	if (urlIdx !== undefined) {
+		searchParams.append('urlIdx', urlIdx);
+	}
+
+	const res = await fetch(
+		`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/spec?${searchParams.toString()}`,
+		{
+			method: 'GET',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				...(token && { authorization: `Bearer ${token}` })
+			}
+		}
+	)
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updatePipelineValves = async (
+	token: string = '',
+	pipeline_id: string,
+	valves: object,
+	urlIdx: string
+) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	if (urlIdx !== undefined) {
+		searchParams.append('urlIdx', urlIdx);
+	}
+
+	const res = await fetch(
+		`${WEBUI_BASE_URL}/api/pipelines/${pipeline_id}/valves/update?${searchParams.toString()}`,
+		{
+			method: 'POST',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				...(token && { authorization: `Bearer ${token}` })
+			},
+			body: JSON.stringify(valves)
+		}
+	)
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = err;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getBackendConfig = async () => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/config`, {
+		method: 'GET',
+		credentials: 'include',
+		headers: {
+			'Content-Type': 'application/json'
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getChangelog = async () => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/changelog`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json'
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getVersionUpdates = async () => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/version/updates`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json'
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getModelFilterConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/config/model/filter`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateModelFilterConfig = async (
+	token: string,
+	enabled: boolean,
+	models: string[]
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/config/model/filter`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			enabled: enabled,
+			models: models
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getWebhookUrl = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.url;
+};
+
+export const updateWebhookUrl = async (token: string, url: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			url: url
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.url;
+};
+
+export const getCommunitySharingEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/community_sharing`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleCommunitySharingEnabledStatus = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/community_sharing/toggle`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getModelConfig = async (token: string): Promise<GlobalModelConfig> => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/config/models`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.models;
+};
+
+export interface ModelConfig {
+	id: string;
+	name: string;
+	meta: ModelMeta;
+	base_model_id?: string;
+	params: ModelParams;
+}
+
+export interface ModelMeta {
+	description?: string;
+	capabilities?: object;
+	profile_image_url?: string;
+}
+
+export interface ModelParams {}
+
+export type GlobalModelConfig = ModelConfig[];
+
+export const updateModelConfig = async (token: string, config: GlobalModelConfig) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/config/models`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			models: config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/knowledge/index.ts b/src/lib/apis/knowledge/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c5fad1323d0e7f340d40027b70091ab15bb84c73
--- /dev/null
+++ b/src/lib/apis/knowledge/index.ts
@@ -0,0 +1,347 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const createNewKnowledge = async (
+	token: string,
+	name: string,
+	description: string,
+	accessControl: null | object
+) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			name: name,
+			description: description,
+			access_control: accessControl
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getKnowledgeBases = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getKnowledgeBaseList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/list`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getKnowledgeById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type KnowledgeUpdateForm = {
+	name?: string;
+	description?: string;
+	data?: object;
+	access_control?: null | object;
+};
+
+export const updateKnowledgeById = async (token: string, id: string, form: KnowledgeUpdateForm) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			name: form?.name ? form.name : undefined,
+			description: form?.description ? form.description : undefined,
+			data: form?.data ? form.data : undefined,
+			access_control: form.access_control
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const addFileToKnowledgeById = async (token: string, id: string, fileId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/file/add`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			file_id: fileId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateFileFromKnowledgeById = async (token: string, id: string, fileId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/file/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			file_id: fileId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const removeFileFromKnowledgeById = async (token: string, id: string, fileId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/file/remove`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			file_id: fileId
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const resetKnowledgeById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/reset`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteKnowledgeById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/knowledge/${id}/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/memories/index.ts b/src/lib/apis/memories/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3fd83ca9e06fdb2cf8cf8e94fd01666134cb024d
--- /dev/null
+++ b/src/lib/apis/memories/index.ts
@@ -0,0 +1,186 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const getMemories = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const addNewMemory = async (token: string, content: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/add`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			content: content
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateMemoryById = async (token: string, id: string, content: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			content: content
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const queryMemory = async (token: string, content: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/query`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			content: content
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteMemoryById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/${id}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteMemoriesByUserId = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/memories/delete/user`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/models/index.ts b/src/lib/apis/models/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5880874bbff61182db8c787e847409f4c7185c79
--- /dev/null
+++ b/src/lib/apis/models/index.ts
@@ -0,0 +1,265 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const getModels = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getBaseModels = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/base`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const createNewModel = async (token: string, model: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify(model)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getModelById = async (token: string, id: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	searchParams.append('id', id);
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/model?${searchParams.toString()}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const toggleModelById = async (token: string, id: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	searchParams.append('id', id);
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/toggle?${searchParams.toString()}`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateModelById = async (token: string, id: string, model: object) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	searchParams.append('id', id);
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/update?${searchParams.toString()}`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify(model)
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteModelById = async (token: string, id: string) => {
+	let error = null;
+
+	const searchParams = new URLSearchParams();
+	searchParams.append('id', id);
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/delete?${searchParams.toString()}`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteAllModels = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/models/delete/all`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16eed9f21aff04ffd10d09ba57a8b05bb178dca3
--- /dev/null
+++ b/src/lib/apis/ollama/index.ts
@@ -0,0 +1,549 @@
+import { OLLAMA_API_BASE_URL } from '$lib/constants';
+
+export const verifyOllamaConnection = async (
+	token: string = '',
+	url: string = '',
+	key: string = ''
+) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/verify`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			Authorization: `Bearer ${token}`,
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			url,
+			key
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = `Ollama: ${err?.error?.message ?? 'Network Problem'}`;
+			return [];
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getOllamaConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type OllamaConfig = {
+	ENABLE_OLLAMA_API: boolean;
+	OLLAMA_BASE_URLS: string[];
+	OLLAMA_API_CONFIGS: object;
+};
+
+export const updateOllamaConfig = async (token: string = '', config: OllamaConfig) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			...config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getOllamaUrls = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/urls`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OLLAMA_BASE_URLS;
+};
+
+export const updateOllamaUrls = async (token: string = '', urls: string[]) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/urls/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			urls: urls
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OLLAMA_BASE_URLS;
+};
+
+export const getOllamaVersion = async (token: string, urlIdx?: number) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/version${urlIdx ? `/${urlIdx}` : ''}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res?.version ?? false;
+};
+
+export const getOllamaModels = async (token: string = '', urlIdx: null | number = null) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/tags${urlIdx !== null ? `/${urlIdx}` : ''}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return (res?.models ?? [])
+		.map((model) => ({ id: model.model, name: model.name ?? model.model, ...model }))
+		.sort((a, b) => {
+			return a.name.localeCompare(b.name);
+		});
+};
+
+export const generatePrompt = async (token: string = '', model: string, conversation: string) => {
+	let error = null;
+
+	if (conversation === '') {
+		conversation = '[no existing conversation]';
+	}
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: `Conversation:
+			${conversation}
+
+			As USER in the conversation above, your task is to continue the conversation. Remember, Your responses should be crafted as if you're a human conversing in a natural, realistic manner, keeping in mind the context and flow of the dialogue. Please generate a fitting response to the last message in the conversation, or if there is no existing conversation, initiate one as a normal person would.
+			
+			Response:
+			`
+		})
+	}).catch((err) => {
+		console.log(err);
+		if ('detail' in err) {
+			error = err.detail;
+		}
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const generateEmbeddings = async (token: string = '', model: string, text: string) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/embeddings`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: text
+		})
+	}).catch((err) => {
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const generateTextCompletion = async (token: string = '', model: string, text: string) => {
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/generate`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			model: model,
+			prompt: text,
+			stream: true
+		})
+	}).catch((err) => {
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const generateChatCompletion = async (token: string = '', body: object) => {
+	let controller = new AbortController();
+	let error = null;
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/chat`, {
+		signal: controller.signal,
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify(body)
+	}).catch((err) => {
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return [res, controller];
+};
+
+export const createModel = async (
+	token: string,
+	tagName: string,
+	content: string,
+	urlIdx: string | null = null
+) => {
+	let error = null;
+
+	const res = await fetch(
+		`${OLLAMA_API_BASE_URL}/api/create${urlIdx !== null ? `/${urlIdx}` : ''}`,
+		{
+			method: 'POST',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				Authorization: `Bearer ${token}`
+			},
+			body: JSON.stringify({
+				name: tagName,
+				modelfile: content
+			})
+		}
+	).catch((err) => {
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteModel = async (token: string, tagName: string, urlIdx: string | null = null) => {
+	let error = null;
+
+	const res = await fetch(
+		`${OLLAMA_API_BASE_URL}/api/delete${urlIdx !== null ? `/${urlIdx}` : ''}`,
+		{
+			method: 'DELETE',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				Authorization: `Bearer ${token}`
+			},
+			body: JSON.stringify({
+				name: tagName
+			})
+		}
+	)
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			console.log(json);
+			return true;
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+
+			if ('detail' in err) {
+				error = err.detail;
+			}
+
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const pullModel = async (token: string, tagName: string, urlIdx: number | null = null) => {
+	let error = null;
+	const controller = new AbortController();
+
+	const res = await fetch(`${OLLAMA_API_BASE_URL}/api/pull${urlIdx !== null ? `/${urlIdx}` : ''}`, {
+		signal: controller.signal,
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			name: tagName
+		})
+	}).catch((err) => {
+		console.log(err);
+		error = err;
+
+		if ('detail' in err) {
+			error = err.detail;
+		}
+
+		return null;
+	});
+	if (error) {
+		throw error;
+	}
+	return [res, controller];
+};
+
+export const downloadModel = async (
+	token: string,
+	download_url: string,
+	urlIdx: string | null = null
+) => {
+	let error = null;
+
+	const res = await fetch(
+		`${OLLAMA_API_BASE_URL}/models/download${urlIdx !== null ? `/${urlIdx}` : ''}`,
+		{
+			method: 'POST',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				Authorization: `Bearer ${token}`
+			},
+			body: JSON.stringify({
+				url: download_url
+			})
+		}
+	).catch((err) => {
+		console.log(err);
+		error = err;
+
+		if ('detail' in err) {
+			error = err.detail;
+		}
+
+		return null;
+	});
+	if (error) {
+		throw error;
+	}
+	return res;
+};
+
+export const uploadModel = async (token: string, file: File, urlIdx: string | null = null) => {
+	let error = null;
+
+	const formData = new FormData();
+	formData.append('file', file);
+
+	const res = await fetch(
+		`${OLLAMA_API_BASE_URL}/models/upload${urlIdx !== null ? `/${urlIdx}` : ''}`,
+		{
+			method: 'POST',
+			headers: {
+				Authorization: `Bearer ${token}`
+			},
+			body: formData
+		}
+	).catch((err) => {
+		console.log(err);
+		error = err;
+
+		if ('detail' in err) {
+			error = err.detail;
+		}
+
+		return null;
+	});
+	if (error) {
+		throw error;
+	}
+	return res;
+};
+
+// export const pullModel = async (token: string, tagName: string) => {
+// 	return await fetch(`${OLLAMA_API_BASE_URL}/pull`, {
+// 		method: 'POST',
+// 		headers: {
+// 			'Content-Type': 'text/event-stream',
+// 			Authorization: `Bearer ${token}`
+// 		},
+// 		body: JSON.stringify({
+// 			name: tagName
+// 		})
+// 	});
+// };
diff --git a/src/lib/apis/openai/index.ts b/src/lib/apis/openai/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1988dc0c36020f1e331d2b136a0974c6c0746dce
--- /dev/null
+++ b/src/lib/apis/openai/index.ts
@@ -0,0 +1,335 @@
+import { OPENAI_API_BASE_URL } from '$lib/constants';
+
+export const getOpenAIConfig = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/config`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type OpenAIConfig = {
+	ENABLE_OPENAI_API: boolean;
+	OPENAI_API_BASE_URLS: string[];
+	OPENAI_API_KEYS: string[];
+	OPENAI_API_CONFIGS: object;
+};
+
+export const updateOpenAIConfig = async (token: string = '', config: OpenAIConfig) => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			...config
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getOpenAIUrls = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/urls`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OPENAI_API_BASE_URLS;
+};
+
+export const updateOpenAIUrls = async (token: string = '', urls: string[]) => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/urls/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			urls: urls
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OPENAI_API_BASE_URLS;
+};
+
+export const getOpenAIKeys = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/keys`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OPENAI_API_KEYS;
+};
+
+export const updateOpenAIKeys = async (token: string = '', keys: string[]) => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/keys/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		},
+		body: JSON.stringify({
+			keys: keys
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			if ('detail' in err) {
+				error = err.detail;
+			} else {
+				error = 'Server connection failed';
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res.OPENAI_API_KEYS;
+};
+
+export const getOpenAIModels = async (token: string, urlIdx?: number) => {
+	let error = null;
+
+	const res = await fetch(
+		`${OPENAI_API_BASE_URL}/models${typeof urlIdx === 'number' ? `/${urlIdx}` : ''}`,
+		{
+			method: 'GET',
+			headers: {
+				Accept: 'application/json',
+				'Content-Type': 'application/json',
+				...(token && { authorization: `Bearer ${token}` })
+			}
+		}
+	)
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = `OpenAI: ${err?.error?.message ?? 'Network Problem'}`;
+			return [];
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const verifyOpenAIConnection = async (
+	token: string = '',
+	url: string = 'https://api.openai.com/v1',
+	key: string = ''
+) => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/verify`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			Authorization: `Bearer ${token}`,
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			url,
+			key
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = `OpenAI: ${err?.error?.message ?? 'Network Problem'}`;
+			return [];
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const generateOpenAIChatCompletion = async (
+	token: string = '',
+	body: object,
+	url: string = OPENAI_API_BASE_URL
+): Promise<[Response | null, AbortController]> => {
+	const controller = new AbortController();
+	let error = null;
+
+	const res = await fetch(`${url}/chat/completions`, {
+		signal: controller.signal,
+		method: 'POST',
+		headers: {
+			Authorization: `Bearer ${token}`,
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify(body)
+	}).catch((err) => {
+		console.log(err);
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return [res, controller];
+};
+
+export const synthesizeOpenAISpeech = async (
+	token: string = '',
+	speaker: string = 'alloy',
+	text: string = '',
+	model: string = 'tts-1'
+) => {
+	let error = null;
+
+	const res = await fetch(`${OPENAI_API_BASE_URL}/audio/speech`, {
+		method: 'POST',
+		headers: {
+			Authorization: `Bearer ${token}`,
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			model: model,
+			input: text,
+			voice: speaker
+		})
+	}).catch((err) => {
+		console.log(err);
+		error = err;
+		return null;
+	});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/prompts/index.ts b/src/lib/apis/prompts/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d796e5cd612ca37030a38874c8432a7ec5bd29f
--- /dev/null
+++ b/src/lib/apis/prompts/index.ts
@@ -0,0 +1,204 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+type PromptItem = {
+	command: string;
+	title: string;
+	content: string;
+	access_control: null | object;
+};
+
+export const createNewPrompt = async (token: string, prompt: PromptItem) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...prompt,
+			command: `/${prompt.command}`
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPrompts = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPromptList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/list`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getPromptByCommand = async (token: string, command: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${command}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updatePromptByCommand = async (token: string, prompt: PromptItem) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${prompt.command}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...prompt,
+			command: `/${prompt.command}`
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deletePromptByCommand = async (token: string, command: string) => {
+	let error = null;
+
+	command = command.charAt(0) === '/' ? command.slice(1) : command;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/prompts/command/${command}/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/retrieval/index.ts b/src/lib/apis/retrieval/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..21ae792fa52d41de32e41e62748343024e9b510b
--- /dev/null
+++ b/src/lib/apis/retrieval/index.ts
@@ -0,0 +1,568 @@
+import { RETRIEVAL_API_BASE_URL } from '$lib/constants';
+
+export const getRAGConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/config`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type ChunkConfigForm = {
+	chunk_size: number;
+	chunk_overlap: number;
+};
+
+type ContentExtractConfigForm = {
+	engine: string;
+	tika_server_url: string | null;
+};
+
+type YoutubeConfigForm = {
+	language: string[];
+	translation?: string | null;
+	proxy_url: string;
+};
+
+type RAGConfigForm = {
+	pdf_extract_images?: boolean;
+	chunk?: ChunkConfigForm;
+	content_extraction?: ContentExtractConfigForm;
+	web_loader_ssl_verification?: boolean;
+	youtube?: YoutubeConfigForm;
+};
+
+export const updateRAGConfig = async (token: string, payload: RAGConfigForm) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/config/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...payload
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getRAGTemplate = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/template`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res?.template ?? '';
+};
+
+export const getQuerySettings = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/query/settings`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type QuerySettings = {
+	k: number | null;
+	r: number | null;
+	template: string | null;
+};
+
+export const updateQuerySettings = async (token: string, settings: QuerySettings) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/query/settings/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...settings
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getEmbeddingConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/embedding`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type OpenAIConfigForm = {
+	key: string;
+	url: string;
+};
+
+type EmbeddingModelUpdateForm = {
+	openai_config?: OpenAIConfigForm;
+	embedding_engine: string;
+	embedding_model: string;
+	embedding_batch_size?: number;
+};
+
+export const updateEmbeddingConfig = async (token: string, payload: EmbeddingModelUpdateForm) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/embedding/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...payload
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getRerankingConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/reranking`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type RerankingModelUpdateForm = {
+	reranking_model: string;
+};
+
+export const updateRerankingConfig = async (token: string, payload: RerankingModelUpdateForm) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/reranking/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...payload
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export interface SearchDocument {
+	status: boolean;
+	collection_name: string;
+	filenames: string[];
+}
+
+export const processFile = async (
+	token: string,
+	file_id: string,
+	collection_name: string | null = null
+) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/process/file`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			file_id: file_id,
+			collection_name: collection_name ? collection_name : undefined
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const processYoutubeVideo = async (token: string, url: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/process/youtube`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			url: url
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const processWeb = async (token: string, collection_name: string, url: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/process/web`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			url: url,
+			collection_name: collection_name
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const processWebSearch = async (
+	token: string,
+	query: string,
+	collection_name?: string
+): Promise<SearchDocument | null> => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/process/web/search`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			query,
+			collection_name: collection_name ?? ''
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const queryDoc = async (
+	token: string,
+	collection_name: string,
+	query: string,
+	k: number | null = null
+) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/query/doc`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			collection_name: collection_name,
+			query: query,
+			k: k
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const queryCollection = async (
+	token: string,
+	collection_names: string,
+	query: string,
+	k: number | null = null
+) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/query/collection`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			collection_names: collection_names,
+			query: query,
+			k: k
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const resetUploadDir = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/reset/uploads`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const resetVectorDB = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${RETRIEVAL_API_BASE_URL}/reset/db`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/streaming/index.ts b/src/lib/apis/streaming/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..54804385d9027202070f5a75a9d2ca9dea95639c
--- /dev/null
+++ b/src/lib/apis/streaming/index.ts
@@ -0,0 +1,124 @@
+import { EventSourceParserStream } from 'eventsource-parser/stream';
+import type { ParsedEvent } from 'eventsource-parser';
+
+type TextStreamUpdate = {
+	done: boolean;
+	value: string;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	sources?: any;
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	selectedModelId?: any;
+	error?: any;
+	usage?: ResponseUsage;
+};
+
+type ResponseUsage = {
+	/** Including images and tools if any */
+	prompt_tokens: number;
+	/** The tokens generated */
+	completion_tokens: number;
+	/** Sum of the above two fields */
+	total_tokens: number;
+	/** Any other fields that aren't part of the base OpenAI spec */
+	[other: string]: unknown;
+};
+
+// createOpenAITextStream takes a responseBody with a SSE response,
+// and returns an async generator that emits delta updates with large deltas chunked into random sized chunks
+export async function createOpenAITextStream(
+	responseBody: ReadableStream<Uint8Array>,
+	splitLargeDeltas: boolean
+): Promise<AsyncGenerator<TextStreamUpdate>> {
+	const eventStream = responseBody
+		.pipeThrough(new TextDecoderStream())
+		.pipeThrough(new EventSourceParserStream())
+		.getReader();
+	let iterator = openAIStreamToIterator(eventStream);
+	if (splitLargeDeltas) {
+		iterator = streamLargeDeltasAsRandomChunks(iterator);
+	}
+	return iterator;
+}
+
+async function* openAIStreamToIterator(
+	reader: ReadableStreamDefaultReader<ParsedEvent>
+): AsyncGenerator<TextStreamUpdate> {
+	while (true) {
+		const { value, done } = await reader.read();
+		if (done) {
+			yield { done: true, value: '' };
+			break;
+		}
+		if (!value) {
+			continue;
+		}
+		const data = value.data;
+		if (data.startsWith('[DONE]')) {
+			yield { done: true, value: '' };
+			break;
+		}
+
+		try {
+			const parsedData = JSON.parse(data);
+			console.log(parsedData);
+
+			if (parsedData.error) {
+				yield { done: true, value: '', error: parsedData.error };
+				break;
+			}
+
+			if (parsedData.sources) {
+				yield { done: false, value: '', sources: parsedData.sources };
+				continue;
+			}
+
+			if (parsedData.selected_model_id) {
+				yield { done: false, value: '', selectedModelId: parsedData.selected_model_id };
+				continue;
+			}
+
+			yield {
+				done: false,
+				value: parsedData.choices?.[0]?.delta?.content ?? '',
+				usage: parsedData.usage
+			};
+		} catch (e) {
+			console.error('Error extracting delta from SSE event:', e);
+		}
+	}
+}
+
+// streamLargeDeltasAsRandomChunks will chunk large deltas (length > 5) into random sized chunks between 1-3 characters
+// This is to simulate a more fluid streaming, even though some providers may send large chunks of text at once
+async function* streamLargeDeltasAsRandomChunks(
+	iterator: AsyncGenerator<TextStreamUpdate>
+): AsyncGenerator<TextStreamUpdate> {
+	for await (const textStreamUpdate of iterator) {
+		if (textStreamUpdate.done) {
+			yield textStreamUpdate;
+			return;
+		}
+		if (textStreamUpdate.sources) {
+			yield textStreamUpdate;
+			continue;
+		}
+		let content = textStreamUpdate.value;
+		if (content.length < 5) {
+			yield { done: false, value: content };
+			continue;
+		}
+		while (content != '') {
+			const chunkSize = Math.min(Math.floor(Math.random() * 3) + 1, content.length);
+			const chunk = content.slice(0, chunkSize);
+			yield { done: false, value: chunk };
+			// Do not sleep if the tab is hidden
+			// Timers are throttled to 1s in hidden tabs
+			if (document?.visibilityState !== 'hidden') {
+				await sleep(5);
+			}
+			content = content.slice(chunkSize);
+		}
+	}
+}
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
diff --git a/src/lib/apis/tools/index.ts b/src/lib/apis/tools/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1dc11c16f740abbae152be9e1fdc2c17534a407
--- /dev/null
+++ b/src/lib/apis/tools/index.ts
@@ -0,0 +1,422 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const createNewTool = async (token: string, tool: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/create`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...tool
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getTools = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getToolList = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/list`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const exportTools = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/export`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getToolById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateToolById = async (token: string, id: string, tool: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...tool
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const deleteToolById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/delete`, {
+		method: 'DELETE',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getToolValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getToolValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateToolValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserValvesSpecById = async (token: string, id: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user/spec`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserValvesById = async (token: string, id: string, valves: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/tools/id/${id}/valves/user/update`, {
+		method: 'POST',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...valves
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.then((json) => {
+			return json;
+		})
+		.catch((err) => {
+			error = err.detail;
+
+			console.log(err);
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/users/index.ts b/src/lib/apis/users/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b0efe39d27ea138275c1024ae2df322bdfb2ddae
--- /dev/null
+++ b/src/lib/apis/users/index.ts
@@ -0,0 +1,363 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+import { getUserPosition } from '$lib/utils';
+
+export const getUserGroups = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/groups`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserDefaultPermissions = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/default/permissions`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserDefaultPermissions = async (token: string, permissions: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/default/permissions`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...permissions
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserRole = async (token: string, id: string, role: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/update/role`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			id: id,
+			role: role
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUsers = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res ? res : [];
+};
+
+export const getUserSettings = async (token: string) => {
+	let error = null;
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/settings`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserSettings = async (token: string, settings: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/settings/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...settings
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserById = async (token: string, userId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getUserInfo = async (token: string) => {
+	let error = null;
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/info`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const updateUserInfo = async (token: string, info: object) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/user/info/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			...info
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const getAndUpdateUserLocation = async (token: string) => {
+	const location = await getUserPosition().catch((err) => {
+		throw err;
+	});
+
+	if (location) {
+		await updateUserInfo(token, { location: location });
+		return location;
+	} else {
+		throw new Error('Failed to get user location');
+	}
+};
+
+export const deleteUserById = async (token: string, userId: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}`, {
+		method: 'DELETE',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+type UserUpdateForm = {
+	profile_image_url: string;
+	email: string;
+	name: string;
+	password: string;
+};
+
+export const updateUserById = async (token: string, userId: string, user: UserUpdateForm) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}/update`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		},
+		body: JSON.stringify({
+			profile_image_url: user.profile_image_url,
+			email: user.email,
+			name: user.name,
+			password: user.password !== '' ? user.password : undefined
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
diff --git a/src/lib/apis/utils/index.ts b/src/lib/apis/utils/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40fdbfcfa22519f699ab525d3649274b1e6dc0ed
--- /dev/null
+++ b/src/lib/apis/utils/index.ts
@@ -0,0 +1,179 @@
+import { WEBUI_API_BASE_URL } from '$lib/constants';
+
+export const getGravatarUrl = async (email: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/gravatar?email=${email}`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json'
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	return res;
+};
+
+export const formatPythonCode = async (code: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/code/format`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			code: code
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+
+			error = err;
+			if (err.detail) {
+				error = err.detail;
+			}
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res;
+};
+
+export const downloadChatAsPDF = async (title: string, messages: object[]) => {
+	let error = null;
+
+	const blob = await fetch(`${WEBUI_API_BASE_URL}/utils/pdf`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			title: title,
+			messages: messages
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.blob();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	return blob;
+};
+
+export const getHTMLFromMarkdown = async (md: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/markdown`, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json'
+		},
+		body: JSON.stringify({
+			md: md
+		})
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	return res.html;
+};
+
+export const downloadDatabase = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/db/download`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (response) => {
+			if (!response.ok) {
+				throw await response.json();
+			}
+			return response.blob();
+		})
+		.then((blob) => {
+			const url = window.URL.createObjectURL(blob);
+			const a = document.createElement('a');
+			a.href = url;
+			a.download = 'webui.db';
+			document.body.appendChild(a);
+			a.click();
+			window.URL.revokeObjectURL(url);
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+};
+
+export const downloadLiteLLMConfig = async (token: string) => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_API_BASE_URL}/utils/litellm/config`, {
+		method: 'GET',
+		headers: {
+			'Content-Type': 'application/json',
+			Authorization: `Bearer ${token}`
+		}
+	})
+		.then(async (response) => {
+			if (!response.ok) {
+				throw await response.json();
+			}
+			return response.blob();
+		})
+		.then((blob) => {
+			const url = window.URL.createObjectURL(blob);
+			const a = document.createElement('a');
+			a.href = url;
+			a.download = 'config.yaml';
+			document.body.appendChild(a);
+			a.click();
+			window.URL.revokeObjectURL(url);
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err.detail;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+};
diff --git a/src/lib/components/AddFilesPlaceholder.svelte b/src/lib/components/AddFilesPlaceholder.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d3d700795552bf2b75983f7973d72ec6a7798c7e
--- /dev/null
+++ b/src/lib/components/AddFilesPlaceholder.svelte
@@ -0,0 +1,28 @@
+<script>
+	import { getContext } from 'svelte';
+
+	export let title = '';
+	export let content = '';
+	const i18n = getContext('i18n');
+</script>
+
+<div class="px-3">
+	<div class="text-center text-6xl mb-3">📄</div>
+	<div class="text-center dark:text-white text-xl font-semibold z-50">
+		{#if title}
+			{title}
+		{:else}
+			{$i18n.t('Add Files')}
+		{/if}
+	</div>
+
+	<slot
+		><div class="px-2 mt-2 text-center text-sm dark:text-gray-200 w-full">
+			{#if content}
+				{content}
+			{:else}
+				{$i18n.t('Drop any files here to add to the conversation')}
+			{/if}
+		</div>
+	</slot>
+</div>
diff --git a/src/lib/components/ChangelogModal.svelte b/src/lib/components/ChangelogModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b395ddcbd6303c9b16d47821cda298bbce32ceda
--- /dev/null
+++ b/src/lib/components/ChangelogModal.svelte
@@ -0,0 +1,120 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+	import { Confetti } from 'svelte-confetti';
+
+	import { WEBUI_NAME, config, settings } from '$lib/stores';
+
+	import { WEBUI_VERSION } from '$lib/constants';
+	import { getChangelog } from '$lib/apis';
+
+	import Modal from './common/Modal.svelte';
+	import { updateUserSettings } from '$lib/apis/users';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+
+	let changelog = null;
+
+	onMount(async () => {
+		const res = await getChangelog();
+		changelog = res;
+	});
+</script>
+
+<Modal bind:show size="lg">
+	<div class="px-5 pt-4 dark:text-gray-300 text-gray-700">
+		<div class="flex justify-between items-start">
+			<div class="text-xl font-semibold">
+				{$i18n.t('What’s New in')}
+				{$WEBUI_NAME}
+				<Confetti x={[-1, -0.25]} y={[0, 0.5]} />
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					localStorage.version = $config.version;
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+		<div class="flex items-center mt-1">
+			<div class="text-sm dark:text-gray-200">{$i18n.t('Release Notes')}</div>
+			<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" />
+			<div class="text-sm dark:text-gray-200">
+				v{WEBUI_VERSION}
+			</div>
+		</div>
+	</div>
+
+	<div class=" w-full p-4 px-5 text-gray-700 dark:text-gray-100">
+		<div class=" overflow-y-scroll max-h-96 scrollbar-hidden">
+			<div class="mb-3">
+				{#if changelog}
+					{#each Object.keys(changelog) as version}
+						<div class=" mb-3 pr-2">
+							<div class="font-semibold text-xl mb-1 dark:text-white">
+								v{version} - {changelog[version].date}
+							</div>
+
+							<hr class=" dark:border-gray-800 my-2" />
+
+							{#each Object.keys(changelog[version]).filter((section) => section !== 'date') as section}
+								<div class="">
+									<div
+										class="font-semibold uppercase text-xs {section === 'added'
+											? 'text-white bg-blue-600'
+											: section === 'fixed'
+												? 'text-white bg-green-600'
+												: section === 'changed'
+													? 'text-white bg-yellow-600'
+													: section === 'removed'
+														? 'text-white bg-red-600'
+														: ''}  w-fit px-3 rounded-full my-2.5"
+									>
+										{section}
+									</div>
+
+									<div class="my-2.5 px-1.5">
+										{#each Object.keys(changelog[version][section]) as item}
+											<div class="text-sm mb-2">
+												<div class="font-semibold uppercase">
+													{changelog[version][section][item].title}
+												</div>
+												<div class="mb-2 mt-1">{changelog[version][section][item].content}</div>
+											</div>
+										{/each}
+									</div>
+								</div>
+							{/each}
+						</div>
+					{/each}
+				{/if}
+			</div>
+		</div>
+		<div class="flex justify-end pt-3 text-sm font-medium">
+			<button
+				on:click={async () => {
+					localStorage.version = $config.version;
+					await settings.set({ ...$settings, ...{ version: $config.version } });
+					await updateUserSettings(localStorage.token, { ui: $settings });
+					show = false;
+				}}
+				class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			>
+				<span class="relative">{$i18n.t("Okay, Let's Go!")}</span>
+			</button>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/OnBoarding.svelte b/src/lib/components/OnBoarding.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2add98a6c377596de3541fa73f5d12c2046c688a
--- /dev/null
+++ b/src/lib/components/OnBoarding.svelte
@@ -0,0 +1,78 @@
+<script>
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import Marquee from './common/Marquee.svelte';
+	import SlideShow from './common/SlideShow.svelte';
+	import ArrowRightCircle from './icons/ArrowRightCircle.svelte';
+
+	export let show = true;
+	export let getStartedHandler = () => {};
+</script>
+
+{#if show}
+	<div class="w-full h-screen max-h-[100dvh] text-white relative">
+		<div class="fixed m-10 z-50">
+			<div class="flex space-x-2">
+				<div class=" self-center">
+					<img
+						crossorigin="anonymous"
+						src="{WEBUI_BASE_URL}/static/favicon.png"
+						class=" w-6 rounded-full"
+						alt="logo"
+					/>
+				</div>
+			</div>
+		</div>
+
+		<SlideShow duration={5000} />
+
+		<div
+			class="w-full h-full absolute top-0 left-0 bg-gradient-to-t from-20% from-black to-transparent"
+		></div>
+
+		<div class="w-full h-full absolute top-0 left-0 backdrop-blur-sm bg-black/50"></div>
+
+		<div class="relative bg-transparent w-full min-h-screen flex z-10">
+			<div class="flex flex-col justify-end w-full items-center pb-10 text-center">
+				<div class="text-5xl lg:text-7xl font-secondary">
+					<Marquee
+						duration={5000}
+						words={[
+							$i18n.t('Explore the cosmos'),
+							$i18n.t('Unlock mysteries'),
+							$i18n.t('Chart new frontiers'),
+							$i18n.t('Dive into knowledge'),
+							$i18n.t('Discover wonders'),
+							$i18n.t('Ignite curiosity'),
+							$i18n.t('Forge new paths'),
+							$i18n.t('Unravel secrets'),
+							$i18n.t('Pioneer insights'),
+							$i18n.t('Embark on adventures')
+						]}
+					/>
+
+					<div class="mt-0.5">{$i18n.t(`wherever you are`)}</div>
+				</div>
+
+				<div class="flex justify-center mt-8">
+					<div class="flex flex-col justify-center items-center">
+						<button
+							class="relative z-20 flex p-1 rounded-full bg-white/5 hover:bg-white/10 transition font-medium text-sm"
+							on:click={() => {
+								getStartedHandler();
+							}}
+						>
+							<ArrowRightCircle className="size-6" />
+						</button>
+						<div class="mt-1.5 font-primary text-base font-medium">{$i18n.t(`Get started`)}</div>
+					</div>
+				</div>
+			</div>
+
+			<!-- <div class="absolute bottom-12 left-0 right-0 w-full"></div> -->
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/admin/Evaluations.svelte b/src/lib/components/admin/Evaluations.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a5532ae2f2e390517313528c86cb1bd7b5265208
--- /dev/null
+++ b/src/lib/components/admin/Evaluations.svelte
@@ -0,0 +1,100 @@
+<script>
+	import { getContext, tick, onMount } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import Leaderboard from './Evaluations/Leaderboard.svelte';
+	import Feedbacks from './Evaluations/Feedbacks.svelte';
+
+	import { getAllFeedbacks } from '$lib/apis/evaluations';
+
+	const i18n = getContext('i18n');
+
+	let selectedTab = 'leaderboard';
+
+	let loaded = false;
+	let feedbacks = [];
+
+	onMount(async () => {
+		feedbacks = await getAllFeedbacks(localStorage.token);
+		loaded = true;
+
+		const containerElement = document.getElementById('users-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>
+
+{#if loaded}
+	<div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
+		<div
+			id="users-tabs-container"
+			class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
+		>
+			<button
+				class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
+				'leaderboard'
+					? ''
+					: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+				on:click={() => {
+					selectedTab = 'leaderboard';
+				}}
+			>
+				<div class=" self-center mr-2">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm6 5.75a.75.75 0 0 1 1.5 0v3.5a.75.75 0 0 1-1.5 0v-3.5Zm-2.75 1.5a.75.75 0 0 1 1.5 0v2a.75.75 0 0 1-1.5 0v-2Zm-2 .75a.75.75 0 0 0-.75.75v.5a.75.75 0 0 0 1.5 0v-.5a.75.75 0 0 0-.75-.75Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center">{$i18n.t('Leaderboard')}</div>
+			</button>
+
+			<button
+				class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
+				'feedbacks'
+					? ''
+					: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+				on:click={() => {
+					selectedTab = 'feedbacks';
+				}}
+			>
+				<div class=" self-center mr-2">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M5.25 2A2.25 2.25 0 0 0 3 4.25v9a.75.75 0 0 0 1.183.613l1.692-1.195 1.692 1.195a.75.75 0 0 0 .866 0l1.692-1.195 1.693 1.195A.75.75 0 0 0 13 13.25v-9A2.25 2.25 0 0 0 10.75 2h-5.5Zm3.03 3.28a.75.75 0 0 0-1.06-1.06L4.97 6.47a.75.75 0 0 0 0 1.06l2.25 2.25a.75.75 0 0 0 1.06-1.06l-.97-.97h1.315c.76 0 1.375.616 1.375 1.375a.75.75 0 0 0 1.5 0A2.875 2.875 0 0 0 8.625 6.25H7.311l.97-.97Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center">{$i18n.t('Feedbacks')}</div>
+			</button>
+		</div>
+
+		<div class="flex-1 mt-1 lg:mt-0 overflow-y-scroll">
+			{#if selectedTab === 'leaderboard'}
+				<Leaderboard {feedbacks} />
+			{:else if selectedTab === 'feedbacks'}
+				<Feedbacks {feedbacks} />
+			{/if}
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/admin/Evaluations/FeedbackMenu.svelte b/src/lib/components/admin/Evaluations/FeedbackMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..83defd8043fe247daabc4683c3fe4f23e72f1ae2
--- /dev/null
+++ b/src/lib/components/admin/Evaluations/FeedbackMenu.svelte
@@ -0,0 +1,46 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Download from '$lib/components/icons/Download.svelte';
+
+	let show = false;
+</script>
+
+<Dropdown bind:show on:change={(e) => {}}>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[150px] rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('delete');
+					show = false;
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/admin/Evaluations/Feedbacks.svelte b/src/lib/components/admin/Evaluations/Feedbacks.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e43081302c038004859a487e22201ae38354e882
--- /dev/null
+++ b/src/lib/components/admin/Evaluations/Feedbacks.svelte
@@ -0,0 +1,283 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	dayjs.extend(relativeTime);
+
+	import { onMount, getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { deleteFeedbackById, exportAllFeedbacks, getAllFeedbacks } from '$lib/apis/evaluations';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	import CloudArrowUp from '$lib/components/icons/CloudArrowUp.svelte';
+	import Pagination from '$lib/components/common/Pagination.svelte';
+	import FeedbackMenu from './FeedbackMenu.svelte';
+	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+
+	export let feedbacks = [];
+
+	let page = 1;
+	$: paginatedFeedbacks = feedbacks.slice((page - 1) * 10, page * 10);
+
+	type Feedback = {
+		id: string;
+		data: {
+			rating: number;
+			model_id: string;
+			sibling_model_ids: string[] | null;
+			reason: string;
+			comment: string;
+			tags: string[];
+		};
+		user: {
+			name: string;
+			profile_image_url: string;
+		};
+		updated_at: number;
+	};
+
+	type ModelStats = {
+		rating: number;
+		won: number;
+		lost: number;
+	};
+
+	//////////////////////
+	//
+	// CRUD operations
+	//
+	//////////////////////
+
+	const deleteFeedbackHandler = async (feedbackId: string) => {
+		const response = await deleteFeedbackById(localStorage.token, feedbackId).catch((err) => {
+			toast.error(err);
+			return null;
+		});
+		if (response) {
+			feedbacks = feedbacks.filter((f) => f.id !== feedbackId);
+		}
+	};
+
+	const shareHandler = async () => {
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+
+		// remove snapshot from feedbacks
+		const feedbacksToShare = feedbacks.map((f) => {
+			const { snapshot, user, ...rest } = f;
+			return rest;
+		});
+		console.log(feedbacksToShare);
+
+		const url = 'https://openwebui.com';
+		const tab = await window.open(`${url}/leaderboard`, '_blank');
+
+		// Define the event handler function
+		const messageHandler = (event) => {
+			if (event.origin !== url) return;
+			if (event.data === 'loaded') {
+				tab.postMessage(JSON.stringify(feedbacksToShare), '*');
+
+				// Remove the event listener after handling the message
+				window.removeEventListener('message', messageHandler);
+			}
+		};
+
+		window.addEventListener('message', messageHandler, false);
+	};
+
+	const exportHandler = async () => {
+		const _feedbacks = await exportAllFeedbacks(localStorage.token).catch((err) => {
+			toast.error(err);
+			return null;
+		});
+
+		if (_feedbacks) {
+			let blob = new Blob([JSON.stringify(_feedbacks)], {
+				type: 'application/json'
+			});
+			saveAs(blob, `feedback-history-export-${Date.now()}.json`);
+		}
+	};
+</script>
+
+<div class="mt-0.5 mb-2 gap-1 flex flex-row justify-between">
+	<div class="flex md:self-center text-lg font-medium px-0.5">
+		{$i18n.t('Feedback History')}
+
+		<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+
+		<span class="text-lg font-medium text-gray-500 dark:text-gray-300">{feedbacks.length}</span>
+	</div>
+
+	<div>
+		<div>
+			<Tooltip content={$i18n.t('Export')}>
+				<button
+					class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1"
+					on:click={() => {
+						exportHandler();
+					}}
+				>
+					<ArrowDownTray className="size-3" />
+				</button>
+			</Tooltip>
+		</div>
+	</div>
+</div>
+
+<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded pt-0.5">
+	{#if (feedbacks ?? []).length === 0}
+		<div class="text-center text-xs text-gray-500 dark:text-gray-400 py-1">
+			{$i18n.t('No feedbacks found')}
+		</div>
+	{:else}
+		<table
+			class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full rounded"
+		>
+			<thead
+				class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5"
+			>
+				<tr class="">
+					<th scope="col" class="px-3 text-right cursor-pointer select-none w-0">
+						{$i18n.t('User')}
+					</th>
+
+					<th scope="col" class="px-3 pr-1.5 cursor-pointer select-none">
+						{$i18n.t('Models')}
+					</th>
+
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-fit">
+						{$i18n.t('Result')}
+					</th>
+
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-0">
+						{$i18n.t('Updated At')}
+					</th>
+
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-0"> </th>
+				</tr>
+			</thead>
+			<tbody class="">
+				{#each paginatedFeedbacks as feedback (feedback.id)}
+					<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
+						<td class=" py-0.5 text-right font-semibold">
+							<div class="flex justify-center">
+								<Tooltip content={feedback?.user?.name}>
+									<div class="flex-shrink-0">
+										<img
+											src={feedback?.user?.profile_image_url ?? '/user.png'}
+											alt={feedback?.user?.name}
+											class="size-5 rounded-full object-cover shrink-0"
+										/>
+									</div>
+								</Tooltip>
+							</div>
+						</td>
+
+						<td class=" py-1 pl-3 flex flex-col">
+							<div class="flex flex-col items-start gap-0.5 h-full">
+								<div class="flex flex-col h-full">
+									{#if feedback.data?.sibling_model_ids}
+										<div class="font-semibold text-gray-600 dark:text-gray-400 flex-1">
+											{feedback.data?.model_id}
+										</div>
+
+										<Tooltip content={feedback.data.sibling_model_ids.join(', ')}>
+											<div class=" text-[0.65rem] text-gray-600 dark:text-gray-400 line-clamp-1">
+												{#if feedback.data.sibling_model_ids.length > 2}
+													<!-- {$i18n.t('and {{COUNT}} more')} -->
+													{feedback.data.sibling_model_ids.slice(0, 2).join(', ')}, {$i18n.t(
+														'and {{COUNT}} more',
+														{ COUNT: feedback.data.sibling_model_ids.length - 2 }
+													)}
+												{:else}
+													{feedback.data.sibling_model_ids.join(', ')}
+												{/if}
+											</div>
+										</Tooltip>
+									{:else}
+										<div
+											class=" text-sm font-medium text-gray-600 dark:text-gray-400 flex-1 py-1.5"
+										>
+											{feedback.data?.model_id}
+										</div>
+									{/if}
+								</div>
+							</div>
+						</td>
+						<td class="px-3 py-1 text-right font-medium text-gray-900 dark:text-white w-max">
+							<div class=" flex justify-end">
+								{#if feedback.data.rating.toString() === '1'}
+									<Badge type="info" content={$i18n.t('Won')} />
+								{:else if feedback.data.rating.toString() === '0'}
+									<Badge type="muted" content={$i18n.t('Draw')} />
+								{:else if feedback.data.rating.toString() === '-1'}
+									<Badge type="error" content={$i18n.t('Lost')} />
+								{/if}
+							</div>
+						</td>
+
+						<td class=" px-3 py-1 text-right font-medium">
+							{dayjs(feedback.updated_at * 1000).fromNow()}
+						</td>
+
+						<td class=" px-3 py-1 text-right font-semibold">
+							<FeedbackMenu
+								on:delete={(e) => {
+									deleteFeedbackHandler(feedback.id);
+								}}
+							>
+								<button
+									class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								>
+									<EllipsisHorizontal />
+								</button>
+							</FeedbackMenu>
+						</td>
+					</tr>
+				{/each}
+			</tbody>
+		</table>
+	{/if}
+</div>
+
+{#if feedbacks.length > 0}
+	<div class=" flex flex-col justify-end w-full text-right gap-1">
+		<div class="line-clamp-1 text-gray-500 text-xs">
+			{$i18n.t('Help us create the best community leaderboard by sharing your feedback history!')}
+		</div>
+
+		<div class="flex space-x-1 ml-auto">
+			<Tooltip
+				content={$i18n.t(
+					'To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.'
+				)}
+			>
+				<button
+					class="flex text-xs items-center px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-200 transition"
+					on:click={async () => {
+						shareHandler();
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">
+						{$i18n.t('Share to OpenWebUI Community')}
+					</div>
+
+					<div class=" self-center">
+						<CloudArrowUp className="size-3" strokeWidth="3" />
+					</div>
+				</button>
+			</Tooltip>
+		</div>
+	</div>
+{/if}
+
+{#if feedbacks.length > 10}
+	<Pagination bind:page count={feedbacks.length} perPage={10} />
+{/if}
diff --git a/src/lib/components/admin/Evaluations/Leaderboard.svelte b/src/lib/components/admin/Evaluations/Leaderboard.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..59f6df916a283545adaeb7b13a6c739800fa2133
--- /dev/null
+++ b/src/lib/components/admin/Evaluations/Leaderboard.svelte
@@ -0,0 +1,410 @@
+<script lang="ts">
+	import * as ort from 'onnxruntime-web';
+	import { AutoModel, AutoTokenizer } from '@huggingface/transformers';
+
+	import { onMount, getContext } from 'svelte';
+	import { models } from '$lib/stores';
+
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
+
+	const i18n = getContext('i18n');
+
+	const EMBEDDING_MODEL = 'TaylorAI/bge-micro-v2';
+
+	let tokenizer = null;
+	let model = null;
+
+	export let feedbacks = [];
+
+	let rankedModels = [];
+
+	let query = '';
+
+	let tagEmbeddings = new Map();
+	let loadingLeaderboard = true;
+	let debounceTimer;
+
+	type Feedback = {
+		id: string;
+		data: {
+			rating: number;
+			model_id: string;
+			sibling_model_ids: string[] | null;
+			reason: string;
+			comment: string;
+			tags: string[];
+		};
+		user: {
+			name: string;
+			profile_image_url: string;
+		};
+		updated_at: number;
+	};
+
+	type ModelStats = {
+		rating: number;
+		won: number;
+		lost: number;
+	};
+
+	//////////////////////
+	//
+	// Rank models by Elo rating
+	//
+	//////////////////////
+
+	const rankHandler = async (similarities: Map<string, number> = new Map()) => {
+		const modelStats = calculateModelStats(feedbacks, similarities);
+
+		rankedModels = $models
+			.filter((m) => m?.owned_by !== 'arena' && (m?.info?.meta?.hidden ?? false) !== true)
+			.map((model) => {
+				const stats = modelStats.get(model.id);
+				return {
+					...model,
+					rating: stats ? Math.round(stats.rating) : '-',
+					stats: {
+						count: stats ? stats.won + stats.lost : 0,
+						won: stats ? stats.won.toString() : '-',
+						lost: stats ? stats.lost.toString() : '-'
+					}
+				};
+			})
+			.sort((a, b) => {
+				if (a.rating === '-' && b.rating !== '-') return 1;
+				if (b.rating === '-' && a.rating !== '-') return -1;
+				if (a.rating !== '-' && b.rating !== '-') return b.rating - a.rating;
+				return a.name.localeCompare(b.name);
+			});
+
+		loadingLeaderboard = false;
+	};
+
+	function calculateModelStats(
+		feedbacks: Feedback[],
+		similarities: Map<string, number>
+	): Map<string, ModelStats> {
+		const stats = new Map<string, ModelStats>();
+		const K = 32;
+
+		function getOrDefaultStats(modelId: string): ModelStats {
+			return stats.get(modelId) || { rating: 1000, won: 0, lost: 0 };
+		}
+
+		function updateStats(modelId: string, ratingChange: number, outcome: number) {
+			const currentStats = getOrDefaultStats(modelId);
+			currentStats.rating += ratingChange;
+			if (outcome === 1) currentStats.won++;
+			else if (outcome === 0) currentStats.lost++;
+			stats.set(modelId, currentStats);
+		}
+
+		function calculateEloChange(
+			ratingA: number,
+			ratingB: number,
+			outcome: number,
+			similarity: number
+		): number {
+			const expectedScore = 1 / (1 + Math.pow(10, (ratingB - ratingA) / 400));
+			return K * (outcome - expectedScore) * similarity;
+		}
+
+		feedbacks.forEach((feedback) => {
+			const modelA = feedback.data.model_id;
+			const statsA = getOrDefaultStats(modelA);
+			let outcome: number;
+
+			switch (feedback.data.rating.toString()) {
+				case '1':
+					outcome = 1;
+					break;
+				case '-1':
+					outcome = 0;
+					break;
+				default:
+					return; // Skip invalid ratings
+			}
+
+			// If the query is empty, set similarity to 1, else get the similarity from the map
+			const similarity = query !== '' ? similarities.get(feedback.id) || 0 : 1;
+			const opponents = feedback.data.sibling_model_ids || [];
+
+			opponents.forEach((modelB) => {
+				const statsB = getOrDefaultStats(modelB);
+				const changeA = calculateEloChange(statsA.rating, statsB.rating, outcome, similarity);
+				const changeB = calculateEloChange(statsB.rating, statsA.rating, 1 - outcome, similarity);
+
+				updateStats(modelA, changeA, outcome);
+				updateStats(modelB, changeB, 1 - outcome);
+			});
+		});
+
+		return stats;
+	}
+
+	//////////////////////
+	//
+	// Calculate cosine similarity
+	//
+	//////////////////////
+
+	const cosineSimilarity = (vecA, vecB) => {
+		// Ensure the lengths of the vectors are the same
+		if (vecA.length !== vecB.length) {
+			throw new Error('Vectors must be the same length');
+		}
+
+		// Calculate the dot product
+		let dotProduct = 0;
+		let normA = 0;
+		let normB = 0;
+
+		for (let i = 0; i < vecA.length; i++) {
+			dotProduct += vecA[i] * vecB[i];
+			normA += vecA[i] ** 2;
+			normB += vecB[i] ** 2;
+		}
+
+		// Calculate the magnitudes
+		normA = Math.sqrt(normA);
+		normB = Math.sqrt(normB);
+
+		// Avoid division by zero
+		if (normA === 0 || normB === 0) {
+			return 0;
+		}
+
+		// Return the cosine similarity
+		return dotProduct / (normA * normB);
+	};
+
+	const calculateMaxSimilarity = (queryEmbedding, tagEmbeddings: Map<string, number[]>) => {
+		let maxSimilarity = 0;
+		for (const tagEmbedding of tagEmbeddings.values()) {
+			const similarity = cosineSimilarity(queryEmbedding, tagEmbedding);
+			maxSimilarity = Math.max(maxSimilarity, similarity);
+		}
+		return maxSimilarity;
+	};
+
+	//////////////////////
+	//
+	// Embedding functions
+	//
+	//////////////////////
+
+	const loadEmbeddingModel = async () => {
+		// Check if the tokenizer and model are already loaded and stored in the window object
+		if (!window.tokenizer) {
+			window.tokenizer = await AutoTokenizer.from_pretrained(EMBEDDING_MODEL);
+		}
+
+		if (!window.model) {
+			window.model = await AutoModel.from_pretrained(EMBEDDING_MODEL);
+		}
+
+		// Use the tokenizer and model from the window object
+		tokenizer = window.tokenizer;
+		model = window.model;
+
+		// Pre-compute embeddings for all unique tags
+		const allTags = new Set(feedbacks.flatMap((feedback) => feedback.data.tags || []));
+		await getTagEmbeddings(Array.from(allTags));
+	};
+
+	const getEmbeddings = async (text: string) => {
+		const tokens = await tokenizer(text);
+		const output = await model(tokens);
+
+		// Perform mean pooling on the last hidden states
+		const embeddings = output.last_hidden_state.mean(1);
+		return embeddings.ort_tensor.data;
+	};
+
+	const getTagEmbeddings = async (tags: string[]) => {
+		const embeddings = new Map();
+		for (const tag of tags) {
+			if (!tagEmbeddings.has(tag)) {
+				tagEmbeddings.set(tag, await getEmbeddings(tag));
+			}
+			embeddings.set(tag, tagEmbeddings.get(tag));
+		}
+		return embeddings;
+	};
+
+	const debouncedQueryHandler = async () => {
+		loadingLeaderboard = true;
+
+		if (query.trim() === '') {
+			rankHandler();
+			return;
+		}
+
+		clearTimeout(debounceTimer);
+
+		debounceTimer = setTimeout(async () => {
+			const queryEmbedding = await getEmbeddings(query);
+			const similarities = new Map<string, number>();
+
+			for (const feedback of feedbacks) {
+				const feedbackTags = feedback.data.tags || [];
+				const tagEmbeddings = await getTagEmbeddings(feedbackTags);
+				const maxSimilarity = calculateMaxSimilarity(queryEmbedding, tagEmbeddings);
+				similarities.set(feedback.id, maxSimilarity);
+			}
+
+			rankHandler(similarities);
+		}, 1500); // Debounce for 1.5 seconds
+	};
+
+	$: query, debouncedQueryHandler();
+
+	onMount(async () => {
+		rankHandler();
+	});
+</script>
+
+<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
+	<div class="flex md:self-center text-lg font-medium px-0.5 shrink-0 items-center">
+		<div class=" gap-1">
+			{$i18n.t('Leaderboard')}
+		</div>
+
+		<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+
+		<span class="text-lg font-medium text-gray-500 dark:text-gray-300 mr-1.5"
+			>{rankedModels.length}</span
+		>
+	</div>
+
+	<div class=" flex space-x-2">
+		<Tooltip content={$i18n.t('Re-rank models by topic similarity')}>
+			<div class="flex flex-1">
+				<div class=" self-center ml-1 mr-3">
+					<MagnifyingGlass className="size-3" />
+				</div>
+				<input
+					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={query}
+					placeholder={$i18n.t('Search')}
+					on:focus={() => {
+						loadEmbeddingModel();
+					}}
+				/>
+			</div>
+		</Tooltip>
+	</div>
+</div>
+
+<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded pt-0.5">
+	{#if loadingLeaderboard}
+		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
+			<div class="m-auto">
+				<Spinner />
+			</div>
+		</div>
+	{/if}
+	{#if (rankedModels ?? []).length === 0}
+		<div class="text-center text-xs text-gray-500 dark:text-gray-400 py-1">
+			{$i18n.t('No models found')}
+		</div>
+	{:else}
+		<table
+			class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full rounded {loadingLeaderboard
+				? 'opacity-20'
+				: ''}"
+		>
+			<thead
+				class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5"
+			>
+				<tr class="">
+					<th scope="col" class="px-3 py-1.5 cursor-pointer select-none w-3">
+						{$i18n.t('RK')}
+					</th>
+					<th scope="col" class="px-3 py-1.5 cursor-pointer select-none">
+						{$i18n.t('Model')}
+					</th>
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-fit">
+						{$i18n.t('Rating')}
+					</th>
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-5">
+						{$i18n.t('Won')}
+					</th>
+					<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-5">
+						{$i18n.t('Lost')}
+					</th>
+				</tr>
+			</thead>
+			<tbody class="">
+				{#each rankedModels as model, modelIdx (model.id)}
+					<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs group">
+						<td class="px-3 py-1.5 text-left font-medium text-gray-900 dark:text-white w-fit">
+							<div class=" line-clamp-1">
+								{model?.rating !== '-' ? modelIdx + 1 : '-'}
+							</div>
+						</td>
+						<td class="px-3 py-1.5 flex flex-col justify-center">
+							<div class="flex items-center gap-2">
+								<div class="flex-shrink-0">
+									<img
+										src={model?.info?.meta?.profile_image_url ?? '/favicon.png'}
+										alt={model.name}
+										class="size-5 rounded-full object-cover shrink-0"
+									/>
+								</div>
+
+								<div class="font-medium text-gray-800 dark:text-gray-200 pr-4">
+									{model.name}
+								</div>
+							</div>
+						</td>
+						<td class="px-3 py-1.5 text-right font-medium text-gray-900 dark:text-white w-max">
+							{model.rating}
+						</td>
+
+						<td class=" px-3 py-1.5 text-right font-semibold text-green-500">
+							<div class=" w-10">
+								{#if model.stats.won === '-'}
+									-
+								{:else}
+									<span class="hidden group-hover:inline"
+										>{((model.stats.won / model.stats.count) * 100).toFixed(1)}%</span
+									>
+									<span class=" group-hover:hidden">{model.stats.won}</span>
+								{/if}
+							</div>
+						</td>
+
+						<td class="px-3 py-1.5 text-right font-semibold text-red-500">
+							<div class=" w-10">
+								{#if model.stats.lost === '-'}
+									-
+								{:else}
+									<span class="hidden group-hover:inline"
+										>{((model.stats.lost / model.stats.count) * 100).toFixed(1)}%</span
+									>
+									<span class=" group-hover:hidden">{model.stats.lost}</span>
+								{/if}
+							</div>
+						</td>
+					</tr>
+				{/each}
+			</tbody>
+		</table>
+	{/if}
+</div>
+
+<div class=" text-gray-500 text-xs mt-1.5 w-full flex justify-end">
+	<div class=" text-right">
+		<div class="line-clamp-1">
+			ⓘ {$i18n.t(
+				'The evaluation leaderboard is based on the Elo rating system and is updated in real-time.'
+			)}
+		</div>
+		{$i18n.t(
+			'The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.'
+		)}
+	</div>
+</div>
diff --git a/src/lib/components/admin/Functions.svelte b/src/lib/components/admin/Functions.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..03da04ea781ddd9082215fe78e33baf6d899d2c5
--- /dev/null
+++ b/src/lib/components/admin/Functions.svelte
@@ -0,0 +1,542 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { WEBUI_NAME, config, functions, models } from '$lib/stores';
+	import { onMount, getContext, tick } from 'svelte';
+
+	import { goto } from '$app/navigation';
+	import {
+		createNewFunction,
+		deleteFunctionById,
+		exportFunctions,
+		getFunctionById,
+		getFunctions,
+		toggleFunctionById,
+		toggleGlobalById
+	} from '$lib/apis/functions';
+
+	import ArrowDownTray from '../icons/ArrowDownTray.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import ConfirmDialog from '../common/ConfirmDialog.svelte';
+	import { getModels } from '$lib/apis';
+	import FunctionMenu from './Functions/FunctionMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import Switch from '../common/Switch.svelte';
+	import ValvesModal from '../workspace/common/ValvesModal.svelte';
+	import ManifestModal from '../workspace/common/ManifestModal.svelte';
+	import Heart from '../icons/Heart.svelte';
+	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import GarbageBin from '../icons/GarbageBin.svelte';
+	import Search from '../icons/Search.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+
+	const i18n = getContext('i18n');
+
+	let shiftKey = false;
+
+	let functionsImportInputElement: HTMLInputElement;
+	let importFiles;
+
+	let showConfirm = false;
+	let query = '';
+
+	let showManifestModal = false;
+	let showValvesModal = false;
+	let selectedFunction = null;
+
+	let showDeleteConfirm = false;
+
+	let filteredItems = [];
+	$: filteredItems = $functions
+		.filter(
+			(f) =>
+				query === '' ||
+				f.name.toLowerCase().includes(query.toLowerCase()) ||
+				f.id.toLowerCase().includes(query.toLowerCase())
+		)
+		.sort((a, b) => a.type.localeCompare(b.type) || a.name.localeCompare(b.name));
+
+	const shareHandler = async (func) => {
+		const item = await getFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+
+		const url = 'https://openwebui.com';
+
+		const tab = await window.open(`${url}/functions/create`, '_blank');
+
+		// Define the event handler function
+		const messageHandler = (event) => {
+			if (event.origin !== url) return;
+			if (event.data === 'loaded') {
+				tab.postMessage(JSON.stringify(item), '*');
+
+				// Remove the event listener after handling the message
+				window.removeEventListener('message', messageHandler);
+			}
+		};
+
+		window.addEventListener('message', messageHandler, false);
+		console.log(item);
+	};
+
+	const cloneHandler = async (func) => {
+		const _function = await getFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_function) {
+			sessionStorage.function = JSON.stringify({
+				..._function,
+				id: `${_function.id}_clone`,
+				name: `${_function.name} (Clone)`
+			});
+			goto('/admin/functions/create');
+		}
+	};
+
+	const exportHandler = async (func) => {
+		const _function = await getFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_function) {
+			let blob = new Blob([JSON.stringify([_function])], {
+				type: 'application/json'
+			});
+			saveAs(blob, `function-${_function.id}-export-${Date.now()}.json`);
+		}
+	};
+
+	const deleteHandler = async (func) => {
+		const res = await deleteFunctionById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Function deleted successfully'));
+
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+		}
+	};
+
+	const toggleGlobalHandler = async (func) => {
+		const res = await toggleGlobalById(localStorage.token, func.id).catch((error) => {
+			toast.error(error);
+		});
+
+		if (res) {
+			if (func.is_global) {
+				func.type === 'filter'
+					? toast.success($i18n.t('Filter is now globally enabled'))
+					: toast.success($i18n.t('Function is now globally enabled'));
+			} else {
+				func.type === 'filter'
+					? toast.success($i18n.t('Filter is now globally disabled'))
+					: toast.success($i18n.t('Function is now globally disabled'));
+			}
+
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+		}
+	};
+
+	onMount(() => {
+		const onKeyDown = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = true;
+			}
+		};
+
+		const onKeyUp = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = false;
+			}
+		};
+
+		const onBlur = () => {
+			shiftKey = false;
+		};
+
+		window.addEventListener('keydown', onKeyDown);
+		window.addEventListener('keyup', onKeyUp);
+		window.addEventListener('blur', onBlur);
+
+		return () => {
+			window.removeEventListener('keydown', onKeyDown);
+			window.removeEventListener('keyup', onKeyUp);
+			window.removeEventListener('blur', onBlur);
+		};
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Functions')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+<div class="flex flex-col gap-1 mt-1.5 mb-2">
+	<div class="flex justify-between items-center">
+		<div class="flex md:self-center text-xl items-center font-medium px-0.5">
+			{$i18n.t('Functions')}
+			<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+			<span class="text-base font-lg text-gray-500 dark:text-gray-300">{filteredItems.length}</span>
+		</div>
+	</div>
+
+	<div class=" flex w-full space-x-2">
+		<div class="flex flex-1">
+			<div class=" self-center ml-1 mr-3">
+				<Search className="size-3.5" />
+			</div>
+			<input
+				class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+				bind:value={query}
+				placeholder={$i18n.t('Search Functions')}
+			/>
+		</div>
+
+		<div>
+			<a
+				class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+				href="/admin/functions/create"
+			>
+				<Plus className="size-3.5" />
+			</a>
+		</div>
+	</div>
+</div>
+
+<div class="mb-5">
+	{#each filteredItems as func (func.id)}
+		<div
+			class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
+		>
+			<a
+				class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
+				href={`/admin/functions/edit?id=${encodeURIComponent(func.id)}`}
+			>
+				<div class="flex items-center text-left">
+					<div class=" flex-1 self-center pl-1">
+						<div class=" font-semibold flex items-center gap-1.5">
+							<div
+								class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+							>
+								{func.type}
+							</div>
+
+							{#if func?.meta?.manifest?.version}
+								<div
+									class="text-xs font-bold px-1 rounded line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+								>
+									v{func?.meta?.manifest?.version ?? ''}
+								</div>
+							{/if}
+
+							<div class=" line-clamp-1">
+								{func.name}
+							</div>
+						</div>
+
+						<div class="flex gap-1.5 px-1">
+							<div class=" text-gray-500 text-xs font-medium flex-shrink-0">{func.id}</div>
+
+							<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
+								{func.meta.description}
+							</div>
+						</div>
+					</div>
+				</div>
+			</a>
+			<div class="flex flex-row gap-0.5 self-center">
+				{#if shiftKey}
+					<Tooltip content={$i18n.t('Delete')}>
+						<button
+							class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+							on:click={() => {
+								deleteHandler(func);
+							}}
+						>
+							<GarbageBin />
+						</button>
+					</Tooltip>
+				{:else}
+					{#if func?.meta?.manifest?.funding_url ?? false}
+						<Tooltip content={$i18n.t('Support')}>
+							<button
+								class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								type="button"
+								on:click={() => {
+									selectedFunction = func;
+									showManifestModal = true;
+								}}
+							>
+								<Heart />
+							</button>
+						</Tooltip>
+					{/if}
+
+					<Tooltip content={$i18n.t('Valves')}>
+						<button
+							class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+							on:click={() => {
+								selectedFunction = func;
+								showValvesModal = true;
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								fill="none"
+								viewBox="0 0 24 24"
+								stroke-width="1.5"
+								stroke="currentColor"
+								class="size-4"
+							>
+								<path
+									stroke-linecap="round"
+									stroke-linejoin="round"
+									d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+								/>
+								<path
+									stroke-linecap="round"
+									stroke-linejoin="round"
+									d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
+								/>
+							</svg>
+						</button>
+					</Tooltip>
+
+					<FunctionMenu
+						{func}
+						editHandler={() => {
+							goto(`/admin/functions/edit?id=${encodeURIComponent(func.id)}`);
+						}}
+						shareHandler={() => {
+							shareHandler(func);
+						}}
+						cloneHandler={() => {
+							cloneHandler(func);
+						}}
+						exportHandler={() => {
+							exportHandler(func);
+						}}
+						deleteHandler={async () => {
+							selectedFunction = func;
+							showDeleteConfirm = true;
+						}}
+						toggleGlobalHandler={() => {
+							if (['filter', 'action'].includes(func.type)) {
+								toggleGlobalHandler(func);
+							}
+						}}
+						onClose={() => {}}
+					>
+						<button
+							class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+						>
+							<EllipsisHorizontal className="size-5" />
+						</button>
+					</FunctionMenu>
+				{/if}
+
+				<div class=" self-center mx-1">
+					<Tooltip content={func.is_active ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
+						<Switch
+							bind:state={func.is_active}
+							on:change={async (e) => {
+								toggleFunctionById(localStorage.token, func.id);
+								models.set(await getModels(localStorage.token));
+							}}
+						/>
+					</Tooltip>
+				</div>
+			</div>
+		</div>
+	{/each}
+</div>
+
+<!-- <div class=" text-gray-500 text-xs mt-1 mb-2">
+	ⓘ {$i18n.t(
+		'Admins have access to all tools at all times; users need tools assigned per model in the workspace.'
+	)}
+</div> -->
+
+<div class=" flex justify-end w-full mb-2">
+	<div class="flex space-x-2">
+		<input
+			id="documents-import-input"
+			bind:this={functionsImportInputElement}
+			bind:files={importFiles}
+			type="file"
+			accept=".json"
+			hidden
+			on:change={() => {
+				console.log(importFiles);
+				showConfirm = true;
+			}}
+		/>
+
+		<button
+			class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+			on:click={() => {
+				functionsImportInputElement.click();
+			}}
+		>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Functions')}</div>
+
+			<div class=" self-center">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+		</button>
+
+		<button
+			class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+			on:click={async () => {
+				const _functions = await exportFunctions(localStorage.token).catch((error) => {
+					toast.error(error);
+					return null;
+				});
+
+				if (_functions) {
+					let blob = new Blob([JSON.stringify(_functions)], {
+						type: 'application/json'
+					});
+					saveAs(blob, `functions-export-${Date.now()}.json`);
+				}
+			}}
+		>
+			<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Functions')}</div>
+
+			<div class=" self-center">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+		</button>
+	</div>
+</div>
+
+{#if $config?.features.enable_community_sharing}
+	<div class=" my-16">
+		<div class=" text-xl font-medium mb-1 line-clamp-1">
+			{$i18n.t('Made by OpenWebUI Community')}
+		</div>
+
+		<a
+			class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
+			href="https://openwebui.com/#open-webui-community"
+			target="_blank"
+		>
+			<div class=" self-center">
+				<div class=" font-semibold line-clamp-1">{$i18n.t('Discover a function')}</div>
+				<div class=" text-sm line-clamp-1">
+					{$i18n.t('Discover, download, and explore custom functions')}
+				</div>
+			</div>
+
+			<div>
+				<div>
+					<ChevronRight />
+				</div>
+			</div>
+		</a>
+	</div>
+{/if}
+
+<DeleteConfirmDialog
+	bind:show={showDeleteConfirm}
+	title={$i18n.t('Delete function?')}
+	on:confirm={() => {
+		deleteHandler(selectedFunction);
+	}}
+>
+	<div class=" text-sm text-gray-500">
+		{$i18n.t('This will delete')} <span class="  font-semibold">{selectedFunction.name}</span>.
+	</div>
+</DeleteConfirmDialog>
+
+<ManifestModal bind:show={showManifestModal} manifest={selectedFunction?.meta?.manifest ?? {}} />
+<ValvesModal
+	bind:show={showValvesModal}
+	type="function"
+	id={selectedFunction?.id ?? null}
+	on:save={async () => {
+		await tick();
+		models.set(await getModels(localStorage.token));
+	}}
+/>
+
+<ConfirmDialog
+	bind:show={showConfirm}
+	on:confirm={() => {
+		const reader = new FileReader();
+		reader.onload = async (event) => {
+			const _functions = JSON.parse(event.target.result);
+			console.log(_functions);
+
+			for (const func of _functions) {
+				const res = await createNewFunction(localStorage.token, func).catch((error) => {
+					toast.error(error);
+					return null;
+				});
+			}
+
+			toast.success($i18n.t('Functions imported successfully'));
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+		};
+
+		reader.readAsText(importFiles[0]);
+	}}
+>
+	<div class="text-sm text-gray-500">
+		<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
+			<div>Please carefully review the following warnings:</div>
+
+			<ul class=" mt-1 list-disc pl-4 text-xs">
+				<li>{$i18n.t('Functions allow arbitrary code execution.')}</li>
+				<li>{$i18n.t('Do not install functions from sources you do not fully trust.')}</li>
+			</ul>
+		</div>
+
+		<div class="my-3">
+			{$i18n.t(
+				'I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.'
+			)}
+		</div>
+	</div>
+</ConfirmDialog>
diff --git a/src/lib/components/admin/Functions/FunctionEditor.svelte b/src/lib/components/admin/Functions/FunctionEditor.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..187110be094e88477c039c384fa35cadfd17f664
--- /dev/null
+++ b/src/lib/components/admin/Functions/FunctionEditor.svelte
@@ -0,0 +1,430 @@
+<script>
+	import { getContext, createEventDispatcher, onMount, tick } from 'svelte';
+	import { goto } from '$app/navigation';
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
+	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
+
+	let formElement = null;
+	let loading = false;
+	let showConfirm = false;
+
+	export let edit = false;
+	export let clone = false;
+
+	export let id = '';
+	export let name = '';
+	export let meta = {
+		description: ''
+	};
+	export let content = '';
+	let _content = '';
+
+	$: if (content) {
+		updateContent();
+	}
+
+	const updateContent = () => {
+		_content = content;
+	};
+
+	$: if (name && !edit && !clone) {
+		id = name.replace(/\s+/g, '_').toLowerCase();
+	}
+
+	let codeEditor;
+	let boilerplate = `"""
+title: Example Filter
+author: open-webui
+author_url: https://github.com/open-webui
+funding_url: https://github.com/open-webui
+version: 0.1
+"""
+
+from pydantic import BaseModel, Field
+from typing import Optional
+
+
+class Filter:
+    class Valves(BaseModel):
+        priority: int = Field(
+            default=0, description="Priority level for the filter operations."
+        )
+        max_turns: int = Field(
+            default=8, description="Maximum allowable conversation turns for a user."
+        )
+        pass
+
+    class UserValves(BaseModel):
+        max_turns: int = Field(
+            default=4, description="Maximum allowable conversation turns for a user."
+        )
+        pass
+
+    def __init__(self):
+        # Indicates custom file handling logic. This flag helps disengage default routines in favor of custom
+        # implementations, informing the WebUI to defer file-related operations to designated methods within this class.
+        # Alternatively, you can remove the files directly from the body in from the inlet hook
+        # self.file_handler = True
+
+        # Initialize 'valves' with specific configurations. Using 'Valves' instance helps encapsulate settings,
+        # which ensures settings are managed cohesively and not confused with operational flags like 'file_handler'.
+        self.valves = self.Valves()
+        pass
+
+    def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
+        # Modify the request body or validate it before processing by the chat completion API.
+        # This function is the pre-processor for the API where various checks on the input can be performed.
+        # It can also modify the request before sending it to the API.
+        print(f"inlet:{__name__}")
+        print(f"inlet:body:{body}")
+        print(f"inlet:user:{__user__}")
+
+        if __user__.get("role", "admin") in ["user", "admin"]:
+            messages = body.get("messages", [])
+
+            max_turns = min(__user__["valves"].max_turns, self.valves.max_turns)
+            if len(messages) > max_turns:
+                raise Exception(
+                    f"Conversation turn limit exceeded. Max turns: {max_turns}"
+                )
+
+        return body
+
+    def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
+        # Modify or analyze the response body after processing by the API.
+        # This function is the post-processor for the API, which can be used to modify the response
+        # or perform additional checks and analytics.
+        print(f"outlet:{__name__}")
+        print(f"outlet:body:{body}")
+        print(f"outlet:user:{__user__}")
+
+        return body
+`;
+
+	const _boilerplate = `from pydantic import BaseModel
+from typing import Optional, Union, Generator, Iterator
+from open_webui.utils.misc import get_last_user_message
+
+import os
+import requests
+
+
+# Filter Class: This class is designed to serve as a pre-processor and post-processor
+# for request and response modifications. It checks and transforms requests and responses
+# to ensure they meet specific criteria before further processing or returning to the user.
+class Filter:
+    class Valves(BaseModel):
+        max_turns: int = 4
+        pass
+
+    def __init__(self):
+        # Indicates custom file handling logic. This flag helps disengage default routines in favor of custom
+        # implementations, informing the WebUI to defer file-related operations to designated methods within this class.
+        # Alternatively, you can remove the files directly from the body in from the inlet hook
+        self.file_handler = True
+
+        # Initialize 'valves' with specific configurations. Using 'Valves' instance helps encapsulate settings,
+        # which ensures settings are managed cohesively and not confused with operational flags like 'file_handler'.
+        self.valves = self.Valves(**{"max_turns": 2})
+        pass
+
+    def inlet(self, body: dict, user: Optional[dict] = None) -> dict:
+        # Modify the request body or validate it before processing by the chat completion API.
+        # This function is the pre-processor for the API where various checks on the input can be performed.
+        # It can also modify the request before sending it to the API.
+        print(f"inlet:{__name__}")
+        print(f"inlet:body:{body}")
+        print(f"inlet:user:{user}")
+
+        if user.get("role", "admin") in ["user", "admin"]:
+            messages = body.get("messages", [])
+            if len(messages) > self.valves.max_turns:
+                raise Exception(
+                    f"Conversation turn limit exceeded. Max turns: {self.valves.max_turns}"
+                )
+
+        return body
+
+    def outlet(self, body: dict, user: Optional[dict] = None) -> dict:
+        # Modify or analyze the response body after processing by the API.
+        # This function is the post-processor for the API, which can be used to modify the response
+        # or perform additional checks and analytics.
+        print(f"outlet:{__name__}")
+        print(f"outlet:body:{body}")
+        print(f"outlet:user:{user}")
+
+        messages = [
+            {
+                **message,
+                "content": f"{message['content']} - @@Modified from Filter Outlet",
+            }
+            for message in body.get("messages", [])
+        ]
+
+        return {"messages": messages}
+
+
+
+# Pipe Class: This class functions as a customizable pipeline.
+# It can be adapted to work with any external or internal models,
+# making it versatile for various use cases outside of just OpenAI models.
+class Pipe:
+    class Valves(BaseModel):
+        OPENAI_API_BASE_URL: str = "https://api.openai.com/v1"
+        OPENAI_API_KEY: str = "your-key"
+        pass
+
+    def __init__(self):
+        self.type = "manifold"
+        self.valves = self.Valves()
+        self.pipes = self.get_openai_models()
+        pass
+
+    def get_openai_models(self):
+        if self.valves.OPENAI_API_KEY:
+            try:
+                headers = {}
+                headers["Authorization"] = f"Bearer {self.valves.OPENAI_API_KEY}"
+                headers["Content-Type"] = "application/json"
+
+                r = requests.get(
+                    f"{self.valves.OPENAI_API_BASE_URL}/models", headers=headers
+                )
+
+                models = r.json()
+                return [
+                    {
+                        "id": model["id"],
+                        "name": model["name"] if "name" in model else model["id"],
+                    }
+                    for model in models["data"]
+                    if "gpt" in model["id"]
+                ]
+
+            except Exception as e:
+
+                print(f"Error: {e}")
+                return [
+                    {
+                        "id": "error",
+                        "name": "Could not fetch models from OpenAI, please update the API Key in the valves.",
+                    },
+                ]
+        else:
+            return []
+
+    def pipe(self, body: dict) -> Union[str, Generator, Iterator]:
+        # This is where you can add your custom pipelines like RAG.
+        print(f"pipe:{__name__}")
+
+        if "user" in body:
+            print(body["user"])
+            del body["user"]
+
+        headers = {}
+        headers["Authorization"] = f"Bearer {self.valves.OPENAI_API_KEY}"
+        headers["Content-Type"] = "application/json"
+
+        model_id = body["model"][body["model"].find(".") + 1 :]
+        payload = {**body, "model": model_id}
+        print(payload)
+
+        try:
+            r = requests.post(
+                url=f"{self.valves.OPENAI_API_BASE_URL}/chat/completions",
+                json=payload,
+                headers=headers,
+                stream=True,
+            )
+
+            r.raise_for_status()
+
+            if body["stream"]:
+                return r.iter_lines()
+            else:
+                return r.json()
+        except Exception as e:
+            return f"Error: {e}"
+`;
+
+	const saveHandler = async () => {
+		loading = true;
+		dispatch('save', {
+			id,
+			name,
+			meta,
+			content
+		});
+	};
+
+	const submitHandler = async () => {
+		if (codeEditor) {
+			content = _content;
+			await tick();
+
+			const res = await codeEditor.formatPythonCodeHandler();
+			await tick();
+
+			content = _content;
+			await tick();
+
+			if (res) {
+				console.log('Code formatted successfully');
+
+				saveHandler();
+			}
+		}
+	};
+</script>
+
+<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
+	<div class="mx-auto w-full md:px-0 h-full">
+		<form
+			bind:this={formElement}
+			class=" flex flex-col max-h-[100dvh] h-full"
+			on:submit|preventDefault={() => {
+				if (edit) {
+					submitHandler();
+				} else {
+					showConfirm = true;
+				}
+			}}
+		>
+			<div class="flex flex-col flex-1 overflow-auto h-0 rounded-lg">
+				<div class="w-full mb-2 flex flex-col gap-0.5">
+					<div class="flex w-full items-center">
+						<div class=" flex-shrink-0 mr-2">
+							<Tooltip content={$i18n.t('Back')}>
+								<button
+									class="w-full text-left text-sm py-1.5 px-1 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
+									on:click={() => {
+										goto('/admin/functions');
+									}}
+									type="button"
+								>
+									<ChevronLeft strokeWidth="2.5" />
+								</button>
+							</Tooltip>
+						</div>
+
+						<div class="flex-1">
+							<Tooltip content={$i18n.t('e.g. My Filter')} placement="top-start">
+								<input
+									class="w-full text-2xl font-medium bg-transparent outline-none font-primary"
+									type="text"
+									placeholder={$i18n.t('Function Name')}
+									bind:value={name}
+									required
+								/>
+							</Tooltip>
+						</div>
+
+						<div>
+							<Badge type="muted" content={$i18n.t('Function')} />
+						</div>
+					</div>
+
+					<div class=" flex gap-2 px-1 items-center">
+						{#if edit}
+							<div class="text-sm text-gray-500 flex-shrink-0">
+								{id}
+							</div>
+						{:else}
+							<Tooltip className="w-full" content={$i18n.t('e.g. my_filter')} placement="top-start">
+								<input
+									class="w-full text-sm disabled:text-gray-500 bg-transparent outline-none"
+									type="text"
+									placeholder={$i18n.t('Function ID')}
+									bind:value={id}
+									required
+									disabled={edit}
+								/>
+							</Tooltip>
+						{/if}
+
+						<Tooltip
+							className="w-full self-center items-center flex"
+							content={$i18n.t('e.g. A filter to remove profanity from text')}
+							placement="top-start"
+						>
+							<input
+								class="w-full text-sm bg-transparent outline-none"
+								type="text"
+								placeholder={$i18n.t('Function Description')}
+								bind:value={meta.description}
+								required
+							/>
+						</Tooltip>
+					</div>
+				</div>
+
+				<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
+					<CodeEditor
+						bind:this={codeEditor}
+						value={content}
+						lang="python"
+						{boilerplate}
+						on:change={(e) => {
+							_content = e.detail.value;
+						}}
+						on:save={async () => {
+							if (formElement) {
+								formElement.requestSubmit();
+							}
+						}}
+					/>
+				</div>
+
+				<div class="pb-3 flex justify-between">
+					<div class="flex-1 pr-3">
+						<div class="text-xs text-gray-500 line-clamp-2">
+							<span class=" font-semibold dark:text-gray-200">{$i18n.t('Warning:')}</span>
+							{$i18n.t('Functions allow arbitrary code execution')} <br />—
+							<span class=" font-medium dark:text-gray-400"
+								>{$i18n.t(`don't install random functions from sources you don't trust.`)}</span
+							>
+						</div>
+					</div>
+
+					<button
+						class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+						type="submit"
+					>
+						{$i18n.t('Save')}
+					</button>
+				</div>
+			</div>
+		</form>
+	</div>
+</div>
+
+<ConfirmDialog
+	bind:show={showConfirm}
+	on:confirm={() => {
+		submitHandler();
+	}}
+>
+	<div class="text-sm text-gray-500">
+		<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
+			<div>{$i18n.t('Please carefully review the following warnings:')}</div>
+
+			<ul class=" mt-1 list-disc pl-4 text-xs">
+				<li>{$i18n.t('Functions allow arbitrary code execution.')}</li>
+				<li>{$i18n.t('Do not install functions from sources you do not fully trust.')}</li>
+			</ul>
+		</div>
+
+		<div class="my-3">
+			{$i18n.t(
+				'I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.'
+			)}
+		</div>
+	</div>
+</ConfirmDialog>
diff --git a/src/lib/components/admin/Functions/FunctionMenu.svelte b/src/lib/components/admin/Functions/FunctionMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1a67a1fe9adc42558a98da03f98c8a4950d5e388
--- /dev/null
+++ b/src/lib/components/admin/Functions/FunctionMenu.svelte
@@ -0,0 +1,138 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import GlobeAlt from '$lib/components/icons/GlobeAlt.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let func;
+
+	export let editHandler: Function;
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let toggleGlobalHandler: Function;
+
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[180px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			{#if ['filter', 'action'].includes(func.type)}
+				<div
+					class="flex gap-2 justify-between items-center px-3 py-2 text-sm font-medium cursor-pointerrounded-md"
+				>
+					<div class="flex gap-2 items-center">
+						<GlobeAlt />
+
+						<div class="flex items-center">{$i18n.t('Global')}</div>
+					</div>
+
+					<div>
+						<Switch on:change={toggleGlobalHandler} bind:state={func.is_global} />
+					</div>
+				</div>
+
+				<hr class="border-gray-100 dark:border-gray-800 my-1" />
+			{/if}
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					editHandler();
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					fill="none"
+					viewBox="0 0 24 24"
+					stroke-width="1.5"
+					stroke="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+					/>
+				</svg>
+
+				<div class="flex items-center">{$i18n.t('Edit')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/admin/Settings.svelte b/src/lib/components/admin/Settings.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f0886ea5c0a8906099dd6301db4313229735dae0
--- /dev/null
+++ b/src/lib/components/admin/Settings.svelte
@@ -0,0 +1,399 @@
+<script>
+	import { getContext, tick, onMount } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	import { config } from '$lib/stores';
+	import { getBackendConfig } from '$lib/apis';
+	import Database from './Settings/Database.svelte';
+
+	import General from './Settings/General.svelte';
+	import Pipelines from './Settings/Pipelines.svelte';
+	import Audio from './Settings/Audio.svelte';
+	import Images from './Settings/Images.svelte';
+	import Interface from './Settings/Interface.svelte';
+	import Models from './Settings/Models.svelte';
+	import Connections from './Settings/Connections.svelte';
+	import Documents from './Settings/Documents.svelte';
+	import WebSearch from './Settings/WebSearch.svelte';
+
+	import ChartBar from '../icons/ChartBar.svelte';
+	import DocumentChartBar from '../icons/DocumentChartBar.svelte';
+	import Evaluations from './Settings/Evaluations.svelte';
+
+	const i18n = getContext('i18n');
+
+	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>
+
+<div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
+	<div
+		id="admin-settings-tabs-container"
+		class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
+	>
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
+			'general'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'general';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('General')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'connections'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'connections';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Connections')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'models'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'models';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M10 1c3.866 0 7 1.79 7 4s-3.134 4-7 4-7-1.79-7-4 3.134-4 7-4zm5.694 8.13c.464-.264.91-.583 1.306-.952V10c0 2.21-3.134 4-7 4s-7-1.79-7-4V8.178c.396.37.842.688 1.306.953C5.838 10.006 7.854 10.5 10 10.5s4.162-.494 5.694-1.37zM3 13.179V15c0 2.21 3.134 4 7 4s7-1.79 7-4v-1.822c-.396.37-.842.688-1.306.953-1.532.875-3.548 1.369-5.694 1.369s-4.162-.494-5.694-1.37A7.009 7.009 0 013 13.179z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Models')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'evaluations'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'evaluations';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<DocumentChartBar />
+			</div>
+			<div class=" self-center">{$i18n.t('Evaluations')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'documents'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'documents';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 24 24"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z" />
+					<path
+						fill-rule="evenodd"
+						d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z"
+						clip-rule="evenodd"
+					/>
+					<path
+						d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Documents')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'web'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'web';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 24 24"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Web Search')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'interface'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'interface';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M2 4.25A2.25 2.25 0 0 1 4.25 2h7.5A2.25 2.25 0 0 1 14 4.25v5.5A2.25 2.25 0 0 1 11.75 12h-1.312c.1.128.21.248.328.36a.75.75 0 0 1 .234.545v.345a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-.345a.75.75 0 0 1 .234-.545c.118-.111.228-.232.328-.36H4.25A2.25 2.25 0 0 1 2 9.75v-5.5Zm2.25-.75a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h7.5a.75.75 0 0 0 .75-.75v-4.5a.75.75 0 0 0-.75-.75h-7.5Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Interface')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'audio'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'audio';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						d="M7.557 2.066A.75.75 0 0 1 8 2.75v10.5a.75.75 0 0 1-1.248.56L3.59 11H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.59l3.162-2.81a.75.75 0 0 1 .805-.124ZM12.95 3.05a.75.75 0 1 0-1.06 1.06 5.5 5.5 0 0 1 0 7.78.75.75 0 1 0 1.06 1.06 7 7 0 0 0 0-9.9Z"
+					/>
+					<path
+						d="M10.828 5.172a.75.75 0 1 0-1.06 1.06 2.5 2.5 0 0 1 0 3.536.75.75 0 1 0 1.06 1.06 4 4 0 0 0 0-5.656Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Audio')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'images'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'images';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Images')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'pipelines'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'pipelines';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 24 24"
+					fill="currentColor"
+					class="size-4"
+				>
+					<path
+						d="M11.644 1.59a.75.75 0 0 1 .712 0l9.75 5.25a.75.75 0 0 1 0 1.32l-9.75 5.25a.75.75 0 0 1-.712 0l-9.75-5.25a.75.75 0 0 1 0-1.32l9.75-5.25Z"
+					/>
+					<path
+						d="m3.265 10.602 7.668 4.129a2.25 2.25 0 0 0 2.134 0l7.668-4.13 1.37.739a.75.75 0 0 1 0 1.32l-9.75 5.25a.75.75 0 0 1-.71 0l-9.75-5.25a.75.75 0 0 1 0-1.32l1.37-.738Z"
+					/>
+					<path
+						d="m10.933 19.231-7.668-4.13-1.37.739a.75.75 0 0 0 0 1.32l9.75 5.25c.221.12.489.12.71 0l9.75-5.25a.75.75 0 0 0 0-1.32l-1.37-.738-7.668 4.13a2.25 2.25 0 0 1-2.134-.001Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Pipelines')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
+			'db'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'db';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path d="M8 7c3.314 0 6-1.343 6-3s-2.686-3-6-3-6 1.343-6 3 2.686 3 6 3Z" />
+					<path
+						d="M8 8.5c1.84 0 3.579-.37 4.914-1.037A6.33 6.33 0 0 0 14 6.78V8c0 1.657-2.686 3-6 3S2 9.657 2 8V6.78c.346.273.72.5 1.087.683C4.42 8.131 6.16 8.5 8 8.5Z"
+					/>
+					<path
+						d="M8 12.5c1.84 0 3.579-.37 4.914-1.037.366-.183.74-.41 1.086-.684V12c0 1.657-2.686 3-6 3s-6-1.343-6-3v-1.22c.346.273.72.5 1.087.683C4.42 12.131 6.16 12.5 8 12.5Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Database')}</div>
+		</button>
+	</div>
+
+	<div class="flex-1 mt-3 lg:mt-0 overflow-y-scroll pr-1 scrollbar-hidden">
+		{#if selectedTab === 'general'}
+			<General
+				saveHandler={async () => {
+					toast.success($i18n.t('Settings saved successfully!'));
+
+					await tick();
+					await config.set(await getBackendConfig());
+				}}
+			/>
+		{:else if selectedTab === 'connections'}
+			<Connections
+				on:save={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{:else if selectedTab === 'models'}
+			<Models />
+		{:else if selectedTab === 'evaluations'}
+			<Evaluations />
+		{:else if selectedTab === 'documents'}
+			<Documents
+				on:save={async () => {
+					toast.success($i18n.t('Settings saved successfully!'));
+
+					await tick();
+					await config.set(await getBackendConfig());
+				}}
+			/>
+		{:else if selectedTab === 'web'}
+			<WebSearch
+				saveHandler={async () => {
+					toast.success($i18n.t('Settings saved successfully!'));
+
+					await tick();
+					await config.set(await getBackendConfig());
+				}}
+			/>
+		{:else if selectedTab === 'interface'}
+			<Interface
+				on:save={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{:else if selectedTab === 'audio'}
+			<Audio
+				saveHandler={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{:else if selectedTab === 'images'}
+			<Images
+				on:save={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{:else if selectedTab === 'db'}
+			<Database
+				saveHandler={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{:else if selectedTab === 'pipelines'}
+			<Pipelines
+				saveHandler={() => {
+					toast.success($i18n.t('Settings saved successfully!'));
+				}}
+			/>
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/admin/Settings/Audio.svelte b/src/lib/components/admin/Settings/Audio.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a7a0300276ab05e9aa08d4ab804ac971cac6c681
--- /dev/null
+++ b/src/lib/components/admin/Settings/Audio.svelte
@@ -0,0 +1,602 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import { getBackendConfig } from '$lib/apis';
+	import {
+		getAudioConfig,
+		updateAudioConfig,
+		getModels as _getModels,
+		getVoices as _getVoices
+	} from '$lib/apis/audio';
+	import { config } from '$lib/stores';
+
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+
+	import { TTS_RESPONSE_SPLIT } from '$lib/types';
+
+	import type { Writable } from 'svelte/store';
+	import type { i18n as i18nType } from 'i18next';
+
+	const i18n = getContext<Writable<i18nType>>('i18n');
+
+	export let saveHandler: () => void;
+
+	// Audio
+	let TTS_OPENAI_API_BASE_URL = '';
+	let TTS_OPENAI_API_KEY = '';
+	let TTS_API_KEY = '';
+	let TTS_ENGINE = '';
+	let TTS_MODEL = '';
+	let TTS_VOICE = '';
+	let TTS_SPLIT_ON: TTS_RESPONSE_SPLIT = TTS_RESPONSE_SPLIT.PUNCTUATION;
+	let TTS_AZURE_SPEECH_REGION = '';
+	let TTS_AZURE_SPEECH_OUTPUT_FORMAT = '';
+
+	let STT_OPENAI_API_BASE_URL = '';
+	let STT_OPENAI_API_KEY = '';
+	let STT_ENGINE = '';
+	let STT_MODEL = '';
+	let STT_WHISPER_MODEL = '';
+
+	let STT_WHISPER_MODEL_LOADING = false;
+
+	// eslint-disable-next-line no-undef
+	let voices: SpeechSynthesisVoice[] = [];
+	let models: Awaited<ReturnType<typeof _getModels>>['models'] = [];
+
+	const getModels = async () => {
+		if (TTS_ENGINE === '') {
+			models = [];
+		} else {
+			const res = await _getModels(localStorage.token).catch((e) => {
+				toast.error(e);
+			});
+
+			if (res) {
+				console.log(res);
+				models = res.models;
+			}
+		}
+	};
+
+	const getVoices = async () => {
+		if (TTS_ENGINE === '') {
+			const getVoicesLoop = setInterval(() => {
+				voices = speechSynthesis.getVoices();
+
+				// do your loop
+				if (voices.length > 0) {
+					clearInterval(getVoicesLoop);
+					voices.sort((a, b) => a.name.localeCompare(b.name, $i18n.resolvedLanguage));
+				}
+			}, 100);
+		} else {
+			const res = await _getVoices(localStorage.token).catch((e) => {
+				toast.error(e);
+			});
+
+			if (res) {
+				console.log(res);
+				voices = res.voices;
+				voices.sort((a, b) => a.name.localeCompare(b.name, $i18n.resolvedLanguage));
+			}
+		}
+	};
+
+	const updateConfigHandler = async () => {
+		const res = await updateAudioConfig(localStorage.token, {
+			tts: {
+				OPENAI_API_BASE_URL: TTS_OPENAI_API_BASE_URL,
+				OPENAI_API_KEY: TTS_OPENAI_API_KEY,
+				API_KEY: TTS_API_KEY,
+				ENGINE: TTS_ENGINE,
+				MODEL: TTS_MODEL,
+				VOICE: TTS_VOICE,
+				SPLIT_ON: TTS_SPLIT_ON,
+				AZURE_SPEECH_REGION: TTS_AZURE_SPEECH_REGION,
+				AZURE_SPEECH_OUTPUT_FORMAT: TTS_AZURE_SPEECH_OUTPUT_FORMAT
+			},
+			stt: {
+				OPENAI_API_BASE_URL: STT_OPENAI_API_BASE_URL,
+				OPENAI_API_KEY: STT_OPENAI_API_KEY,
+				ENGINE: STT_ENGINE,
+				MODEL: STT_MODEL,
+				WHISPER_MODEL: STT_WHISPER_MODEL
+			}
+		});
+
+		if (res) {
+			saveHandler();
+			config.set(await getBackendConfig());
+		}
+	};
+
+	const sttModelUpdateHandler = async () => {
+		STT_WHISPER_MODEL_LOADING = true;
+		await updateConfigHandler();
+		STT_WHISPER_MODEL_LOADING = false;
+	};
+
+	onMount(async () => {
+		const res = await getAudioConfig(localStorage.token);
+
+		if (res) {
+			console.log(res);
+			TTS_OPENAI_API_BASE_URL = res.tts.OPENAI_API_BASE_URL;
+			TTS_OPENAI_API_KEY = res.tts.OPENAI_API_KEY;
+			TTS_API_KEY = res.tts.API_KEY;
+
+			TTS_ENGINE = res.tts.ENGINE;
+			TTS_MODEL = res.tts.MODEL;
+			TTS_VOICE = res.tts.VOICE;
+
+			TTS_SPLIT_ON = res.tts.SPLIT_ON || TTS_RESPONSE_SPLIT.PUNCTUATION;
+
+			TTS_AZURE_SPEECH_OUTPUT_FORMAT = res.tts.AZURE_SPEECH_OUTPUT_FORMAT;
+			TTS_AZURE_SPEECH_REGION = res.tts.AZURE_SPEECH_REGION;
+
+			STT_OPENAI_API_BASE_URL = res.stt.OPENAI_API_BASE_URL;
+			STT_OPENAI_API_KEY = res.stt.OPENAI_API_KEY;
+
+			STT_ENGINE = res.stt.ENGINE;
+			STT_MODEL = res.stt.MODEL;
+			STT_WHISPER_MODEL = res.stt.WHISPER_MODEL;
+		}
+
+		await getVoices();
+		await getModels();
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		await updateConfigHandler();
+		dispatch('save');
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
+		<div class="flex flex-col gap-3">
+			<div>
+				<div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div>
+
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 cursor-pointer w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							bind:value={STT_ENGINE}
+							placeholder="Select an engine"
+						>
+							<option value="">{$i18n.t('Whisper (Local)')}</option>
+							<option value="openai">OpenAI</option>
+							<option value="web">{$i18n.t('Web API')}</option>
+						</select>
+					</div>
+				</div>
+
+				{#if STT_ENGINE === 'openai'}
+					<div>
+						<div class="mt-1 flex gap-2 mb-1">
+							<input
+								class="flex-1 w-full bg-transparent outline-none"
+								placeholder={$i18n.t('API Base URL')}
+								bind:value={STT_OPENAI_API_BASE_URL}
+								required
+							/>
+
+							<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={STT_OPENAI_API_KEY} />
+						</div>
+					</div>
+
+					<hr class=" dark:border-gray-850 my-2" />
+
+					<div>
+						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('STT Model')}</div>
+						<div class="flex w-full">
+							<div class="flex-1">
+								<input
+									list="model-list"
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									bind:value={STT_MODEL}
+									placeholder="Select a model"
+								/>
+
+								<datalist id="model-list">
+									<option value="whisper-1" />
+								</datalist>
+							</div>
+						</div>
+					</div>
+				{:else if STT_ENGINE === ''}
+					<div>
+						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('STT Model')}</div>
+
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Set whisper model')}
+									bind:value={STT_WHISPER_MODEL}
+								/>
+							</div>
+
+							<button
+								class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+								on:click={() => {
+									sttModelUpdateHandler();
+								}}
+								disabled={STT_WHISPER_MODEL_LOADING}
+							>
+								{#if STT_WHISPER_MODEL_LOADING}
+									<div class="self-center">
+										<svg
+											class=" w-4 h-4"
+											viewBox="0 0 24 24"
+											fill="currentColor"
+											xmlns="http://www.w3.org/2000/svg"
+										>
+											<style>
+												.spinner_ajPY {
+													transform-origin: center;
+													animation: spinner_AtaB 0.75s infinite linear;
+												}
+
+												@keyframes spinner_AtaB {
+													100% {
+														transform: rotate(360deg);
+													}
+												}
+											</style>
+											<path
+												d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+												opacity=".25"
+											/>
+											<path
+												d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+												class="spinner_ajPY"
+											/>
+										</svg>
+									</div>
+								{:else}
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 16 16"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+										/>
+										<path
+											d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+										/>
+									</svg>
+								{/if}
+							</button>
+						</div>
+
+						<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t(`Open WebUI uses faster-whisper internally.`)}
+
+							<a
+								class=" hover:underline dark:text-gray-200 text-gray-800"
+								href="https://github.com/SYSTRAN/faster-whisper"
+								target="_blank"
+							>
+								{$i18n.t(
+									`Click here to learn more about faster-whisper and see the available models.`
+								)}
+							</a>
+						</div>
+					</div>
+				{/if}
+			</div>
+
+			<hr class=" dark:border-gray-800" />
+
+			<div>
+				<div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div>
+
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Text-to-Speech Engine')}</div>
+					<div class="flex items-center relative">
+						<select
+							class=" dark:bg-gray-900 w-fit pr-8 cursor-pointer rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							bind:value={TTS_ENGINE}
+							placeholder="Select a mode"
+							on:change={async (e) => {
+								await updateConfigHandler();
+								await getVoices();
+								await getModels();
+
+								if (e.target?.value === 'openai') {
+									TTS_VOICE = 'alloy';
+									TTS_MODEL = 'tts-1';
+								} else {
+									TTS_VOICE = '';
+									TTS_MODEL = '';
+								}
+							}}
+						>
+							<option value="">{$i18n.t('Web API')}</option>
+							<option value="transformers">{$i18n.t('Transformers')} ({$i18n.t('Local')})</option>
+							<option value="openai">{$i18n.t('OpenAI')}</option>
+							<option value="elevenlabs">{$i18n.t('ElevenLabs')}</option>
+							<option value="azure">{$i18n.t('Azure AI Speech')}</option>
+						</select>
+					</div>
+				</div>
+
+				{#if TTS_ENGINE === 'openai'}
+					<div>
+						<div class="mt-1 flex gap-2 mb-1">
+							<input
+								class="flex-1 w-full bg-transparent outline-none"
+								placeholder={$i18n.t('API Base URL')}
+								bind:value={TTS_OPENAI_API_BASE_URL}
+								required
+							/>
+
+							<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={TTS_OPENAI_API_KEY} />
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'elevenlabs'}
+					<div>
+						<div class="mt-1 flex gap-2 mb-1">
+							<input
+								class="flex-1 w-full rounded-lg py-2 pl-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('API Key')}
+								bind:value={TTS_API_KEY}
+								required
+							/>
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'azure'}
+					<div>
+						<div class="mt-1 flex gap-2 mb-1">
+							<input
+								class="flex-1 w-full rounded-lg py-2 pl-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('API Key')}
+								bind:value={TTS_API_KEY}
+								required
+							/>
+							<input
+								class="flex-1 w-full rounded-lg py-2 pl-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('Azure Region')}
+								bind:value={TTS_AZURE_SPEECH_REGION}
+								required
+							/>
+						</div>
+					</div>
+				{/if}
+
+				<hr class=" dark:border-gray-850 my-2" />
+
+				{#if TTS_ENGINE === ''}
+					<div>
+						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Voice')}</div>
+						<div class="flex w-full">
+							<div class="flex-1">
+								<select
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									bind:value={TTS_VOICE}
+								>
+									<option value="" selected={TTS_VOICE !== ''}>{$i18n.t('Default')}</option>
+									{#each voices as voice}
+										<option
+											value={voice.voiceURI}
+											class="bg-gray-100 dark:bg-gray-700"
+											selected={TTS_VOICE === voice.voiceURI}
+											>{voice.name.replace('+', ', ')}</option
+										>
+									{/each}
+								</select>
+							</div>
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'transformers'}
+					<div>
+						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Model')}</div>
+						<div class="flex w-full">
+							<div class="flex-1">
+								<input
+									list="model-list"
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									bind:value={TTS_MODEL}
+									placeholder="CMU ARCTIC speaker embedding name"
+								/>
+
+								<datalist id="model-list">
+									<option value="tts-1" />
+								</datalist>
+							</div>
+						</div>
+						<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t(`Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.`)}
+
+							To learn more about SpeechT5,
+
+							<a
+								class=" hover:underline dark:text-gray-200 text-gray-800"
+								href="https://github.com/microsoft/SpeechT5"
+								target="_blank"
+							>
+								{$i18n.t(`click here`, {
+									name: 'SpeechT5'
+								})}.
+							</a>
+							To see the available CMU Arctic speaker embeddings,
+							<a
+								class=" hover:underline dark:text-gray-200 text-gray-800"
+								href="https://huggingface.co/datasets/Matthijs/cmu-arctic-xvectors"
+								target="_blank"
+							>
+								{$i18n.t(`click here`)}.
+							</a>
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'openai'}
+					<div class=" flex gap-2">
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Voice')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="voice-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_VOICE}
+										placeholder="Select a voice"
+									/>
+
+									<datalist id="voice-list">
+										{#each voices as voice}
+											<option value={voice.id}>{voice.name}</option>
+										{/each}
+									</datalist>
+								</div>
+							</div>
+						</div>
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Model')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="tts-model-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_MODEL}
+										placeholder="Select a model"
+									/>
+
+									<datalist id="tts-model-list">
+										{#each models as model}
+											<option value={model.id} class="bg-gray-50 dark:bg-gray-700" />
+										{/each}
+									</datalist>
+								</div>
+							</div>
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'elevenlabs'}
+					<div class=" flex gap-2">
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Voice')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="voice-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_VOICE}
+										placeholder="Select a voice"
+									/>
+
+									<datalist id="voice-list">
+										{#each voices as voice}
+											<option value={voice.id}>{voice.name}</option>
+										{/each}
+									</datalist>
+								</div>
+							</div>
+						</div>
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Model')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="tts-model-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_MODEL}
+										placeholder="Select a model"
+									/>
+
+									<datalist id="tts-model-list">
+										{#each models as model}
+											<option value={model.id} class="bg-gray-50 dark:bg-gray-700" />
+										{/each}
+									</datalist>
+								</div>
+							</div>
+						</div>
+					</div>
+				{:else if TTS_ENGINE === 'azure'}
+					<div class=" flex gap-2">
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">{$i18n.t('TTS Voice')}</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="voice-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_VOICE}
+										placeholder="Select a voice"
+									/>
+
+									<datalist id="voice-list">
+										{#each voices as voice}
+											<option value={voice.id}>{voice.name}</option>
+										{/each}
+									</datalist>
+								</div>
+							</div>
+						</div>
+						<div class="w-full">
+							<div class=" mb-1.5 text-sm font-medium">
+								{$i18n.t('Output format')}
+								<a
+									href="https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech?tabs=streaming#audio-outputs"
+									target="_blank"
+								>
+									<small>{$i18n.t('Available list')}</small>
+								</a>
+							</div>
+							<div class="flex w-full">
+								<div class="flex-1">
+									<input
+										list="tts-model-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										bind:value={TTS_AZURE_SPEECH_OUTPUT_FORMAT}
+										placeholder="Select a output format"
+									/>
+								</div>
+							</div>
+						</div>
+					</div>
+				{/if}
+
+				<hr class="dark:border-gray-850 my-2" />
+
+				<div class="pt-0.5 flex w-full justify-between">
+					<div class="self-center text-xs font-medium">{$i18n.t('Response splitting')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 w-fit pr-8 cursor-pointer rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							aria-label="Select how to split message text for TTS requests"
+							bind:value={TTS_SPLIT_ON}
+						>
+							{#each Object.values(TTS_RESPONSE_SPLIT) as split}
+								<option value={split}
+									>{$i18n.t(split.charAt(0).toUpperCase() + split.slice(1))}</option
+								>
+							{/each}
+						</select>
+					</div>
+				</div>
+				<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
+					{$i18n.t(
+						"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string."
+					)}
+				</div>
+			</div>
+		</div>
+	</div>
+	<div class="flex justify-end text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Connections.svelte b/src/lib/components/admin/Settings/Connections.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ddc19bb8f0bfb35a410cb3932e94585a50ee371b
--- /dev/null
+++ b/src/lib/components/admin/Settings/Connections.svelte
@@ -0,0 +1,347 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import { getOllamaConfig, updateOllamaConfig } from '$lib/apis/ollama';
+	import { getOpenAIConfig, updateOpenAIConfig, getOpenAIModels } from '$lib/apis/openai';
+	import { getModels as _getModels } from '$lib/apis';
+
+	import { models, user } from '$lib/stores';
+
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+
+	import OpenAIConnection from './Connections/OpenAIConnection.svelte';
+	import AddConnectionModal from './Connections/AddConnectionModal.svelte';
+	import OllamaConnection from './Connections/OllamaConnection.svelte';
+
+	const i18n = getContext('i18n');
+
+	const getModels = async () => {
+		const models = await _getModels(localStorage.token);
+		return models;
+	};
+
+	// External
+	let OLLAMA_BASE_URLS = [''];
+	let OLLAMA_API_CONFIGS = {};
+
+	let OPENAI_API_KEYS = [''];
+	let OPENAI_API_BASE_URLS = [''];
+	let OPENAI_API_CONFIGS = {};
+
+	let ENABLE_OPENAI_API: null | boolean = null;
+	let ENABLE_OLLAMA_API: null | boolean = null;
+
+	let pipelineUrls = {};
+	let showAddOpenAIConnectionModal = false;
+	let showAddOllamaConnectionModal = false;
+
+	const updateOpenAIHandler = async () => {
+		if (ENABLE_OPENAI_API !== null) {
+			OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
+				(url, urlIdx) => OPENAI_API_BASE_URLS.indexOf(url) === urlIdx && url !== ''
+			).map((url) => url.replace(/\/$/, ''));
+
+			// Check if API KEYS length is same than API URLS length
+			if (OPENAI_API_KEYS.length !== OPENAI_API_BASE_URLS.length) {
+				// if there are more keys than urls, remove the extra keys
+				if (OPENAI_API_KEYS.length > OPENAI_API_BASE_URLS.length) {
+					OPENAI_API_KEYS = OPENAI_API_KEYS.slice(0, OPENAI_API_BASE_URLS.length);
+				}
+
+				// if there are more urls than keys, add empty keys
+				if (OPENAI_API_KEYS.length < OPENAI_API_BASE_URLS.length) {
+					const diff = OPENAI_API_BASE_URLS.length - OPENAI_API_KEYS.length;
+					for (let i = 0; i < diff; i++) {
+						OPENAI_API_KEYS.push('');
+					}
+				}
+			}
+
+			const res = await updateOpenAIConfig(localStorage.token, {
+				ENABLE_OPENAI_API: ENABLE_OPENAI_API,
+				OPENAI_API_BASE_URLS: OPENAI_API_BASE_URLS,
+				OPENAI_API_KEYS: OPENAI_API_KEYS,
+				OPENAI_API_CONFIGS: OPENAI_API_CONFIGS
+			}).catch((error) => {
+				toast.error(error);
+			});
+
+			if (res) {
+				toast.success($i18n.t('OpenAI API settings updated'));
+				await models.set(await getModels());
+			}
+		}
+	};
+
+	const updateOllamaHandler = async () => {
+		if (ENABLE_OLLAMA_API !== null) {
+			// Remove duplicate URLs
+			OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter(
+				(url, urlIdx) => OLLAMA_BASE_URLS.indexOf(url) === urlIdx && url !== ''
+			).map((url) => url.replace(/\/$/, ''));
+
+			console.log(OLLAMA_BASE_URLS);
+
+			if (OLLAMA_BASE_URLS.length === 0) {
+				ENABLE_OLLAMA_API = false;
+				toast.info($i18n.t('Ollama API disabled'));
+			}
+
+			const res = await updateOllamaConfig(localStorage.token, {
+				ENABLE_OLLAMA_API: ENABLE_OLLAMA_API,
+				OLLAMA_BASE_URLS: OLLAMA_BASE_URLS,
+				OLLAMA_API_CONFIGS: OLLAMA_API_CONFIGS
+			}).catch((error) => {
+				toast.error(error);
+			});
+
+			if (res) {
+				toast.success($i18n.t('Ollama API settings updated'));
+				await models.set(await getModels());
+			}
+		}
+	};
+
+	const addOpenAIConnectionHandler = async (connection) => {
+		OPENAI_API_BASE_URLS = [...OPENAI_API_BASE_URLS, connection.url];
+		OPENAI_API_KEYS = [...OPENAI_API_KEYS, connection.key];
+		OPENAI_API_CONFIGS[connection.url] = connection.config;
+
+		await updateOpenAIHandler();
+	};
+
+	const addOllamaConnectionHandler = async (connection) => {
+		OLLAMA_BASE_URLS = [...OLLAMA_BASE_URLS, connection.url];
+		OLLAMA_API_CONFIGS[connection.url] = connection.config;
+
+		await updateOllamaHandler();
+	};
+
+	onMount(async () => {
+		if ($user.role === 'admin') {
+			let ollamaConfig = {};
+			let openaiConfig = {};
+
+			await Promise.all([
+				(async () => {
+					ollamaConfig = await getOllamaConfig(localStorage.token);
+				})(),
+				(async () => {
+					openaiConfig = await getOpenAIConfig(localStorage.token);
+				})()
+			]);
+
+			ENABLE_OPENAI_API = openaiConfig.ENABLE_OPENAI_API;
+			ENABLE_OLLAMA_API = ollamaConfig.ENABLE_OLLAMA_API;
+
+			OPENAI_API_BASE_URLS = openaiConfig.OPENAI_API_BASE_URLS;
+			OPENAI_API_KEYS = openaiConfig.OPENAI_API_KEYS;
+			OPENAI_API_CONFIGS = openaiConfig.OPENAI_API_CONFIGS;
+
+			OLLAMA_BASE_URLS = ollamaConfig.OLLAMA_BASE_URLS;
+			OLLAMA_API_CONFIGS = ollamaConfig.OLLAMA_API_CONFIGS;
+
+			if (ENABLE_OPENAI_API) {
+				for (const url of OPENAI_API_BASE_URLS) {
+					if (!OPENAI_API_CONFIGS[url]) {
+						OPENAI_API_CONFIGS[url] = {};
+					}
+				}
+
+				OPENAI_API_BASE_URLS.forEach(async (url, idx) => {
+					OPENAI_API_CONFIGS[url] = OPENAI_API_CONFIGS[url] || {};
+					if (!(OPENAI_API_CONFIGS[url]?.enable ?? true)) {
+						return;
+					}
+					const res = await getOpenAIModels(localStorage.token, idx);
+					if (res.pipelines) {
+						pipelineUrls[url] = true;
+					}
+				});
+			}
+
+			if (ENABLE_OLLAMA_API) {
+				for (const url of OLLAMA_BASE_URLS) {
+					if (!OLLAMA_API_CONFIGS[url]) {
+						OLLAMA_API_CONFIGS[url] = {};
+					}
+				}
+			}
+		}
+	});
+</script>
+
+<AddConnectionModal
+	bind:show={showAddOpenAIConnectionModal}
+	onSubmit={addOpenAIConnectionHandler}
+/>
+
+<AddConnectionModal
+	ollama
+	bind:show={showAddOllamaConnectionModal}
+	onSubmit={addOllamaConnectionHandler}
+/>
+
+<form
+	class="flex flex-col h-full justify-between text-sm"
+	on:submit|preventDefault={() => {
+		updateOpenAIHandler();
+		updateOllamaHandler();
+
+		dispatch('save');
+	}}
+>
+	<div class=" overflow-y-scroll scrollbar-hidden h-full">
+		{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null}
+			<div class="my-2">
+				<div class="mt-2 space-y-2 pr-1.5">
+					<div class="flex justify-between items-center text-sm">
+						<div class="  font-medium">{$i18n.t('OpenAI API')}</div>
+
+						<div class="flex items-center">
+							<div class="">
+								<Switch
+									bind:state={ENABLE_OPENAI_API}
+									on:change={async () => {
+										updateOpenAIHandler();
+									}}
+								/>
+							</div>
+						</div>
+					</div>
+
+					{#if ENABLE_OPENAI_API}
+						<hr class=" border-gray-50 dark:border-gray-850" />
+
+						<div class="">
+							<div class="flex justify-between items-center">
+								<div class="font-medium">{$i18n.t('Manage OpenAI API Connections')}</div>
+
+								<Tooltip content={$i18n.t(`Add Connection`)}>
+									<button
+										class="px-1"
+										on:click={() => {
+											showAddOpenAIConnectionModal = true;
+										}}
+										type="button"
+									>
+										<Plus />
+									</button>
+								</Tooltip>
+							</div>
+
+							<div class="flex flex-col gap-1.5 mt-1.5">
+								{#each OPENAI_API_BASE_URLS as url, idx}
+									<OpenAIConnection
+										pipeline={pipelineUrls[url] ? true : false}
+										bind:url
+										bind:key={OPENAI_API_KEYS[idx]}
+										bind:config={OPENAI_API_CONFIGS[url]}
+										onSubmit={() => {
+											updateOpenAIHandler();
+										}}
+										onDelete={() => {
+											OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS.filter(
+												(url, urlIdx) => idx !== urlIdx
+											);
+											OPENAI_API_KEYS = OPENAI_API_KEYS.filter((key, keyIdx) => idx !== keyIdx);
+										}}
+									/>
+								{/each}
+							</div>
+						</div>
+					{/if}
+				</div>
+			</div>
+
+			<hr class=" border-gray-50 dark:border-gray-850" />
+
+			<div class="pr-1.5 my-2">
+				<div class="flex justify-between items-center text-sm mb-2">
+					<div class="  font-medium">{$i18n.t('Ollama API')}</div>
+
+					<div class="mt-1">
+						<Switch
+							bind:state={ENABLE_OLLAMA_API}
+							on:change={async () => {
+								updateOllamaHandler();
+							}}
+						/>
+					</div>
+				</div>
+
+				{#if ENABLE_OLLAMA_API}
+					<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+					<div class="">
+						<div class="flex justify-between items-center">
+							<div class="font-medium">{$i18n.t('Manage Ollama API Connections')}</div>
+
+							<Tooltip content={$i18n.t(`Add Connection`)}>
+								<button
+									class="px-1"
+									on:click={() => {
+										showAddOllamaConnectionModal = true;
+									}}
+									type="button"
+								>
+									<Plus />
+								</button>
+							</Tooltip>
+						</div>
+
+						<div class="flex w-full gap-1.5">
+							<div class="flex-1 flex flex-col gap-1.5 mt-1.5">
+								{#each OLLAMA_BASE_URLS as url, idx}
+									<OllamaConnection
+										bind:url
+										bind:config={OLLAMA_API_CONFIGS[url]}
+										{idx}
+										onSubmit={() => {
+											updateOllamaHandler();
+										}}
+										onDelete={() => {
+											OLLAMA_BASE_URLS = OLLAMA_BASE_URLS.filter((url, urlIdx) => idx !== urlIdx);
+										}}
+									/>
+								{/each}
+							</div>
+						</div>
+
+						<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t('Trouble accessing Ollama?')}
+							<a
+								class=" text-gray-300 font-medium underline"
+								href="https://github.com/open-webui/open-webui#troubleshooting"
+								target="_blank"
+							>
+								{$i18n.t('Click here for help.')}
+							</a>
+						</div>
+					</div>
+				{/if}
+			</div>
+		{:else}
+			<div class="flex h-full justify-center">
+				<div class="my-auto">
+					<Spinner className="size-6" />
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Connections/AddConnectionModal.svelte b/src/lib/components/admin/Settings/Connections/AddConnectionModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..3f24dc6d7c97d4af49717ce33b82e5a3b6fef370
--- /dev/null
+++ b/src/lib/components/admin/Settings/Connections/AddConnectionModal.svelte
@@ -0,0 +1,365 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { models } from '$lib/stores';
+	import { verifyOpenAIConnection } from '$lib/apis/openai';
+	import { verifyOllamaConnection } from '$lib/apis/ollama';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import Minus from '$lib/components/icons/Minus.svelte';
+	import PencilSolid from '$lib/components/icons/PencilSolid.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+
+	export let onSubmit: Function = () => {};
+	export let onDelete: Function = () => {};
+
+	export let show = false;
+	export let edit = false;
+	export let ollama = false;
+
+	export let connection = null;
+
+	let url = '';
+	let key = '';
+
+	let prefixId = '';
+	let enable = true;
+
+	let modelId = '';
+	let modelIds = [];
+
+	let loading = false;
+
+	const verifyOllamaHandler = async () => {
+		const res = await verifyOllamaConnection(localStorage.token, url, key).catch((error) => {
+			toast.error(error);
+		});
+
+		if (res) {
+			toast.success($i18n.t('Server connection verified'));
+		}
+	};
+
+	const verifyOpenAIHandler = async () => {
+		const res = await verifyOpenAIConnection(localStorage.token, url, key).catch((error) => {
+			toast.error(error);
+		});
+
+		if (res) {
+			toast.success($i18n.t('Server connection verified'));
+		}
+	};
+
+	const verifyHandler = () => {
+		if (ollama) {
+			verifyOllamaHandler();
+		} else {
+			verifyOpenAIHandler();
+		}
+	};
+
+	const addModelHandler = () => {
+		if (modelId) {
+			modelIds = [...modelIds, modelId];
+			modelId = '';
+		}
+	};
+
+	const submitHandler = async () => {
+		loading = true;
+
+		if (!ollama && (!url || !key)) {
+			loading = false;
+			toast.error('URL and Key are required');
+			return;
+		}
+
+		const connection = {
+			url,
+			key,
+			config: {
+				enable: enable,
+				prefix_id: prefixId,
+				model_ids: modelIds
+			}
+		};
+
+		await onSubmit(connection);
+
+		loading = false;
+		show = false;
+
+		url = '';
+		key = '';
+		prefixId = '';
+		modelIds = [];
+	};
+
+	const init = () => {
+		if (connection) {
+			url = connection.url;
+			key = connection.key;
+
+			enable = connection.config?.enable ?? true;
+			prefixId = connection.config?.prefix_id ?? '';
+			modelIds = connection.config?.model_ids ?? [];
+		}
+	};
+
+	$: if (show) {
+		init();
+	}
+
+	onMount(() => {
+		init();
+	});
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center font-primary">
+				{#if edit}
+					{$i18n.t('Edit Connection')}
+				{:else}
+					{$i18n.t('Add Connection')}
+				{/if}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit={(e) => {
+						e.preventDefault();
+						submitHandler();
+					}}
+				>
+					<div class="px-1">
+						<div class="flex gap-2">
+							<div class="flex flex-col w-full">
+								<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('URL')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										type="text"
+										bind:value={url}
+										placeholder={$i18n.t('API Base URL')}
+										autocomplete="off"
+										required
+									/>
+								</div>
+							</div>
+
+							<Tooltip content="Verify Connection" className="self-end -mb-1">
+								<button
+									class="self-center p-1 bg-transparent hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
+									on:click={() => {
+										verifyHandler();
+									}}
+									type="button"
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+
+							<div class="flex flex-col flex-shrink-0 self-end">
+								<Tooltip content={enable ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
+									<Switch bind:state={enable} />
+								</Tooltip>
+							</div>
+						</div>
+
+						<div class="flex gap-2 mt-2">
+							<div class="flex flex-col w-full">
+								<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Key')}</div>
+
+								<div class="flex-1">
+									<SensitiveInput
+										className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										bind:value={key}
+										placeholder={$i18n.t('API Key')}
+										required={!ollama}
+									/>
+								</div>
+							</div>
+
+							<div class="flex flex-col w-full">
+								<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Prefix ID')}</div>
+
+								<div class="flex-1">
+									<Tooltip
+										content={$i18n.t(
+											'Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable'
+										)}
+									>
+										<input
+											class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+											type="text"
+											bind:value={prefixId}
+											placeholder={$i18n.t('Prefix ID')}
+											autocomplete="off"
+										/>
+									</Tooltip>
+								</div>
+							</div>
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div class="flex flex-col w-full">
+							<div class="mb-1 flex justify-between">
+								<div class="text-xs text-gray-500">{$i18n.t('Model IDs')}</div>
+							</div>
+
+							{#if modelIds.length > 0}
+								<div class="flex flex-col">
+									{#each modelIds as modelId, modelIdx}
+										<div class=" flex gap-2 w-full justify-between items-center">
+											<div class=" text-sm flex-1 py-1 rounded-lg">
+												{modelId}
+											</div>
+											<div class="flex-shrink-0">
+												<button
+													type="button"
+													on:click={() => {
+														modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
+													}}
+												>
+													<Minus strokeWidth="2" className="size-3.5" />
+												</button>
+											</div>
+										</div>
+									{/each}
+								</div>
+							{:else}
+								<div class="text-gray-500 text-xs text-center py-2 px-10">
+									{#if ollama}
+										{$i18n.t('Leave empty to include all models from "{{URL}}/api/tags" endpoint', {
+											URL: url
+										})}
+									{:else}
+										{$i18n.t('Leave empty to include all models from "{{URL}}/models" endpoint', {
+											URL: url
+										})}
+									{/if}
+								</div>
+							{/if}
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div class="flex items-center">
+							<input
+								class="w-full py-1 text-sm rounded-lg bg-transparent {modelId
+									? ''
+									: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+								bind:value={modelId}
+								placeholder={$i18n.t('Add a model ID')}
+							/>
+
+							<div>
+								<button
+									type="button"
+									on:click={() => {
+										addModelHandler();
+									}}
+								>
+									<Plus className="size-3.5" strokeWidth="2" />
+								</button>
+							</div>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
+						{#if edit}
+							<button
+								class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-900 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
+								type="button"
+								on:click={() => {
+									onDelete();
+									show = false;
+								}}
+							>
+								{$i18n.t('Delete')}
+							</button>
+						{/if}
+
+						<button
+							class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Save')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Settings/Connections/ManageOllamaModal.svelte b/src/lib/components/admin/Settings/Connections/ManageOllamaModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..220214ed10e770a6d67f6b19e111d550aadb9159
--- /dev/null
+++ b/src/lib/components/admin/Settings/Connections/ManageOllamaModal.svelte
@@ -0,0 +1,1054 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user, config } from '$lib/stores';
+	import { splitStream } from '$lib/utils';
+
+	import {
+		createModel,
+		deleteModel,
+		downloadModel,
+		getOllamaUrls,
+		getOllamaVersion,
+		pullModel,
+		uploadModel,
+		getOllamaConfig,
+		getOllamaModels
+	} from '$lib/apis/ollama';
+	import { getModels } from '$lib/apis';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ModelDeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
+	export let show = false;
+
+	let modelUploadInputElement: HTMLInputElement;
+	let showModelDeleteConfirm = false;
+
+	let loading = true;
+
+	// Models
+	export let urlIdx: number | null = null;
+
+	let ollamaModels = [];
+
+	let updateModelId = null;
+	let updateProgress = null;
+	let showExperimentalOllama = false;
+
+	const MAX_PARALLEL_DOWNLOADS = 3;
+
+	let modelTransferring = false;
+	let modelTag = '';
+
+	let createModelLoading = false;
+	let createModelTag = '';
+	let createModelContent = '';
+	let createModelDigest = '';
+	let createModelPullProgress = null;
+
+	let digest = '';
+	let pullProgress = null;
+
+	let modelUploadMode = 'file';
+	let modelInputFile: File[] | null = null;
+	let modelFileUrl = '';
+	let modelFileContent = `TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`;
+	let modelFileDigest = '';
+
+	let uploadProgress = null;
+	let uploadMessage = '';
+
+	let deleteModelTag = '';
+
+	const updateModelsHandler = async () => {
+		for (const model of ollamaModels) {
+			console.log(model);
+
+			updateModelId = model.id;
+			const [res, controller] = await pullModel(localStorage.token, model.id, urlIdx).catch(
+				(error) => {
+					toast.error(error);
+					return null;
+				}
+			);
+
+			if (res) {
+				const reader = res.body
+					.pipeThrough(new TextDecoderStream())
+					.pipeThrough(splitStream('\n'))
+					.getReader();
+
+				while (true) {
+					try {
+						const { value, done } = await reader.read();
+						if (done) break;
+
+						let lines = value.split('\n');
+
+						for (const line of lines) {
+							if (line !== '') {
+								let data = JSON.parse(line);
+
+								console.log(data);
+								if (data.error) {
+									throw data.error;
+								}
+								if (data.detail) {
+									throw data.detail;
+								}
+								if (data.status) {
+									if (data.digest) {
+										updateProgress = 0;
+										if (data.completed) {
+											updateProgress = Math.round((data.completed / data.total) * 1000) / 10;
+										} else {
+											updateProgress = 100;
+										}
+									} else {
+										toast.success(data.status);
+									}
+								}
+							}
+						}
+					} catch (error) {
+						console.log(error);
+					}
+				}
+			}
+		}
+
+		updateModelId = null;
+		updateProgress = null;
+	};
+
+	const pullModelHandler = async () => {
+		const sanitizedModelTag = modelTag.trim().replace(/^ollama\s+(run|pull)\s+/, '');
+		console.log($MODEL_DOWNLOAD_POOL);
+		if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]) {
+			toast.error(
+				$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
+					modelTag: sanitizedModelTag
+				})
+			);
+			return;
+		}
+		if (Object.keys($MODEL_DOWNLOAD_POOL).length === MAX_PARALLEL_DOWNLOADS) {
+			toast.error(
+				$i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.')
+			);
+			return;
+		}
+
+		const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, urlIdx).catch(
+			(error) => {
+				toast.error(error);
+				return null;
+			}
+		);
+
+		if (res) {
+			const reader = res.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL,
+				[sanitizedModelTag]: {
+					...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+					abortController: controller,
+					reader,
+					done: false
+				}
+			});
+
+			while (true) {
+				try {
+					const { value, done } = await reader.read();
+					if (done) break;
+
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							let data = JSON.parse(line);
+							console.log(data);
+							if (data.error) {
+								throw data.error;
+							}
+							if (data.detail) {
+								throw data.detail;
+							}
+
+							if (data.status) {
+								if (data.digest) {
+									let downloadProgress = 0;
+									if (data.completed) {
+										downloadProgress = Math.round((data.completed / data.total) * 1000) / 10;
+									} else {
+										downloadProgress = 100;
+									}
+
+									MODEL_DOWNLOAD_POOL.set({
+										...$MODEL_DOWNLOAD_POOL,
+										[sanitizedModelTag]: {
+											...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+											pullProgress: downloadProgress,
+											digest: data.digest
+										}
+									});
+								} else {
+									toast.success(data.status);
+
+									MODEL_DOWNLOAD_POOL.set({
+										...$MODEL_DOWNLOAD_POOL,
+										[sanitizedModelTag]: {
+											...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+											done: data.status === 'success'
+										}
+									});
+								}
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+					if (typeof error !== 'string') {
+						error = error.message;
+					}
+
+					toast.error(error);
+					// opts.callback({ success: false, error, modelName: opts.modelName });
+				}
+			}
+
+			console.log($MODEL_DOWNLOAD_POOL[sanitizedModelTag]);
+
+			if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag].done) {
+				toast.success(
+					$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, {
+						modelName: sanitizedModelTag
+					})
+				);
+
+				models.set(await getModels(localStorage.token));
+			} else {
+				toast.error($i18n.t('Download canceled'));
+			}
+
+			delete $MODEL_DOWNLOAD_POOL[sanitizedModelTag];
+
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL
+			});
+		}
+
+		modelTag = '';
+		modelTransferring = false;
+	};
+
+	const uploadModelHandler = async () => {
+		modelTransferring = true;
+
+		let uploaded = false;
+		let fileResponse = null;
+		let name = '';
+
+		if (modelUploadMode === 'file') {
+			const file = modelInputFile ? modelInputFile[0] : null;
+
+			if (file) {
+				uploadMessage = 'Uploading...';
+
+				fileResponse = await uploadModel(localStorage.token, file, urlIdx).catch((error) => {
+					toast.error(error);
+					return null;
+				});
+			}
+		} else {
+			uploadProgress = 0;
+			fileResponse = await downloadModel(localStorage.token, modelFileUrl, urlIdx).catch(
+				(error) => {
+					toast.error(error);
+					return null;
+				}
+			);
+		}
+
+		if (fileResponse && fileResponse.ok) {
+			const reader = fileResponse.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			while (true) {
+				const { value, done } = await reader.read();
+				if (done) break;
+
+				try {
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							let data = JSON.parse(line.replace(/^data: /, ''));
+
+							if (data.progress) {
+								if (uploadMessage) {
+									uploadMessage = '';
+								}
+								uploadProgress = data.progress;
+							}
+
+							if (data.error) {
+								throw data.error;
+							}
+
+							if (data.done) {
+								modelFileDigest = data.blob;
+								name = data.name;
+								uploaded = true;
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+				}
+			}
+		} else {
+			const error = await fileResponse?.json();
+			toast.error(error?.detail ?? error);
+		}
+
+		if (uploaded) {
+			const res = await createModel(
+				localStorage.token,
+				`${name}:latest`,
+				`FROM @${modelFileDigest}\n${modelFileContent}`
+			);
+
+			if (res && res.ok) {
+				const reader = res.body
+					.pipeThrough(new TextDecoderStream())
+					.pipeThrough(splitStream('\n'))
+					.getReader();
+
+				while (true) {
+					const { value, done } = await reader.read();
+					if (done) break;
+
+					try {
+						let lines = value.split('\n');
+
+						for (const line of lines) {
+							if (line !== '') {
+								console.log(line);
+								let data = JSON.parse(line);
+								console.log(data);
+
+								if (data.error) {
+									throw data.error;
+								}
+								if (data.detail) {
+									throw data.detail;
+								}
+
+								if (data.status) {
+									if (
+										!data.digest &&
+										!data.status.includes('writing') &&
+										!data.status.includes('sha256')
+									) {
+										toast.success(data.status);
+									} else {
+										if (data.digest) {
+											digest = data.digest;
+
+											if (data.completed) {
+												pullProgress = Math.round((data.completed / data.total) * 1000) / 10;
+											} else {
+												pullProgress = 100;
+											}
+										}
+									}
+								}
+							}
+						}
+					} catch (error) {
+						console.log(error);
+						toast.error(error);
+					}
+				}
+			}
+		}
+
+		modelFileUrl = '';
+
+		if (modelUploadInputElement) {
+			modelUploadInputElement.value = '';
+		}
+		modelInputFile = null;
+		modelTransferring = false;
+		uploadProgress = null;
+
+		models.set(await getModels(localStorage.token));
+	};
+
+	const deleteModelHandler = async () => {
+		const res = await deleteModel(localStorage.token, deleteModelTag, urlIdx).catch((error) => {
+			toast.error(error);
+		});
+
+		if (res) {
+			toast.success($i18n.t(`Deleted {{deleteModelTag}}`, { deleteModelTag }));
+		}
+
+		deleteModelTag = '';
+		models.set(await getModels(localStorage.token));
+	};
+
+	const cancelModelPullHandler = async (model: string) => {
+		const { reader, abortController } = $MODEL_DOWNLOAD_POOL[model];
+		if (abortController) {
+			abortController.abort();
+		}
+		if (reader) {
+			await reader.cancel();
+			delete $MODEL_DOWNLOAD_POOL[model];
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL
+			});
+			await deleteModel(localStorage.token, model);
+			toast.success(`${model} download has been canceled`);
+		}
+	};
+
+	const createModelHandler = async () => {
+		createModelLoading = true;
+		const res = await createModel(
+			localStorage.token,
+			createModelTag,
+			createModelContent,
+			urlIdx
+		).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res && res.ok) {
+			const reader = res.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			while (true) {
+				const { value, done } = await reader.read();
+				if (done) break;
+
+				try {
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							console.log(line);
+							let data = JSON.parse(line);
+							console.log(data);
+
+							if (data.error) {
+								throw data.error;
+							}
+							if (data.detail) {
+								throw data.detail;
+							}
+
+							if (data.status) {
+								if (
+									!data.digest &&
+									!data.status.includes('writing') &&
+									!data.status.includes('sha256')
+								) {
+									toast.success(data.status);
+								} else {
+									if (data.digest) {
+										createModelDigest = data.digest;
+
+										if (data.completed) {
+											createModelPullProgress =
+												Math.round((data.completed / data.total) * 1000) / 10;
+										} else {
+											createModelPullProgress = 100;
+										}
+									}
+								}
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+					toast.error(error);
+				}
+			}
+		}
+
+		models.set(await getModels(localStorage.token));
+
+		createModelLoading = false;
+
+		createModelTag = '';
+		createModelContent = '';
+		createModelDigest = '';
+		createModelPullProgress = null;
+	};
+
+	const init = async () => {
+		loading = true;
+		ollamaModels = await getOllamaModels(localStorage.token, urlIdx);
+
+		console.log(ollamaModels);
+		loading = false;
+	};
+
+	$: if (show) {
+		init();
+	}
+</script>
+
+<ModelDeleteConfirmDialog
+	bind:show={showModelDeleteConfirm}
+	on:confirm={() => {
+		deleteModelHandler();
+	}}
+/>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
+			<div
+				class="flex w-full justify-between items-center text-lg font-medium self-center font-primary"
+			>
+				<div class=" flex-shrink-0">
+					{$i18n.t('Manage Ollama')}
+				</div>
+
+				<div>
+					<Tooltip content="Update All Models" placement="top">
+						<button
+							class="p-2.5 flex gap-2 items-center bg-transparent rounded-lg transition"
+							on:click={() => {
+								updateModelsHandler();
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									d="M7 1a.75.75 0 0 1 .75.75V6h-1.5V1.75A.75.75 0 0 1 7 1ZM6.25 6v2.94L5.03 7.72a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06L7.75 8.94V6H10a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.25Z"
+								/>
+								<path
+									d="M4.268 14A2 2 0 0 0 6 15h6a2 2 0 0 0 2-2v-3a2 2 0 0 0-1-1.732V11a3 3 0 0 1-3 3H4.268Z"
+								/>
+							</svg>
+						</button>
+					</Tooltip>
+				</div>
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			{#if !loading}
+				<div class=" flex flex-col w-full">
+					<div>
+						<div class="space-y-2">
+							{#if updateModelId}
+								<div class="text-xs">
+									Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''}
+								</div>
+							{/if}
+
+							<div>
+								<div class=" mb-2 text-sm font-medium">
+									{$i18n.t('Pull a model from Ollama.com')}
+								</div>
+								<div class="flex w-full">
+									<div class="flex-1 mr-2">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
+												modelTag: 'mistral:7b'
+											})}
+											bind:value={modelTag}
+										/>
+									</div>
+									<button
+										class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+										on:click={() => {
+											pullModelHandler();
+										}}
+										disabled={modelTransferring}
+									>
+										{#if modelTransferring}
+											<div class="self-center">
+												<svg
+													class=" w-4 h-4"
+													viewBox="0 0 24 24"
+													fill="currentColor"
+													xmlns="http://www.w3.org/2000/svg"
+												>
+													<style>
+														.spinner_ajPY {
+															transform-origin: center;
+															animation: spinner_AtaB 0.75s infinite linear;
+														}
+
+														@keyframes spinner_AtaB {
+															100% {
+																transform: rotate(360deg);
+															}
+														}
+													</style>
+													<path
+														d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+														opacity=".25"
+													/>
+													<path
+														d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+														class="spinner_ajPY"
+													/>
+												</svg>
+											</div>
+										{:else}
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 16 16"
+												fill="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+												/>
+												<path
+													d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+												/>
+											</svg>
+										{/if}
+									</button>
+								</div>
+
+								<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
+									{$i18n.t('To access the available model names for downloading,')}
+									<a
+										class=" text-gray-500 dark:text-gray-300 font-medium underline"
+										href="https://ollama.com/library"
+										target="_blank">{$i18n.t('click here.')}</a
+									>
+								</div>
+
+								{#if Object.keys($MODEL_DOWNLOAD_POOL).length > 0}
+									{#each Object.keys($MODEL_DOWNLOAD_POOL) as model}
+										{#if 'pullProgress' in $MODEL_DOWNLOAD_POOL[model]}
+											<div class="flex flex-col">
+												<div class="font-medium mb-1">{model}</div>
+												<div class="">
+													<div class="flex flex-row justify-between space-x-4 pr-2">
+														<div class=" flex-1">
+															<div
+																class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
+																style="width: {Math.max(
+																	15,
+																	$MODEL_DOWNLOAD_POOL[model].pullProgress ?? 0
+																)}%"
+															>
+																{$MODEL_DOWNLOAD_POOL[model].pullProgress ?? 0}%
+															</div>
+														</div>
+
+														<Tooltip content={$i18n.t('Cancel')}>
+															<button
+																class="text-gray-800 dark:text-gray-100"
+																on:click={() => {
+																	cancelModelPullHandler(model);
+																}}
+															>
+																<svg
+																	class="w-4 h-4 text-gray-800 dark:text-white"
+																	aria-hidden="true"
+																	xmlns="http://www.w3.org/2000/svg"
+																	width="24"
+																	height="24"
+																	fill="currentColor"
+																	viewBox="0 0 24 24"
+																>
+																	<path
+																		stroke="currentColor"
+																		stroke-linecap="round"
+																		stroke-linejoin="round"
+																		stroke-width="2"
+																		d="M6 18 17.94 6M18 18 6.06 6"
+																	/>
+																</svg>
+															</button>
+														</Tooltip>
+													</div>
+													{#if 'digest' in $MODEL_DOWNLOAD_POOL[model]}
+														<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
+															{$MODEL_DOWNLOAD_POOL[model].digest}
+														</div>
+													{/if}
+												</div>
+											</div>
+										{/if}
+									{/each}
+								{/if}
+							</div>
+
+							<div>
+								<div class=" mb-2 text-sm font-medium">{$i18n.t('Delete a model')}</div>
+								<div class="flex w-full">
+									<div class="flex-1 mr-2">
+										<select
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											bind:value={deleteModelTag}
+											placeholder={$i18n.t('Select a model')}
+										>
+											{#if !deleteModelTag}
+												<option value="" disabled selected>{$i18n.t('Select a model')}</option>
+											{/if}
+											{#each ollamaModels as model}
+												<option value={model.id} class="bg-gray-50 dark:bg-gray-700"
+													>{model.name +
+														' (' +
+														(model.size / 1024 ** 3).toFixed(1) +
+														' GB)'}</option
+												>
+											{/each}
+										</select>
+									</div>
+									<button
+										class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+										on:click={() => {
+											showModelDeleteConfirm = true;
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</button>
+								</div>
+							</div>
+
+							<div>
+								<div class=" mb-2 text-sm font-medium">{$i18n.t('Create a model')}</div>
+								<div class="flex w-full">
+									<div class="flex-1 mr-2 flex flex-col gap-2">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											placeholder={$i18n.t('Enter model tag (e.g. {{modelTag}})', {
+												modelTag: 'my-modelfile'
+											})}
+											bind:value={createModelTag}
+											disabled={createModelLoading}
+										/>
+
+										<textarea
+											bind:value={createModelContent}
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none scrollbar-hidden"
+											rows="6"
+											placeholder={`TEMPLATE """{{ .System }}\nUSER: {{ .Prompt }}\nASSISTANT: """\nPARAMETER num_ctx 4096\nPARAMETER stop "</s>"\nPARAMETER stop "USER:"\nPARAMETER stop "ASSISTANT:"`}
+											disabled={createModelLoading}
+										/>
+									</div>
+
+									<div class="flex self-start">
+										<button
+											class="px-2.5 py-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition disabled:cursor-not-allowed"
+											on:click={() => {
+												createModelHandler();
+											}}
+											disabled={createModelLoading}
+										>
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 16 16"
+												fill="currentColor"
+												class="size-4"
+											>
+												<path
+													d="M7.25 10.25a.75.75 0 0 0 1.5 0V4.56l2.22 2.22a.75.75 0 1 0 1.06-1.06l-3.5-3.5a.75.75 0 0 0-1.06 0l-3.5 3.5a.75.75 0 0 0 1.06 1.06l2.22-2.22v5.69Z"
+												/>
+												<path
+													d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+												/>
+											</svg>
+										</button>
+									</div>
+								</div>
+
+								{#if createModelDigest !== ''}
+									<div class="flex flex-col mt-1">
+										<div class="font-medium mb-1">{createModelTag}</div>
+										<div class="">
+											<div class="flex flex-row justify-between space-x-4 pr-2">
+												<div class=" flex-1">
+													<div
+														class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
+														style="width: {Math.max(15, createModelPullProgress ?? 0)}%"
+													>
+														{createModelPullProgress ?? 0}%
+													</div>
+												</div>
+											</div>
+											{#if createModelDigest}
+												<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
+													{createModelDigest}
+												</div>
+											{/if}
+										</div>
+									</div>
+								{/if}
+							</div>
+
+							<div class="pt-1">
+								<div class="flex justify-between items-center text-xs">
+									<div class=" text-sm font-medium">{$i18n.t('Experimental')}</div>
+									<button
+										class=" text-xs font-medium text-gray-500"
+										type="button"
+										on:click={() => {
+											showExperimentalOllama = !showExperimentalOllama;
+										}}>{showExperimentalOllama ? $i18n.t('Hide') : $i18n.t('Show')}</button
+									>
+								</div>
+							</div>
+
+							{#if showExperimentalOllama}
+								<form
+									on:submit|preventDefault={() => {
+										uploadModelHandler();
+									}}
+								>
+									<div class=" mb-2 flex w-full justify-between">
+										<div class="  text-sm font-medium">{$i18n.t('Upload a GGUF model')}</div>
+
+										<button
+											class="p-1 px-3 text-xs flex rounded transition"
+											on:click={() => {
+												if (modelUploadMode === 'file') {
+													modelUploadMode = 'url';
+												} else {
+													modelUploadMode = 'file';
+												}
+											}}
+											type="button"
+										>
+											{#if modelUploadMode === 'file'}
+												<span class="ml-2 self-center">{$i18n.t('File Mode')}</span>
+											{:else}
+												<span class="ml-2 self-center">{$i18n.t('URL Mode')}</span>
+											{/if}
+										</button>
+									</div>
+
+									<div class="flex w-full mb-1.5">
+										<div class="flex flex-col w-full">
+											{#if modelUploadMode === 'file'}
+												<div
+													class="flex-1 {modelInputFile && modelInputFile.length > 0 ? 'mr-2' : ''}"
+												>
+													<input
+														id="model-upload-input"
+														bind:this={modelUploadInputElement}
+														type="file"
+														bind:files={modelInputFile}
+														on:change={() => {
+															console.log(modelInputFile);
+														}}
+														accept=".gguf,.safetensors"
+														required
+														hidden
+													/>
+
+													<button
+														type="button"
+														class="w-full rounded-lg text-left py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850"
+														on:click={() => {
+															modelUploadInputElement.click();
+														}}
+													>
+														{#if modelInputFile && modelInputFile.length > 0}
+															{modelInputFile[0].name}
+														{:else}
+															{$i18n.t('Click here to select')}
+														{/if}
+													</button>
+												</div>
+											{:else}
+												<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
+													<input
+														class="w-full rounded-lg text-left py-2 px-4 bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
+														''
+															? 'mr-2'
+															: ''}"
+														type="url"
+														required
+														bind:value={modelFileUrl}
+														placeholder={$i18n.t('Type Hugging Face Resolve (Download) URL')}
+													/>
+												</div>
+											{/if}
+										</div>
+
+										{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
+											<button
+												class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg disabled:cursor-not-allowed transition"
+												type="submit"
+												disabled={modelTransferring}
+											>
+												{#if modelTransferring}
+													<div class="self-center">
+														<svg
+															class=" w-4 h-4"
+															viewBox="0 0 24 24"
+															fill="currentColor"
+															xmlns="http://www.w3.org/2000/svg"
+														>
+															<style>
+																.spinner_ajPY {
+																	transform-origin: center;
+																	animation: spinner_AtaB 0.75s infinite linear;
+																}
+
+																@keyframes spinner_AtaB {
+																	100% {
+																		transform: rotate(360deg);
+																	}
+																}
+															</style>
+															<path
+																d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+																opacity=".25"
+															/>
+															<path
+																d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+																class="spinner_ajPY"
+															/>
+														</svg>
+													</div>
+												{:else}
+													<svg
+														xmlns="http://www.w3.org/2000/svg"
+														viewBox="0 0 16 16"
+														fill="currentColor"
+														class="w-4 h-4"
+													>
+														<path
+															d="M7.25 10.25a.75.75 0 0 0 1.5 0V4.56l2.22 2.22a.75.75 0 1 0 1.06-1.06l-3.5-3.5a.75.75 0 0 0-1.06 0l-3.5 3.5a.75.75 0 0 0 1.06 1.06l2.22-2.22v5.69Z"
+														/>
+														<path
+															d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+														/>
+													</svg>
+												{/if}
+											</button>
+										{/if}
+									</div>
+
+									{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
+										<div>
+											<div>
+												<div class=" my-2.5 text-sm font-medium">
+													{$i18n.t('Modelfile Content')}
+												</div>
+												<textarea
+													bind:value={modelFileContent}
+													class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-100 dark:bg-gray-850 outline-none resize-none"
+													rows="6"
+												/>
+											</div>
+										</div>
+									{/if}
+									<div class=" mt-1 text-xs text-gray-400 dark:text-gray-500">
+										{$i18n.t('To access the GGUF models available for downloading,')}
+										<a
+											class=" text-gray-500 dark:text-gray-300 font-medium underline"
+											href="https://huggingface.co/models?search=gguf"
+											target="_blank">{$i18n.t('click here.')}</a
+										>
+									</div>
+
+									{#if uploadMessage}
+										<div class="mt-2">
+											<div class=" mb-2 text-xs">{$i18n.t('Upload Progress')}</div>
+
+											<div class="w-full rounded-full dark:bg-gray-800">
+												<div
+													class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
+													style="width: 100%"
+												>
+													{uploadMessage}
+												</div>
+											</div>
+											<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
+												{modelFileDigest}
+											</div>
+										</div>
+									{:else if uploadProgress !== null}
+										<div class="mt-2">
+											<div class=" mb-2 text-xs">{$i18n.t('Upload Progress')}</div>
+
+											<div class="w-full rounded-full dark:bg-gray-800">
+												<div
+													class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
+													style="width: {Math.max(15, uploadProgress ?? 0)}%"
+												>
+													{uploadProgress ?? 0}%
+												</div>
+											</div>
+											<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
+												{modelFileDigest}
+											</div>
+										</div>
+									{/if}
+								</form>
+							{/if}
+						</div>
+					</div>
+				</div>
+			{:else}
+				<Spinner />
+			{/if}
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte b/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..dec88ccc78a52917f940a5443ac3552dfbdbd65d
--- /dev/null
+++ b/src/lib/components/admin/Settings/Connections/OllamaConnection.svelte
@@ -0,0 +1,89 @@
+<script lang="ts">
+	import { getContext, tick } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import AddConnectionModal from './AddConnectionModal.svelte';
+
+	import Cog6 from '$lib/components/icons/Cog6.svelte';
+	import Wrench from '$lib/components/icons/Wrench.svelte';
+	import ManageOllamaModal from './ManageOllamaModal.svelte';
+
+	export let onDelete = () => {};
+	export let onSubmit = () => {};
+
+	export let url = '';
+	export let idx = 0;
+	export let config = {};
+
+	let showManageModal = false;
+	let showConfigModal = false;
+</script>
+
+<AddConnectionModal
+	ollama
+	edit
+	bind:show={showConfigModal}
+	connection={{
+		url,
+		key: config?.key ?? '',
+		config: config
+	}}
+	{onDelete}
+	onSubmit={(connection) => {
+		url = connection.url;
+		config = { ...connection.config, key: connection.key };
+		onSubmit(connection);
+	}}
+/>
+
+<ManageOllamaModal bind:show={showManageModal} urlIdx={idx} />
+
+<div class="flex gap-1.5">
+	<Tooltip
+		className="w-full relative"
+		content={$i18n.t(`WebUI will make requests to "{{url}}/api/chat"`, {
+			url
+		})}
+		placement="top-start"
+	>
+		{#if !(config?.enable ?? true)}
+			<div
+				class="absolute top-0 bottom-0 left-0 right-0 opacity-60 bg-white dark:bg-gray-900 z-10"
+			></div>
+		{/if}
+
+		<input
+			class="w-full text-sm bg-transparent outline-none"
+			placeholder={$i18n.t('Enter URL (e.g. http://localhost:11434)')}
+			bind:value={url}
+		/>
+	</Tooltip>
+
+	<div class="flex gap-1">
+		<Tooltip content={$i18n.t('Manage')} className="self-start">
+			<button
+				class="self-center p-1 bg-transparent hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
+				on:click={() => {
+					showManageModal = true;
+				}}
+				type="button"
+			>
+				<Wrench />
+			</button>
+		</Tooltip>
+
+		<Tooltip content={$i18n.t('Configure')} className="self-start">
+			<button
+				class="self-center p-1 bg-transparent hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
+				on:click={() => {
+					showConfigModal = true;
+				}}
+				type="button"
+			>
+				<Cog6 />
+			</button>
+		</Tooltip>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte b/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e6953bd78c332db3119d5f21e0f33ab8c1a50d39
--- /dev/null
+++ b/src/lib/components/admin/Settings/Connections/OpenAIConnection.svelte
@@ -0,0 +1,107 @@
+<script lang="ts">
+	import { getContext, tick } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import Cog6 from '$lib/components/icons/Cog6.svelte';
+	import AddConnectionModal from './AddConnectionModal.svelte';
+	import { connect } from 'socket.io-client';
+
+	export let onDelete = () => {};
+	export let onSubmit = () => {};
+
+	export let pipeline = false;
+
+	export let url = '';
+	export let key = '';
+	export let config = {};
+
+	let showConfigModal = false;
+</script>
+
+<AddConnectionModal
+	edit
+	bind:show={showConfigModal}
+	connection={{
+		url,
+		key,
+		config
+	}}
+	{onDelete}
+	onSubmit={(connection) => {
+		url = connection.url;
+		key = connection.key;
+		config = connection.config;
+		onSubmit(connection);
+	}}
+/>
+
+<div class="flex w-full gap-2 items-center">
+	<Tooltip
+		className="w-full relative"
+		content={$i18n.t(`WebUI will make requests to "{{url}}/chat/completions"`, {
+			url
+		})}
+		placement="top-start"
+	>
+		{#if !(config?.enable ?? true)}
+			<div
+				class="absolute top-0 bottom-0 left-0 right-0 opacity-60 bg-white dark:bg-gray-900 z-10"
+			></div>
+		{/if}
+		<div class="flex w-full">
+			<div class="flex-1 relative">
+				<input
+					class=" outline-none w-full bg-transparent {pipeline ? 'pr-8' : ''}"
+					placeholder={$i18n.t('API Base URL')}
+					bind:value={url}
+					autocomplete="off"
+				/>
+
+				{#if pipeline}
+					<div class=" absolute top-0.5 right-2.5">
+						<Tooltip content="Pipelines">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 24 24"
+								fill="currentColor"
+								class="size-4"
+							>
+								<path
+									d="M11.644 1.59a.75.75 0 0 1 .712 0l9.75 5.25a.75.75 0 0 1 0 1.32l-9.75 5.25a.75.75 0 0 1-.712 0l-9.75-5.25a.75.75 0 0 1 0-1.32l9.75-5.25Z"
+								/>
+								<path
+									d="m3.265 10.602 7.668 4.129a2.25 2.25 0 0 0 2.134 0l7.668-4.13 1.37.739a.75.75 0 0 1 0 1.32l-9.75 5.25a.75.75 0 0 1-.71 0l-9.75-5.25a.75.75 0 0 1 0-1.32l1.37-.738Z"
+								/>
+								<path
+									d="m10.933 19.231-7.668-4.13-1.37.739a.75.75 0 0 0 0 1.32l9.75 5.25c.221.12.489.12.71 0l9.75-5.25a.75.75 0 0 0 0-1.32l-1.37-.738-7.668 4.13a2.25 2.25 0 0 1-2.134-.001Z"
+								/>
+							</svg>
+						</Tooltip>
+					</div>
+				{/if}
+			</div>
+
+			<SensitiveInput
+				inputClassName=" outline-none bg-transparent w-full"
+				placeholder={$i18n.t('API Key')}
+				bind:value={key}
+			/>
+		</div>
+	</Tooltip>
+
+	<div class="flex gap-1">
+		<Tooltip content={$i18n.t('Configure')} className="self-start">
+			<button
+				class="self-center p-1 bg-transparent hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 rounded-lg transition"
+				on:click={() => {
+					showConfigModal = true;
+				}}
+				type="button"
+			>
+				<Cog6 />
+			</button>
+		</Tooltip>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Settings/Database.svelte b/src/lib/components/admin/Settings/Database.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..71fda7642274acae5760e0c8bc78455c6c315505
--- /dev/null
+++ b/src/lib/components/admin/Settings/Database.svelte
@@ -0,0 +1,196 @@
+<script lang="ts">
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { downloadDatabase, downloadLiteLLMConfig } from '$lib/apis/utils';
+	import { onMount, getContext } from 'svelte';
+	import { config, user } from '$lib/stores';
+	import { toast } from 'svelte-sonner';
+	import { getAllUserChats } from '$lib/apis/chats';
+	import { exportConfig, importConfig } from '$lib/apis/configs';
+
+	const i18n = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	const exportAllUserChats = async () => {
+		let blob = new Blob([JSON.stringify(await getAllUserChats(localStorage.token))], {
+			type: 'application/json'
+		});
+		saveAs(blob, `all-chats-export-${Date.now()}.json`);
+	};
+
+	onMount(async () => {
+		// permissions = await getUserPermissions(localStorage.token);
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
+		<div>
+			<div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
+
+			<input
+				id="config-json-input"
+				hidden
+				type="file"
+				accept=".json"
+				on:change={(e) => {
+					const file = e.target.files[0];
+					const reader = new FileReader();
+
+					reader.onload = async (e) => {
+						const res = await importConfig(localStorage.token, JSON.parse(e.target.result)).catch(
+							(error) => {
+								toast.error(error);
+							}
+						);
+
+						if (res) {
+							toast.success('Config imported successfully');
+						}
+						e.target.value = null;
+					};
+
+					reader.readAsText(file);
+				}}
+			/>
+
+			<button
+				type="button"
+				class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={async () => {
+					document.getElementById('config-json-input').click();
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+						<path
+							fill-rule="evenodd"
+							d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">
+					{$i18n.t('Import Config from JSON File')}
+				</div>
+			</button>
+
+			<button
+				type="button"
+				class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={async () => {
+					const config = await exportConfig(localStorage.token);
+					const blob = new Blob([JSON.stringify(config)], {
+						type: 'application/json'
+					});
+					saveAs(blob, `config-${Date.now()}.json`);
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+						<path
+							fill-rule="evenodd"
+							d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">
+					{$i18n.t('Export Config to JSON File')}
+				</div>
+			</button>
+
+			<hr class=" dark:border-gray-850 my-1" />
+
+			{#if $config?.features.enable_admin_export ?? true}
+				<div class="  flex w-full justify-between">
+					<!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
+
+					<button
+						class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+						type="button"
+						on:click={() => {
+							// exportAllUserChats();
+
+							downloadDatabase(localStorage.token).catch((error) => {
+								toast.error(error);
+							});
+						}}
+					>
+						<div class=" self-center mr-3">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+								<path
+									fill-rule="evenodd"
+									d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</div>
+						<div class=" self-center text-sm font-medium">{$i18n.t('Download Database')}</div>
+					</button>
+				</div>
+
+				<button
+					class=" flex rounded-md py-2 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+					on:click={() => {
+						exportAllUserChats();
+					}}
+				>
+					<div class=" self-center mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+							<path
+								fill-rule="evenodd"
+								d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM8.75 7.75a.75.75 0 0 0-1.5 0v2.69L6.03 9.22a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06l-1.22 1.22V7.75Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center text-sm font-medium">
+						{$i18n.t('Export All Chats (All Users)')}
+					</div>
+				</button>
+			{/if}
+		</div>
+	</div>
+
+	<!-- <div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+
+	</div> -->
+</form>
diff --git a/src/lib/components/admin/Settings/Documents.svelte b/src/lib/components/admin/Settings/Documents.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a596c293c6f44a3fb3c30ab9c54950fe1209e243
--- /dev/null
+++ b/src/lib/components/admin/Settings/Documents.svelte
@@ -0,0 +1,834 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import {
+		getQuerySettings,
+		updateQuerySettings,
+		resetVectorDB,
+		getEmbeddingConfig,
+		updateEmbeddingConfig,
+		getRerankingConfig,
+		updateRerankingConfig,
+		resetUploadDir,
+		getRAGConfig,
+		updateRAGConfig
+	} from '$lib/apis/retrieval';
+
+	import { knowledge, models } from '$lib/stores';
+	import { getKnowledgeBases } from '$lib/apis/knowledge';
+	import { uploadDir, deleteAllFiles, deleteFileById } from '$lib/apis/files';
+
+	import ResetUploadDirConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import ResetVectorDBConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import { text } from '@sveltejs/kit';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+
+	const i18n = getContext('i18n');
+
+	let scanDirLoading = false;
+	let updateEmbeddingModelLoading = false;
+	let updateRerankingModelLoading = false;
+
+	let showResetConfirm = false;
+	let showResetUploadDirConfirm = false;
+
+	let embeddingEngine = '';
+	let embeddingModel = '';
+	let embeddingBatchSize = 1;
+	let rerankingModel = '';
+
+	let fileMaxSize = null;
+	let fileMaxCount = null;
+
+	let contentExtractionEngine = 'default';
+	let tikaServerUrl = '';
+	let showTikaServerUrl = false;
+
+	let textSplitter = '';
+	let chunkSize = 0;
+	let chunkOverlap = 0;
+	let pdfExtractImages = true;
+
+	let OpenAIUrl = '';
+	let OpenAIKey = '';
+
+	let OllamaUrl = '';
+	let OllamaKey = '';
+
+	let querySettings = {
+		template: '',
+		r: 0.0,
+		k: 4,
+		hybrid: false
+	};
+
+	const embeddingModelUpdateHandler = async () => {
+		if (embeddingEngine === '' && embeddingModel.split('/').length - 1 > 1) {
+			toast.error(
+				$i18n.t(
+					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
+				)
+			);
+			return;
+		}
+		if (embeddingEngine === 'ollama' && embeddingModel === '') {
+			toast.error(
+				$i18n.t(
+					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
+				)
+			);
+			return;
+		}
+
+		if (embeddingEngine === 'openai' && embeddingModel === '') {
+			toast.error(
+				$i18n.t(
+					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
+				)
+			);
+			return;
+		}
+
+		if ((embeddingEngine === 'openai' && OpenAIKey === '') || OpenAIUrl === '') {
+			toast.error($i18n.t('OpenAI URL/Key required.'));
+			return;
+		}
+
+		console.log('Update embedding model attempt:', embeddingModel);
+
+		updateEmbeddingModelLoading = true;
+		const res = await updateEmbeddingConfig(localStorage.token, {
+			embedding_engine: embeddingEngine,
+			embedding_model: embeddingModel,
+			embedding_batch_size: embeddingBatchSize,
+			ollama_config: {
+				key: OllamaKey,
+				url: OllamaUrl
+			},
+			openai_config: {
+				key: OpenAIKey,
+				url: OpenAIUrl
+			}
+		}).catch(async (error) => {
+			toast.error(error);
+			await setEmbeddingConfig();
+			return null;
+		});
+		updateEmbeddingModelLoading = false;
+
+		if (res) {
+			console.log('embeddingModelUpdateHandler:', res);
+			if (res.status === true) {
+				toast.success($i18n.t('Embedding model set to "{{embedding_model}}"', res), {
+					duration: 1000 * 10
+				});
+			}
+		}
+	};
+
+	const rerankingModelUpdateHandler = async () => {
+		console.log('Update reranking model attempt:', rerankingModel);
+
+		updateRerankingModelLoading = true;
+		const res = await updateRerankingConfig(localStorage.token, {
+			reranking_model: rerankingModel
+		}).catch(async (error) => {
+			toast.error(error);
+			await setRerankingConfig();
+			return null;
+		});
+		updateRerankingModelLoading = false;
+
+		if (res) {
+			console.log('rerankingModelUpdateHandler:', res);
+			if (res.status === true) {
+				if (rerankingModel === '') {
+					toast.success($i18n.t('Reranking model disabled', res), {
+						duration: 1000 * 10
+					});
+				} else {
+					toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
+						duration: 1000 * 10
+					});
+				}
+			}
+		}
+	};
+
+	const submitHandler = async () => {
+		await embeddingModelUpdateHandler();
+
+		if (querySettings.hybrid) {
+			await rerankingModelUpdateHandler();
+		}
+
+		if (contentExtractionEngine === 'tika' && tikaServerUrl === '') {
+			toast.error($i18n.t('Tika Server URL required.'));
+			return;
+		}
+		const res = await updateRAGConfig(localStorage.token, {
+			pdf_extract_images: pdfExtractImages,
+			file: {
+				max_size: fileMaxSize === '' ? null : fileMaxSize,
+				max_count: fileMaxCount === '' ? null : fileMaxCount
+			},
+			chunk: {
+				text_splitter: textSplitter,
+				chunk_overlap: chunkOverlap,
+				chunk_size: chunkSize
+			},
+			content_extraction: {
+				engine: contentExtractionEngine,
+				tika_server_url: tikaServerUrl
+			}
+		});
+
+		await updateQuerySettings(localStorage.token, querySettings);
+
+		dispatch('save');
+	};
+
+	const setEmbeddingConfig = async () => {
+		const embeddingConfig = await getEmbeddingConfig(localStorage.token);
+
+		if (embeddingConfig) {
+			embeddingEngine = embeddingConfig.embedding_engine;
+			embeddingModel = embeddingConfig.embedding_model;
+			embeddingBatchSize = embeddingConfig.embedding_batch_size ?? 1;
+
+			OpenAIKey = embeddingConfig.openai_config.key;
+			OpenAIUrl = embeddingConfig.openai_config.url;
+
+			OllamaKey = embeddingConfig.ollama_config.key;
+			OllamaUrl = embeddingConfig.ollama_config.url;
+		}
+	};
+
+	const setRerankingConfig = async () => {
+		const rerankingConfig = await getRerankingConfig(localStorage.token);
+
+		if (rerankingConfig) {
+			rerankingModel = rerankingConfig.reranking_model;
+		}
+	};
+
+	const toggleHybridSearch = async () => {
+		querySettings.hybrid = !querySettings.hybrid;
+		querySettings = await updateQuerySettings(localStorage.token, querySettings);
+	};
+
+	onMount(async () => {
+		await setEmbeddingConfig();
+		await setRerankingConfig();
+
+		querySettings = await getQuerySettings(localStorage.token);
+
+		const res = await getRAGConfig(localStorage.token);
+
+		if (res) {
+			pdfExtractImages = res.pdf_extract_images;
+
+			textSplitter = res.chunk.text_splitter;
+			chunkSize = res.chunk.chunk_size;
+			chunkOverlap = res.chunk.chunk_overlap;
+
+			contentExtractionEngine = res.content_extraction.engine;
+			tikaServerUrl = res.content_extraction.tika_server_url;
+			showTikaServerUrl = contentExtractionEngine === 'tika';
+
+			fileMaxSize = res?.file.max_size ?? '';
+			fileMaxCount = res?.file.max_count ?? '';
+		}
+	});
+</script>
+
+<ResetUploadDirConfirmDialog
+	bind:show={showResetUploadDirConfirm}
+	on:confirm={async () => {
+		const res = await deleteAllFiles(localStorage.token).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Success'));
+		}
+	}}
+/>
+
+<ResetVectorDBConfirmDialog
+	bind:show={showResetConfirm}
+	on:confirm={() => {
+		const res = resetVectorDB(localStorage.token).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Success'));
+		}
+	}}
+/>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={() => {
+		submitHandler();
+	}}
+>
+	<div class=" space-y-2.5 overflow-y-scroll scrollbar-hidden h-full pr-1.5">
+		<div class="flex flex-col gap-0.5">
+			<div class=" mb-0.5 text-sm font-medium">{$i18n.t('General Settings')}</div>
+
+			<div class=" flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Model Engine')}</div>
+				<div class="flex items-center relative">
+					<select
+						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+						bind:value={embeddingEngine}
+						placeholder="Select an embedding model engine"
+						on:change={(e) => {
+							if (e.target.value === 'ollama') {
+								embeddingModel = '';
+							} else if (e.target.value === 'openai') {
+								embeddingModel = 'text-embedding-3-small';
+							} else if (e.target.value === '') {
+								embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
+							}
+						}}
+					>
+						<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
+						<option value="ollama">{$i18n.t('Ollama')}</option>
+						<option value="openai">{$i18n.t('OpenAI')}</option>
+					</select>
+				</div>
+			</div>
+
+			{#if embeddingEngine === 'openai'}
+				<div class="my-0.5 flex gap-2 pr-2">
+					<input
+						class="flex-1 w-full rounded-lg text-sm bg-transparent outline-none"
+						placeholder={$i18n.t('API Base URL')}
+						bind:value={OpenAIUrl}
+						required
+					/>
+
+					<SensitiveInput placeholder={$i18n.t('API Key')} bind:value={OpenAIKey} />
+				</div>
+			{:else if embeddingEngine === 'ollama'}
+				<div class="my-0.5 flex gap-2 pr-2">
+					<input
+						class="flex-1 w-full rounded-lg text-sm bg-transparent outline-none"
+						placeholder={$i18n.t('API Base URL')}
+						bind:value={OllamaUrl}
+						required
+					/>
+
+					<SensitiveInput
+						placeholder={$i18n.t('API Key')}
+						bind:value={OllamaKey}
+						required={false}
+					/>
+				</div>
+			{/if}
+
+			{#if embeddingEngine === 'ollama' || embeddingEngine === 'openai'}
+				<div class="flex mt-0.5 space-x-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Batch Size')}</div>
+					<div class=" flex-1">
+						<input
+							id="steps-range"
+							type="range"
+							min="1"
+							max="2048"
+							step="1"
+							bind:value={embeddingBatchSize}
+							class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+						/>
+					</div>
+					<div class="">
+						<input
+							bind:value={embeddingBatchSize}
+							type="number"
+							class=" bg-transparent text-center w-14"
+							min="-2"
+							max="16000"
+							step="1"
+						/>
+					</div>
+				</div>
+			{/if}
+
+			<div class=" flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						toggleHybridSearch();
+					}}
+					type="button"
+				>
+					{#if querySettings.hybrid === true}
+						<span class="ml-2 self-center">{$i18n.t('On')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+					{/if}
+				</button>
+			</div>
+		</div>
+
+		<hr class="dark:border-gray-850" />
+
+		<div class="space-y-2" />
+		<div>
+			<div class=" mb-2 text-sm font-medium">{$i18n.t('Embedding Model')}</div>
+
+			{#if embeddingEngine === 'ollama'}
+				<div class="flex w-full">
+					<div class="flex-1 mr-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							bind:value={embeddingModel}
+							placeholder={$i18n.t('Set embedding model')}
+							required
+						/>
+					</div>
+				</div>
+			{:else}
+				<div class="flex w-full">
+					<div class="flex-1 mr-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
+								model: embeddingModel.slice(-40)
+							})}
+							bind:value={embeddingModel}
+						/>
+					</div>
+
+					{#if embeddingEngine === ''}
+						<button
+							class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+							on:click={() => {
+								embeddingModelUpdateHandler();
+							}}
+							disabled={updateEmbeddingModelLoading}
+						>
+							{#if updateEmbeddingModelLoading}
+								<div class="self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+									>
+										<style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style>
+										<path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/>
+										<path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/>
+									</svg>
+								</div>
+							{:else}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+									/>
+									<path
+										d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+									/>
+								</svg>
+							{/if}
+						</button>
+					{/if}
+				</div>
+			{/if}
+
+			<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
+				{$i18n.t(
+					'Warning: If you update or change your embedding model, you will need to re-import all documents.'
+				)}
+			</div>
+
+			{#if querySettings.hybrid === true}
+				<div class=" ">
+					<div class=" mb-2 text-sm font-medium">{$i18n.t('Reranking Model')}</div>
+
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
+									model: 'BAAI/bge-reranker-v2-m3'
+								})}
+								bind:value={rerankingModel}
+							/>
+						</div>
+						<button
+							class="px-2.5 bg-gray-50 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+							on:click={() => {
+								rerankingModelUpdateHandler();
+							}}
+							disabled={updateRerankingModelLoading}
+						>
+							{#if updateRerankingModelLoading}
+								<div class="self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+									>
+										<style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style>
+										<path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/>
+										<path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/>
+									</svg>
+								</div>
+							{:else}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+									/>
+									<path
+										d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+									/>
+								</svg>
+							{/if}
+						</button>
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class="">
+			<div class="text-sm font-medium mb-1">{$i18n.t('Content Extraction')}</div>
+
+			<div class="flex w-full justify-between">
+				<div class="self-center text-xs font-medium">{$i18n.t('Engine')}</div>
+				<div class="flex items-center relative">
+					<select
+						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 text-xs bg-transparent outline-none text-right"
+						bind:value={contentExtractionEngine}
+						on:change={(e) => {
+							showTikaServerUrl = e.target.value === 'tika';
+						}}
+					>
+						<option value="">{$i18n.t('Default')} </option>
+						<option value="tika">{$i18n.t('Tika')}</option>
+					</select>
+				</div>
+			</div>
+
+			{#if showTikaServerUrl}
+				<div class="flex w-full mt-1">
+					<div class="flex-1 mr-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							placeholder={$i18n.t('Enter Tika Server URL')}
+							bind:value={tikaServerUrl}
+						/>
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class=" ">
+			<div class=" text-sm font-medium mb-1">{$i18n.t('Query Params')}</div>
+
+			<div class=" flex gap-1.5">
+				<div class="flex flex-col w-full gap-1">
+					<div class=" text-xs font-medium w-full">{$i18n.t('Top K')}</div>
+
+					<div class="w-full">
+						<input
+							class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="number"
+							placeholder={$i18n.t('Enter Top K')}
+							bind:value={querySettings.k}
+							autocomplete="off"
+							min="0"
+						/>
+					</div>
+				</div>
+
+				{#if querySettings.hybrid === true}
+					<div class=" flex flex-col w-full gap-1">
+						<div class="text-xs font-medium w-full">
+							{$i18n.t('Minimum Score')}
+						</div>
+
+						<div class="w-full">
+							<input
+								class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="number"
+								step="0.01"
+								placeholder={$i18n.t('Enter Score')}
+								bind:value={querySettings.r}
+								autocomplete="off"
+								min="0.0"
+								title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
+							/>
+						</div>
+					</div>
+				{/if}
+			</div>
+
+			{#if querySettings.hybrid === true}
+				<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+					{$i18n.t(
+						'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
+					)}
+				</div>
+			{/if}
+
+			<div class="mt-2">
+				<div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
+				<Tooltip
+					content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+					placement="top-start"
+				>
+					<Textarea
+						bind:value={querySettings.template}
+						placeholder={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+					/>
+				</Tooltip>
+			</div>
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class=" ">
+			<div class="mb-1 text-sm font-medium">{$i18n.t('Chunk Params')}</div>
+
+			<div class="flex w-full justify-between mb-1.5">
+				<div class="self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
+				<div class="flex items-center relative">
+					<select
+						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 text-xs bg-transparent outline-none text-right"
+						bind:value={textSplitter}
+					>
+						<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
+						<option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
+					</select>
+				</div>
+			</div>
+
+			<div class=" flex gap-1.5">
+				<div class="  w-full justify-between">
+					<div class="self-center text-xs font-medium min-w-fit mb-1">{$i18n.t('Chunk Size')}</div>
+					<div class="self-center">
+						<input
+							class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="number"
+							placeholder={$i18n.t('Enter Chunk Size')}
+							bind:value={chunkSize}
+							autocomplete="off"
+							min="0"
+						/>
+					</div>
+				</div>
+
+				<div class="w-full">
+					<div class=" self-center text-xs font-medium min-w-fit mb-1">
+						{$i18n.t('Chunk Overlap')}
+					</div>
+
+					<div class="self-center">
+						<input
+							class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="number"
+							placeholder={$i18n.t('Enter Chunk Overlap')}
+							bind:value={chunkOverlap}
+							autocomplete="off"
+							min="0"
+						/>
+					</div>
+				</div>
+			</div>
+
+			<div class="my-2">
+				<div class="flex justify-between items-center text-xs">
+					<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
+
+					<div>
+						<Switch bind:state={pdfExtractImages} />
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class="">
+			<div class="text-sm font-medium mb-1">{$i18n.t('Files')}</div>
+
+			<div class=" flex gap-1.5">
+				<div class="w-full">
+					<div class=" self-center text-xs font-medium min-w-fit mb-1">
+						{$i18n.t('Max Upload Size')}
+					</div>
+
+					<div class="self-center">
+						<Tooltip
+							content={$i18n.t(
+								'The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.'
+							)}
+							placement="top-start"
+						>
+							<input
+								class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="number"
+								placeholder={$i18n.t('Leave empty for unlimited')}
+								bind:value={fileMaxSize}
+								autocomplete="off"
+								min="0"
+							/>
+						</Tooltip>
+					</div>
+				</div>
+
+				<div class="  w-full">
+					<div class="self-center text-xs font-medium min-w-fit mb-1">
+						{$i18n.t('Max Upload Count')}
+					</div>
+					<div class="self-center">
+						<Tooltip
+							content={$i18n.t(
+								'The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.'
+							)}
+							placement="top-start"
+						>
+							<input
+								class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="number"
+								placeholder={$i18n.t('Leave empty for unlimited')}
+								bind:value={fileMaxCount}
+								autocomplete="off"
+								min="0"
+							/>
+						</Tooltip>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div>
+			<button
+				class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={() => {
+					showResetUploadDirConfirm = true;
+				}}
+				type="button"
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 24 24"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM9.75 14.25a.75.75 0 0 0 0 1.5H15a.75.75 0 0 0 0-1.5H9.75Z"
+							clip-rule="evenodd"
+						/>
+						<path
+							d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">{$i18n.t('Reset Upload Directory')}</div>
+			</button>
+
+			<button
+				class=" flex rounded-xl py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={() => {
+					showResetConfirm = true;
+				}}
+				type="button"
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">
+					{$i18n.t('Reset Vector Storage/Knowledge')}
+				</div>
+			</button>
+		</div>
+	</div>
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Evaluations.svelte b/src/lib/components/admin/Settings/Evaluations.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c0d1b4f32f480587e79717eb9706f331f61504cb
--- /dev/null
+++ b/src/lib/components/admin/Settings/Evaluations.svelte
@@ -0,0 +1,159 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { models, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+	import { getModels } from '$lib/apis';
+	import { getConfig, updateConfig } from '$lib/apis/evaluations';
+
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import Model from './Evaluations/Model.svelte';
+	import ArenaModelModal from './Evaluations/ArenaModelModal.svelte';
+
+	const i18n = getContext('i18n');
+
+	let config = null;
+	let showAddModel = false;
+
+	const submitHandler = async () => {
+		config = await updateConfig(localStorage.token, config).catch((err) => {
+			toast.error(err);
+			return null;
+		});
+
+		if (config) {
+			toast.success('Settings saved successfully');
+			models.set(await getModels(localStorage.token));
+		}
+	};
+
+	const addModelHandler = async (model) => {
+		config.EVALUATION_ARENA_MODELS.push(model);
+		config.EVALUATION_ARENA_MODELS = [...config.EVALUATION_ARENA_MODELS];
+
+		await submitHandler();
+		models.set(await getModels(localStorage.token));
+	};
+
+	const editModelHandler = async (model, modelIdx) => {
+		config.EVALUATION_ARENA_MODELS[modelIdx] = model;
+		config.EVALUATION_ARENA_MODELS = [...config.EVALUATION_ARENA_MODELS];
+
+		await submitHandler();
+		models.set(await getModels(localStorage.token));
+	};
+
+	const deleteModelHandler = async (modelIdx) => {
+		config.EVALUATION_ARENA_MODELS = config.EVALUATION_ARENA_MODELS.filter(
+			(m, mIdx) => mIdx !== modelIdx
+		);
+
+		await submitHandler();
+		models.set(await getModels(localStorage.token));
+	};
+
+	onMount(async () => {
+		if ($user.role === 'admin') {
+			config = await getConfig(localStorage.token).catch((err) => {
+				toast.error(err);
+				return null;
+			});
+		}
+	});
+</script>
+
+<ArenaModelModal
+	bind:show={showAddModel}
+	on:submit={async (e) => {
+		addModelHandler(e.detail);
+	}}
+/>
+
+<form
+	class="flex flex-col h-full justify-between text-sm"
+	on:submit|preventDefault={() => {
+		submitHandler();
+		dispatch('save');
+	}}
+>
+	<div class="overflow-y-scroll scrollbar-hidden h-full">
+		{#if config !== null}
+			<div class="">
+				<div class="text-sm font-medium mb-2">{$i18n.t('General Settings')}</div>
+
+				<div class=" mb-2">
+					<div class="flex justify-between items-center text-xs">
+						<div class=" text-xs font-medium">{$i18n.t('Arena Models')}</div>
+
+						<Tooltip content={$i18n.t(`Message rating should be enabled to use this feature`)}>
+							<Switch bind:state={config.ENABLE_EVALUATION_ARENA_MODELS} />
+						</Tooltip>
+					</div>
+				</div>
+
+				{#if config.ENABLE_EVALUATION_ARENA_MODELS}
+					<hr class=" border-gray-50 dark:border-gray-700/10 my-2" />
+
+					<div class="flex justify-between items-center mb-2">
+						<div class="text-sm font-medium">{$i18n.t('Manage Arena Models')}</div>
+
+						<div>
+							<Tooltip content={$i18n.t('Add Arena Model')}>
+								<button
+									class="p-1"
+									type="button"
+									on:click={() => {
+										showAddModel = true;
+									}}
+								>
+									<Plus />
+								</button>
+							</Tooltip>
+						</div>
+					</div>
+
+					<div class="flex flex-col gap-2">
+						{#if (config?.EVALUATION_ARENA_MODELS ?? []).length > 0}
+							{#each config.EVALUATION_ARENA_MODELS as model, index}
+								<Model
+									{model}
+									on:edit={(e) => {
+										editModelHandler(e.detail, index);
+									}}
+									on:delete={(e) => {
+										deleteModelHandler(index);
+									}}
+								/>
+							{/each}
+						{:else}
+							<div class=" text-center text-xs text-gray-500">
+								{$i18n.t(
+									`Using the default arena model with all models. Click the plus button to add custom models.`
+								)}
+							</div>
+						{/if}
+					</div>
+				{/if}
+			</div>
+		{:else}
+			<div class="flex h-full justify-center">
+				<div class="my-auto">
+					<Spinner className="size-6" />
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ac45e7f2c67ae65706d4cd7eadcdd0535bb863aa
--- /dev/null
+++ b/src/lib/components/admin/Settings/Evaluations/ArenaModelModal.svelte
@@ -0,0 +1,431 @@
+<script>
+	import { createEventDispatcher, getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import { models } from '$lib/stores';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import Minus from '$lib/components/icons/Minus.svelte';
+	import PencilSolid from '$lib/components/icons/PencilSolid.svelte';
+	import { toast } from 'svelte-sonner';
+	import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
+
+	export let show = false;
+	export let edit = false;
+
+	export let model = null;
+
+	let name = '';
+	let id = '';
+
+	$: if (name) {
+		generateId();
+	}
+
+	const generateId = () => {
+		if (!edit) {
+			id = name
+				.toLowerCase()
+				.replace(/[^a-z0-9]/g, '-')
+				.replace(/-+/g, '-')
+				.replace(/^-|-$/g, '');
+		}
+	};
+
+	let profileImageUrl = '/favicon.png';
+	let description = '';
+
+	let selectedModelId = '';
+	let modelIds = [];
+	let filterMode = 'include';
+
+	let accessControl = {};
+
+	let imageInputElement;
+	let loading = false;
+
+	const addModelHandler = () => {
+		if (selectedModelId) {
+			modelIds = [...modelIds, selectedModelId];
+			selectedModelId = '';
+		}
+	};
+
+	const submitHandler = () => {
+		loading = true;
+
+		if (!name || !id) {
+			loading = false;
+			toast.error('Name and ID are required, please fill them out');
+			return;
+		}
+
+		if (!edit) {
+			if ($models.find((model) => model.name === name)) {
+				loading = false;
+				name = '';
+				toast.error('Model name already exists, please choose a different one');
+				return;
+			}
+		}
+
+		const model = {
+			id: id,
+			name: name,
+			meta: {
+				profile_image_url: profileImageUrl,
+				description: description || null,
+				model_ids: modelIds.length > 0 ? modelIds : null,
+				filter_mode: modelIds.length > 0 ? (filterMode ? filterMode : null) : null,
+				access_control: accessControl
+			}
+		};
+
+		dispatch('submit', model);
+		loading = false;
+		show = false;
+
+		name = '';
+		id = '';
+		profileImageUrl = '/favicon.png';
+		description = '';
+		modelIds = [];
+		selectedModelId = '';
+	};
+
+	const initModel = () => {
+		if (model) {
+			name = model.name;
+			id = model.id;
+			profileImageUrl = model.meta.profile_image_url;
+			description = model.meta.description;
+			modelIds = model.meta.model_ids || [];
+			filterMode = model.meta?.filter_mode ?? 'include';
+			accessControl = 'access_control' in model.meta ? model.meta.access_control : {};
+		}
+	};
+
+	$: if (show) {
+		initModel();
+	}
+
+	onMount(() => {
+		initModel();
+	});
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center font-primary">
+				{#if edit}
+					{$i18n.t('Edit Arena Model')}
+				{:else}
+					{$i18n.t('Add Arena Model')}
+				{/if}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="px-1">
+						<div class="flex justify-center pb-3">
+							<input
+								bind:this={imageInputElement}
+								type="file"
+								hidden
+								accept="image/*"
+								on:change={(e) => {
+									const files = e.target.files ?? [];
+									let reader = new FileReader();
+									reader.onload = (event) => {
+										let originalImageUrl = `${event.target.result}`;
+
+										const img = new Image();
+										img.src = originalImageUrl;
+
+										img.onload = function () {
+											const canvas = document.createElement('canvas');
+											const ctx = canvas.getContext('2d');
+
+											// Calculate the aspect ratio of the image
+											const aspectRatio = img.width / img.height;
+
+											// Calculate the new width and height to fit within 250x250
+											let newWidth, newHeight;
+											if (aspectRatio > 1) {
+												newWidth = 250 * aspectRatio;
+												newHeight = 250;
+											} else {
+												newWidth = 250;
+												newHeight = 250 / aspectRatio;
+											}
+
+											// Set the canvas size
+											canvas.width = 250;
+											canvas.height = 250;
+
+											// Calculate the position to center the image
+											const offsetX = (250 - newWidth) / 2;
+											const offsetY = (250 - newHeight) / 2;
+
+											// Draw the image on the canvas
+											ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
+
+											// Get the base64 representation of the compressed image
+											const compressedSrc = canvas.toDataURL('image/jpeg');
+
+											// Display the compressed image
+											profileImageUrl = compressedSrc;
+
+											e.target.files = null;
+										};
+									};
+
+									if (
+										files.length > 0 &&
+										['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(
+											files[0]['type']
+										)
+									) {
+										reader.readAsDataURL(files[0]);
+									}
+								}}
+							/>
+
+							<button
+								class="relative rounded-full w-fit h-fit shrink-0"
+								type="button"
+								on:click={() => {
+									imageInputElement.click();
+								}}
+							>
+								<img
+									src={profileImageUrl}
+									class="size-16 rounded-full object-cover shrink-0"
+									alt="Profile"
+								/>
+
+								<div
+									class="absolute flex justify-center rounded-full bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-gray-700 bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-50"
+								>
+									<div class="my-auto text-white">
+										<PencilSolid className="size-4" />
+									</div>
+								</div>
+							</button>
+						</div>
+						<div class="flex gap-2">
+							<div class="flex flex-col w-full">
+								<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Name')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										type="text"
+										bind:value={name}
+										placeholder={$i18n.t('Model Name')}
+										autocomplete="off"
+										required
+									/>
+								</div>
+							</div>
+
+							<div class="flex flex-col w-full">
+								<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('ID')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										type="text"
+										bind:value={id}
+										placeholder={$i18n.t('Model ID')}
+										autocomplete="off"
+										required
+										disabled={edit}
+									/>
+								</div>
+							</div>
+						</div>
+
+						<div class="flex flex-col w-full mt-2">
+							<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Description')}</div>
+
+							<div class="flex-1">
+								<input
+									class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+									type="text"
+									bind:value={description}
+									placeholder={$i18n.t('Enter description')}
+									autocomplete="off"
+								/>
+							</div>
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div class="my-2 -mx-2">
+							<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
+								<AccessControl bind:accessControl />
+							</div>
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div class="flex flex-col w-full">
+							<div class="mb-1 flex justify-between">
+								<div class="text-xs text-gray-500">{$i18n.t('Models')}</div>
+
+								<div>
+									<button
+										class=" text-xs text-gray-500"
+										type="button"
+										on:click={() => {
+											filterMode = filterMode === 'include' ? 'exclude' : 'include';
+										}}
+									>
+										{#if filterMode === 'include'}
+											{$i18n.t('Include')}
+										{:else}
+											{$i18n.t('Exclude')}
+										{/if}
+									</button>
+								</div>
+							</div>
+
+							{#if modelIds.length > 0}
+								<div class="flex flex-col">
+									{#each modelIds as modelId, modelIdx}
+										<div class=" flex gap-2 w-full justify-between items-center">
+											<div class=" text-sm flex-1 py-1 rounded-lg">
+												{$models.find((model) => model.id === modelId)?.name}
+											</div>
+											<div class="flex-shrink-0">
+												<button
+													type="button"
+													on:click={() => {
+														modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
+													}}
+												>
+													<Minus strokeWidth="2" className="size-3.5" />
+												</button>
+											</div>
+										</div>
+									{/each}
+								</div>
+							{:else}
+								<div class="text-gray-500 text-xs text-center py-2">
+									{$i18n.t('Leave empty to include all models or select specific models')}
+								</div>
+							{/if}
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div class="flex items-center">
+							<select
+								class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
+									? ''
+									: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+								bind:value={selectedModelId}
+							>
+								<option value="">{$i18n.t('Select a model')}</option>
+								{#each $models.filter((m) => m?.owned_by !== 'arena') as model}
+									<option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
+								{/each}
+							</select>
+
+							<div>
+								<button
+									type="button"
+									on:click={() => {
+										addModelHandler();
+									}}
+								>
+									<Plus className="size-3.5" strokeWidth="2" />
+								</button>
+							</div>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
+						{#if edit}
+							<button
+								class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-950 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
+								type="button"
+								on:click={() => {
+									dispatch('delete', model);
+									show = false;
+								}}
+							>
+								{$i18n.t('Delete')}
+							</button>
+						{/if}
+
+						<button
+							class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-950 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Save')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Settings/Evaluations/Model.svelte b/src/lib/components/admin/Settings/Evaluations/Model.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1edf58730a1669ab3a1e5755c2bff372c514e455
--- /dev/null
+++ b/src/lib/components/admin/Settings/Evaluations/Model.svelte
@@ -0,0 +1,63 @@
+<script lang="ts">
+	import { getContext, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import Cog6 from '$lib/components/icons/Cog6.svelte';
+	import ArenaModelModal from './ArenaModelModal.svelte';
+	export let model;
+
+	let showModel = false;
+</script>
+
+<ArenaModelModal
+	bind:show={showModel}
+	edit={true}
+	{model}
+	on:submit={async (e) => {
+		dispatch('edit', e.detail);
+	}}
+	on:delete={async () => {
+		dispatch('delete');
+	}}
+/>
+
+<div class="py-0.5">
+	<div class="flex justify-between items-center mb-1">
+		<div class="flex flex-col flex-1">
+			<div class="flex gap-2.5 items-center">
+				<img
+					src={model.meta.profile_image_url}
+					alt={model.name}
+					class="size-8 rounded-full object-cover shrink-0"
+				/>
+
+				<div class="w-full flex flex-col">
+					<div class="flex items-center gap-1">
+						<div class="flex-shrink-0 line-clamp-1">
+							{model.name}
+						</div>
+					</div>
+
+					<div class="flex items-center gap-1">
+						<div class=" text-xs w-full text-gray-500 bg-transparent line-clamp-1">
+							{model?.meta?.description ?? model.id}
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class="flex items-center">
+			<button
+				class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+				type="button"
+				on:click={() => {
+					showModel = true;
+				}}
+			>
+				<Cog6 />
+			</button>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Settings/General.svelte b/src/lib/components/admin/Settings/General.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8fabe5bce9ca3e6b2339f3c27f15a44b95ae99da
--- /dev/null
+++ b/src/lib/components/admin/Settings/General.svelte
@@ -0,0 +1,385 @@
+<script lang="ts">
+	import { getBackendConfig, getWebhookUrl, updateWebhookUrl } from '$lib/apis';
+	import {
+		getAdminConfig,
+		getLdapConfig,
+		getLdapServer,
+		updateAdminConfig,
+		updateLdapConfig,
+		updateLdapServer
+	} from '$lib/apis/auths';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { config } from '$lib/stores';
+	import { onMount, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	let adminConfig = null;
+	let webhookUrl = '';
+
+	// LDAP
+	let ENABLE_LDAP = false;
+	let LDAP_SERVER = {
+		label: '',
+		host: '',
+		port: '',
+		attribute_for_username: 'uid',
+		app_dn: '',
+		app_dn_password: '',
+		search_base: '',
+		search_filters: '',
+		use_tls: false,
+		certificate_path: '',
+		ciphers: ''
+	};
+
+	const updateLdapServerHandler = async () => {
+		if (!ENABLE_LDAP) return;
+		const res = await updateLdapServer(localStorage.token, LDAP_SERVER).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+		if (res) {
+			toast.success($i18n.t('LDAP server updated'));
+		}
+	};
+
+	const updateHandler = async () => {
+		webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
+		const res = await updateAdminConfig(localStorage.token, adminConfig);
+		await updateLdapServerHandler();
+
+		if (res) {
+			saveHandler();
+		} else {
+			toast.error(i18n.t('Failed to update settings'));
+		}
+	};
+
+	onMount(async () => {
+		await Promise.all([
+			(async () => {
+				adminConfig = await getAdminConfig(localStorage.token);
+			})(),
+
+			(async () => {
+				webhookUrl = await getWebhookUrl(localStorage.token);
+			})(),
+			(async () => {
+				LDAP_SERVER = await getLdapServer(localStorage.token);
+			})()
+		]);
+
+		const ldapConfig = await getLdapConfig(localStorage.token);
+		ENABLE_LDAP = ldapConfig.ENABLE_LDAP;
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		updateHandler();
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
+		{#if adminConfig !== null}
+			<div>
+				<div class=" mb-3 text-sm font-medium">{$i18n.t('General Settings')}</div>
+
+				<div class="  flex w-full justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable New Sign Ups')}</div>
+
+					<Switch bind:state={adminConfig.ENABLE_SIGNUP} />
+				</div>
+
+				<div class="  my-3 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Default User Role')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 w-fit pr-8 rounded px-2 text-xs bg-transparent outline-none text-right"
+							bind:value={adminConfig.DEFAULT_USER_ROLE}
+							placeholder="Select a role"
+						>
+							<option value="pending">{$i18n.t('pending')}</option>
+							<option value="user">{$i18n.t('user')}</option>
+							<option value="admin">{$i18n.t('admin')}</option>
+						</select>
+					</div>
+				</div>
+
+				<div class="  flex w-full justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable API Key Auth')}</div>
+
+					<Switch bind:state={adminConfig.ENABLE_API_KEY} />
+				</div>
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+				<div class="my-3 flex w-full items-center justify-between pr-2">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Show Admin Details in Account Pending Overlay')}
+					</div>
+
+					<Switch bind:state={adminConfig.SHOW_ADMIN_DETAILS} />
+				</div>
+
+				<div class="my-3 flex w-full items-center justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable Community Sharing')}</div>
+
+					<Switch bind:state={adminConfig.ENABLE_COMMUNITY_SHARING} />
+				</div>
+
+				<div class="my-3 flex w-full items-center justify-between pr-2">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Enable Message Rating')}</div>
+
+					<Switch bind:state={adminConfig.ENABLE_MESSAGE_RATING} />
+				</div>
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+				<div class=" w-full justify-between">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
+					</div>
+
+					<div class="flex mt-2 space-x-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							placeholder={`e.g.) "30m","1h", "10d". `}
+							bind:value={adminConfig.JWT_EXPIRES_IN}
+						/>
+					</div>
+
+					<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+						{$i18n.t('Valid time units:')}
+						<span class=" text-gray-300 font-medium"
+							>{$i18n.t("'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.")}</span
+						>
+					</div>
+				</div>
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+				<div class=" w-full justify-between">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
+					</div>
+
+					<div class="flex mt-2 space-x-2">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							placeholder={`https://example.com/webhook`}
+							bind:value={webhookUrl}
+						/>
+					</div>
+				</div>
+			</div>
+		{/if}
+
+		<hr class=" border-gray-50 dark:border-gray-850" />
+
+		<div class=" space-y-3">
+			<div class="mt-2 space-y-2 pr-1.5">
+				<div class="flex justify-between items-center text-sm">
+					<div class="  font-medium">{$i18n.t('LDAP')}</div>
+
+					<div class="mt-1">
+						<Switch
+							bind:state={ENABLE_LDAP}
+							on:change={async () => {
+								updateLdapConfig(localStorage.token, ENABLE_LDAP);
+							}}
+						/>
+					</div>
+				</div>
+
+				{#if ENABLE_LDAP}
+					<div class="flex flex-col gap-1">
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Label')}
+								</div>
+								<input
+									class="w-full bg-transparent outline-none py-0.5"
+									required
+									placeholder={$i18n.t('Enter server label')}
+									bind:value={LDAP_SERVER.label}
+								/>
+							</div>
+							<div class="w-full"></div>
+						</div>
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Host')}
+								</div>
+								<input
+									class="w-full bg-transparent outline-none py-0.5"
+									required
+									placeholder={$i18n.t('Enter server host')}
+									bind:value={LDAP_SERVER.host}
+								/>
+							</div>
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Port')}
+								</div>
+								<Tooltip
+									placement="top-start"
+									content={$i18n.t('Default to 389 or 636 if TLS is enabled')}
+									className="w-full"
+								>
+									<input
+										class="w-full bg-transparent outline-none py-0.5"
+										type="number"
+										placeholder={$i18n.t('Enter server port')}
+										bind:value={LDAP_SERVER.port}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Application DN')}
+								</div>
+								<Tooltip
+									content={$i18n.t('The Application Account DN you bind with for search')}
+									placement="top-start"
+								>
+									<input
+										class="w-full bg-transparent outline-none py-0.5"
+										required
+										placeholder={$i18n.t('Enter Application DN')}
+										bind:value={LDAP_SERVER.app_dn}
+									/>
+								</Tooltip>
+							</div>
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Application DN Password')}
+								</div>
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Application DN Password')}
+									bind:value={LDAP_SERVER.app_dn_password}
+								/>
+							</div>
+						</div>
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Attribute for Username')}
+								</div>
+								<Tooltip
+									content={$i18n.t(
+										'The LDAP attribute that maps to the username that users use to sign in.'
+									)}
+									placement="top-start"
+								>
+									<input
+										class="w-full bg-transparent outline-none py-0.5"
+										required
+										placeholder={$i18n.t('Example: sAMAccountName or uid or userPrincipalName')}
+										bind:value={LDAP_SERVER.attribute_for_username}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Search Base')}
+								</div>
+								<Tooltip content={$i18n.t('The base to search for users')} placement="top-start">
+									<input
+										class="w-full bg-transparent outline-none py-0.5"
+										required
+										placeholder={$i18n.t('Example: ou=users,dc=foo,dc=example')}
+										bind:value={LDAP_SERVER.search_base}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+						<div class="flex w-full gap-2">
+							<div class="w-full">
+								<div class=" self-center text-xs font-medium min-w-fit mb-1">
+									{$i18n.t('Search Filters')}
+								</div>
+								<input
+									class="w-full bg-transparent outline-none py-0.5"
+									placeholder={$i18n.t('Example: (&(objectClass=inetOrgPerson)(uid=%s))')}
+									bind:value={LDAP_SERVER.search_filters}
+								/>
+							</div>
+						</div>
+						<div class="text-xs text-gray-400 dark:text-gray-500">
+							<a
+								class=" text-gray-300 font-medium underline"
+								href="https://ldap.com/ldap-filters/"
+								target="_blank"
+							>
+								{$i18n.t('Click here for filter guides.')}
+							</a>
+						</div>
+						<div>
+							<div class="flex justify-between items-center text-sm">
+								<div class="  font-medium">{$i18n.t('TLS')}</div>
+
+								<div class="mt-1">
+									<Switch bind:state={LDAP_SERVER.use_tls} />
+								</div>
+							</div>
+							{#if LDAP_SERVER.use_tls}
+								<div class="flex w-full gap-2">
+									<div class="w-full">
+										<div class=" self-center text-xs font-medium min-w-fit mb-1 mt-1">
+											{$i18n.t('Certificate Path')}
+										</div>
+										<input
+											class="w-full bg-transparent outline-none py-0.5"
+											required
+											placeholder={$i18n.t('Enter certificate path')}
+											bind:value={LDAP_SERVER.certificate_path}
+										/>
+									</div>
+								</div>
+								<div class="flex w-full gap-2">
+									<div class="w-full">
+										<div class=" self-center text-xs font-medium min-w-fit mb-1">
+											{$i18n.t('Ciphers')}
+										</div>
+										<Tooltip content={$i18n.t('Default to ALL')} placement="top-start">
+											<input
+												class="w-full bg-transparent outline-none py-0.5"
+												placeholder={$i18n.t('Example: ALL')}
+												bind:value={LDAP_SERVER.ciphers}
+											/>
+										</Tooltip>
+									</div>
+									<div class="w-full"></div>
+								</div>
+							{/if}
+						</div>
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Images.svelte b/src/lib/components/admin/Settings/Images.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c76e192bf3721e607faaa2dfec7b3c0a5dc8add4
--- /dev/null
+++ b/src/lib/components/admin/Settings/Images.svelte
@@ -0,0 +1,688 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	import { config as backendConfig, user } from '$lib/stores';
+
+	import { getBackendConfig } from '$lib/apis';
+	import {
+		getImageGenerationModels,
+		getImageGenerationConfig,
+		updateImageGenerationConfig,
+		getConfig,
+		updateConfig,
+		verifyConfigUrl
+	} from '$lib/apis/images';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	let loading = false;
+
+	let config = null;
+	let imageGenerationConfig = null;
+
+	let models = null;
+
+	let samplers = [
+		'DPM++ 2M',
+		'DPM++ SDE',
+		'DPM++ 2M SDE',
+		'DPM++ 2M SDE Heun',
+		'DPM++ 2S a',
+		'DPM++ 3M SDE',
+		'Euler a',
+		'Euler',
+		'LMS',
+		'Heun',
+		'DPM2',
+		'DPM2 a',
+		'DPM fast',
+		'DPM adaptive',
+		'Restart',
+		'DDIM',
+		'DDIM CFG++',
+		'PLMS',
+		'UniPC'
+	];
+
+	let schedulers = [
+		'Automatic',
+		'Uniform',
+		'Karras',
+		'Exponential',
+		'Polyexponential',
+		'SGM Uniform',
+		'KL Optimal',
+		'Align Your Steps',
+		'Simple',
+		'Normal',
+		'DDIM',
+		'Beta'
+	];
+
+	let requiredWorkflowNodes = [
+		{
+			type: 'prompt',
+			key: 'text',
+			node_ids: ''
+		},
+		{
+			type: 'model',
+			key: 'ckpt_name',
+			node_ids: ''
+		},
+		{
+			type: 'width',
+			key: 'width',
+			node_ids: ''
+		},
+		{
+			type: 'height',
+			key: 'height',
+			node_ids: ''
+		},
+		{
+			type: 'steps',
+			key: 'steps',
+			node_ids: ''
+		},
+		{
+			type: 'seed',
+			key: 'seed',
+			node_ids: ''
+		}
+	];
+
+	const getModels = async () => {
+		models = await getImageGenerationModels(localStorage.token).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+	};
+
+	const updateConfigHandler = async () => {
+		const res = await updateConfig(localStorage.token, config).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			config = res;
+		}
+
+		if (config.enabled) {
+			backendConfig.set(await getBackendConfig());
+			getModels();
+		}
+	};
+
+	const validateJSON = (json) => {
+		try {
+			const obj = JSON.parse(json);
+
+			if (obj && typeof obj === 'object') {
+				return true;
+			}
+		} catch (e) {}
+		return false;
+	};
+
+	const saveHandler = async () => {
+		loading = true;
+
+		if (config?.comfyui?.COMFYUI_WORKFLOW) {
+			if (!validateJSON(config.comfyui.COMFYUI_WORKFLOW)) {
+				toast.error('Invalid JSON format for ComfyUI Workflow.');
+				loading = false;
+				return;
+			}
+		}
+
+		if (config?.comfyui?.COMFYUI_WORKFLOW) {
+			config.comfyui.COMFYUI_WORKFLOW_NODES = requiredWorkflowNodes.map((node) => {
+				return {
+					type: node.type,
+					key: node.key,
+					node_ids:
+						node.node_ids.trim() === '' ? [] : node.node_ids.split(',').map((id) => id.trim())
+				};
+			});
+		}
+
+		await updateConfig(localStorage.token, config).catch((error) => {
+			toast.error(error);
+			loading = false;
+			return null;
+		});
+
+		await updateImageGenerationConfig(localStorage.token, imageGenerationConfig).catch((error) => {
+			toast.error(error);
+			loading = false;
+			return null;
+		});
+
+		getModels();
+		dispatch('save');
+		loading = false;
+	};
+
+	onMount(async () => {
+		if ($user.role === 'admin') {
+			const res = await getConfig(localStorage.token).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (res) {
+				config = res;
+			}
+
+			if (config.enabled) {
+				getModels();
+			}
+
+			if (config.comfyui.COMFYUI_WORKFLOW) {
+				config.comfyui.COMFYUI_WORKFLOW = JSON.stringify(
+					JSON.parse(config.comfyui.COMFYUI_WORKFLOW),
+					null,
+					2
+				);
+			}
+
+			requiredWorkflowNodes = requiredWorkflowNodes.map((node) => {
+				const n = config.comfyui.COMFYUI_WORKFLOW_NODES.find((n) => n.type === node.type) ?? node;
+
+				console.log(n);
+
+				return {
+					type: n.type,
+					key: n.key,
+					node_ids: typeof n.node_ids === 'string' ? n.node_ids : n.node_ids.join(',')
+				};
+			});
+
+			const imageConfigRes = await getImageGenerationConfig(localStorage.token).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (imageConfigRes) {
+				imageGenerationConfig = imageConfigRes;
+			}
+		}
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden pr-2">
+		{#if config && imageGenerationConfig}
+			<div>
+				<div class=" mb-1 text-sm font-medium">{$i18n.t('Image Settings')}</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">
+							{$i18n.t('Image Generation (Experimental)')}
+						</div>
+
+						<div class="px-1">
+							<Switch
+								bind:state={config.enabled}
+								on:change={(e) => {
+									const enabled = e.detail;
+
+									if (enabled) {
+										if (
+											config.engine === 'automatic1111' &&
+											config.automatic1111.AUTOMATIC1111_BASE_URL === ''
+										) {
+											toast.error($i18n.t('AUTOMATIC1111 Base URL is required.'));
+											config.enabled = false;
+										} else if (
+											config.engine === 'comfyui' &&
+											config.comfyui.COMFYUI_BASE_URL === ''
+										) {
+											toast.error($i18n.t('ComfyUI Base URL is required.'));
+											config.enabled = false;
+										} else if (config.engine === 'openai' && config.openai.OPENAI_API_KEY === '') {
+											toast.error($i18n.t('OpenAI API Key is required.'));
+											config.enabled = false;
+										}
+									}
+
+									updateConfigHandler();
+								}}
+							/>
+						</div>
+					</div>
+				</div>
+
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Image Generation Engine')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							bind:value={config.engine}
+							placeholder={$i18n.t('Select Engine')}
+							on:change={async () => {
+								updateConfigHandler();
+							}}
+						>
+							<option value="openai">{$i18n.t('Default (Open AI)')}</option>
+							<option value="comfyui">{$i18n.t('ComfyUI')}</option>
+							<option value="automatic1111">{$i18n.t('Automatic1111')}</option>
+						</select>
+					</div>
+				</div>
+			</div>
+			<hr class=" dark:border-gray-850" />
+
+			<div class="flex flex-col gap-2">
+				{#if (config?.engine ?? 'automatic1111') === 'automatic1111'}
+					<div>
+						<div class=" mb-2 text-sm font-medium">{$i18n.t('AUTOMATIC1111 Base URL')}</div>
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
+									bind:value={config.automatic1111.AUTOMATIC1111_BASE_URL}
+								/>
+							</div>
+							<button
+								class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+								type="button"
+								on:click={async () => {
+									await updateConfigHandler();
+									const res = await verifyConfigUrl(localStorage.token).catch((error) => {
+										toast.error(error);
+										return null;
+									});
+
+									if (res) {
+										toast.success($i18n.t('Server connection verified'));
+									}
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							</button>
+						</div>
+
+						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t('Include `--api` flag when running stable-diffusion-webui')}
+							<a
+								class=" text-gray-300 font-medium"
+								href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/3734"
+								target="_blank"
+							>
+								{$i18n.t('(e.g. `sh webui.sh --api`)')}
+							</a>
+						</div>
+					</div>
+
+					<div>
+						<div class=" mb-2 text-sm font-medium">
+							{$i18n.t('AUTOMATIC1111 Api Auth String')}
+						</div>
+						<SensitiveInput
+							placeholder={$i18n.t('Enter api auth string (e.g. username:password)')}
+							bind:value={config.automatic1111.AUTOMATIC1111_API_AUTH}
+							required={false}
+						/>
+
+						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t('Include `--api-auth` flag when running stable-diffusion-webui')}
+							<a
+								class=" text-gray-300 font-medium"
+								href="https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/13993"
+								target="_blank"
+							>
+								{$i18n
+									.t('(e.g. `sh webui.sh --api --api-auth username_password`)')
+									.replace('_', ':')}
+							</a>
+						</div>
+					</div>
+
+					<!---Sampler-->
+					<div>
+						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Sampler')}</div>
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<Tooltip content={$i18n.t('Enter Sampler (e.g. Euler a)')} placement="top-start">
+									<input
+										list="sampler-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										placeholder={$i18n.t('Enter Sampler (e.g. Euler a)')}
+										bind:value={config.automatic1111.AUTOMATIC1111_SAMPLER}
+									/>
+
+									<datalist id="sampler-list">
+										{#each samplers ?? [] as sampler}
+											<option value={sampler}>{sampler}</option>
+										{/each}
+									</datalist>
+								</Tooltip>
+							</div>
+						</div>
+					</div>
+					<!---Scheduler-->
+					<div>
+						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Scheduler')}</div>
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<Tooltip content={$i18n.t('Enter Scheduler (e.g. Karras)')} placement="top-start">
+									<input
+										list="scheduler-list"
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										placeholder={$i18n.t('Enter Scheduler (e.g. Karras)')}
+										bind:value={config.automatic1111.AUTOMATIC1111_SCHEDULER}
+									/>
+
+									<datalist id="scheduler-list">
+										{#each schedulers ?? [] as scheduler}
+											<option value={scheduler}>{scheduler}</option>
+										{/each}
+									</datalist>
+								</Tooltip>
+							</div>
+						</div>
+					</div>
+					<!---CFG scale-->
+					<div>
+						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set CFG Scale')}</div>
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<Tooltip content={$i18n.t('Enter CFG Scale (e.g. 7.0)')} placement="top-start">
+									<input
+										class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+										placeholder={$i18n.t('Enter CFG Scale (e.g. 7.0)')}
+										bind:value={config.automatic1111.AUTOMATIC1111_CFG_SCALE}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+					</div>
+				{:else if config?.engine === 'comfyui'}
+					<div class="">
+						<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Base URL')}</div>
+						<div class="flex w-full">
+							<div class="flex-1 mr-2">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Enter URL (e.g. http://127.0.0.1:7860/)')}
+									bind:value={config.comfyui.COMFYUI_BASE_URL}
+								/>
+							</div>
+							<button
+								class="px-2.5 bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+								type="button"
+								on:click={async () => {
+									await updateConfigHandler();
+									const res = await verifyConfigUrl(localStorage.token).catch((error) => {
+										toast.error(error);
+										return null;
+									});
+
+									if (res) {
+										toast.success($i18n.t('Server connection verified'));
+									}
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M15.312 11.424a5.5 5.5 0 01-9.201 2.466l-.312-.311h2.433a.75.75 0 000-1.5H3.989a.75.75 0 00-.75.75v4.242a.75.75 0 001.5 0v-2.43l.31.31a7 7 0 0011.712-3.138.75.75 0 00-1.449-.39zm1.23-3.723a.75.75 0 00.219-.53V2.929a.75.75 0 00-1.5 0V5.36l-.31-.31A7 7 0 003.239 8.188a.75.75 0 101.448.389A5.5 5.5 0 0113.89 6.11l.311.31h-2.432a.75.75 0 000 1.5h4.243a.75.75 0 00.53-.219z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							</button>
+						</div>
+					</div>
+
+					<div class="">
+						<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Workflow')}</div>
+
+						{#if config.comfyui.COMFYUI_WORKFLOW}
+							<textarea
+								class="w-full rounded-lg mb-1 py-2 px-4 text-xs bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none disabled:text-gray-600 resize-none"
+								rows="10"
+								bind:value={config.comfyui.COMFYUI_WORKFLOW}
+								required
+							/>
+						{/if}
+
+						<div class="flex w-full">
+							<div class="flex-1">
+								<input
+									id="upload-comfyui-workflow-input"
+									hidden
+									type="file"
+									accept=".json"
+									on:change={(e) => {
+										const file = e.target.files[0];
+										const reader = new FileReader();
+
+										reader.onload = (e) => {
+											config.comfyui.COMFYUI_WORKFLOW = e.target.result;
+											e.target.value = null;
+										};
+
+										reader.readAsText(file);
+									}}
+								/>
+
+								<button
+									class="w-full text-sm font-medium py-2 bg-transparent hover:bg-gray-100 border border-dashed dark:border-gray-800 dark:hover:bg-gray-850 text-center rounded-xl"
+									type="button"
+									on:click={() => {
+										document.getElementById('upload-comfyui-workflow-input')?.click();
+									}}
+								>
+									{$i18n.t('Click here to upload a workflow.json file.')}
+								</button>
+							</div>
+						</div>
+
+						<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+							{$i18n.t('Make sure to export a workflow.json file as API format from ComfyUI.')}
+						</div>
+					</div>
+
+					{#if config.comfyui.COMFYUI_WORKFLOW}
+						<div class="">
+							<div class=" mb-2 text-sm font-medium">{$i18n.t('ComfyUI Workflow Nodes')}</div>
+
+							<div class="text-xs flex flex-col gap-1.5">
+								{#each requiredWorkflowNodes as node}
+									<div class="flex w-full items-center border dark:border-gray-850 rounded-lg">
+										<div class="flex-shrink-0">
+											<div
+												class=" capitalize line-clamp-1 font-medium px-3 py-1 w-20 text-center rounded-l-lg bg-green-500/10 text-green-700 dark:text-green-200"
+											>
+												{node.type}{node.type === 'prompt' ? '*' : ''}
+											</div>
+										</div>
+										<div class="">
+											<Tooltip content="Input Key (e.g. text, unet_name, steps)">
+												<input
+													class="py-1 px-3 w-24 text-xs text-center bg-transparent outline-none border-r dark:border-gray-850"
+													placeholder="Key"
+													bind:value={node.key}
+													required
+												/>
+											</Tooltip>
+										</div>
+
+										<div class="w-full">
+											<Tooltip
+												content="Comma separated Node Ids (e.g. 1 or 1,2)"
+												placement="top-start"
+											>
+												<input
+													class="w-full py-1 px-4 rounded-r-lg text-xs bg-transparent outline-none"
+													placeholder="Node Ids"
+													bind:value={node.node_ids}
+												/>
+											</Tooltip>
+										</div>
+									</div>
+								{/each}
+							</div>
+
+							<div class="mt-2 text-xs text-right text-gray-400 dark:text-gray-500">
+								{$i18n.t('*Prompt node ID(s) are required for image generation')}
+							</div>
+						</div>
+					{/if}
+				{:else if config?.engine === 'openai'}
+					<div>
+						<div class=" mb-1.5 text-sm font-medium">{$i18n.t('OpenAI API Config')}</div>
+
+						<div class="flex gap-2 mb-1">
+							<input
+								class="flex-1 w-full text-sm bg-transparent outline-none"
+								placeholder={$i18n.t('API Base URL')}
+								bind:value={config.openai.OPENAI_API_BASE_URL}
+								required
+							/>
+
+							<SensitiveInput
+								placeholder={$i18n.t('API Key')}
+								bind:value={config.openai.OPENAI_API_KEY}
+							/>
+						</div>
+					</div>
+				{/if}
+			</div>
+
+			{#if config?.enabled}
+				<hr class=" dark:border-gray-850" />
+
+				<div>
+					<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Default Model')}</div>
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<div class="flex w-full">
+								<div class="flex-1">
+									<Tooltip content={$i18n.t('Enter Model ID')} placement="top-start">
+										<input
+											list="model-list"
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											bind:value={imageGenerationConfig.MODEL}
+											placeholder="Select a model"
+											required
+										/>
+
+										<datalist id="model-list">
+											{#each models ?? [] as model}
+												<option value={model.id}>{model.name}</option>
+											{/each}
+										</datalist>
+									</Tooltip>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<div>
+					<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Image Size')}</div>
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<Tooltip content={$i18n.t('Enter Image Size (e.g. 512x512)')} placement="top-start">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
+									bind:value={imageGenerationConfig.IMAGE_SIZE}
+									required
+								/>
+							</Tooltip>
+						</div>
+					</div>
+				</div>
+
+				<div>
+					<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Steps')}</div>
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<Tooltip content={$i18n.t('Enter Number of Steps (e.g. 50)')} placement="top-start">
+								<input
+									class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+									placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
+									bind:value={imageGenerationConfig.IMAGE_STEPS}
+									required
+								/>
+							</Tooltip>
+						</div>
+					</div>
+				</div>
+			{/if}
+		{/if}
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+				? ' cursor-not-allowed'
+				: ''}"
+			type="submit"
+			disabled={loading}
+		>
+			{$i18n.t('Save')}
+
+			{#if loading}
+				<div class="ml-2 self-center">
+					<svg
+						class=" w-4 h-4"
+						viewBox="0 0 24 24"
+						fill="currentColor"
+						xmlns="http://www.w3.org/2000/svg"
+						><style>
+							.spinner_ajPY {
+								transform-origin: center;
+								animation: spinner_AtaB 0.75s infinite linear;
+							}
+							@keyframes spinner_AtaB {
+								100% {
+									transform: rotate(360deg);
+								}
+							}
+						</style><path
+							d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+							opacity=".25"
+						/><path
+							d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+							class="spinner_ajPY"
+						/></svg
+					>
+				</div>
+			{/if}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Settings/Interface.svelte b/src/lib/components/admin/Settings/Interface.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9c669dae53322257165e42c516c1d37a0defc1e7
--- /dev/null
+++ b/src/lib/components/admin/Settings/Interface.svelte
@@ -0,0 +1,430 @@
+<script lang="ts">
+	import { v4 as uuidv4 } from 'uuid';
+	import { toast } from 'svelte-sonner';
+
+	import { getBackendConfig, getTaskConfig, updateTaskConfig } from '$lib/apis';
+	import { setDefaultPromptSuggestions } from '$lib/apis/configs';
+	import { config, models, settings, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+
+	import { banners as _banners } from '$lib/stores';
+	import type { Banner } from '$lib/types';
+
+	import { getBanners, setBanners } from '$lib/apis/configs';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	let taskConfig = {
+		TASK_MODEL: '',
+		TASK_MODEL_EXTERNAL: '',
+		TITLE_GENERATION_PROMPT_TEMPLATE: '',
+		ENABLE_AUTOCOMPLETE_GENERATION: true,
+		AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: -1,
+		TAGS_GENERATION_PROMPT_TEMPLATE: '',
+		ENABLE_TAGS_GENERATION: true,
+		ENABLE_SEARCH_QUERY_GENERATION: true,
+		ENABLE_RETRIEVAL_QUERY_GENERATION: true,
+		QUERY_GENERATION_PROMPT_TEMPLATE: ''
+	};
+
+	let promptSuggestions = [];
+	let banners: Banner[] = [];
+
+	const updateInterfaceHandler = async () => {
+		taskConfig = await updateTaskConfig(localStorage.token, taskConfig);
+
+		promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
+		await updateBanners();
+
+		await config.set(await getBackendConfig());
+	};
+
+	onMount(async () => {
+		taskConfig = await getTaskConfig(localStorage.token);
+
+		promptSuggestions = $config?.default_prompt_suggestions;
+		banners = await getBanners(localStorage.token);
+	});
+
+	const updateBanners = async () => {
+		_banners.set(await setBanners(localStorage.token, banners));
+	};
+</script>
+
+{#if taskConfig}
+	<form
+		class="flex flex-col h-full justify-between space-y-3 text-sm"
+		on:submit|preventDefault={() => {
+			updateInterfaceHandler();
+			dispatch('save');
+		}}
+	>
+		<div class="  overflow-y-scroll scrollbar-hidden h-full pr-1.5">
+			<div>
+				<div class=" mb-2.5 text-sm font-medium flex items-center">
+					<div class=" mr-1">{$i18n.t('Set Task Model')}</div>
+					<Tooltip
+						content={$i18n.t(
+							'A task model is used when performing tasks such as generating titles for chats and web search queries'
+						)}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="size-3.5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
+							/>
+						</svg>
+					</Tooltip>
+				</div>
+				<div class="flex w-full gap-2">
+					<div class="flex-1">
+						<div class=" text-xs mb-1">{$i18n.t('Local Models')}</div>
+						<select
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							bind:value={taskConfig.TASK_MODEL}
+							placeholder={$i18n.t('Select a model')}
+						>
+							<option value="" selected>{$i18n.t('Current Model')}</option>
+							{#each $models.filter((m) => m.owned_by === 'ollama') as model}
+								<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
+									{model.name}
+								</option>
+							{/each}
+						</select>
+					</div>
+
+					<div class="flex-1">
+						<div class=" text-xs mb-1">{$i18n.t('External Models')}</div>
+						<select
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							bind:value={taskConfig.TASK_MODEL_EXTERNAL}
+							placeholder={$i18n.t('Select a model')}
+						>
+							<option value="" selected>{$i18n.t('Current Model')}</option>
+							{#each $models as model}
+								<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
+									{model.name}
+								</option>
+							{/each}
+						</select>
+					</div>
+				</div>
+
+				<div class="mt-3">
+					<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Title Generation Prompt')}</div>
+
+					<Tooltip
+						content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+						placement="top-start"
+					>
+						<Textarea
+							bind:value={taskConfig.TITLE_GENERATION_PROMPT_TEMPLATE}
+							placeholder={$i18n.t(
+								'Leave empty to use the default prompt, or enter a custom prompt'
+							)}
+						/>
+					</Tooltip>
+				</div>
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-3" />
+
+				<div class="my-3 flex w-full items-center justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Autocomplete Generation')}
+					</div>
+
+					<Tooltip content={$i18n.t('Enable autocomplete generation for chat messages')}>
+						<Switch bind:state={taskConfig.ENABLE_AUTOCOMPLETE_GENERATION} />
+					</Tooltip>
+				</div>
+
+				{#if taskConfig.ENABLE_AUTOCOMPLETE_GENERATION}
+					<div class="mt-3">
+						<div class=" mb-2.5 text-xs font-medium">
+							{$i18n.t('Autocomplete Generation Input Max Length')}
+						</div>
+
+						<Tooltip
+							content={$i18n.t('Character limit for autocomplete generation input')}
+							placement="top-start"
+						>
+							<input
+								class="w-full outline-none bg-transparent"
+								bind:value={taskConfig.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH}
+								placeholder={$i18n.t('-1 for no limit, or a positive integer for a specific limit')}
+							/>
+						</Tooltip>
+					</div>
+				{/if}
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-3" />
+
+				<div class="my-3 flex w-full items-center justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Tags Generation')}
+					</div>
+
+					<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} />
+				</div>
+
+				{#if taskConfig.ENABLE_TAGS_GENERATION}
+					<div class="mt-3">
+						<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Tags Generation Prompt')}</div>
+
+						<Tooltip
+							content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+							placement="top-start"
+						>
+							<Textarea
+								bind:value={taskConfig.TAGS_GENERATION_PROMPT_TEMPLATE}
+								placeholder={$i18n.t(
+									'Leave empty to use the default prompt, or enter a custom prompt'
+								)}
+							/>
+						</Tooltip>
+					</div>
+				{/if}
+
+				<hr class=" border-gray-50 dark:border-gray-850 my-3" />
+
+				<div class="my-3 flex w-full items-center justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Retrieval Query Generation')}
+					</div>
+
+					<Switch bind:state={taskConfig.ENABLE_RETRIEVAL_QUERY_GENERATION} />
+				</div>
+
+				<div class="my-3 flex w-full items-center justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('Web Search Query Generation')}
+					</div>
+
+					<Switch bind:state={taskConfig.ENABLE_SEARCH_QUERY_GENERATION} />
+				</div>
+
+				<div class="">
+					<div class=" mb-2.5 text-xs font-medium">{$i18n.t('Query Generation Prompt')}</div>
+
+					<Tooltip
+						content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
+						placement="top-start"
+					>
+						<Textarea
+							bind:value={taskConfig.QUERY_GENERATION_PROMPT_TEMPLATE}
+							placeholder={$i18n.t(
+								'Leave empty to use the default prompt, or enter a custom prompt'
+							)}
+						/>
+					</Tooltip>
+				</div>
+			</div>
+
+			<hr class=" border-gray-50 dark:border-gray-850 my-3" />
+
+			<div class=" space-y-3 {banners.length > 0 ? ' mb-3' : ''}">
+				<div class="flex w-full justify-between">
+					<div class=" self-center text-sm font-semibold">
+						{$i18n.t('Banners')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						type="button"
+						on:click={() => {
+							if (banners.length === 0 || banners.at(-1).content !== '') {
+								banners = [
+									...banners,
+									{
+										id: uuidv4(),
+										type: '',
+										title: '',
+										content: '',
+										dismissible: true,
+										timestamp: Math.floor(Date.now() / 1000)
+									}
+								];
+							}
+						}}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
+							/>
+						</svg>
+					</button>
+				</div>
+				<div class="flex flex-col space-y-1">
+					{#each banners as banner, bannerIdx}
+						<div class=" flex justify-between">
+							<div class="flex flex-row flex-1 border rounded-xl dark:border-gray-800">
+								<select
+									class="w-fit capitalize rounded-xl py-2 px-4 text-xs bg-transparent outline-none"
+									bind:value={banner.type}
+									required
+								>
+									{#if banner.type == ''}
+										<option value="" selected disabled class="text-gray-900"
+											>{$i18n.t('Type')}</option
+										>
+									{/if}
+									<option value="info" class="text-gray-900">{$i18n.t('Info')}</option>
+									<option value="warning" class="text-gray-900">{$i18n.t('Warning')}</option>
+									<option value="error" class="text-gray-900">{$i18n.t('Error')}</option>
+									<option value="success" class="text-gray-900">{$i18n.t('Success')}</option>
+								</select>
+
+								<input
+									class="pr-5 py-1.5 text-xs w-full bg-transparent outline-none"
+									placeholder={$i18n.t('Content')}
+									bind:value={banner.content}
+								/>
+
+								<div class="relative top-1.5 -left-2">
+									<Tooltip content={$i18n.t('Dismissible')} className="flex h-fit items-center">
+										<Switch bind:state={banner.dismissible} />
+									</Tooltip>
+								</div>
+							</div>
+
+							<button
+								class="px-2"
+								type="button"
+								on:click={() => {
+									banners.splice(bannerIdx, 1);
+									banners = banners;
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+									/>
+								</svg>
+							</button>
+						</div>
+					{/each}
+				</div>
+			</div>
+
+			{#if $user.role === 'admin'}
+				<div class=" space-y-3">
+					<div class="flex w-full justify-between mb-2">
+						<div class=" self-center text-sm font-semibold">
+							{$i18n.t('Default Prompt Suggestions')}
+						</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							type="button"
+							on:click={() => {
+								if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') {
+									promptSuggestions = [...promptSuggestions, { content: '', title: ['', ''] }];
+								}
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
+								/>
+							</svg>
+						</button>
+					</div>
+					<div class="grid lg:grid-cols-2 flex-col gap-1.5">
+						{#each promptSuggestions as prompt, promptIdx}
+							<div
+								class=" flex border border-gray-100 dark:border-none dark:bg-gray-850 rounded-xl py-1.5"
+							>
+								<div class="flex flex-col flex-1 pl-1">
+									<div class="flex border-b border-gray-100 dark:border-gray-800 w-full">
+										<input
+											class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
+											placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')}
+											bind:value={prompt.title[0]}
+										/>
+
+										<input
+											class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800"
+											placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')}
+											bind:value={prompt.title[1]}
+										/>
+									</div>
+
+									<textarea
+										class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r border-gray-100 dark:border-gray-800 resize-none"
+										placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')}
+										rows="3"
+										bind:value={prompt.content}
+									/>
+								</div>
+
+								<button
+									class="px-3"
+									type="button"
+									on:click={() => {
+										promptSuggestions.splice(promptIdx, 1);
+										promptSuggestions = promptSuggestions;
+									}}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+										/>
+									</svg>
+								</button>
+							</div>
+						{/each}
+					</div>
+
+					{#if promptSuggestions.length > 0}
+						<div class="text-xs text-left w-full mt-2">
+							{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
+						</div>
+					{/if}
+				</div>
+			{/if}
+		</div>
+
+		<div class="flex justify-end text-sm font-medium">
+			<button
+				class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+				type="submit"
+			>
+				{$i18n.t('Save')}
+			</button>
+		</div>
+	</form>
+{/if}
diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7a3361682ec19ae06e110c563c3ca96121c25b44
--- /dev/null
+++ b/src/lib/components/admin/Settings/Models.svelte
@@ -0,0 +1,397 @@
+<script lang="ts">
+	import { marked } from 'marked';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { onMount, getContext, tick } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { WEBUI_NAME, config, mobile, models as _models, settings, user } from '$lib/stores';
+	import {
+		createNewModel,
+		deleteAllModels,
+		getBaseModels,
+		toggleModelById,
+		updateModelById
+	} from '$lib/apis/models';
+
+	import { getModels } from '$lib/apis';
+	import Search from '$lib/components/icons/Search.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
+	import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte';
+	import { toast } from 'svelte-sonner';
+	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Cog6 from '$lib/components/icons/Cog6.svelte';
+	import ConfigureModelsModal from './Models/ConfigureModelsModal.svelte';
+
+	let importFiles;
+	let modelsImportInputElement: HTMLInputElement;
+
+	let models = null;
+
+	let workspaceModels = null;
+	let baseModels = null;
+
+	let filteredModels = [];
+	let selectedModelId = null;
+
+	let showConfigModal = false;
+
+	$: if (models) {
+		filteredModels = models
+			.filter((m) => searchValue === '' || m.name.toLowerCase().includes(searchValue.toLowerCase()))
+			.sort((a, b) => {
+				// // Check if either model is inactive and push them to the bottom
+				// if ((a.is_active ?? true) !== (b.is_active ?? true)) {
+				// 	return (b.is_active ?? true) - (a.is_active ?? true);
+				// }
+				// If both models' active states are the same, sort alphabetically
+				return a.name.localeCompare(b.name);
+			});
+	}
+
+	let searchValue = '';
+
+	const downloadModels = async (models) => {
+		let blob = new Blob([JSON.stringify(models)], {
+			type: 'application/json'
+		});
+		saveAs(blob, `models-export-${Date.now()}.json`);
+	};
+
+	const init = async () => {
+		workspaceModels = await getBaseModels(localStorage.token);
+		baseModels = await getModels(localStorage.token, true);
+
+		models = baseModels.map((m) => {
+			const workspaceModel = workspaceModels.find((wm) => wm.id === m.id);
+
+			if (workspaceModel) {
+				return {
+					...m,
+					...workspaceModel
+				};
+			} else {
+				return {
+					...m,
+					id: m.id,
+					name: m.name,
+
+					is_active: true
+				};
+			}
+		});
+	};
+
+	const upsertModelHandler = async (model) => {
+		model.base_model_id = null;
+
+		if (workspaceModels.find((m) => m.id === model.id)) {
+			const res = await updateModelById(localStorage.token, model.id, model).catch((error) => {
+				return null;
+			});
+
+			if (res) {
+				toast.success($i18n.t('Model updated successfully'));
+			}
+		} else {
+			const res = await createNewModel(localStorage.token, model).catch((error) => {
+				return null;
+			});
+
+			if (res) {
+				toast.success($i18n.t('Model updated successfully'));
+			}
+		}
+
+		_models.set(await getModels(localStorage.token));
+		await init();
+	};
+
+	const toggleModelHandler = async (model) => {
+		if (!Object.keys(model).includes('base_model_id')) {
+			await createNewModel(localStorage.token, {
+				id: model.id,
+				name: model.name,
+				base_model_id: null,
+				meta: {},
+				params: {},
+				access_control: {},
+				is_active: model.is_active
+			}).catch((error) => {
+				return null;
+			});
+		} else {
+			await toggleModelById(localStorage.token, model.id);
+		}
+
+		await init();
+		_models.set(await getModels(localStorage.token));
+	};
+
+	onMount(async () => {
+		init();
+	});
+</script>
+
+<ConfigureModelsModal bind:show={showConfigModal} {init} />
+
+{#if models !== null}
+	{#if selectedModelId === null}
+		<div class="flex flex-col gap-1 mt-1.5 mb-2">
+			<div class="flex justify-between items-center">
+				<div class="flex items-center md:self-center text-xl font-medium px-0.5">
+					{$i18n.t('Models')}
+					<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+					<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
+						>{filteredModels.length}</span
+					>
+				</div>
+
+				<div>
+					<Tooltip content={$i18n.t('Configure')}>
+						<button
+							class=" px-2.5 py-1 rounded-full flex gap-1 items-center"
+							type="button"
+							on:click={() => {
+								showConfigModal = true;
+							}}
+						>
+							<Cog6 />
+						</button>
+					</Tooltip>
+				</div>
+			</div>
+
+			<div class=" flex flex-1 items-center w-full space-x-2">
+				<div class="flex flex-1 items-center">
+					<div class=" self-center ml-1 mr-3">
+						<Search className="size-3.5" />
+					</div>
+					<input
+						class=" w-full text-sm py-1 rounded-r-xl outline-none bg-transparent"
+						bind:value={searchValue}
+						placeholder={$i18n.t('Search Models')}
+					/>
+				</div>
+			</div>
+		</div>
+
+		<div class=" my-2 mb-5" id="model-list">
+			{#if models.length > 0}
+				{#each filteredModels as model, modelIdx (model.id)}
+					<div
+						class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-lg transition"
+						id="model-item-{model.id}"
+					>
+						<button
+							class=" flex flex-1 text-left space-x-3.5 cursor-pointer w-full"
+							type="button"
+							on:click={() => {
+								selectedModelId = model.id;
+							}}
+						>
+							<div class=" self-center w-8">
+								<div
+									class=" rounded-full object-cover {(model?.is_active ?? true)
+										? ''
+										: 'opacity-50 dark:opacity-50'} "
+								>
+									<img
+										src={model?.meta?.profile_image_url ?? '/static/favicon.png'}
+										alt="modelfile profile"
+										class=" rounded-full w-full h-auto object-cover"
+									/>
+								</div>
+							</div>
+
+							<div class=" flex-1 self-center {(model?.is_active ?? true) ? '' : 'text-gray-500'}">
+								<Tooltip
+									content={marked.parse(
+										!!model?.meta?.description
+											? model?.meta?.description
+											: model?.ollama?.digest
+												? `${model?.ollama?.digest} **(${model?.ollama?.modified_at})**`
+												: model.id
+									)}
+									className=" w-fit"
+									placement="top-start"
+								>
+									<div class="  font-semibold line-clamp-1">{model.name}</div>
+								</Tooltip>
+								<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1 text-gray-500">
+									<span class=" line-clamp-1">
+										{!!model?.meta?.description
+											? model?.meta?.description
+											: model?.ollama?.digest
+												? `${model.id} (${model?.ollama?.digest})`
+												: model.id}
+									</span>
+								</div>
+							</div>
+						</button>
+						<div class="flex flex-row gap-0.5 items-center self-center">
+							<button
+								class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								type="button"
+								on:click={() => {
+									selectedModelId = model.id;
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="1.5"
+									stroke="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
+									/>
+								</svg>
+							</button>
+
+							<div class="ml-1">
+								<Tooltip
+									content={(model?.is_active ?? true) ? $i18n.t('Enabled') : $i18n.t('Disabled')}
+								>
+									<Switch
+										bind:state={model.is_active}
+										on:change={async () => {
+											toggleModelHandler(model);
+										}}
+									/>
+								</Tooltip>
+							</div>
+						</div>
+					</div>
+				{/each}
+			{:else}
+				<div class="flex flex-col items-center justify-center w-full h-20">
+					<div class="text-gray-500 dark:text-gray-400 text-xs">
+						{$i18n.t('No models found')}
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		{#if $user?.role === 'admin'}
+			<div class=" flex justify-end w-full mb-3">
+				<div class="flex space-x-1">
+					<input
+						id="models-import-input"
+						bind:this={modelsImportInputElement}
+						bind:files={importFiles}
+						type="file"
+						accept=".json"
+						hidden
+						on:change={() => {
+							console.log(importFiles);
+
+							let reader = new FileReader();
+							reader.onload = async (event) => {
+								let savedModels = JSON.parse(event.target.result);
+								console.log(savedModels);
+
+								for (const model of savedModels) {
+									if (Object.keys(model).includes('base_model_id')) {
+										if (model.base_model_id === null) {
+											upsertModelHandler(model);
+										}
+									} else {
+										if (model?.info ?? false) {
+											if (model.info.base_model_id === null) {
+												upsertModelHandler(model.info);
+											}
+										}
+									}
+								}
+
+								await _models.set(await getModels(localStorage.token));
+								init();
+							};
+
+							reader.readAsText(importFiles[0]);
+						}}
+					/>
+
+					<button
+						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+						on:click={() => {
+							modelsImportInputElement.click();
+						}}
+					>
+						<div class=" self-center mr-2 font-medium line-clamp-1">
+							{$i18n.t('Import Presets')}
+						</div>
+
+						<div class=" self-center">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-3.5 h-3.5"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</div>
+					</button>
+
+					<button
+						class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+						on:click={async () => {
+							downloadModels(models);
+						}}
+					>
+						<div class=" self-center mr-2 font-medium line-clamp-1">
+							{$i18n.t('Export Presets')}
+						</div>
+
+						<div class=" self-center">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="w-3.5 h-3.5"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</div>
+					</button>
+				</div>
+			</div>
+		{/if}
+	{:else}
+		<ModelEditor
+			edit
+			model={models.find((m) => m.id === selectedModelId)}
+			preset={false}
+			onSubmit={(model) => {
+				console.log(model);
+				upsertModelHandler(model);
+				selectedModelId = null;
+			}}
+			onBack={() => {
+				selectedModelId = null;
+			}}
+		/>
+	{/if}
+{:else}
+	<div class=" h-full w-full flex justify-center items-center">
+		<Spinner />
+	</div>
+{/if}
diff --git a/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4922b5b6f085b30136697b9111d1c7f346c1c958
--- /dev/null
+++ b/src/lib/components/admin/Settings/Models/ConfigureModelsModal.svelte
@@ -0,0 +1,268 @@
+<script>
+	import { toast } from 'svelte-sonner';
+
+	import { createEventDispatcher, getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import { models } from '$lib/stores';
+	import { deleteAllModels } from '$lib/apis/models';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ModelList from './ModelList.svelte';
+	import { getModelsConfig, setModelsConfig } from '$lib/apis/configs';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Minus from '$lib/components/icons/Minus.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+
+	export let show = false;
+	export let init = () => {};
+
+	let config = null;
+
+	let selectedModelId = '';
+	let defaultModelIds = [];
+	let modelIds = [];
+
+	let loading = false;
+	let showResetModal = false;
+
+	const submitHandler = async () => {
+		loading = true;
+
+		const res = await setModelsConfig(localStorage.token, {
+			DEFAULT_MODELS: defaultModelIds.join(','),
+			MODEL_ORDER_LIST: modelIds
+		});
+
+		if (res) {
+			toast.success($i18n.t('Models configuration saved successfully'));
+			init();
+			show = false;
+		} else {
+			toast.error($i18n.t('Failed to save models configuration'));
+		}
+
+		loading = false;
+	};
+
+	onMount(async () => {
+		config = await getModelsConfig(localStorage.token);
+
+		if (config?.DEFAULT_MODELS) {
+			defaultModelIds = (config?.DEFAULT_MODELS).split(',').filter((id) => id);
+		} else {
+			defaultModelIds = [];
+		}
+		const modelOrderList = config.MODEL_ORDER_LIST || [];
+		const allModelIds = $models.map((model) => model.id);
+
+		// Create a Set for quick lookup of ordered IDs
+		const orderedSet = new Set(modelOrderList);
+
+		modelIds = [
+			// Add all IDs from MODEL_ORDER_LIST that exist in allModelIds
+			...modelOrderList.filter((id) => orderedSet.has(id) && allModelIds.includes(id)),
+			// Add remaining IDs not in MODEL_ORDER_LIST, sorted alphabetically
+			...allModelIds.filter((id) => !orderedSet.has(id)).sort((a, b) => a.localeCompare(b))
+		];
+	});
+</script>
+
+<ConfirmDialog
+	title={$i18n.t('Reset All Models')}
+	message={$i18n.t('This will delete all models including custom models and cannot be undone.')}
+	bind:show={showResetModal}
+	onConfirm={async () => {
+		const res = deleteAllModels(localStorage.token);
+		if (res) {
+			toast.success($i18n.t('All models deleted successfully'));
+			init();
+		}
+	}}
+/>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center font-primary">
+				{$i18n.t('Configure Models')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				{#if config}
+					<form
+						class="flex flex-col w-full"
+						on:submit|preventDefault={() => {
+							submitHandler();
+						}}
+					>
+						<div>
+							<div class="flex flex-col w-full">
+								<div class="mb-1 flex justify-between">
+									<div class="text-xs text-gray-500">{$i18n.t('Reorder Models')}</div>
+								</div>
+
+								<ModelList bind:modelIds />
+							</div>
+						</div>
+
+						<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+						<div>
+							<div class="flex flex-col w-full">
+								<div class="mb-1 flex justify-between">
+									<div class="text-xs text-gray-500">{$i18n.t('Default Models')}</div>
+								</div>
+
+								{#if defaultModelIds.length > 0}
+									<div class="flex flex-col">
+										{#each defaultModelIds as modelId, modelIdx}
+											<div class=" flex gap-2 w-full justify-between items-center">
+												<div class=" text-sm flex-1 py-1 rounded-lg">
+													{$models.find((model) => model.id === modelId)?.name}
+												</div>
+												<div class="flex-shrink-0">
+													<button
+														type="button"
+														on:click={() => {
+															defaultModelIds = defaultModelIds.filter(
+																(_, idx) => idx !== modelIdx
+															);
+														}}
+													>
+														<Minus strokeWidth="2" className="size-3.5" />
+													</button>
+												</div>
+											</div>
+										{/each}
+									</div>
+								{:else}
+									<div class="text-gray-500 text-xs text-center py-2">
+										{$i18n.t('No models selected')}
+									</div>
+								{/if}
+
+								<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
+
+								<div class="flex items-center">
+									<select
+										class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
+											? ''
+											: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										bind:value={selectedModelId}
+									>
+										<option value="">{$i18n.t('Select a model')}</option>
+										{#each $models as model}
+											<option value={model.id} class="bg-gray-50 dark:bg-gray-700"
+												>{model.name}</option
+											>
+										{/each}
+									</select>
+
+									<div>
+										<button
+											type="button"
+											on:click={() => {
+												if (selectedModelId === '') {
+													return;
+												}
+
+												if (defaultModelIds.includes(selectedModelId)) {
+													return;
+												}
+
+												defaultModelIds = [...defaultModelIds, selectedModelId];
+												selectedModelId = '';
+											}}
+										>
+											<Plus className="size-3.5" strokeWidth="2" />
+										</button>
+									</div>
+								</div>
+							</div>
+						</div>
+
+						<div class="flex justify-between pt-3 text-sm font-medium gap-1.5">
+							<Tooltip content={$i18n.t('This will delete all models including custom models')}>
+								<button
+									class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-950 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
+									type="button"
+									on:click={() => {
+										showResetModal = true;
+									}}
+								>
+									<!-- {$i18n.t('Delete All Models')} -->
+									{$i18n.t('Reset All Models')}
+								</button>
+							</Tooltip>
+
+							<button
+								class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+									? ' cursor-not-allowed'
+									: ''}"
+								type="submit"
+								disabled={loading}
+							>
+								{$i18n.t('Save')}
+
+								{#if loading}
+									<div class="ml-2 self-center">
+										<svg
+											class=" w-4 h-4"
+											viewBox="0 0 24 24"
+											fill="currentColor"
+											xmlns="http://www.w3.org/2000/svg"
+											><style>
+												.spinner_ajPY {
+													transform-origin: center;
+													animation: spinner_AtaB 0.75s infinite linear;
+												}
+												@keyframes spinner_AtaB {
+													100% {
+														transform: rotate(360deg);
+													}
+												}
+											</style><path
+												d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+												opacity=".25"
+											/><path
+												d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+												class="spinner_ajPY"
+											/></svg
+										>
+									</div>
+								{/if}
+							</button>
+						</div>
+					</form>
+				{:else}
+					<div>
+						<Spinner />
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Settings/Models/ModelList.svelte b/src/lib/components/admin/Settings/Models/ModelList.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c54d19ee443431eef6f8ebbe40668a2d850341da
--- /dev/null
+++ b/src/lib/components/admin/Settings/Models/ModelList.svelte
@@ -0,0 +1,58 @@
+<script lang="ts">
+	import Sortable from 'sortablejs';
+
+	import { createEventDispatcher, getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { models } from '$lib/stores';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
+
+	export let modelIds = [];
+
+	let sortable = null;
+	let modelListElement = null;
+
+	const positionChangeHandler = () => {
+		const modelList = Array.from(modelListElement.children).map((child) =>
+			child.id.replace('model-item-', '')
+		);
+
+		modelIds = modelList;
+	};
+
+	onMount(() => {
+		sortable = Sortable.create(modelListElement, {
+			animation: 150,
+			onUpdate: async (event) => {
+				positionChangeHandler();
+			}
+		});
+	});
+</script>
+
+{#if modelIds.length > 0}
+	<div class="flex flex-col -translate-x-1" bind:this={modelListElement}>
+		{#each modelIds as modelId, modelIdx (modelId)}
+			<div class=" flex gap-2 w-full justify-between items-center" id="model-item-{modelId}">
+				<Tooltip content={modelId} placement="top-start">
+					<div class="flex items-center gap-1">
+						<EllipsisVertical className="size-4 cursor-move" />
+
+						<div class=" text-sm flex-1 py-1 rounded-lg">
+							{#if $models.find((model) => model.id === modelId)}
+								{$models.find((model) => model.id === modelId).name}
+							{:else}
+								{modelId}
+							{/if}
+						</div>
+					</div>
+				</Tooltip>
+			</div>
+		{/each}
+	</div>
+{:else}
+	<div class="text-gray-500 text-xs text-center py-2">
+		{$i18n.t('No models found')}
+	</div>
+{/if}
diff --git a/src/lib/components/admin/Settings/Pipelines.svelte b/src/lib/components/admin/Settings/Pipelines.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..fb229d9ff0b2efe7832f4d6fe8de44a2fec1032c
--- /dev/null
+++ b/src/lib/components/admin/Settings/Pipelines.svelte
@@ -0,0 +1,559 @@
+<script lang="ts">
+	import { v4 as uuidv4 } from 'uuid';
+
+	import { toast } from 'svelte-sonner';
+	import { models } from '$lib/stores';
+	import { getContext, onMount, tick } from 'svelte';
+	import type { Writable } from 'svelte/store';
+	import type { i18n as i18nType } from 'i18next';
+	import {
+		getPipelineValves,
+		getPipelineValvesSpec,
+		updatePipelineValves,
+		getPipelines,
+		getModels,
+		getPipelinesList,
+		downloadPipeline,
+		deletePipeline,
+		uploadPipeline
+	} from '$lib/apis';
+
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+
+	const i18n: Writable<i18nType> = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	let downloading = false;
+	let uploading = false;
+
+	let pipelineFiles;
+
+	let PIPELINES_LIST = null;
+	let selectedPipelinesUrlIdx = '';
+
+	let pipelines = null;
+
+	let valves = null;
+	let valves_spec = null;
+	let selectedPipelineIdx = null;
+
+	let pipelineDownloadUrl = '';
+
+	const updateHandler = async () => {
+		const pipeline = pipelines[selectedPipelineIdx];
+
+		if (pipeline && (pipeline?.valves ?? false)) {
+			for (const property in valves_spec.properties) {
+				if (valves_spec.properties[property]?.type === 'array') {
+					valves[property] = valves[property].split(',').map((v) => v.trim());
+				}
+			}
+
+			const res = await updatePipelineValves(
+				localStorage.token,
+				pipeline.id,
+				valves,
+				selectedPipelinesUrlIdx
+			).catch((error) => {
+				toast.error(error);
+			});
+
+			if (res) {
+				toast.success($i18n.t('Valves updated successfully'));
+				setPipelines();
+				models.set(await getModels(localStorage.token));
+				saveHandler();
+			}
+		} else {
+			toast.error($i18n.t('No valves to update'));
+		}
+	};
+
+	const getValves = async (idx) => {
+		valves = null;
+		valves_spec = null;
+
+		valves_spec = await getPipelineValvesSpec(
+			localStorage.token,
+			pipelines[idx].id,
+			selectedPipelinesUrlIdx
+		);
+		valves = await getPipelineValves(
+			localStorage.token,
+			pipelines[idx].id,
+			selectedPipelinesUrlIdx
+		);
+
+		for (const property in valves_spec.properties) {
+			if (valves_spec.properties[property]?.type === 'array') {
+				valves[property] = valves[property].join(',');
+			}
+		}
+	};
+
+	const setPipelines = async () => {
+		pipelines = null;
+		valves = null;
+		valves_spec = null;
+
+		if (PIPELINES_LIST.length > 0) {
+			console.log(selectedPipelinesUrlIdx);
+			pipelines = await getPipelines(localStorage.token, selectedPipelinesUrlIdx);
+
+			if (pipelines.length > 0) {
+				selectedPipelineIdx = 0;
+				await getValves(selectedPipelineIdx);
+			}
+		} else {
+			pipelines = [];
+		}
+	};
+
+	const addPipelineHandler = async () => {
+		downloading = true;
+		const res = await downloadPipeline(
+			localStorage.token,
+			pipelineDownloadUrl,
+			selectedPipelinesUrlIdx
+		).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Pipeline downloaded successfully'));
+			setPipelines();
+			models.set(await getModels(localStorage.token));
+		}
+
+		downloading = false;
+	};
+
+	const uploadPipelineHandler = async () => {
+		uploading = true;
+
+		if (pipelineFiles && pipelineFiles.length !== 0) {
+			const file = pipelineFiles[0];
+
+			console.log(file);
+
+			const res = await uploadPipeline(localStorage.token, file, selectedPipelinesUrlIdx).catch(
+				(error) => {
+					console.log(error);
+					toast.error('Something went wrong :/');
+					return null;
+				}
+			);
+
+			if (res) {
+				toast.success($i18n.t('Pipeline downloaded successfully'));
+				setPipelines();
+				models.set(await getModels(localStorage.token));
+			}
+		} else {
+			toast.error($i18n.t('No file selected'));
+		}
+
+		pipelineFiles = null;
+		const pipelineUploadInputElement = document.getElementById('pipeline-upload-input');
+
+		if (pipelineUploadInputElement) {
+			pipelineUploadInputElement.value = null;
+		}
+
+		uploading = false;
+	};
+
+	const deletePipelineHandler = async () => {
+		const res = await deletePipeline(
+			localStorage.token,
+			pipelines[selectedPipelineIdx].id,
+			selectedPipelinesUrlIdx
+		).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Pipeline deleted successfully'));
+			setPipelines();
+			models.set(await getModels(localStorage.token));
+		}
+	};
+
+	onMount(async () => {
+		PIPELINES_LIST = await getPipelinesList(localStorage.token);
+		console.log(PIPELINES_LIST);
+
+		if (PIPELINES_LIST.length > 0) {
+			selectedPipelinesUrlIdx = PIPELINES_LIST[0]['idx'].toString();
+		}
+
+		await setPipelines();
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		updateHandler();
+	}}
+>
+	<div class="overflow-y-scroll scrollbar-hidden h-full">
+		{#if PIPELINES_LIST !== null}
+			<div class="flex w-full justify-between mb-2">
+				<div class=" self-center text-sm font-semibold">
+					{$i18n.t('Manage Pipelines')}
+				</div>
+			</div>
+
+			{#if PIPELINES_LIST.length > 0}
+				<div class="space-y-1">
+					<div class="flex gap-2">
+						<div class="flex-1">
+							<select
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								bind:value={selectedPipelinesUrlIdx}
+								placeholder={$i18n.t('Select a pipeline url')}
+								on:change={async () => {
+									await tick();
+									await setPipelines();
+								}}
+							>
+								<option value="" selected disabled class="bg-gray-100 dark:bg-gray-700"
+									>{$i18n.t('Select a pipeline url')}</option
+								>
+
+								{#each PIPELINES_LIST as pipelines, idx}
+									<option value={pipelines.idx.toString()} class="bg-gray-100 dark:bg-gray-700"
+										>{pipelines.url}</option
+									>
+								{/each}
+							</select>
+						</div>
+					</div>
+				</div>
+
+				<div class=" my-2">
+					<div class=" mb-2 text-sm font-medium">
+						{$i18n.t('Upload Pipeline')}
+					</div>
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<input
+								id="pipelines-upload-input"
+								bind:files={pipelineFiles}
+								type="file"
+								accept=".py"
+								hidden
+							/>
+
+							<button
+								class="w-full text-sm font-medium py-2 bg-transparent hover:bg-gray-100 border border-dashed dark:border-gray-800 dark:hover:bg-gray-850 text-center rounded-xl"
+								type="button"
+								on:click={() => {
+									document.getElementById('pipelines-upload-input')?.click();
+								}}
+							>
+								{#if pipelineFiles}
+									{pipelineFiles.length > 0 ? `${pipelineFiles.length}` : ''} pipeline(s) selected.
+								{:else}
+									{$i18n.t('Click here to select a py file.')}
+								{/if}
+							</button>
+						</div>
+						<button
+							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+							on:click={() => {
+								uploadPipelineHandler();
+							}}
+							disabled={uploading}
+							type="button"
+						>
+							{#if uploading}
+								<div class="self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+									>
+										<style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style>
+										<path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/>
+										<path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/>
+									</svg>
+								</div>
+							{:else}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="size-4"
+								>
+									<path
+										d="M7.25 10.25a.75.75 0 0 0 1.5 0V4.56l2.22 2.22a.75.75 0 1 0 1.06-1.06l-3.5-3.5a.75.75 0 0 0-1.06 0l-3.5 3.5a.75.75 0 0 0 1.06 1.06l2.22-2.22v5.69Z"
+									/>
+									<path
+										d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+									/>
+								</svg>
+							{/if}
+						</button>
+					</div>
+				</div>
+
+				<div class=" my-2">
+					<div class=" mb-2 text-sm font-medium">
+						{$i18n.t('Install from Github URL')}
+					</div>
+					<div class="flex w-full">
+						<div class="flex-1 mr-2">
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('Enter Github Raw URL')}
+								bind:value={pipelineDownloadUrl}
+							/>
+						</div>
+						<button
+							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+							on:click={() => {
+								addPipelineHandler();
+							}}
+							disabled={downloading}
+							type="button"
+						>
+							{#if downloading}
+								<div class="self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+									>
+										<style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style>
+										<path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/>
+										<path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/>
+									</svg>
+								</div>
+							{:else}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
+									/>
+									<path
+										d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
+									/>
+								</svg>
+							{/if}
+						</button>
+					</div>
+
+					<div class="mt-2 text-xs text-gray-500">
+						<span class=" font-semibold dark:text-gray-200">Warning:</span> Pipelines are a plugin
+						system with arbitrary code execution —
+						<span class=" font-medium dark:text-gray-400"
+							>don't fetch random pipelines from sources you don't trust.</span
+						>
+					</div>
+				</div>
+
+				<hr class=" dark:border-gray-800 my-3 w-full" />
+
+				{#if pipelines !== null}
+					{#if pipelines.length > 0}
+						<div class="flex w-full justify-between mb-2">
+							<div class=" self-center text-sm font-semibold">
+								{$i18n.t('Pipelines Valves')}
+							</div>
+						</div>
+						<div class="space-y-1">
+							{#if pipelines.length > 0}
+								<div class="flex gap-2">
+									<div class="flex-1">
+										<select
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											bind:value={selectedPipelineIdx}
+											placeholder={$i18n.t('Select a pipeline')}
+											on:change={async () => {
+												await tick();
+												await getValves(selectedPipelineIdx);
+											}}
+										>
+											{#each pipelines as pipeline, idx}
+												<option value={idx} class="bg-gray-100 dark:bg-gray-700"
+													>{pipeline.name} ({pipeline.type ?? 'pipe'})</option
+												>
+											{/each}
+										</select>
+									</div>
+
+									<button
+										class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
+										on:click={() => {
+											deletePipelineHandler();
+										}}
+										type="button"
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</button>
+								</div>
+							{/if}
+
+							<div class="space-y-1">
+								{#if pipelines[selectedPipelineIdx].valves}
+									{#if valves}
+										{#each Object.keys(valves_spec.properties) as property, idx}
+											<div class=" py-0.5 w-full justify-between">
+												<div class="flex w-full justify-between">
+													<div class=" self-center text-xs font-medium">
+														{valves_spec.properties[property].title}
+													</div>
+
+													<button
+														class="p-1 px-3 text-xs flex rounded transition"
+														type="button"
+														on:click={() => {
+															valves[property] = (valves[property] ?? null) === null ? '' : null;
+														}}
+													>
+														{#if (valves[property] ?? null) === null}
+															<span class="ml-2 self-center"> {$i18n.t('None')} </span>
+														{:else}
+															<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+														{/if}
+													</button>
+												</div>
+
+												{#if (valves[property] ?? null) !== null}
+													<!-- {valves[property]} -->
+													<div class="flex mt-0.5 mb-1.5 space-x-2">
+														<div class=" flex-1">
+															{#if valves_spec.properties[property]?.enum ?? null}
+																<select
+																	class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+																	bind:value={valves[property]}
+																>
+																	{#each valves_spec.properties[property].enum as option}
+																		<option value={option} selected={option === valves[property]}>
+																			{option}
+																		</option>
+																	{/each}
+																</select>
+															{:else if (valves_spec.properties[property]?.type ?? null) === 'boolean'}
+																<div class="flex justify-between items-center">
+																	<div class="text-xs text-gray-500">
+																		{valves[property] ? 'Enabled' : 'Disabled'}
+																	</div>
+
+																	<div class=" pr-2">
+																		<Switch bind:state={valves[property]} />
+																	</div>
+																</div>
+															{:else}
+																<input
+																	class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+																	type="text"
+																	placeholder={valves_spec.properties[property].title}
+																	bind:value={valves[property]}
+																	autocomplete="off"
+																	required
+																/>
+															{/if}
+														</div>
+													</div>
+												{/if}
+											</div>
+										{/each}
+									{:else}
+										<Spinner className="size-5" />
+									{/if}
+								{:else}
+									<div>No valves</div>
+								{/if}
+							</div>
+						</div>
+					{:else if pipelines.length === 0}
+						<div>Pipelines Not Detected</div>
+					{/if}
+				{:else}
+					<div class="flex justify-center">
+						<div class="my-auto">
+							<Spinner className="size-4" />
+						</div>
+					</div>
+				{/if}
+			{:else}
+				<div>{$i18n.t('Pipelines Not Detected')}</div>
+			{/if}
+		{:else}
+			<div class="flex justify-center h-full">
+				<div class="my-auto">
+					<Spinner className="size-6" />
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	{#if PIPELINES_LIST !== null && PIPELINES_LIST.length > 0}
+		<div class="flex justify-end pt-3 text-sm font-medium">
+			<button
+				class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+				type="submit"
+			>
+				{$i18n.t('Save')}
+			</button>
+		</div>
+	{/if}
+</form>
diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a3ccbec1dbfb85479d29ef0a72af5954fc18002c
--- /dev/null
+++ b/src/lib/components/admin/Settings/WebSearch.svelte
@@ -0,0 +1,390 @@
+<script lang="ts">
+	import { getRAGConfig, updateRAGConfig } from '$lib/apis/retrieval';
+	import Switch from '$lib/components/common/Switch.svelte';
+
+	import { models } from '$lib/stores';
+	import { onMount, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	let webConfig = null;
+	let webSearchEngines = [
+		'searxng',
+		'google_pse',
+		'brave',
+		'mojeek',
+		'serpstack',
+		'serper',
+		'serply',
+		'searchapi',
+		'duckduckgo',
+		'tavily',
+		'jina',
+		'bing'
+	];
+
+	let youtubeLanguage = 'en';
+	let youtubeTranslation = null;
+	let youtubeProxyUrl = '';
+
+	const submitHandler = async () => {
+		const res = await updateRAGConfig(localStorage.token, {
+			web: webConfig,
+			youtube: {
+				language: youtubeLanguage.split(',').map((lang) => lang.trim()),
+				translation: youtubeTranslation,
+				proxy_url: youtubeProxyUrl
+			}
+		});
+	};
+
+	onMount(async () => {
+		const res = await getRAGConfig(localStorage.token);
+
+		if (res) {
+			webConfig = res.web;
+
+			youtubeLanguage = res.youtube.language.join(',');
+			youtubeTranslation = res.youtube.translation;
+			youtubeProxyUrl = res.youtube.proxy_url;
+		}
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		await submitHandler();
+		saveHandler();
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
+		{#if webConfig}
+			<div>
+				<div class=" mb-1 text-sm font-medium">
+					{$i18n.t('Web Search')}
+				</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">
+							{$i18n.t('Enable Web Search')}
+						</div>
+
+						<Switch bind:state={webConfig.search.enabled} />
+					</div>
+				</div>
+
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Web Search Engine')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							bind:value={webConfig.search.engine}
+							placeholder={$i18n.t('Select a engine')}
+							required
+						>
+							<option disabled selected value="">{$i18n.t('Select a engine')}</option>
+							{#each webSearchEngines as engine}
+								<option value={engine}>{engine}</option>
+							{/each}
+						</select>
+					</div>
+				</div>
+
+				{#if webConfig.search.engine !== ''}
+					<div class="mt-1.5">
+						{#if webConfig.search.engine === 'searxng'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Searxng Query URL')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											type="text"
+											placeholder={$i18n.t('Enter Searxng Query URL')}
+											bind:value={webConfig.search.searxng_query_url}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+						{:else if webConfig.search.engine === 'google_pse'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Google PSE API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Google PSE API Key')}
+									bind:value={webConfig.search.google_pse_api_key}
+								/>
+							</div>
+							<div class="mt-1.5">
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Google PSE Engine Id')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											type="text"
+											placeholder={$i18n.t('Enter Google PSE Engine Id')}
+											bind:value={webConfig.search.google_pse_engine_id}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+						{:else if webConfig.search.engine === 'brave'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Brave Search API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Brave Search API Key')}
+									bind:value={webConfig.search.brave_search_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'mojeek'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Mojeek Search API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Mojeek Search API Key')}
+									bind:value={webConfig.search.mojeek_search_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'serpstack'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Serpstack API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Serpstack API Key')}
+									bind:value={webConfig.search.serpstack_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'serper'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Serper API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Serper API Key')}
+									bind:value={webConfig.search.serper_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'serply'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Serply API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Serply API Key')}
+									bind:value={webConfig.search.serply_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'searchapi'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('SearchApi API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter SearchApi API Key')}
+									bind:value={webConfig.search.searchapi_api_key}
+								/>
+							</div>
+							<div class="mt-1.5">
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('SearchApi Engine')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											type="text"
+											placeholder={$i18n.t('Enter SearchApi Engine')}
+											bind:value={webConfig.search.searchapi_engine}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+						{:else if webConfig.search.engine === 'tavily'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Tavily API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Tavily API Key')}
+									bind:value={webConfig.search.tavily_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'jina'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Jina API Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Jina API Key')}
+									bind:value={webConfig.search.jina_api_key}
+								/>
+							</div>
+						{:else if webConfig.search.engine === 'bing'}
+							<div>
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Bing Search V7 Endpoint')}
+								</div>
+
+								<div class="flex w-full">
+									<div class="flex-1">
+										<input
+											class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+											type="text"
+											placeholder={$i18n.t('Enter Bing Search V7 Endpoint')}
+											bind:value={webConfig.search.bing_search_v7_endpoint}
+											autocomplete="off"
+										/>
+									</div>
+								</div>
+							</div>
+
+							<div class="mt-2">
+								<div class=" self-center text-xs font-medium mb-1">
+									{$i18n.t('Bing Search V7 Subscription Key')}
+								</div>
+
+								<SensitiveInput
+									placeholder={$i18n.t('Enter Bing Search V7 Subscription Key')}
+									bind:value={webConfig.search.bing_search_v7_subscription_key}
+								/>
+							</div>
+						{/if}
+					</div>
+				{/if}
+
+				{#if webConfig.search.enabled}
+					<div class="mt-2 flex gap-2 mb-1">
+						<div class="w-full">
+							<div class=" self-center text-xs font-medium mb-1">
+								{$i18n.t('Search Result Count')}
+							</div>
+
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('Search Result Count')}
+								bind:value={webConfig.search.result_count}
+								required
+							/>
+						</div>
+
+						<div class="w-full">
+							<div class=" self-center text-xs font-medium mb-1">
+								{$i18n.t('Concurrent Requests')}
+							</div>
+
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								placeholder={$i18n.t('Concurrent Requests')}
+								bind:value={webConfig.search.concurrent_requests}
+								required
+							/>
+						</div>
+					</div>
+				{/if}
+			</div>
+
+			<hr class=" dark:border-gray-850 my-2" />
+
+			<div>
+				<div class=" mb-1 text-sm font-medium">
+					{$i18n.t('Web Loader Settings')}
+				</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">
+							{$i18n.t('Bypass SSL verification for Websites')}
+						</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							on:click={() => {
+								webConfig.web_loader_ssl_verification = !webConfig.web_loader_ssl_verification;
+								submitHandler();
+							}}
+							type="button"
+						>
+							{#if webConfig.web_loader_ssl_verification === false}
+								<span class="ml-2 self-center">{$i18n.t('On')}</span>
+							{:else}
+								<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+							{/if}
+						</button>
+					</div>
+				</div>
+
+				<div class=" mt-2 mb-1 text-sm font-medium">
+					{$i18n.t('Youtube Loader Settings')}
+				</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
+						<div class=" flex-1 self-center">
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="text"
+								placeholder={$i18n.t('Enter language codes')}
+								bind:value={youtubeLanguage}
+								autocomplete="off"
+							/>
+						</div>
+					</div>
+				</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Proxy URL')}</div>
+						<div class=" flex-1 self-center">
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="text"
+								placeholder={$i18n.t('Enter proxy URL (e.g. https://user:password@host:port)')}
+								bind:value={youtubeProxyUrl}
+								autocomplete="off"
+							/>
+						</div>
+					</div>
+				</div>
+			</div>
+		{/if}
+	</div>
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/admin/Users.svelte b/src/lib/components/admin/Users.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..00659ad3ea540fd3d561e9760d6c5d1490598e22
--- /dev/null
+++ b/src/lib/components/admin/Users.svelte
@@ -0,0 +1,110 @@
+<script>
+	import { getContext, tick, onMount } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	import { goto } from '$app/navigation';
+	import { user } from '$lib/stores';
+
+	import { getUsers } from '$lib/apis/users';
+
+	import UserList from './Users/UserList.svelte';
+	import Groups from './Users/Groups.svelte';
+
+	const i18n = getContext('i18n');
+
+	let users = [];
+
+	let selectedTab = 'overview';
+	let loaded = false;
+
+	$: if (selectedTab) {
+		getUsersHandler();
+	}
+
+	const getUsersHandler = async () => {
+		users = await getUsers(localStorage.token);
+	};
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			await goto('/');
+		} else {
+			users = await getUsers(localStorage.token);
+		}
+		loaded = true;
+
+		const containerElement = document.getElementById('users-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>
+
+<div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
+	<div
+		id="users-tabs-container"
+		class=" flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
+	>
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
+			'overview'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'overview';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="size-4"
+				>
+					<path
+						d="M8.5 4.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0ZM10.9 12.006c.11.542-.348.994-.9.994H2c-.553 0-1.01-.452-.902-.994a5.002 5.002 0 0 1 9.803 0ZM14.002 12h-1.59a2.556 2.556 0 0 0-.04-.29 6.476 6.476 0 0 0-1.167-2.603 3.002 3.002 0 0 1 3.633 1.911c.18.522-.283.982-.836.982ZM12 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Overview')}</div>
+		</button>
+
+		<button
+			class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
+			'groups'
+				? ''
+				: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+			on:click={() => {
+				selectedTab = 'groups';
+			}}
+		>
+			<div class=" self-center mr-2">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					class="size-4"
+				>
+					<path
+						d="M8 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM3.156 11.763c.16-.629.44-1.21.813-1.72a2.5 2.5 0 0 0-2.725 1.377c-.136.287.102.58.418.58h1.449c.01-.077.025-.156.045-.237ZM12.847 11.763c.02.08.036.16.046.237h1.446c.316 0 .554-.293.417-.579a2.5 2.5 0 0 0-2.722-1.378c.374.51.653 1.09.813 1.72ZM14 7.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0ZM3.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM5 13c-.552 0-1.013-.455-.876-.99a4.002 4.002 0 0 1 7.753 0c.136.535-.324.99-.877.99H5Z"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center">{$i18n.t('Groups')}</div>
+		</button>
+	</div>
+
+	<div class="flex-1 mt-1 lg:mt-0 overflow-y-scroll">
+		{#if selectedTab === 'overview'}
+			<UserList {users} />
+		{:else if selectedTab === 'groups'}
+			<Groups {users} />
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/admin/Users/Groups.svelte b/src/lib/components/admin/Users/Groups.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5911718ba29dfcf5663acf3fb988bcb8fb18228b
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups.svelte
@@ -0,0 +1,237 @@
+<script>
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	dayjs.extend(relativeTime);
+
+	import { onMount, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+
+	import { WEBUI_NAME, config, user, showSidebar, knowledge } from '$lib/stores';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	import UsersSolid from '$lib/components/icons/UsersSolid.svelte';
+	import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
+	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+	import User from '$lib/components/icons/User.svelte';
+	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
+	import GroupModal from './Groups/EditGroupModal.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import GroupItem from './Groups/GroupItem.svelte';
+	import AddGroupModal from './Groups/AddGroupModal.svelte';
+	import { createNewGroup, getGroups } from '$lib/apis/groups';
+	import { getUserDefaultPermissions, updateUserDefaultPermissions } from '$lib/apis/users';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	export let users = [];
+
+	let groups = [];
+	let filteredGroups;
+
+	$: filteredGroups = groups.filter((user) => {
+		if (search === '') {
+			return true;
+		} else {
+			let name = user.name.toLowerCase();
+			const query = search.toLowerCase();
+			return name.includes(query);
+		}
+	});
+
+	let search = '';
+	let defaultPermissions = {
+		workspace: {
+			models: false,
+			knowledge: false,
+			prompts: false,
+			tools: false
+		},
+		chat: {
+			file_upload: true,
+			delete: true,
+			edit: true,
+			temporary: true
+		}
+	};
+
+	let showCreateGroupModal = false;
+	let showDefaultPermissionsModal = false;
+
+	const setGroups = async () => {
+		groups = await getGroups(localStorage.token);
+	};
+
+	const addGroupHandler = async (group) => {
+		const res = await createNewGroup(localStorage.token, group).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group created successfully'));
+			groups = await getGroups(localStorage.token);
+		}
+	};
+
+	const updateDefaultPermissionsHandler = async (group) => {
+		console.log(group.permissions);
+
+		const res = await updateUserDefaultPermissions(localStorage.token, group.permissions).catch(
+			(error) => {
+				toast.error(error);
+				return null;
+			}
+		);
+
+		if (res) {
+			toast.success($i18n.t('Default permissions updated successfully'));
+			defaultPermissions = await getUserDefaultPermissions(localStorage.token);
+		}
+	};
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			await goto('/');
+		} else {
+			await setGroups();
+			defaultPermissions = await getUserDefaultPermissions(localStorage.token);
+		}
+		loaded = true;
+	});
+</script>
+
+{#if loaded}
+	<AddGroupModal bind:show={showCreateGroupModal} onSubmit={addGroupHandler} />
+	<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
+		<div class="flex md:self-center text-lg font-medium px-0.5">
+			{$i18n.t('Groups')}
+			<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+
+			<span class="text-lg font-medium text-gray-500 dark:text-gray-300">{groups.length}</span>
+		</div>
+
+		<div class="flex gap-1">
+			<div class=" flex w-full space-x-2">
+				<div class="flex flex-1">
+					<div class=" self-center ml-1 mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<input
+						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+						bind:value={search}
+						placeholder={$i18n.t('Search')}
+					/>
+				</div>
+
+				<div>
+					<Tooltip content={$i18n.t('Create Group')}>
+						<button
+							class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1"
+							on:click={() => {
+								showCreateGroupModal = !showCreateGroupModal;
+							}}
+						>
+							<Plus className="size-3.5" />
+						</button>
+					</Tooltip>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div>
+		{#if filteredGroups.length === 0}
+			<div class="flex flex-col items-center justify-center h-40">
+				<div class=" text-xl font-medium">
+					{$i18n.t('Organize your users')}
+				</div>
+
+				<div class="mt-1 text-sm dark:text-gray-300">
+					{$i18n.t('Use groups to group your users and assign permissions.')}
+				</div>
+
+				<div class="mt-3">
+					<button
+						class=" px-4 py-1.5 text-sm rounded-full bg-black hover:bg-gray-800 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition font-medium flex items-center space-x-1"
+						aria-label={$i18n.t('Create Group')}
+						on:click={() => {
+							showCreateGroupModal = true;
+						}}
+					>
+						{$i18n.t('Create Group')}
+					</button>
+				</div>
+			</div>
+		{:else}
+			<div>
+				<div class=" flex items-center gap-3 justify-between text-xs uppercase px-1 font-bold">
+					<div class="w-full">Group</div>
+
+					<div class="w-full">Users</div>
+
+					<div class="w-full"></div>
+				</div>
+
+				<hr class="mt-1.5 border-gray-50 dark:border-gray-850" />
+
+				{#each filteredGroups as group}
+					<div class="my-2">
+						<GroupItem {group} {users} {setGroups} />
+					</div>
+				{/each}
+			</div>
+		{/if}
+
+		<hr class="mb-2 border-gray-50 dark:border-gray-850" />
+
+		<GroupModal
+			bind:show={showDefaultPermissionsModal}
+			tabs={['permissions']}
+			bind:permissions={defaultPermissions}
+			custom={false}
+			onSubmit={updateDefaultPermissionsHandler}
+		/>
+
+		<button
+			class="flex items-center justify-between rounded-lg w-full transition pt-1"
+			on:click={() => {
+				showDefaultPermissionsModal = true;
+			}}
+		>
+			<div class="flex items-center gap-2.5">
+				<div class="p-1.5 bg-black/5 dark:bg-white/10 rounded-full">
+					<UsersSolid className="size-4" />
+				</div>
+
+				<div class="text-left">
+					<div class=" text-sm font-medium">{$i18n.t('Default permissions')}</div>
+
+					<div class="flex text-xs mt-0.5">
+						{$i18n.t('applies to all users with the "user" role')}
+					</div>
+				</div>
+			</div>
+
+			<div>
+				<ChevronRight strokeWidth="2.5" />
+			</div>
+		</button>
+	</div>
+{/if}
diff --git a/src/lib/components/admin/Users/Groups/AddGroupModal.svelte b/src/lib/components/admin/Users/Groups/AddGroupModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4001d218bae8127eba9cd4d981a3be34b46967d2
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/AddGroupModal.svelte
@@ -0,0 +1,149 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+	export let onSubmit: Function = () => {};
+	export let show = false;
+
+	let name = '';
+	let description = '';
+	let userIds = [];
+
+	let loading = false;
+
+	const submitHandler = async () => {
+		loading = true;
+
+		const group = {
+			name,
+			description
+		};
+
+		await onSubmit(group);
+
+		loading = false;
+		show = false;
+
+		name = '';
+		description = '';
+		userIds = [];
+	};
+
+	onMount(() => {
+		console.log('mounted');
+	});
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
+			<div class=" text-lg font-medium self-center font-primary">
+				{$i18n.t('Add User Group')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit={(e) => {
+						e.preventDefault();
+						submitHandler();
+					}}
+				>
+					<div class="px-1 flex flex-col w-full">
+						<div class="flex gap-2">
+							<div class="flex flex-col w-full">
+								<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Name')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+										type="text"
+										bind:value={name}
+										placeholder={$i18n.t('Group Name')}
+										autocomplete="off"
+										required
+									/>
+								</div>
+							</div>
+						</div>
+
+						<div class="flex flex-col w-full mt-2">
+							<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Description')}</div>
+
+							<div class="flex-1">
+								<Textarea
+									className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none resize-none"
+									rows={2}
+									bind:value={description}
+									placeholder={$i18n.t('Group Description')}
+								/>
+							</div>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
+						<button
+							class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Create')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Users/Groups/Display.svelte b/src/lib/components/admin/Users/Groups/Display.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a36171a71dae65e8991fddf0cd944d769a5eb75f
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/Display.svelte
@@ -0,0 +1,61 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let name = '';
+	export let color = '';
+	export let description = '';
+</script>
+
+<div class="flex gap-2">
+	<div class="flex flex-col w-full">
+		<div class=" mb-0.5 text-xs text-gray-500">{$i18n.t('Name')}</div>
+
+		<div class="flex-1">
+			<input
+				class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+				type="text"
+				bind:value={name}
+				placeholder={$i18n.t('Group Name')}
+				autocomplete="off"
+				required
+			/>
+		</div>
+	</div>
+</div>
+
+<!-- <div class="flex flex-col w-full mt-2">
+	<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Color')}</div>
+
+	<div class="flex-1">
+		<Tooltip content={$i18n.t('Hex Color - Leave empty for default color')} placement="top-start">
+			<div class="flex gap-0.5">
+				<div class="text-gray-500">#</div>
+
+				<input
+					class="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+					type="text"
+					bind:value={color}
+					placeholder={$i18n.t('Hex Color')}
+					autocomplete="off"
+				/>
+			</div>
+		</Tooltip>
+	</div>
+</div> -->
+
+<div class="flex flex-col w-full mt-2">
+	<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Description')}</div>
+
+	<div class="flex-1">
+		<Textarea
+			className="w-full text-sm bg-transparent placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none resize-none"
+			rows={4}
+			bind:value={description}
+			placeholder={$i18n.t('Group Description')}
+		/>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6d28db992a147091fd32ec2e7fda8356e6dc6a4d
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte
@@ -0,0 +1,328 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { getContext, onMount } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Display from './Display.svelte';
+	import Permissions from './Permissions.svelte';
+	import Users from './Users.svelte';
+	import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
+	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
+
+	export let onSubmit: Function = () => {};
+	export let onDelete: Function = () => {};
+
+	export let show = false;
+	export let edit = false;
+
+	export let users = [];
+	export let group = null;
+
+	export let custom = true;
+
+	export let tabs = ['general', 'permissions', 'users'];
+
+	let selectedTab = 'general';
+	let loading = false;
+
+	export let name = '';
+	export let description = '';
+
+	export let permissions = {
+		workspace: {
+			models: false,
+			knowledge: false,
+			prompts: false,
+			tools: false
+		},
+		chat: {
+			file_upload: true,
+			delete: true,
+			edit: true,
+			temporary: true
+		}
+	};
+	export let userIds = [];
+
+	const submitHandler = async () => {
+		loading = true;
+
+		const group = {
+			name,
+			description,
+			permissions,
+			user_ids: userIds
+		};
+
+		await onSubmit(group);
+
+		loading = false;
+		show = false;
+	};
+
+	const init = () => {
+		if (group) {
+			name = group.name;
+			description = group.description;
+			permissions = group?.permissions ?? {
+				workspace: {
+					models: false,
+					knowledge: false,
+					prompts: false,
+					tools: false
+				},
+				chat: {
+					file_upload: true,
+					delete: true,
+					edit: true,
+					temporary: true
+				}
+			};
+			userIds = group?.user_ids ?? [];
+		}
+	};
+
+	$: if (show) {
+		init();
+	}
+
+	onMount(() => {
+		console.log(tabs);
+		selectedTab = tabs[0];
+		init();
+	});
+</script>
+
+<Modal size="md" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-4 mb-1.5">
+			<div class=" text-lg font-medium self-center font-primary">
+				{#if custom}
+					{#if edit}
+						{$i18n.t('Edit User Group')}
+					{:else}
+						{$i18n.t('Add User Group')}
+					{/if}
+				{:else}
+					{$i18n.t('Edit Default Permissions')}
+				{/if}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit={(e) => {
+						e.preventDefault();
+						submitHandler();
+					}}
+				>
+					<div class="flex flex-col lg:flex-row w-full h-full pb-2 lg:space-x-4">
+						<div
+							id="admin-settings-tabs-container"
+							class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
+						>
+							{#if tabs.includes('general')}
+								<button
+									class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
+									'general'
+										? ''
+										: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+									on:click={() => {
+										selectedTab = 'general';
+									}}
+									type="button"
+								>
+									<div class=" self-center mr-2">
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M6.955 1.45A.5.5 0 0 1 7.452 1h1.096a.5.5 0 0 1 .497.45l.17 1.699c.484.12.94.312 1.356.562l1.321-1.081a.5.5 0 0 1 .67.033l.774.775a.5.5 0 0 1 .034.67l-1.08 1.32c.25.417.44.873.561 1.357l1.699.17a.5.5 0 0 1 .45.497v1.096a.5.5 0 0 1-.45.497l-1.699.17c-.12.484-.312.94-.562 1.356l1.082 1.322a.5.5 0 0 1-.034.67l-.774.774a.5.5 0 0 1-.67.033l-1.322-1.08c-.416.25-.872.44-1.356.561l-.17 1.699a.5.5 0 0 1-.497.45H7.452a.5.5 0 0 1-.497-.45l-.17-1.699a4.973 4.973 0 0 1-1.356-.562L4.108 13.37a.5.5 0 0 1-.67-.033l-.774-.775a.5.5 0 0 1-.034-.67l1.08-1.32a4.971 4.971 0 0 1-.561-1.357l-1.699-.17A.5.5 0 0 1 1 8.548V7.452a.5.5 0 0 1 .45-.497l1.699-.17c.12-.484.312-.94.562-1.356L2.629 4.107a.5.5 0 0 1 .034-.67l.774-.774a.5.5 0 0 1 .67-.033L5.43 3.71a4.97 4.97 0 0 1 1.356-.561l.17-1.699ZM6 8c0 .538.212 1.026.558 1.385l.057.057a2 2 0 0 0 2.828-2.828l-.058-.056A2 2 0 0 0 6 8Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</div>
+									<div class=" self-center">{$i18n.t('General')}</div>
+								</button>
+							{/if}
+
+							{#if tabs.includes('permissions')}
+								<button
+									class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
+									'permissions'
+										? ''
+										: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+									on:click={() => {
+										selectedTab = 'permissions';
+									}}
+									type="button"
+								>
+									<div class=" self-center mr-2">
+										<WrenchSolid />
+									</div>
+									<div class=" self-center">{$i18n.t('Permissions')}</div>
+								</button>
+							{/if}
+
+							{#if tabs.includes('users')}
+								<button
+									class="px-0.5 py-1 max-w-fit w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
+									'users'
+										? ''
+										: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+									on:click={() => {
+										selectedTab = 'users';
+									}}
+									type="button"
+								>
+									<div class=" self-center mr-2">
+										<UserPlusSolid />
+									</div>
+									<div class=" self-center">{$i18n.t('Users')} ({userIds.length})</div>
+								</button>
+							{/if}
+						</div>
+
+						<div
+							class="flex-1 mt-1 lg:mt-1 lg:h-[22rem] lg:max-h-[22rem] overflow-y-auto scrollbar-hidden"
+						>
+							{#if selectedTab == 'general'}
+								<Display bind:name bind:description />
+							{:else if selectedTab == 'permissions'}
+								<Permissions bind:permissions />
+							{:else if selectedTab == 'users'}
+								<Users bind:userIds {users} />
+							{/if}
+						</div>
+					</div>
+
+					<!-- <div
+						class=" tabs flex flex-row overflow-x-auto gap-2.5 text-sm font-medium border-b border-b-gray-800 scrollbar-hidden"
+					>
+						{#if tabs.includes('display')}
+							<button
+								class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
+								'display'
+									? ' dark:border-white'
+									: 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'display';
+								}}
+								type="button"
+							>
+								{$i18n.t('Display')}
+							</button>
+						{/if}
+
+						{#if tabs.includes('permissions')}
+							<button
+								class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
+								'permissions'
+									? '  dark:border-white'
+									: 'border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'permissions';
+								}}
+								type="button"
+							>
+								{$i18n.t('Permissions')}
+							</button>
+						{/if}
+
+						{#if tabs.includes('users')}
+							<button
+								class="px-0.5 pb-1.5 min-w-fit flex text-right transition border-b-2 {selectedTab ===
+								'users'
+									? ' dark:border-white'
+									: ' border-transparent text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'users';
+								}}
+								type="button"
+							>
+								{$i18n.t('Users')} ({userIds.length})
+							</button>
+						{/if}
+					</div> -->
+
+					<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
+						{#if edit}
+							<button
+								class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-gray-900 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
+								type="button"
+								on:click={() => {
+									onDelete();
+									show = false;
+								}}
+							>
+								{$i18n.t('Delete')}
+							</button>
+						{/if}
+
+						<button
+							class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Save')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/admin/Users/Groups/GroupItem.svelte b/src/lib/components/admin/Users/Groups/GroupItem.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..dd2b8c311f2459c08b9b1b977e4bd5d4a3b76b37
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/GroupItem.svelte
@@ -0,0 +1,84 @@
+<script>
+	import { toast } from 'svelte-sonner';
+	import { getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { deleteGroupById, updateGroupById } from '$lib/apis/groups';
+
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import User from '$lib/components/icons/User.svelte';
+	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
+	import GroupModal from './EditGroupModal.svelte';
+
+	export let users = [];
+	export let group = {
+		name: 'Admins',
+		user_ids: [1, 2, 3]
+	};
+
+	export let setGroups = () => {};
+
+	let showEdit = false;
+
+	const updateHandler = async (_group) => {
+		const res = await updateGroupById(localStorage.token, group.id, _group).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group updated successfully'));
+			setGroups();
+		}
+	};
+
+	const deleteHandler = async () => {
+		const res = await deleteGroupById(localStorage.token, group.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Group deleted successfully'));
+			setGroups();
+		}
+	};
+</script>
+
+<GroupModal
+	bind:show={showEdit}
+	edit
+	{users}
+	{group}
+	onSubmit={updateHandler}
+	onDelete={deleteHandler}
+/>
+
+<button
+	class="flex items-center gap-3 justify-between px-1 text-xs w-full transition"
+	on:click={() => {
+		showEdit = true;
+	}}
+>
+	<div class="flex items-center gap-1.5 w-full font-medium">
+		<div>
+			<UserCircleSolid className="size-4" />
+		</div>
+		{group.name}
+	</div>
+
+	<div class="flex items-center gap-1.5 w-full font-medium">
+		{group.user_ids.length}
+
+		<div>
+			<User className="size-3.5" />
+		</div>
+	</div>
+
+	<div class="w-full flex justify-end">
+		<div class=" rounded-lg p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition">
+			<Pencil className="size-3.5" />
+		</div>
+	</div>
+</button>
diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5d67ff76848080de14426456b882096a72dfa4bc
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/Permissions.svelte
@@ -0,0 +1,204 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	export let permissions = {
+		workspace: {
+			models: false,
+			knowledge: false,
+			prompts: false,
+			tools: false
+		},
+		chat: {
+			delete: true,
+			edit: true,
+			temporary: true,
+			file_upload: true
+		}
+	};
+</script>
+
+<div>
+	<!-- <div>
+		<div class=" mb-2 text-sm font-medium">{$i18n.t('Model Permissions')}</div>
+
+		<div class="mb-2">
+			<div class="flex justify-between items-center text-xs pr-2">
+				<div class=" text-xs font-medium">{$i18n.t('Model Filtering')}</div>
+
+				<Switch bind:state={permissions.model.filter} />
+			</div>
+		</div>
+
+		{#if permissions.model.filter}
+			<div class="mb-2">
+				<div class=" space-y-1.5">
+					<div class="flex flex-col w-full">
+						<div class="mb-1 flex justify-between">
+							<div class="text-xs text-gray-500">{$i18n.t('Model IDs')}</div>
+						</div>
+
+						{#if model_ids.length > 0}
+							<div class="flex flex-col">
+								{#each model_ids as modelId, modelIdx}
+									<div class=" flex gap-2 w-full justify-between items-center">
+										<div class=" text-sm flex-1 rounded-lg">
+											{modelId}
+										</div>
+										<div class="flex-shrink-0">
+											<button
+												type="button"
+												on:click={() => {
+													model_ids = model_ids.filter((_, idx) => idx !== modelIdx);
+												}}
+											>
+												<Minus strokeWidth="2" className="size-3.5" />
+											</button>
+										</div>
+									</div>
+								{/each}
+							</div>
+						{:else}
+							<div class="text-gray-500 text-xs text-center py-2 px-10">
+								{$i18n.t('No model IDs')}
+							</div>
+						{/if}
+					</div>
+				</div>
+				<hr class=" border-gray-100 dark:border-gray-700/10 mt-2.5 mb-1 w-full" />
+
+				<div class="flex items-center">
+					<select
+						class="w-full py-1 text-sm rounded-lg bg-transparent {selectedModelId
+							? ''
+							: 'text-gray-500'} placeholder:text-gray-300 dark:placeholder:text-gray-700 outline-none"
+						bind:value={selectedModelId}
+					>
+						<option value="">{$i18n.t('Select a model')}</option>
+						{#each $models.filter((m) => m?.owned_by !== 'arena') as model}
+							<option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
+						{/each}
+					</select>
+
+					<div>
+						<button
+							type="button"
+							on:click={() => {
+								if (selectedModelId && !permissions.model.model_ids.includes(selectedModelId)) {
+									permissions.model.model_ids = [...permissions.model.model_ids, selectedModelId];
+									selectedModelId = '';
+								}
+							}}
+						>
+							<Plus className="size-3.5" strokeWidth="2" />
+						</button>
+					</div>
+				</div>
+			</div>
+		{/if}
+
+		<div class=" space-y-1 mb-3">
+			<div class="">
+				<div class="flex justify-between items-center text-xs">
+					<div class=" text-xs font-medium">{$i18n.t('Default Model')}</div>
+				</div>
+			</div>
+
+			<div class="flex-1 mr-2">
+				<select
+					class="w-full bg-transparent outline-none py-0.5 text-sm"
+					bind:value={permissions.model.default_id}
+					placeholder="Select a model"
+				>
+					<option value="" disabled selected>{$i18n.t('Select a model')}</option>
+					{#each permissions.model.filter ? $models.filter( (model) => filterModelIds.includes(model.id) ) : $models.filter((model) => model.id) as model}
+						<option value={model.id} class="bg-gray-100 dark:bg-gray-700">{model.name}</option>
+					{/each}
+				</select>
+			</div>
+		</div>
+	</div>
+
+	<hr class=" border-gray-50 dark:border-gray-850 my-2" /> -->
+
+	<div>
+		<div class=" mb-2 text-sm font-medium">{$i18n.t('Workspace Permissions')}</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Models Access')}
+			</div>
+			<Switch bind:state={permissions.workspace.models} />
+		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Knowledge Access')}
+			</div>
+			<Switch bind:state={permissions.workspace.knowledge} />
+		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Prompts Access')}
+			</div>
+			<Switch bind:state={permissions.workspace.prompts} />
+		</div>
+
+		<div class=" ">
+			<Tooltip
+				className=" flex w-full justify-between my-2 pr-2"
+				content={$i18n.t(
+					'Warning: Enabling this will allow users to upload arbitrary code on the server.'
+				)}
+				placement="top-start"
+			>
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Tools Access')}
+				</div>
+				<Switch bind:state={permissions.workspace.tools} />
+			</Tooltip>
+		</div>
+	</div>
+
+	<hr class=" border-gray-50 dark:border-gray-850 my-2" />
+
+	<div>
+		<div class=" mb-2 text-sm font-medium">{$i18n.t('Chat Permissions')}</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Allow File Upload')}
+			</div>
+
+			<Switch bind:state={permissions.chat.file_upload} />
+		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Allow Chat Delete')}
+			</div>
+
+			<Switch bind:state={permissions.chat.delete} />
+		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Allow Chat Edit')}
+			</div>
+
+			<Switch bind:state={permissions.chat.edit} />
+		</div>
+
+		<div class="  flex w-full justify-between my-2 pr-2">
+			<div class=" self-center text-xs font-medium">
+				{$i18n.t('Allow Temporary Chat')}
+			</div>
+
+			<Switch bind:state={permissions.chat.temporary} />
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Users/Groups/Users.svelte b/src/lib/components/admin/Users/Groups/Users.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..22f9877ab75d77f311e4fd0e1125c54946d5f5a2
--- /dev/null
+++ b/src/lib/components/admin/Users/Groups/Users.svelte
@@ -0,0 +1,122 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+
+	export let users = [];
+	export let userIds = [];
+
+	let filteredUsers = [];
+
+	$: filteredUsers = users
+		.filter((user) => {
+			if (user?.role === 'admin') {
+				return false;
+			}
+
+			if (query === '') {
+				return true;
+			}
+
+			return (
+				user.name.toLowerCase().includes(query.toLowerCase()) ||
+				user.email.toLowerCase().includes(query.toLowerCase())
+			);
+		})
+		.sort((a, b) => {
+			const aUserIndex = userIds.indexOf(a.id);
+			const bUserIndex = userIds.indexOf(b.id);
+
+			// Compare based on userIds or fall back to alphabetical order
+			if (aUserIndex !== -1 && bUserIndex === -1) return -1; // 'a' has valid userId -> prioritize
+			if (bUserIndex !== -1 && aUserIndex === -1) return 1; // 'b' has valid userId -> prioritize
+
+			// Both a and b are either in the userIds array or not, so we'll sort them by their indices
+			if (aUserIndex !== -1 && bUserIndex !== -1) return aUserIndex - bUserIndex;
+
+			// If both are not in the userIds, fallback to alphabetical sorting by name
+			return a.name.localeCompare(b.name);
+		});
+
+	let query = '';
+</script>
+
+<div>
+	<div class="flex w-full">
+		<div class="flex flex-1">
+			<div class=" self-center mr-3">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<input
+				class=" w-full text-sm pr-4 rounded-r-xl outline-none bg-transparent"
+				bind:value={query}
+				placeholder={$i18n.t('Search')}
+			/>
+		</div>
+	</div>
+
+	<div class="mt-3 max-h-[22rem] overflow-y-auto scrollbar-hidden">
+		<div class="flex flex-col gap-2.5">
+			{#if filteredUsers.length > 0}
+				{#each filteredUsers as user, userIdx (user.id)}
+					<div class="flex flex-row items-center gap-3 w-full text-sm">
+						<div class="flex items-center">
+							<Checkbox
+								state={userIds.includes(user.id) ? 'checked' : 'unchecked'}
+								on:change={(e) => {
+									if (e.detail === 'checked') {
+										userIds = [...userIds, user.id];
+									} else {
+										userIds = userIds.filter((id) => id !== user.id);
+									}
+								}}
+							/>
+						</div>
+
+						<div class="flex w-full items-center justify-between">
+							<Tooltip content={user.email} placement="top-start">
+								<div class="flex">
+									<img
+										class=" rounded-full size-5 object-cover mr-2.5"
+										src={user.profile_image_url.startsWith(WEBUI_BASE_URL) ||
+										user.profile_image_url.startsWith('https://www.gravatar.com/avatar/') ||
+										user.profile_image_url.startsWith('data:')
+											? user.profile_image_url
+											: `/user.png`}
+										alt="user"
+									/>
+
+									<div class=" font-medium self-center">{user.name}</div>
+								</div>
+							</Tooltip>
+
+							{#if userIds.includes(user.id)}
+								<Badge type="success" content="member" />
+							{/if}
+						</div>
+					</div>
+				{/each}
+			{:else}
+				<div class="text-gray-500 text-xs text-center py-2 px-10">
+					{$i18n.t('No users were found.')}
+				</div>
+			{/if}
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/admin/Users/UserList.svelte b/src/lib/components/admin/Users/UserList.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bbf6fb25e2280087b9a0389f7493d71ba93c2cc4
--- /dev/null
+++ b/src/lib/components/admin/Users/UserList.svelte
@@ -0,0 +1,451 @@
+<script>
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import { WEBUI_NAME, config, user, showSidebar } from '$lib/stores';
+	import { goto } from '$app/navigation';
+	import { onMount, getContext } from 'svelte';
+
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	dayjs.extend(relativeTime);
+
+	import { toast } from 'svelte-sonner';
+
+	import { updateUserRole, getUsers, deleteUserById } from '$lib/apis/users';
+
+	import Pagination from '$lib/components/common/Pagination.svelte';
+	import ChatBubbles from '$lib/components/icons/ChatBubbles.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	import EditUserModal from '$lib/components/admin/Users/UserList/EditUserModal.svelte';
+	import UserChatsModal from '$lib/components/admin/Users/UserList/UserChatsModal.svelte';
+	import AddUserModal from '$lib/components/admin/Users/UserList/AddUserModal.svelte';
+
+	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
+	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
+	import About from '$lib/components/chat/Settings/About.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let users = [];
+
+	let search = '';
+	let selectedUser = null;
+
+	let page = 1;
+
+	let showDeleteConfirmDialog = false;
+	let showAddUserModal = false;
+
+	let showUserChatsModal = false;
+	let showEditUserModal = false;
+
+	const updateRoleHandler = async (id, role) => {
+		const res = await updateUserRole(localStorage.token, id, role).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			users = await getUsers(localStorage.token);
+		}
+	};
+
+	const deleteUserHandler = async (id) => {
+		const res = await deleteUserById(localStorage.token, id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+		if (res) {
+			users = await getUsers(localStorage.token);
+		}
+	};
+
+	let sortKey = 'created_at'; // default sort key
+	let sortOrder = 'asc'; // default sort order
+
+	function setSortKey(key) {
+		if (sortKey === key) {
+			sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
+		} else {
+			sortKey = key;
+			sortOrder = 'asc';
+		}
+	}
+
+	let filteredUsers;
+
+	$: filteredUsers = users
+		.filter((user) => {
+			if (search === '') {
+				return true;
+			} else {
+				let name = user.name.toLowerCase();
+				const query = search.toLowerCase();
+				return name.includes(query);
+			}
+		})
+		.sort((a, b) => {
+			if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
+			if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
+			return 0;
+		})
+		.slice((page - 1) * 20, page * 20);
+</script>
+
+<ConfirmDialog
+	bind:show={showDeleteConfirmDialog}
+	on:confirm={() => {
+		deleteUserHandler(selectedUser.id);
+	}}
+/>
+
+{#key selectedUser}
+	<EditUserModal
+		bind:show={showEditUserModal}
+		{selectedUser}
+		sessionUser={$user}
+		on:save={async () => {
+			users = await getUsers(localStorage.token);
+		}}
+	/>
+{/key}
+
+<AddUserModal
+	bind:show={showAddUserModal}
+	on:save={async () => {
+		users = await getUsers(localStorage.token);
+	}}
+/>
+<UserChatsModal bind:show={showUserChatsModal} user={selectedUser} />
+
+<div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between">
+	<div class="flex md:self-center text-lg font-medium px-0.5">
+		{$i18n.t('Users')}
+		<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+
+		<span class="text-lg font-medium text-gray-500 dark:text-gray-300">{users.length}</span>
+	</div>
+
+	<div class="flex gap-1">
+		<div class=" flex w-full space-x-2">
+			<div class="flex flex-1">
+				<div class=" self-center ml-1 mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<input
+					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={search}
+					placeholder={$i18n.t('Search')}
+				/>
+			</div>
+
+			<div>
+				<Tooltip content={$i18n.t('Add User')}>
+					<button
+						class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1"
+						on:click={() => {
+							showAddUserModal = !showAddUserModal;
+						}}
+					>
+						<Plus className="size-3.5" />
+					</button>
+				</Tooltip>
+			</div>
+		</div>
+	</div>
+</div>
+
+<div class="scrollbar-hidden relative whitespace-nowrap overflow-x-auto max-w-full rounded pt-0.5">
+	<table
+		class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto max-w-full rounded"
+	>
+		<thead
+			class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5"
+		>
+			<tr class="">
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('role')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('Role')}
+
+						{#if sortKey === 'role'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('name')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('Name')}
+
+						{#if sortKey === 'name'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('email')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('Email')}
+
+						{#if sortKey === 'email'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('last_active_at')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('Last Active')}
+
+						{#if sortKey === 'last_active_at'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('created_at')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('Created at')}
+						{#if sortKey === 'created_at'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+
+				<th
+					scope="col"
+					class="px-3 py-1.5 cursor-pointer select-none"
+					on:click={() => setSortKey('oauth_sub')}
+				>
+					<div class="flex gap-1.5 items-center">
+						{$i18n.t('OAuth ID')}
+
+						{#if sortKey === 'oauth_sub'}
+							<span class="font-normal"
+								>{#if sortOrder === 'asc'}
+									<ChevronUp className="size-2" />
+								{:else}
+									<ChevronDown className="size-2" />
+								{/if}
+							</span>
+						{:else}
+							<span class="invisible">
+								<ChevronUp className="size-2" />
+							</span>
+						{/if}
+					</div>
+				</th>
+
+				<th scope="col" class="px-3 py-2 text-right" />
+			</tr>
+		</thead>
+		<tbody class="">
+			{#each filteredUsers as user, userIdx}
+				<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
+					<td class="px-3 py-1 min-w-[7rem] w-28">
+						<button
+							class=" translate-y-0.5"
+							on:click={() => {
+								if (user.role === 'user') {
+									updateRoleHandler(user.id, 'admin');
+								} else if (user.role === 'pending') {
+									updateRoleHandler(user.id, 'user');
+								} else {
+									updateRoleHandler(user.id, 'pending');
+								}
+							}}
+						>
+							<Badge
+								type={user.role === 'admin' ? 'info' : user.role === 'user' ? 'success' : 'muted'}
+								content={$i18n.t(user.role)}
+							/>
+						</button>
+					</td>
+					<td class="px-3 py-1 font-medium text-gray-900 dark:text-white w-max">
+						<div class="flex flex-row w-max">
+							<img
+								class=" rounded-full w-6 h-6 object-cover mr-2.5"
+								src={user.profile_image_url.startsWith(WEBUI_BASE_URL) ||
+								user.profile_image_url.startsWith('https://www.gravatar.com/avatar/') ||
+								user.profile_image_url.startsWith('data:')
+									? user.profile_image_url
+									: `/user.png`}
+								alt="user"
+							/>
+
+							<div class=" font-medium self-center">{user.name}</div>
+						</div>
+					</td>
+					<td class=" px-3 py-1"> {user.email} </td>
+
+					<td class=" px-3 py-1">
+						{dayjs(user.last_active_at * 1000).fromNow()}
+					</td>
+
+					<td class=" px-3 py-1">
+						{dayjs(user.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))}
+					</td>
+
+					<td class=" px-3 py-1"> {user.oauth_sub ?? ''} </td>
+
+					<td class="px-3 py-1 text-right">
+						<div class="flex justify-end w-full">
+							{#if $config.features.enable_admin_chat_access && user.role !== 'admin'}
+								<Tooltip content={$i18n.t('Chats')}>
+									<button
+										class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+										on:click={async () => {
+											showUserChatsModal = !showUserChatsModal;
+											selectedUser = user;
+										}}
+									>
+										<ChatBubbles />
+									</button>
+								</Tooltip>
+							{/if}
+
+							<Tooltip content={$i18n.t('Edit User')}>
+								<button
+									class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+									on:click={async () => {
+										showEditUserModal = !showEditUserModal;
+										selectedUser = user;
+									}}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="1.5"
+										stroke="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+
+							{#if user.role !== 'admin'}
+								<Tooltip content={$i18n.t('Delete User')}>
+									<button
+										class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+										on:click={async () => {
+											showDeleteConfirmDialog = true;
+											selectedUser = user;
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke-width="1.5"
+											stroke="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+											/>
+										</svg>
+									</button>
+								</Tooltip>
+							{/if}
+						</div>
+					</td>
+				</tr>
+			{/each}
+		</tbody>
+	</table>
+</div>
+
+<div class=" text-gray-500 text-xs mt-1.5 text-right">
+	ⓘ {$i18n.t("Click on the user role button to change a user's role.")}
+</div>
+
+<Pagination bind:page count={users.length} />
diff --git a/src/lib/components/admin/Users/UserList/AddUserModal.svelte b/src/lib/components/admin/Users/UserList/AddUserModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c29a39e826acfc56987924672a5c36bacbb2dceb
--- /dev/null
+++ b/src/lib/components/admin/Users/UserList/AddUserModal.svelte
@@ -0,0 +1,339 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+	import { addUser } from '$lib/apis/auths';
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+
+	let loading = false;
+	let tab = '';
+	let inputFiles;
+
+	let _user = {
+		name: '',
+		email: '',
+		password: '',
+		role: 'user'
+	};
+
+	$: if (show) {
+		_user = {
+			name: '',
+			email: '',
+			password: '',
+			role: 'user'
+		};
+	}
+
+	const submitHandler = async () => {
+		const stopLoading = () => {
+			dispatch('save');
+			loading = false;
+		};
+
+		if (tab === '') {
+			loading = true;
+
+			const res = await addUser(
+				localStorage.token,
+				_user.name,
+				_user.email,
+				_user.password,
+				_user.role
+			).catch((error) => {
+				toast.error(error);
+			});
+
+			if (res) {
+				stopLoading();
+				show = false;
+			}
+		} else {
+			if (inputFiles) {
+				loading = true;
+
+				const file = inputFiles[0];
+				const reader = new FileReader();
+
+				reader.onload = async (e) => {
+					const csv = e.target.result;
+					const rows = csv.split('\n');
+
+					let userCount = 0;
+
+					for (const [idx, row] of rows.entries()) {
+						const columns = row.split(',').map((col) => col.trim());
+						console.log(idx, columns);
+
+						if (idx > 0) {
+							if (
+								columns.length === 4 &&
+								['admin', 'user', 'pending'].includes(columns[3].toLowerCase())
+							) {
+								const res = await addUser(
+									localStorage.token,
+									columns[0],
+									columns[1],
+									columns[2],
+									columns[3].toLowerCase()
+								).catch((error) => {
+									toast.error(`Row ${idx + 1}: ${error}`);
+									return null;
+								});
+
+								if (res) {
+									userCount = userCount + 1;
+								}
+							} else {
+								toast.error(`Row ${idx + 1}: invalid format.`);
+							}
+						}
+					}
+
+					toast.success(`Successfully imported ${userCount} users.`);
+					inputFiles = null;
+					const uploadInputElement = document.getElementById('upload-user-csv-input');
+
+					if (uploadInputElement) {
+						uploadInputElement.value = null;
+					}
+
+					stopLoading();
+				};
+
+				reader.readAsText(file);
+			} else {
+				toast.error($i18n.t('File not found.'));
+			}
+		}
+	};
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Add User')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-3 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="flex text-center text-sm font-medium rounded-full bg-transparent/10 p-1 mb-2">
+						<button
+							class="w-full rounded-full p-1.5 {tab === '' ? 'bg-gray-50 dark:bg-gray-850' : ''}"
+							type="button"
+							on:click={() => {
+								tab = '';
+							}}>{$i18n.t('Form')}</button
+						>
+
+						<button
+							class="w-full rounded-full p-1 {tab === 'import'
+								? 'bg-gray-50 dark:bg-gray-850'
+								: ''}"
+							type="button"
+							on:click={() => {
+								tab = 'import';
+							}}>{$i18n.t('CSV Import')}</button
+						>
+					</div>
+					<div class="px-1">
+						{#if tab === ''}
+							<div class="flex flex-col w-full">
+								<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Role')}</div>
+
+								<div class="flex-1">
+									<select
+										class="w-full capitalize rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
+										bind:value={_user.role}
+										placeholder={$i18n.t('Enter Your Role')}
+										required
+									>
+										<option value="pending"> {$i18n.t('pending')} </option>
+										<option value="user"> {$i18n.t('user')} </option>
+										<option value="admin"> {$i18n.t('admin')} </option>
+									</select>
+								</div>
+							</div>
+
+							<div class="flex flex-col w-full mt-1">
+								<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
+										type="text"
+										bind:value={_user.name}
+										placeholder={$i18n.t('Enter Your Full Name')}
+										autocomplete="off"
+										required
+									/>
+								</div>
+							</div>
+
+							<hr class=" border-gray-50 dark:border-gray-850 my-2.5 w-full" />
+
+							<div class="flex flex-col w-full">
+								<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
+										type="email"
+										bind:value={_user.email}
+										placeholder={$i18n.t('Enter Your Email')}
+										required
+									/>
+								</div>
+							</div>
+
+							<div class="flex flex-col w-full mt-1">
+								<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Password')}</div>
+
+								<div class="flex-1">
+									<input
+										class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
+										type="password"
+										bind:value={_user.password}
+										placeholder={$i18n.t('Enter Your Password')}
+										autocomplete="off"
+									/>
+								</div>
+							</div>
+						{:else if tab === 'import'}
+							<div>
+								<div class="mb-3 w-full">
+									<input
+										id="upload-user-csv-input"
+										hidden
+										bind:files={inputFiles}
+										type="file"
+										accept=".csv"
+									/>
+
+									<button
+										class="w-full text-sm font-medium py-3 bg-transparent hover:bg-gray-100 border border-dashed dark:border-gray-800 dark:hover:bg-gray-850 text-center rounded-xl"
+										type="button"
+										on:click={() => {
+											document.getElementById('upload-user-csv-input')?.click();
+										}}
+									>
+										{#if inputFiles}
+											{inputFiles.length > 0 ? `${inputFiles.length}` : ''} document(s) selected.
+										{:else}
+											{$i18n.t('Click here to select a csv file.')}
+										{/if}
+									</button>
+								</div>
+
+								<div class=" text-xs text-gray-500">
+									ⓘ {$i18n.t(
+										'Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.'
+									)}
+									<a
+										class="underline dark:text-gray-200"
+										href="{WEBUI_BASE_URL}/static/user-import.csv"
+									>
+										{$i18n.t('Click here to download user import template file.')}
+									</a>
+								</div>
+							</div>
+						{/if}
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<button
+							class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Save')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/admin/Users/UserList/EditUserModal.svelte b/src/lib/components/admin/Users/UserList/EditUserModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2b01e4fe55a5682955df232f2ba2975d11bb7cda
--- /dev/null
+++ b/src/lib/components/admin/Users/UserList/EditUserModal.svelte
@@ -0,0 +1,175 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+
+	import { updateUserById } from '$lib/apis/users';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+	export let selectedUser;
+	export let sessionUser;
+
+	let _user = {
+		profile_image_url: '',
+		name: '',
+		email: '',
+		password: ''
+	};
+
+	const submitHandler = async () => {
+		const res = await updateUserById(localStorage.token, selectedUser.id, _user).catch((error) => {
+			toast.error(error);
+		});
+
+		if (res) {
+			dispatch('save');
+			show = false;
+		}
+	};
+
+	onMount(() => {
+		if (selectedUser) {
+			_user = selectedUser;
+			_user.password = '';
+		}
+	});
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 py-4">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Edit User')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+		<hr class=" dark:border-gray-800" />
+
+		<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class=" flex items-center rounded-md py-2 px-4 w-full">
+						<div class=" self-center mr-5">
+							<img
+								src={selectedUser.profile_image_url}
+								class=" max-w-[55px] object-cover rounded-full"
+								alt="User profile"
+							/>
+						</div>
+
+						<div>
+							<div class=" self-center capitalize font-semibold">{selectedUser.name}</div>
+
+							<div class="text-xs text-gray-500">
+								{$i18n.t('Created at')}
+								{dayjs(selectedUser.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))}
+							</div>
+						</div>
+					</div>
+
+					<hr class=" dark:border-gray-800 my-3 w-full" />
+
+					<div class=" flex flex-col space-y-1.5">
+						<div class="flex flex-col w-full">
+							<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
+
+							<div class="flex-1">
+								<input
+									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 disabled:text-gray-500 dark:disabled:text-gray-500 outline-none"
+									type="email"
+									bind:value={_user.email}
+									autocomplete="off"
+									required
+									disabled={_user.id == sessionUser.id}
+								/>
+							</div>
+						</div>
+
+						<div class="flex flex-col w-full">
+							<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
+
+							<div class="flex-1">
+								<input
+									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
+									type="text"
+									bind:value={_user.name}
+									autocomplete="off"
+									required
+								/>
+							</div>
+						</div>
+
+						<div class="flex flex-col w-full">
+							<div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
+
+							<div class="flex-1">
+								<input
+									class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
+									type="password"
+									bind:value={_user.password}
+									autocomplete="new-password"
+								/>
+							</div>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<button
+							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
+							type="submit"
+						>
+							{$i18n.t('Save')}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/admin/Users/UserList/UserChatsModal.svelte b/src/lib/components/admin/Users/UserList/UserChatsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7cf03b4b75ae0349c58a84435ba22f14a6f5860e
--- /dev/null
+++ b/src/lib/components/admin/Users/UserList/UserChatsModal.svelte
@@ -0,0 +1,186 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import { getChatListByUserId, deleteChatById, getArchivedChatList } from '$lib/apis/chats';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+	export let user;
+
+	let chats = null;
+
+	const deleteChatHandler = async (chatId) => {
+		const res = await deleteChatById(localStorage.token, chatId).catch((error) => {
+			toast.error(error);
+		});
+
+		chats = await getChatListByUserId(localStorage.token, user.id);
+	};
+
+	$: if (show) {
+		(async () => {
+			if (user.id) {
+				chats = await getChatListByUserId(localStorage.token, user.id);
+			}
+		})();
+	} else {
+		chats = null;
+	}
+
+	let sortKey = 'updated_at'; // default sort key
+	let sortOrder = 'desc'; // default sort order
+	function setSortKey(key) {
+		if (sortKey === key) {
+			sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
+		} else {
+			sortKey = key;
+			sortOrder = 'asc';
+		}
+	}
+</script>
+
+<Modal size="lg" bind:show>
+	<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
+		<div class=" text-lg font-medium self-center capitalize">
+			{$i18n.t("{{user}}'s Chats", { user: user.name })}
+		</div>
+		<button
+			class="self-center"
+			on:click={() => {
+				show = false;
+			}}
+		>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 20 20"
+				fill="currentColor"
+				class="w-5 h-5"
+			>
+				<path
+					d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+				/>
+			</svg>
+		</button>
+	</div>
+
+	<div class="flex flex-col md:flex-row w-full px-5 pt-2 pb-4 md:space-x-4 dark:text-gray-200">
+		<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+			{#if chats}
+				{#if chats.length > 0}
+					<div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">
+						<div class="relative overflow-x-auto">
+							<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
+								<thead
+									class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-800"
+								>
+									<tr>
+										<th
+											scope="col"
+											class="px-3 py-2 cursor-pointer select-none"
+											on:click={() => setSortKey('title')}
+										>
+											{$i18n.t('Title')}
+											{#if sortKey === 'title'}
+												{sortOrder === 'asc' ? '▲' : '▼'}
+											{:else}
+												<span class="invisible">▲</span>
+											{/if}
+										</th>
+										<th
+											scope="col"
+											class="px-3 py-2 hidden md:flex cursor-pointer select-none justify-end"
+											on:click={() => setSortKey('updated_at')}
+										>
+											{$i18n.t('Updated at')}
+											{#if sortKey === 'updated_at'}
+												{sortOrder === 'asc' ? '▲' : '▼'}
+											{:else}
+												<span class="invisible">▲</span>
+											{/if}
+										</th>
+										<th scope="col" class="px-3 py-2 text-right" />
+									</tr>
+								</thead>
+								<tbody>
+									{#each chats.sort((a, b) => {
+										if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
+										if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
+										return 0;
+									}) as chat, idx}
+										<tr
+											class="bg-transparent {idx !== chats.length - 1 &&
+												'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
+										>
+											<td class="px-3 py-1">
+												<a href="/s/{chat.id}" target="_blank">
+													<div class=" underline line-clamp-1 max-w-96">
+														{chat.title}
+													</div>
+												</a>
+											</td>
+
+											<td class=" px-3 py-1 hidden md:flex h-[2.5rem] justify-end">
+												<div class="my-auto shrink-0">
+													{dayjs(chat.updated_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))}
+												</div>
+											</td>
+
+											<td class="px-3 py-1 text-right">
+												<div class="flex justify-end w-full">
+													<Tooltip content={$i18n.t('Delete Chat')}>
+														<button
+															class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+															on:click={async () => {
+																deleteChatHandler(chat.id);
+															}}
+														>
+															<svg
+																xmlns="http://www.w3.org/2000/svg"
+																fill="none"
+																viewBox="0 0 24 24"
+																stroke-width="1.5"
+																stroke="currentColor"
+																class="w-4 h-4"
+															>
+																<path
+																	stroke-linecap="round"
+																	stroke-linejoin="round"
+																	d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+																/>
+															</svg>
+														</button>
+													</Tooltip>
+												</div>
+											</td>
+										</tr>
+									{/each}
+								</tbody>
+							</table>
+						</div>
+						<!-- {#each chats as chat}
+							<div>
+								{JSON.stringify(chat)}
+							</div>
+						{/each} -->
+					</div>
+				{:else}
+					<div class="text-left text-sm w-full mb-8">
+						{user.name}
+						{$i18n.t('has no conversations.')}
+					</div>
+				{/if}
+			{:else}
+				<Spinner />
+			{/if}
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Artifacts.svelte b/src/lib/components/chat/Artifacts.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0a8ab956aba2fce6d2f1b7be263ba83245b380a4
--- /dev/null
+++ b/src/lib/components/chat/Artifacts.svelte
@@ -0,0 +1,322 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import { chatId, showArtifacts, showControls } from '$lib/stores';
+	import XMark from '../icons/XMark.svelte';
+	import { copyToClipboard, createMessagesList } from '$lib/utils';
+	import ArrowsPointingOut from '../icons/ArrowsPointingOut.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import SvgPanZoom from '../common/SVGPanZoom.svelte';
+	import ArrowLeft from '../icons/ArrowLeft.svelte';
+
+	export let overlay = false;
+	export let history;
+	let messages = [];
+
+	let contents: Array<{ type: string; content: string }> = [];
+	let selectedContentIdx = 0;
+
+	let copied = false;
+	let iframeElement: HTMLIFrameElement;
+
+	$: if (history) {
+		messages = createMessagesList(history, history.currentId);
+		getContents();
+	} else {
+		messages = [];
+		getContents();
+	}
+
+	const getContents = () => {
+		contents = [];
+		messages.forEach((message) => {
+			if (message?.role !== 'user' && message?.content) {
+				const codeBlockContents = message.content.match(/```[\s\S]*?```/g);
+				let codeBlocks = [];
+
+				if (codeBlockContents) {
+					codeBlockContents.forEach((block) => {
+						const lang = block.split('\n')[0].replace('```', '').trim().toLowerCase();
+						const code = block.replace(/```[\s\S]*?\n/, '').replace(/```$/, '');
+						codeBlocks.push({ lang, code });
+					});
+				}
+
+				let htmlContent = '';
+				let cssContent = '';
+				let jsContent = '';
+
+				codeBlocks.forEach((block) => {
+					const { lang, code } = block;
+
+					if (lang === 'html') {
+						htmlContent += code + '\n';
+					} else if (lang === 'css') {
+						cssContent += code + '\n';
+					} else if (lang === 'javascript' || lang === 'js') {
+						jsContent += code + '\n';
+					}
+				});
+
+				const inlineHtml = message.content.match(/<html>[\s\S]*?<\/html>/gi);
+				const inlineCss = message.content.match(/<style>[\s\S]*?<\/style>/gi);
+				const inlineJs = message.content.match(/<script>[\s\S]*?<\/script>/gi);
+
+				if (inlineHtml) {
+					inlineHtml.forEach((block) => {
+						const content = block.replace(/<\/?html>/gi, ''); // Remove <html> tags
+						htmlContent += content + '\n';
+					});
+				}
+				if (inlineCss) {
+					inlineCss.forEach((block) => {
+						const content = block.replace(/<\/?style>/gi, ''); // Remove <style> tags
+						cssContent += content + '\n';
+					});
+				}
+				if (inlineJs) {
+					inlineJs.forEach((block) => {
+						const content = block.replace(/<\/?script>/gi, ''); // Remove <script> tags
+						jsContent += content + '\n';
+					});
+				}
+
+				if (htmlContent || cssContent || jsContent) {
+					const renderedContent = `
+                        <!DOCTYPE html>
+                        <html lang="en">
+                        <head>
+                            <meta charset="UTF-8">
+                            <meta name="viewport" content="width=device-width, initial-scale=1.0">
+							<${''}style>
+								body {
+									background-color: white; /* Ensure the iframe has a white background */
+								}
+
+								${cssContent}
+							</${''}style>
+                        </head>
+                        <body>
+                            ${htmlContent}
+
+							<${''}script>
+                            	${jsContent}
+							</${''}script>
+                        </body>
+                        </html>
+                    `;
+					contents = [...contents, { type: 'iframe', content: renderedContent }];
+				} else {
+					// Check for SVG content
+					for (const block of codeBlocks) {
+						if (block.lang === 'svg' || (block.lang === 'xml' && block.code.includes('<svg'))) {
+							contents = [...contents, { type: 'svg', content: block.code }];
+						}
+					}
+				}
+			}
+		});
+
+		if (contents.length === 0) {
+			showControls.set(false);
+			showArtifacts.set(false);
+		}
+
+		selectedContentIdx = contents ? contents.length - 1 : 0;
+	};
+
+	function navigateContent(direction: 'prev' | 'next') {
+		console.log(selectedContentIdx);
+
+		selectedContentIdx =
+			direction === 'prev'
+				? Math.max(selectedContentIdx - 1, 0)
+				: Math.min(selectedContentIdx + 1, contents.length - 1);
+
+		console.log(selectedContentIdx);
+	}
+
+	const iframeLoadHandler = () => {
+		iframeElement.contentWindow.addEventListener(
+			'click',
+			function (e) {
+				const target = e.target.closest('a');
+				if (target && target.href) {
+					e.preventDefault();
+					const url = new URL(target.href, iframeElement.baseURI);
+					if (url.origin === window.location.origin) {
+						iframeElement.contentWindow.history.pushState(
+							null,
+							'',
+							url.pathname + url.search + url.hash
+						);
+					} else {
+						console.log('External navigation blocked:', url.href);
+					}
+				}
+			},
+			true
+		);
+
+		// Cancel drag when hovering over iframe
+		iframeElement.contentWindow.addEventListener('mouseenter', function (e) {
+			e.preventDefault();
+			iframeElement.contentWindow.addEventListener('dragstart', (event) => {
+				event.preventDefault();
+			});
+		});
+	};
+
+	const showFullScreen = () => {
+		if (iframeElement.requestFullscreen) {
+			iframeElement.requestFullscreen();
+		} else if (iframeElement.webkitRequestFullscreen) {
+			iframeElement.webkitRequestFullscreen();
+		} else if (iframeElement.msRequestFullscreen) {
+			iframeElement.msRequestFullscreen();
+		}
+	};
+
+	onMount(() => {});
+</script>
+
+<div class=" w-full h-full relative flex flex-col bg-gray-50 dark:bg-gray-850">
+	<div class="w-full h-full flex-1 relative">
+		{#if overlay}
+			<div class=" absolute top-0 left-0 right-0 bottom-0 z-10"></div>
+		{/if}
+
+		<div class="absolute pointer-events-none z-50 w-full flex items-center justify-start p-4">
+			<button
+				class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
+				on:click={() => {
+					showArtifacts.set(false);
+				}}
+			>
+				<ArrowLeft className="size-3.5  text-gray-900 dark:text-white" />
+			</button>
+		</div>
+
+		<div class=" absolute pointer-events-none z-50 w-full flex items-center justify-end p-4">
+			<button
+				class="self-center pointer-events-auto p-1 rounded-full bg-white dark:bg-gray-850"
+				on:click={() => {
+					dispatch('close');
+					showControls.set(false);
+					showArtifacts.set(false);
+				}}
+			>
+				<XMark className="size-3.5 text-gray-900 dark:text-white" />
+			</button>
+		</div>
+
+		<div class="flex-1 w-full h-full">
+			<div class=" h-full flex flex-col">
+				{#if contents.length > 0}
+					<div class="max-w-full w-full h-full">
+						{#if contents[selectedContentIdx].type === 'iframe'}
+							<iframe
+								bind:this={iframeElement}
+								title="Content"
+								srcdoc={contents[selectedContentIdx].content}
+								class="w-full border-0 h-full rounded-none"
+								sandbox="allow-scripts allow-forms allow-same-origin"
+								on:load={iframeLoadHandler}
+							></iframe>
+						{:else if contents[selectedContentIdx].type === 'svg'}
+							<SvgPanZoom
+								className=" w-full h-full max-h-full overflow-hidden"
+								svg={contents[selectedContentIdx].content}
+							/>
+						{/if}
+					</div>
+				{:else}
+					<div class="m-auto font-medium text-xs text-gray-900 dark:text-white">
+						{$i18n.t('No HTML, CSS, or JavaScript content found.')}
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+
+	{#if contents.length > 0}
+		<div class="flex justify-between items-center p-2.5 font-primar text-gray-900 dark:text-white">
+			<div class="flex items-center space-x-2">
+				<div class="flex items-center gap-0.5 self-center min-w-fit" dir="ltr">
+					<button
+						class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
+						on:click={() => navigateContent('prev')}
+						disabled={contents.length <= 1}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke="currentColor"
+							stroke-width="2.5"
+							class="size-3.5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M15.75 19.5 8.25 12l7.5-7.5"
+							/>
+						</svg>
+					</button>
+
+					<div class="text-xs self-center dark:text-gray-100 min-w-fit">
+						{$i18n.t('Version {{selectedVersion}} of {{totalVersions}}', {
+							selectedVersion: selectedContentIdx + 1,
+							totalVersions: contents.length
+						})}
+					</div>
+
+					<button
+						class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition disabled:cursor-not-allowed"
+						on:click={() => navigateContent('next')}
+						disabled={contents.length <= 1}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke="currentColor"
+							stroke-width="2.5"
+							class="size-3.5"
+						>
+							<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
+						</svg>
+					</button>
+				</div>
+			</div>
+
+			<div class="flex items-center gap-1">
+				<button
+					class="copy-code-button bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
+					on:click={() => {
+						copyToClipboard(contents[selectedContentIdx].content);
+						copied = true;
+
+						setTimeout(() => {
+							copied = false;
+						}, 2000);
+					}}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
+				>
+
+				{#if contents[selectedContentIdx].type === 'iframe'}
+					<Tooltip content={$i18n.t('Open in full screen')}>
+						<button
+							class=" bg-none border-none text-xs bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md p-0.5"
+							on:click={showFullScreen}
+						>
+							<ArrowsPointingOut className="size-3.5" />
+						</button>
+					</Tooltip>
+				{/if}
+			</div>
+		</div>
+	{/if}
+</div>
diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e6a653420c3603f273cdd2e894116afce7da4b31
--- /dev/null
+++ b/src/lib/components/chat/Chat.svelte
@@ -0,0 +1,2389 @@
+<script lang="ts">
+	import { v4 as uuidv4 } from 'uuid';
+	import { toast } from 'svelte-sonner';
+	import mermaid from 'mermaid';
+	import { PaneGroup, Pane, PaneResizer } from 'paneforge';
+
+	import { getContext, onDestroy, onMount, tick } from 'svelte';
+	const i18n: Writable<i18nType> = getContext('i18n');
+
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+
+	import { get, type Unsubscriber, type Writable } from 'svelte/store';
+	import type { i18n as i18nType } from 'i18next';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import {
+		chatId,
+		chats,
+		config,
+		type Model,
+		models,
+		tags as allTags,
+		settings,
+		showSidebar,
+		WEBUI_NAME,
+		banners,
+		user,
+		socket,
+		showControls,
+		showCallOverlay,
+		currentChatPage,
+		temporaryChatEnabled,
+		mobile,
+		showOverview,
+		chatTitle,
+		showArtifacts,
+		tools
+	} from '$lib/stores';
+	import {
+		convertMessagesToHistory,
+		copyToClipboard,
+		getMessageContentParts,
+		extractSentencesForAudio,
+		promptTemplate,
+		splitStream
+	} from '$lib/utils';
+
+	import { generateChatCompletion } from '$lib/apis/ollama';
+	import {
+		addTagById,
+		createNewChat,
+		deleteTagById,
+		deleteTagsById,
+		getAllTags,
+		getChatById,
+		getChatList,
+		getTagsById,
+		updateChatById
+	} from '$lib/apis/chats';
+	import { generateOpenAIChatCompletion } from '$lib/apis/openai';
+	import { processWeb, processWebSearch, processYoutubeVideo } from '$lib/apis/retrieval';
+	import { createOpenAITextStream } from '$lib/apis/streaming';
+	import { queryMemory } from '$lib/apis/memories';
+	import { getAndUpdateUserLocation, getUserSettings } from '$lib/apis/users';
+	import {
+		chatCompleted,
+		generateTitle,
+		generateQueries,
+		chatAction,
+		generateMoACompletion,
+		generateTags
+	} from '$lib/apis';
+
+	import Banner from '../common/Banner.svelte';
+	import MessageInput from '$lib/components/chat/MessageInput.svelte';
+	import Messages from '$lib/components/chat/Messages.svelte';
+	import Navbar from '$lib/components/layout/Navbar.svelte';
+	import ChatControls from './ChatControls.svelte';
+	import EventConfirmDialog from '../common/ConfirmDialog.svelte';
+	import Placeholder from './Placeholder.svelte';
+	import { getTools } from '$lib/apis/tools';
+
+	export let chatIdProp = '';
+
+	let loaded = false;
+	const eventTarget = new EventTarget();
+	let controlPane;
+	let controlPaneComponent;
+
+	let stopResponseFlag = false;
+	let autoScroll = true;
+	let processing = '';
+	let messagesContainerElement: HTMLDivElement;
+
+	let navbarElement;
+
+	let showEventConfirmation = false;
+	let eventConfirmationTitle = '';
+	let eventConfirmationMessage = '';
+	let eventConfirmationInput = false;
+	let eventConfirmationInputPlaceholder = '';
+	let eventConfirmationInputValue = '';
+	let eventCallback = null;
+
+	let chatIdUnsubscriber: Unsubscriber | undefined;
+
+	let selectedModels = [''];
+	let atSelectedModel: Model | undefined;
+	let selectedModelIds = [];
+	$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
+
+	let selectedToolIds = [];
+	let webSearchEnabled = false;
+
+	let chat = null;
+	let tags = [];
+
+	let history = {
+		messages: {},
+		currentId: null
+	};
+
+	// Chat Input
+	let prompt = '';
+	let chatFiles = [];
+	let files = [];
+	let params = {};
+
+	$: if (chatIdProp) {
+		(async () => {
+			console.log(chatIdProp);
+			if (chatIdProp && (await loadChat())) {
+				await tick();
+				loaded = true;
+
+				window.setTimeout(() => scrollToBottom(), 0);
+				const chatInput = document.getElementById('chat-input');
+				chatInput?.focus();
+			} else {
+				await goto('/');
+			}
+		})();
+	}
+
+	$: if (selectedModels && chatIdProp !== '') {
+		saveSessionSelectedModels();
+	}
+
+	const saveSessionSelectedModels = () => {
+		if (selectedModels.length === 0 || (selectedModels.length === 1 && selectedModels[0] === '')) {
+			return;
+		}
+		sessionStorage.selectedModels = JSON.stringify(selectedModels);
+		console.log('saveSessionSelectedModels', selectedModels, sessionStorage.selectedModels);
+	};
+
+	$: if (selectedModels) {
+		setToolIds();
+	}
+
+	const setToolIds = async () => {
+		if (!$tools) {
+			tools.set(await getTools(localStorage.token));
+		}
+
+		if (selectedModels.length !== 1) {
+			return;
+		}
+		const model = $models.find((m) => m.id === selectedModels[0]);
+		if (model) {
+			selectedToolIds = (model?.info?.meta?.toolIds ?? []).filter((id) =>
+				$tools.find((t) => t.id === id)
+			);
+		}
+	};
+
+	const showMessage = async (message) => {
+		const _chatId = JSON.parse(JSON.stringify($chatId));
+		let _messageId = JSON.parse(JSON.stringify(message.id));
+
+		let messageChildrenIds = history.messages[_messageId].childrenIds;
+
+		while (messageChildrenIds.length !== 0) {
+			_messageId = messageChildrenIds.at(-1);
+			messageChildrenIds = history.messages[_messageId].childrenIds;
+		}
+
+		history.currentId = _messageId;
+
+		await tick();
+		await tick();
+		await tick();
+
+		const messageElement = document.getElementById(`message-${message.id}`);
+		if (messageElement) {
+			messageElement.scrollIntoView({ behavior: 'smooth' });
+		}
+
+		await tick();
+		saveChatHandler(_chatId);
+	};
+
+	const chatEventHandler = async (event, cb) => {
+		if (event.chat_id === $chatId) {
+			await tick();
+			console.log(event);
+			let message = history.messages[event.message_id];
+
+			const type = event?.data?.type ?? null;
+			const data = event?.data?.data ?? null;
+
+			if (type === 'status') {
+				if (message?.statusHistory) {
+					message.statusHistory.push(data);
+				} else {
+					message.statusHistory = [data];
+				}
+			} else if (type === 'source' || type === 'citation') {
+				if (data?.type === 'code_execution') {
+					// Code execution; update existing code execution by ID, or add new one.
+					if (!message?.code_executions) {
+						message.code_executions = [];
+					}
+
+					const existingCodeExecutionIndex = message.code_executions.findIndex(
+						(execution) => execution.id === data.id
+					);
+
+					if (existingCodeExecutionIndex !== -1) {
+						message.code_executions[existingCodeExecutionIndex] = data;
+					} else {
+						message.code_executions.push(data);
+					}
+
+					message.code_executions = message.code_executions;
+				} else {
+					// Regular source.
+					if (message?.sources) {
+						message.sources.push(data);
+					} else {
+						message.sources = [data];
+					}
+				}
+			} else if (type === 'message') {
+				message.content += data.content;
+			} else if (type === 'replace') {
+				message.content = data.content;
+			} else if (type === 'action') {
+				if (data.action === 'continue') {
+					const continueButton = document.getElementById('continue-response-button');
+
+					if (continueButton) {
+						continueButton.click();
+					}
+				}
+			} else if (type === 'confirmation') {
+				eventCallback = cb;
+
+				eventConfirmationInput = false;
+				showEventConfirmation = true;
+
+				eventConfirmationTitle = data.title;
+				eventConfirmationMessage = data.message;
+			} else if (type === 'execute') {
+				eventCallback = cb;
+
+				try {
+					// Use Function constructor to evaluate code in a safer way
+					const asyncFunction = new Function(`return (async () => { ${data.code} })()`);
+					const result = await asyncFunction(); // Await the result of the async function
+
+					if (cb) {
+						cb(result);
+					}
+				} catch (error) {
+					console.error('Error executing code:', error);
+				}
+			} else if (type === 'input') {
+				eventCallback = cb;
+
+				eventConfirmationInput = true;
+				showEventConfirmation = true;
+
+				eventConfirmationTitle = data.title;
+				eventConfirmationMessage = data.message;
+				eventConfirmationInputPlaceholder = data.placeholder;
+				eventConfirmationInputValue = data?.value ?? '';
+			} else {
+				console.log('Unknown message type', data);
+			}
+
+			history.messages[event.message_id] = message;
+		}
+	};
+
+	const onMessageHandler = async (event: {
+		origin: string;
+		data: { type: string; text: string };
+	}) => {
+		if (event.origin !== window.origin) {
+			return;
+		}
+
+		// Replace with your iframe's origin
+		if (event.data.type === 'input:prompt') {
+			console.debug(event.data.text);
+
+			const inputElement = document.getElementById('chat-input');
+
+			if (inputElement) {
+				prompt = event.data.text;
+				inputElement.focus();
+			}
+		}
+
+		if (event.data.type === 'action:submit') {
+			console.debug(event.data.text);
+
+			if (prompt !== '') {
+				await tick();
+				submitPrompt(prompt);
+			}
+		}
+
+		if (event.data.type === 'input:prompt:submit') {
+			console.debug(event.data.text);
+
+			if (prompt !== '') {
+				await tick();
+				submitPrompt(event.data.text);
+			}
+		}
+	};
+
+	onMount(async () => {
+		console.log('mounted');
+		window.addEventListener('message', onMessageHandler);
+		$socket?.on('chat-events', chatEventHandler);
+
+		if (!$chatId) {
+			chatIdUnsubscriber = chatId.subscribe(async (value) => {
+				if (!value) {
+					await initNewChat();
+				}
+			});
+		} else {
+			if ($temporaryChatEnabled) {
+				await goto('/');
+			}
+		}
+
+		showControls.subscribe(async (value) => {
+			if (controlPane && !$mobile) {
+				try {
+					if (value) {
+						controlPaneComponent.openPane();
+					} else {
+						controlPane.collapse();
+					}
+				} catch (e) {
+					// ignore
+				}
+			}
+
+			if (!value) {
+				showCallOverlay.set(false);
+				showOverview.set(false);
+				showArtifacts.set(false);
+			}
+		});
+
+		const chatInput = document.getElementById('chat-input');
+		chatInput?.focus();
+
+		chats.subscribe(() => {});
+	});
+
+	onDestroy(() => {
+		chatIdUnsubscriber?.();
+		window.removeEventListener('message', onMessageHandler);
+		$socket?.off('chat-events');
+	});
+
+	// File upload functions
+
+	const uploadWeb = async (url) => {
+		console.log(url);
+
+		const fileItem = {
+			type: 'doc',
+			name: url,
+			collection_name: '',
+			status: 'uploading',
+			url: url,
+			error: ''
+		};
+
+		try {
+			files = [...files, fileItem];
+			const res = await processWeb(localStorage.token, '', url);
+
+			if (res) {
+				fileItem.status = 'uploaded';
+				fileItem.collection_name = res.collection_name;
+				fileItem.file = {
+					...res.file,
+					...fileItem.file
+				};
+
+				files = files;
+			}
+		} catch (e) {
+			// Remove the failed doc from the files array
+			files = files.filter((f) => f.name !== url);
+			toast.error(JSON.stringify(e));
+		}
+	};
+
+	const uploadYoutubeTranscription = async (url) => {
+		console.log(url);
+
+		const fileItem = {
+			type: 'doc',
+			name: url,
+			collection_name: '',
+			status: 'uploading',
+			context: 'full',
+			url: url,
+			error: ''
+		};
+
+		try {
+			files = [...files, fileItem];
+			const res = await processYoutubeVideo(localStorage.token, url);
+
+			if (res) {
+				fileItem.status = 'uploaded';
+				fileItem.collection_name = res.collection_name;
+				fileItem.file = {
+					...res.file,
+					...fileItem.file
+				};
+				files = files;
+			}
+		} catch (e) {
+			// Remove the failed doc from the files array
+			files = files.filter((f) => f.name !== url);
+			toast.error(e);
+		}
+	};
+
+	//////////////////////////
+	// Web functions
+	//////////////////////////
+
+	const initNewChat = async () => {
+		if (sessionStorage.selectedModels) {
+			selectedModels = JSON.parse(sessionStorage.selectedModels);
+			sessionStorage.removeItem('selectedModels');
+		} else {
+			if ($page.url.searchParams.get('models')) {
+				selectedModels = $page.url.searchParams.get('models')?.split(',');
+			} else if ($page.url.searchParams.get('model')) {
+				const urlModels = $page.url.searchParams.get('model')?.split(',');
+
+				if (urlModels.length === 1) {
+					const m = $models.find((m) => m.id === urlModels[0]);
+					if (!m) {
+						const modelSelectorButton = document.getElementById('model-selector-0-button');
+						if (modelSelectorButton) {
+							modelSelectorButton.click();
+							await tick();
+
+							const modelSelectorInput = document.getElementById('model-search-input');
+							if (modelSelectorInput) {
+								modelSelectorInput.focus();
+								modelSelectorInput.value = urlModels[0];
+								modelSelectorInput.dispatchEvent(new Event('input'));
+							}
+						}
+					} else {
+						selectedModels = urlModels;
+					}
+				} else {
+					selectedModels = urlModels;
+				}
+			} else if ($settings?.models) {
+				selectedModels = $settings?.models;
+			} else if ($config?.default_models) {
+				console.log($config?.default_models.split(',') ?? '');
+				selectedModels = $config?.default_models.split(',');
+			}
+		}
+
+		selectedModels = selectedModels.filter((modelId) => $models.map((m) => m.id).includes(modelId));
+		if (selectedModels.length === 0 || (selectedModels.length === 1 && selectedModels[0] === '')) {
+			if ($models.length > 0) {
+				selectedModels = [$models[0].id];
+			} else {
+				selectedModels = [''];
+			}
+		}
+
+		await showControls.set(false);
+		await showCallOverlay.set(false);
+		await showOverview.set(false);
+		await showArtifacts.set(false);
+
+		if ($page.url.pathname.includes('/c/')) {
+			window.history.replaceState(history.state, '', `/`);
+		}
+
+		autoScroll = true;
+
+		await chatId.set('');
+		await chatTitle.set('');
+
+		history = {
+			messages: {},
+			currentId: null
+		};
+
+		chatFiles = [];
+		params = {};
+
+		if ($page.url.searchParams.get('youtube')) {
+			uploadYoutubeTranscription(
+				`https://www.youtube.com/watch?v=${$page.url.searchParams.get('youtube')}`
+			);
+		}
+		if ($page.url.searchParams.get('web-search') === 'true') {
+			webSearchEnabled = true;
+		}
+
+		if ($page.url.searchParams.get('tools')) {
+			selectedToolIds = $page.url.searchParams
+				.get('tools')
+				?.split(',')
+				.map((id) => id.trim())
+				.filter((id) => id);
+		} else if ($page.url.searchParams.get('tool-ids')) {
+			selectedToolIds = $page.url.searchParams
+				.get('tool-ids')
+				?.split(',')
+				.map((id) => id.trim())
+				.filter((id) => id);
+		}
+
+		if ($page.url.searchParams.get('call') === 'true') {
+			showCallOverlay.set(true);
+			showControls.set(true);
+		}
+
+		if ($page.url.searchParams.get('q')) {
+			prompt = $page.url.searchParams.get('q') ?? '';
+
+			if (prompt) {
+				await tick();
+				submitPrompt(prompt);
+			}
+		}
+
+		selectedModels = selectedModels.map((modelId) =>
+			$models.map((m) => m.id).includes(modelId) ? modelId : ''
+		);
+
+		const userSettings = await getUserSettings(localStorage.token);
+
+		if (userSettings) {
+			settings.set(userSettings.ui);
+		} else {
+			settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
+		}
+
+		const chatInput = document.getElementById('chat-input');
+		setTimeout(() => chatInput?.focus(), 0);
+	};
+
+	const loadChat = async () => {
+		chatId.set(chatIdProp);
+		chat = await getChatById(localStorage.token, $chatId).catch(async (error) => {
+			await goto('/');
+			return null;
+		});
+
+		if (chat) {
+			tags = await getTagsById(localStorage.token, $chatId).catch(async (error) => {
+				return [];
+			});
+
+			const chatContent = chat.chat;
+
+			if (chatContent) {
+				console.log(chatContent);
+
+				selectedModels =
+					(chatContent?.models ?? undefined) !== undefined
+						? chatContent.models
+						: [chatContent.models ?? ''];
+				history =
+					(chatContent?.history ?? undefined) !== undefined
+						? chatContent.history
+						: convertMessagesToHistory(chatContent.messages);
+
+				chatTitle.set(chatContent.title);
+
+				const userSettings = await getUserSettings(localStorage.token);
+
+				if (userSettings) {
+					await settings.set(userSettings.ui);
+				} else {
+					await settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}'));
+				}
+
+				params = chatContent?.params ?? {};
+				chatFiles = chatContent?.files ?? [];
+
+				autoScroll = true;
+				await tick();
+
+				if (history.currentId) {
+					history.messages[history.currentId].done = true;
+				}
+				await tick();
+
+				return true;
+			} else {
+				return null;
+			}
+		}
+	};
+
+	const scrollToBottom = async () => {
+		await tick();
+		if (messagesContainerElement) {
+			messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
+		}
+	};
+
+	const createMessagesList = (responseMessageId) => {
+		if (responseMessageId === null) {
+			return [];
+		}
+
+		const message = history.messages[responseMessageId];
+		if (message?.parentId) {
+			return [...createMessagesList(message.parentId), message];
+		} else {
+			return [message];
+		}
+	};
+
+	const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
+		await mermaid.run({
+			querySelector: '.mermaid'
+		});
+
+		const res = await chatCompleted(localStorage.token, {
+			model: modelId,
+			messages: messages.map((m) => ({
+				id: m.id,
+				role: m.role,
+				content: m.content,
+				info: m.info ? m.info : undefined,
+				timestamp: m.timestamp,
+				...(m.sources ? { sources: m.sources } : {})
+			})),
+			chat_id: chatId,
+			session_id: $socket?.id,
+			id: responseMessageId
+		}).catch((error) => {
+			toast.error(error);
+			messages.at(-1).error = { content: error };
+
+			return null;
+		});
+
+		if (res !== null) {
+			// Update chat history with the new messages
+			for (const message of res.messages) {
+				history.messages[message.id] = {
+					...history.messages[message.id],
+					...(history.messages[message.id].content !== message.content
+						? { originalContent: history.messages[message.id].content }
+						: {}),
+					...message
+				};
+			}
+		}
+
+		await tick();
+
+		if ($chatId == chatId) {
+			if (!$temporaryChatEnabled) {
+				chat = await updateChatById(localStorage.token, chatId, {
+					models: selectedModels,
+					messages: messages,
+					history: history,
+					params: params,
+					files: chatFiles
+				});
+
+				currentChatPage.set(1);
+				await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			}
+		}
+	};
+
+	const chatActionHandler = async (chatId, actionId, modelId, responseMessageId, event = null) => {
+		const messages = createMessagesList(responseMessageId);
+
+		const res = await chatAction(localStorage.token, actionId, {
+			model: modelId,
+			messages: messages.map((m) => ({
+				id: m.id,
+				role: m.role,
+				content: m.content,
+				info: m.info ? m.info : undefined,
+				timestamp: m.timestamp,
+				...(m.sources ? { sources: m.sources } : {})
+			})),
+			...(event ? { event: event } : {}),
+			chat_id: chatId,
+			session_id: $socket?.id,
+			id: responseMessageId
+		}).catch((error) => {
+			toast.error(error);
+			messages.at(-1).error = { content: error };
+			return null;
+		});
+
+		if (res !== null) {
+			// Update chat history with the new messages
+			for (const message of res.messages) {
+				history.messages[message.id] = {
+					...history.messages[message.id],
+					...(history.messages[message.id].content !== message.content
+						? { originalContent: history.messages[message.id].content }
+						: {}),
+					...message
+				};
+			}
+		}
+
+		if ($chatId == chatId) {
+			if (!$temporaryChatEnabled) {
+				chat = await updateChatById(localStorage.token, chatId, {
+					models: selectedModels,
+					messages: messages,
+					history: history,
+					params: params,
+					files: chatFiles
+				});
+
+				currentChatPage.set(1);
+				await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			}
+		}
+	};
+
+	const getChatEventEmitter = async (modelId: string, chatId: string = '') => {
+		return setInterval(() => {
+			$socket?.emit('usage', {
+				action: 'chat',
+				model: modelId,
+				chat_id: chatId
+			});
+		}, 1000);
+	};
+
+	const createMessagePair = async (userPrompt) => {
+		prompt = '';
+		if (selectedModels.length === 0) {
+			toast.error($i18n.t('Model not selected'));
+		} else {
+			const modelId = selectedModels[0];
+			const model = $models.filter((m) => m.id === modelId).at(0);
+
+			const messages = createMessagesList(history.currentId);
+			const parentMessage = messages.length !== 0 ? messages.at(-1) : null;
+
+			const userMessageId = uuidv4();
+			const responseMessageId = uuidv4();
+
+			const userMessage = {
+				id: userMessageId,
+				parentId: parentMessage ? parentMessage.id : null,
+				childrenIds: [responseMessageId],
+				role: 'user',
+				content: userPrompt ? userPrompt : `[PROMPT] ${userMessageId}`,
+				timestamp: Math.floor(Date.now() / 1000)
+			};
+
+			const responseMessage = {
+				id: responseMessageId,
+				parentId: userMessageId,
+				childrenIds: [],
+				role: 'assistant',
+				content: `[RESPONSE] ${responseMessageId}`,
+				done: true,
+
+				model: modelId,
+				modelName: model.name ?? model.id,
+				modelIdx: 0,
+				timestamp: Math.floor(Date.now() / 1000)
+			};
+
+			if (parentMessage) {
+				parentMessage.childrenIds.push(userMessageId);
+				history.messages[parentMessage.id] = parentMessage;
+			}
+			history.messages[userMessageId] = userMessage;
+			history.messages[responseMessageId] = responseMessage;
+
+			history.currentId = responseMessageId;
+
+			await tick();
+
+			if (autoScroll) {
+				scrollToBottom();
+			}
+
+			if (messages.length === 0) {
+				await initChatHandler();
+			} else {
+				await saveChatHandler($chatId);
+			}
+		}
+	};
+
+	//////////////////////////
+	// Chat functions
+	//////////////////////////
+
+	const submitPrompt = async (userPrompt, { _raw = false } = {}) => {
+		console.log('submitPrompt', userPrompt, $chatId);
+
+		const messages = createMessagesList(history.currentId);
+		const _selectedModels = selectedModels.map((modelId) =>
+			$models.map((m) => m.id).includes(modelId) ? modelId : ''
+		);
+		if (JSON.stringify(selectedModels) !== JSON.stringify(_selectedModels)) {
+			selectedModels = _selectedModels;
+		}
+
+		if (userPrompt === '') {
+			toast.error($i18n.t('Please enter a prompt'));
+			return;
+		}
+		if (selectedModels.includes('')) {
+			toast.error($i18n.t('Model not selected'));
+			return;
+		}
+
+		if (messages.length != 0 && messages.at(-1).done != true) {
+			// Response not done
+			return;
+		}
+		if (messages.length != 0 && messages.at(-1).error) {
+			// Error in response
+			toast.error($i18n.t(`Oops! There was an error in the previous response.`));
+			return;
+		}
+		if (
+			files.length > 0 &&
+			files.filter((file) => file.type !== 'image' && file.status === 'uploading').length > 0
+		) {
+			toast.error(
+				$i18n.t(`Oops! There are files still uploading. Please wait for the upload to complete.`)
+			);
+			return;
+		}
+		if (
+			($config?.file?.max_count ?? null) !== null &&
+			files.length + chatFiles.length > $config?.file?.max_count
+		) {
+			toast.error(
+				$i18n.t(`You can only chat with a maximum of {{maxCount}} file(s) at a time.`, {
+					maxCount: $config?.file?.max_count
+				})
+			);
+			return;
+		}
+
+		let _responses = [];
+		prompt = '';
+		await tick();
+
+		// Reset chat input textarea
+		const chatInputElement = document.getElementById('chat-input');
+
+		if (chatInputElement) {
+			chatInputElement.style.height = '';
+		}
+
+		const _files = JSON.parse(JSON.stringify(files));
+		chatFiles.push(..._files.filter((item) => ['doc', 'file', 'collection'].includes(item.type)));
+		chatFiles = chatFiles.filter(
+			// Remove duplicates
+			(item, index, array) =>
+				array.findIndex((i) => JSON.stringify(i) === JSON.stringify(item)) === index
+		);
+
+		files = [];
+		prompt = '';
+
+		// Create user message
+		let userMessageId = uuidv4();
+		let userMessage = {
+			id: userMessageId,
+			parentId: messages.length !== 0 ? messages.at(-1).id : null,
+			childrenIds: [],
+			role: 'user',
+			content: userPrompt,
+			files: _files.length > 0 ? _files : undefined,
+			timestamp: Math.floor(Date.now() / 1000), // Unix epoch
+			models: selectedModels
+		};
+
+		// Add message to history and Set currentId to messageId
+		history.messages[userMessageId] = userMessage;
+		history.currentId = userMessageId;
+
+		// Append messageId to childrenIds of parent message
+		if (messages.length !== 0) {
+			history.messages[messages.at(-1).id].childrenIds.push(userMessageId);
+		}
+
+		// Wait until history/message have been updated
+		await tick();
+
+		// focus on chat input
+		const chatInput = document.getElementById('chat-input');
+		chatInput?.focus();
+
+		saveSessionSelectedModels();
+		_responses = await sendPrompt(userPrompt, userMessageId, { newChat: true });
+
+		return _responses;
+	};
+
+	const sendPrompt = async (
+		prompt: string,
+		parentId: string,
+		{ modelId = null, modelIdx = null, newChat = false } = {}
+	) => {
+		// Create new chat if newChat is true and first user message
+		if (
+			newChat &&
+			history.messages[history.currentId].parentId === null &&
+			history.messages[history.currentId].role === 'user'
+		) {
+			await initChatHandler();
+		}
+
+		let _responses: string[] = [];
+		// If modelId is provided, use it, else use selected model
+		let selectedModelIds = modelId
+			? [modelId]
+			: atSelectedModel !== undefined
+				? [atSelectedModel.id]
+				: selectedModels;
+
+		// Create response messages for each selected model
+		const responseMessageIds: Record<PropertyKey, string> = {};
+		for (const [_modelIdx, modelId] of selectedModelIds.entries()) {
+			const model = $models.filter((m) => m.id === modelId).at(0);
+
+			if (model) {
+				let responseMessageId = uuidv4();
+				let responseMessage = {
+					parentId: parentId,
+					id: responseMessageId,
+					childrenIds: [],
+					role: 'assistant',
+					content: '',
+					model: model.id,
+					modelName: model.name ?? model.id,
+					modelIdx: modelIdx ? modelIdx : _modelIdx,
+					userContext: null,
+					timestamp: Math.floor(Date.now() / 1000) // Unix epoch
+				};
+
+				// Add message to history and Set currentId to messageId
+				history.messages[responseMessageId] = responseMessage;
+				history.currentId = responseMessageId;
+
+				// Append messageId to childrenIds of parent message
+				if (parentId !== null) {
+					history.messages[parentId].childrenIds = [
+						...history.messages[parentId].childrenIds,
+						responseMessageId
+					];
+				}
+
+				responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`] = responseMessageId;
+			}
+		}
+		await tick();
+
+		const _chatId = JSON.parse(JSON.stringify($chatId));
+		await Promise.all(
+			selectedModelIds.map(async (modelId, _modelIdx) => {
+				console.log('modelId', modelId);
+				const model = $models.filter((m) => m.id === modelId).at(0);
+
+				if (model) {
+					const messages = createMessagesList(parentId);
+					// If there are image files, check if model is vision capable
+					const hasImages = messages.some((message) =>
+						message.files?.some((file) => file.type === 'image')
+					);
+
+					if (hasImages && !(model.info?.meta?.capabilities?.vision ?? true)) {
+						toast.error(
+							$i18n.t('Model {{modelName}} is not vision capable', {
+								modelName: model.name ?? model.id
+							})
+						);
+					}
+
+					let responseMessageId =
+						responseMessageIds[`${modelId}-${modelIdx ? modelIdx : _modelIdx}`];
+					let responseMessage = history.messages[responseMessageId];
+
+					let userContext = null;
+					if ($settings?.memory ?? false) {
+						if (userContext === null) {
+							const res = await queryMemory(localStorage.token, prompt).catch((error) => {
+								toast.error(error);
+								return null;
+							});
+							if (res) {
+								if (res.documents[0].length > 0) {
+									userContext = res.documents[0].reduce((acc, doc, index) => {
+										const createdAtTimestamp = res.metadatas[0][index].created_at;
+										const createdAtDate = new Date(createdAtTimestamp * 1000)
+											.toISOString()
+											.split('T')[0];
+										return `${acc}${index + 1}. [${createdAtDate}]. ${doc}\n`;
+									}, '');
+								}
+
+								console.log(userContext);
+							}
+						}
+					}
+					responseMessage.userContext = userContext;
+
+					const chatEventEmitter = await getChatEventEmitter(model.id, _chatId);
+
+					scrollToBottom();
+					if (webSearchEnabled) {
+						await getWebSearchResults(model.id, parentId, responseMessageId);
+					}
+
+					let _response = null;
+					if (model?.owned_by === 'ollama') {
+						_response = await sendPromptOllama(model, prompt, responseMessageId, _chatId);
+					} else if (model) {
+						_response = await sendPromptOpenAI(model, prompt, responseMessageId, _chatId);
+					}
+					_responses.push(_response);
+
+					if (chatEventEmitter) clearInterval(chatEventEmitter);
+				} else {
+					toast.error($i18n.t(`Model {{modelId}} not found`, { modelId }));
+				}
+			})
+		);
+
+		currentChatPage.set(1);
+		chats.set(await getChatList(localStorage.token, $currentChatPage));
+
+		return _responses;
+	};
+
+	const sendPromptOllama = async (model, userPrompt, responseMessageId, _chatId) => {
+		let _response: string | null = null;
+
+		const responseMessage = history.messages[responseMessageId];
+		const userMessage = history.messages[responseMessage.parentId];
+
+		// Wait until history/message have been updated
+		await tick();
+
+		// Scroll down
+		scrollToBottom();
+
+		const messagesBody = [
+			params?.system || $settings.system || (responseMessage?.userContext ?? null)
+				? {
+						role: 'system',
+						content: `${promptTemplate(
+							params?.system ?? $settings?.system ?? '',
+							$user.name,
+							$settings?.userLocation
+								? await getAndUpdateUserLocation(localStorage.token)
+								: undefined
+						)}${
+							(responseMessage?.userContext ?? null)
+								? `\n\nUser Context:\n${responseMessage?.userContext ?? ''}`
+								: ''
+						}`
+					}
+				: undefined,
+			...createMessagesList(responseMessageId)
+		]
+			.filter((message) => message?.content?.trim())
+			.map((message) => {
+				// Prepare the base message object
+				const baseMessage = {
+					role: message.role,
+					content: message?.merged?.content ?? message.content
+				};
+
+				// Extract and format image URLs if any exist
+				const imageUrls = message.files
+					?.filter((file) => file.type === 'image')
+					.map((file) => file.url.slice(file.url.indexOf(',') + 1));
+
+				// Add images array only if it contains elements
+				if (imageUrls && imageUrls.length > 0 && message.role === 'user') {
+					baseMessage.images = imageUrls;
+				}
+				return baseMessage;
+			});
+
+		let lastImageIndex = -1;
+
+		// Find the index of the last object with images
+		messagesBody.forEach((item, index) => {
+			if (item.images) {
+				lastImageIndex = index;
+			}
+		});
+
+		// Remove images from all but the last one
+		messagesBody.forEach((item, index) => {
+			if (index !== lastImageIndex) {
+				delete item.images;
+			}
+		});
+
+		let files = JSON.parse(JSON.stringify(chatFiles));
+		if (model?.info?.meta?.knowledge ?? false) {
+			// Only initialize and add status if knowledge exists
+			responseMessage.statusHistory = [
+				{
+					action: 'knowledge_search',
+					description: $i18n.t(`Searching Knowledge for "{{searchQuery}}"`, {
+						searchQuery: userMessage.content
+					}),
+					done: false
+				}
+			];
+			files.push(
+				...model.info.meta.knowledge.map((item) => {
+					if (item?.collection_name) {
+						return {
+							id: item.collection_name,
+							name: item.name,
+							legacy: true
+						};
+					} else if (item?.collection_names) {
+						return {
+							name: item.name,
+							type: 'collection',
+							collection_names: item.collection_names,
+							legacy: true
+						};
+					} else {
+						return item;
+					}
+				})
+			);
+			history.messages[responseMessageId] = responseMessage;
+		}
+		files.push(
+			...(userMessage?.files ?? []).filter((item) =>
+				['doc', 'file', 'collection'].includes(item.type)
+			),
+			...(responseMessage?.files ?? []).filter((item) => ['web_search_results'].includes(item.type))
+		);
+
+		// Remove duplicates
+		files = files.filter(
+			(item, index, array) =>
+				array.findIndex((i) => JSON.stringify(i) === JSON.stringify(item)) === index
+		);
+
+		scrollToBottom();
+
+		eventTarget.dispatchEvent(
+			new CustomEvent('chat:start', {
+				detail: {
+					id: responseMessageId
+				}
+			})
+		);
+
+		await tick();
+
+		const stream =
+			model?.info?.params?.stream_response ??
+			$settings?.params?.stream_response ??
+			params?.stream_response ??
+			true;
+		const [res, controller] = await generateChatCompletion(localStorage.token, {
+			stream: stream,
+			model: model.id,
+			messages: messagesBody,
+			options: {
+				...{ ...($settings?.params ?? {}), ...params },
+				stop:
+					(params?.stop ?? $settings?.params?.stop ?? undefined)
+						? (params?.stop.split(',').map((token) => token.trim()) ?? $settings.params.stop).map(
+								(str) => decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
+							)
+						: undefined,
+				num_predict: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
+				repeat_penalty:
+					params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined
+			},
+			format: $settings.requestFormat ?? undefined,
+			keep_alive: $settings.keepAlive ?? undefined,
+			tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
+			files: files.length > 0 ? files : undefined,
+			session_id: $socket?.id,
+			chat_id: $chatId,
+			id: responseMessageId
+		});
+
+		if (res && res.ok) {
+			if (!stream) {
+				const response = await res.json();
+				console.log(response);
+
+				responseMessage.content = response.message.content;
+				responseMessage.info = {
+					eval_count: response.eval_count,
+					eval_duration: response.eval_duration,
+					load_duration: response.load_duration,
+					prompt_eval_count: response.prompt_eval_count,
+					prompt_eval_duration: response.prompt_eval_duration,
+					total_duration: response.total_duration
+				};
+				responseMessage.done = true;
+			} else {
+				console.log('controller', controller);
+
+				const reader = res.body
+					.pipeThrough(new TextDecoderStream())
+					.pipeThrough(splitStream('\n'))
+					.getReader();
+
+				while (true) {
+					const { value, done } = await reader.read();
+					if (done || stopResponseFlag || _chatId !== $chatId) {
+						responseMessage.done = true;
+						history.messages[responseMessageId] = responseMessage;
+
+						if (stopResponseFlag) {
+							controller.abort('User: Stop Response');
+						}
+
+						_response = responseMessage.content;
+						break;
+					}
+
+					try {
+						let lines = value.split('\n');
+
+						for (const line of lines) {
+							if (line !== '') {
+								console.log(line);
+								let data = JSON.parse(line);
+
+								if ('sources' in data) {
+									responseMessage.sources = data.sources;
+									// Only remove status if it was initially set
+									if (model?.info?.meta?.knowledge ?? false) {
+										responseMessage.statusHistory = responseMessage.statusHistory.filter(
+											(status) => status.action !== 'knowledge_search'
+										);
+									}
+									continue;
+								}
+
+								if ('detail' in data) {
+									throw data;
+								}
+
+								if (data.done == false) {
+									if (responseMessage.content == '' && data.message.content == '\n') {
+										continue;
+									} else {
+										responseMessage.content += data.message.content;
+
+										if (navigator.vibrate && ($settings?.hapticFeedback ?? false)) {
+											navigator.vibrate(5);
+										}
+
+										const messageContentParts = getMessageContentParts(
+											responseMessage.content,
+											$config?.audio?.tts?.split_on ?? 'punctuation'
+										);
+										messageContentParts.pop();
+
+										// dispatch only last sentence and make sure it hasn't been dispatched before
+										if (
+											messageContentParts.length > 0 &&
+											messageContentParts[messageContentParts.length - 1] !==
+												responseMessage.lastSentence
+										) {
+											responseMessage.lastSentence =
+												messageContentParts[messageContentParts.length - 1];
+											eventTarget.dispatchEvent(
+												new CustomEvent('chat', {
+													detail: {
+														id: responseMessageId,
+														content: messageContentParts[messageContentParts.length - 1]
+													}
+												})
+											);
+										}
+
+										history.messages[responseMessageId] = responseMessage;
+									}
+								} else {
+									responseMessage.done = true;
+
+									if (responseMessage.content == '') {
+										responseMessage.error = {
+											code: 400,
+											content: `Oops! No text generated from Ollama, Please try again.`
+										};
+									}
+
+									responseMessage.context = data.context ?? null;
+									responseMessage.info = {
+										total_duration: data.total_duration,
+										load_duration: data.load_duration,
+										sample_count: data.sample_count,
+										sample_duration: data.sample_duration,
+										prompt_eval_count: data.prompt_eval_count,
+										prompt_eval_duration: data.prompt_eval_duration,
+										eval_count: data.eval_count,
+										eval_duration: data.eval_duration
+									};
+
+									history.messages[responseMessageId] = responseMessage;
+
+									if ($settings.notificationEnabled && !document.hasFocus()) {
+										const notification = new Notification(`${model.id}`, {
+											body: responseMessage.content,
+											icon: `${WEBUI_BASE_URL}/static/favicon.png`
+										});
+									}
+
+									if ($settings?.responseAutoCopy ?? false) {
+										copyToClipboard(responseMessage.content);
+									}
+
+									if ($settings.responseAutoPlayback && !$showCallOverlay) {
+										await tick();
+										document.getElementById(`speak-button-${responseMessage.id}`)?.click();
+									}
+								}
+							}
+						}
+					} catch (error) {
+						console.log(error);
+						if ('detail' in error) {
+							toast.error(error.detail);
+						}
+						break;
+					}
+
+					if (autoScroll) {
+						scrollToBottom();
+					}
+				}
+			}
+		} else {
+			if (res !== null) {
+				const error = await res.json();
+				console.log(error);
+				if ('detail' in error) {
+					toast.error(error.detail);
+					responseMessage.error = { content: error.detail };
+				} else {
+					toast.error(error.error);
+					responseMessage.error = { content: error.error };
+				}
+			} else {
+				toast.error(
+					$i18n.t(`Uh-oh! There was an issue connecting to {{provider}}.`, { provider: 'Ollama' })
+				);
+				responseMessage.error = {
+					content: $i18n.t(`Uh-oh! There was an issue connecting to {{provider}}.`, {
+						provider: 'Ollama'
+					})
+				};
+			}
+			responseMessage.done = true;
+
+			if (responseMessage.statusHistory) {
+				responseMessage.statusHistory = responseMessage.statusHistory.filter(
+					(status) => status.action !== 'knowledge_search'
+				);
+			}
+		}
+		await saveChatHandler(_chatId);
+
+		history.messages[responseMessageId] = responseMessage;
+
+		await chatCompletedHandler(
+			_chatId,
+			model.id,
+			responseMessageId,
+			createMessagesList(responseMessageId)
+		);
+
+		stopResponseFlag = false;
+		await tick();
+
+		let lastMessageContentPart =
+			getMessageContentParts(
+				responseMessage.content,
+				$config?.audio?.tts?.split_on ?? 'punctuation'
+			)?.at(-1) ?? '';
+		if (lastMessageContentPart) {
+			eventTarget.dispatchEvent(
+				new CustomEvent('chat', {
+					detail: { id: responseMessageId, content: lastMessageContentPart }
+				})
+			);
+		}
+
+		eventTarget.dispatchEvent(
+			new CustomEvent('chat:finish', {
+				detail: {
+					id: responseMessageId,
+					content: responseMessage.content
+				}
+			})
+		);
+
+		if (autoScroll) {
+			scrollToBottom();
+		}
+
+		const messages = createMessagesList(responseMessageId);
+		if (messages.length == 2 && messages.at(-1).content !== '' && selectedModels[0] === model.id) {
+			window.history.replaceState(history.state, '', `/c/${_chatId}`);
+
+			const title = await generateChatTitle(messages);
+			await setChatTitle(_chatId, title);
+
+			if ($settings?.autoTags ?? true) {
+				await setChatTags(messages);
+			}
+		}
+
+		return _response;
+	};
+
+	const sendPromptOpenAI = async (model, userPrompt, responseMessageId, _chatId) => {
+		let _response = null;
+
+		const responseMessage = history.messages[responseMessageId];
+		const userMessage = history.messages[responseMessage.parentId];
+
+		let files = JSON.parse(JSON.stringify(chatFiles));
+		if (model?.info?.meta?.knowledge ?? false) {
+			// Only initialize and add status if knowledge exists
+			responseMessage.statusHistory = [
+				{
+					action: 'knowledge_search',
+					description: $i18n.t(`Searching Knowledge for "{{searchQuery}}"`, {
+						searchQuery: userMessage.content
+					}),
+					done: false
+				}
+			];
+			files.push(
+				...model.info.meta.knowledge.map((item) => {
+					if (item?.collection_name) {
+						return {
+							id: item.collection_name,
+							name: item.name,
+							legacy: true
+						};
+					} else if (item?.collection_names) {
+						return {
+							name: item.name,
+							type: 'collection',
+							collection_names: item.collection_names,
+							legacy: true
+						};
+					} else {
+						return item;
+					}
+				})
+			);
+			history.messages[responseMessageId] = responseMessage;
+		}
+		files.push(
+			...(userMessage?.files ?? []).filter((item) =>
+				['doc', 'file', 'collection'].includes(item.type)
+			),
+			...(responseMessage?.files ?? []).filter((item) => ['web_search_results'].includes(item.type))
+		);
+		// Remove duplicates
+		files = files.filter(
+			(item, index, array) =>
+				array.findIndex((i) => JSON.stringify(i) === JSON.stringify(item)) === index
+		);
+
+		scrollToBottom();
+
+		eventTarget.dispatchEvent(
+			new CustomEvent('chat:start', {
+				detail: {
+					id: responseMessageId
+				}
+			})
+		);
+		await tick();
+
+		try {
+			const stream =
+				model?.info?.params?.stream_response ??
+				$settings?.params?.stream_response ??
+				params?.stream_response ??
+				true;
+
+			const [res, controller] = await generateOpenAIChatCompletion(
+				localStorage.token,
+				{
+					stream: stream,
+					model: model.id,
+					...(stream && (model.info?.meta?.capabilities?.usage ?? false)
+						? {
+								stream_options: {
+									include_usage: true
+								}
+							}
+						: {}),
+					messages: [
+						params?.system || $settings.system || (responseMessage?.userContext ?? null)
+							? {
+									role: 'system',
+									content: `${promptTemplate(
+										params?.system ?? $settings?.system ?? '',
+										$user.name,
+										$settings?.userLocation
+											? await getAndUpdateUserLocation(localStorage.token)
+											: undefined
+									)}${
+										(responseMessage?.userContext ?? null)
+											? `\n\nUser Context:\n${responseMessage?.userContext ?? ''}`
+											: ''
+									}`
+								}
+							: undefined,
+						...createMessagesList(responseMessageId)
+					]
+						.filter((message) => message?.content?.trim())
+						.map((message, idx, arr) => ({
+							role: message.role,
+							...((message.files?.filter((file) => file.type === 'image').length > 0 ?? false) &&
+							message.role === 'user'
+								? {
+										content: [
+											{
+												type: 'text',
+												text: message?.merged?.content ?? message.content
+											},
+											...message.files
+												.filter((file) => file.type === 'image')
+												.map((file) => ({
+													type: 'image_url',
+													image_url: {
+														url: file.url
+													}
+												}))
+										]
+									}
+								: {
+										content: message?.merged?.content ?? message.content
+									})
+						})),
+					seed: params?.seed ?? $settings?.params?.seed ?? undefined,
+					stop:
+						(params?.stop ?? $settings?.params?.stop ?? undefined)
+							? (params?.stop.split(',').map((token) => token.trim()) ?? $settings.params.stop).map(
+									(str) => decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
+								)
+							: undefined,
+					temperature: params?.temperature ?? $settings?.params?.temperature ?? undefined,
+					top_p: params?.top_p ?? $settings?.params?.top_p ?? undefined,
+					frequency_penalty:
+						params?.frequency_penalty ?? $settings?.params?.frequency_penalty ?? undefined,
+					max_tokens: params?.max_tokens ?? $settings?.params?.max_tokens ?? undefined,
+					tool_ids: selectedToolIds.length > 0 ? selectedToolIds : undefined,
+					files: files.length > 0 ? files : undefined,
+					session_id: $socket?.id,
+					chat_id: $chatId,
+					id: responseMessageId
+				},
+				`${WEBUI_BASE_URL}/api`
+			);
+
+			// Wait until history/message have been updated
+			await tick();
+
+			scrollToBottom();
+
+			if (res && res.ok && res.body) {
+				if (!stream) {
+					const response = await res.json();
+					console.log(response);
+
+					responseMessage.content = response.choices[0].message.content;
+					responseMessage.info = { ...response.usage, openai: true };
+					responseMessage.done = true;
+				} else {
+					const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
+
+					for await (const update of textStream) {
+						const { value, done, sources, selectedModelId, error, usage } = update;
+						if (error) {
+							await handleOpenAIError(error, null, model, responseMessage);
+							break;
+						}
+						if (done || stopResponseFlag || _chatId !== $chatId) {
+							responseMessage.done = true;
+							history.messages[responseMessageId] = responseMessage;
+
+							if (stopResponseFlag) {
+								controller.abort('User: Stop Response');
+							}
+							_response = responseMessage.content;
+							break;
+						}
+
+						if (usage) {
+							responseMessage.info = { ...usage, openai: true, usage };
+						}
+
+						if (selectedModelId) {
+							responseMessage.selectedModelId = selectedModelId;
+							responseMessage.arena = true;
+							continue;
+						}
+
+						if (sources) {
+							responseMessage.sources = sources;
+							// Only remove status if it was initially set
+							if (model?.info?.meta?.knowledge ?? false) {
+								responseMessage.statusHistory = responseMessage.statusHistory.filter(
+									(status) => status.action !== 'knowledge_search'
+								);
+							}
+							continue;
+						}
+
+						if (responseMessage.content == '' && value == '\n') {
+							continue;
+						} else {
+							responseMessage.content += value;
+
+							if (navigator.vibrate && ($settings?.hapticFeedback ?? false)) {
+								navigator.vibrate(5);
+							}
+
+							const messageContentParts = getMessageContentParts(
+								responseMessage.content,
+								$config?.audio?.tts?.split_on ?? 'punctuation'
+							);
+							messageContentParts.pop();
+
+							// dispatch only last sentence and make sure it hasn't been dispatched before
+							if (
+								messageContentParts.length > 0 &&
+								messageContentParts[messageContentParts.length - 1] !== responseMessage.lastSentence
+							) {
+								responseMessage.lastSentence = messageContentParts[messageContentParts.length - 1];
+								eventTarget.dispatchEvent(
+									new CustomEvent('chat', {
+										detail: {
+											id: responseMessageId,
+											content: messageContentParts[messageContentParts.length - 1]
+										}
+									})
+								);
+							}
+
+							history.messages[responseMessageId] = responseMessage;
+						}
+
+						if (autoScroll) {
+							scrollToBottom();
+						}
+					}
+				}
+
+				if ($settings.notificationEnabled && !document.hasFocus()) {
+					const notification = new Notification(`${model.id}`, {
+						body: responseMessage.content,
+						icon: `${WEBUI_BASE_URL}/static/favicon.png`
+					});
+				}
+
+				if ($settings.responseAutoCopy) {
+					copyToClipboard(responseMessage.content);
+				}
+
+				if ($settings.responseAutoPlayback && !$showCallOverlay) {
+					await tick();
+
+					document.getElementById(`speak-button-${responseMessage.id}`)?.click();
+				}
+			} else {
+				await handleOpenAIError(null, res, model, responseMessage);
+			}
+		} catch (error) {
+			await handleOpenAIError(error, null, model, responseMessage);
+		}
+
+		await saveChatHandler(_chatId);
+
+		history.messages[responseMessageId] = responseMessage;
+
+		await chatCompletedHandler(
+			_chatId,
+			model.id,
+			responseMessageId,
+			createMessagesList(responseMessageId)
+		);
+
+		stopResponseFlag = false;
+		await tick();
+
+		let lastMessageContentPart =
+			getMessageContentParts(
+				responseMessage.content,
+				$config?.audio?.tts?.split_on ?? 'punctuation'
+			)?.at(-1) ?? '';
+		if (lastMessageContentPart) {
+			eventTarget.dispatchEvent(
+				new CustomEvent('chat', {
+					detail: { id: responseMessageId, content: lastMessageContentPart }
+				})
+			);
+		}
+
+		eventTarget.dispatchEvent(
+			new CustomEvent('chat:finish', {
+				detail: {
+					id: responseMessageId,
+					content: responseMessage.content
+				}
+			})
+		);
+
+		if (autoScroll) {
+			scrollToBottom();
+		}
+
+		const messages = createMessagesList(responseMessageId);
+		if (messages.length == 2 && selectedModels[0] === model.id) {
+			window.history.replaceState(history.state, '', `/c/${_chatId}`);
+
+			const title = await generateChatTitle(messages);
+			await setChatTitle(_chatId, title);
+
+			if ($settings?.autoTags ?? true) {
+				await setChatTags(messages);
+			}
+		}
+
+		return _response;
+	};
+
+	const handleOpenAIError = async (error, res: Response | null, model, responseMessage) => {
+		let errorMessage = '';
+		let innerError;
+
+		if (error) {
+			innerError = error;
+		} else if (res !== null) {
+			innerError = await res.json();
+		}
+		console.error(innerError);
+		if ('detail' in innerError) {
+			toast.error(innerError.detail);
+			errorMessage = innerError.detail;
+		} else if ('error' in innerError) {
+			if ('message' in innerError.error) {
+				toast.error(innerError.error.message);
+				errorMessage = innerError.error.message;
+			} else {
+				toast.error(innerError.error);
+				errorMessage = innerError.error;
+			}
+		} else if ('message' in innerError) {
+			toast.error(innerError.message);
+			errorMessage = innerError.message;
+		}
+
+		responseMessage.error = {
+			content:
+				$i18n.t(`Uh-oh! There was an issue connecting to {{provider}}.`, {
+					provider: model.name ?? model.id
+				}) +
+				'\n' +
+				errorMessage
+		};
+		responseMessage.done = true;
+
+		if (responseMessage.statusHistory) {
+			responseMessage.statusHistory = responseMessage.statusHistory.filter(
+				(status) => status.action !== 'knowledge_search'
+			);
+		}
+
+		history.messages[responseMessage.id] = responseMessage;
+	};
+
+	const stopResponse = () => {
+		stopResponseFlag = true;
+		console.log('stopResponse');
+	};
+
+	const submitMessage = async (parentId, prompt) => {
+		let userPrompt = prompt;
+		let userMessageId = uuidv4();
+
+		let userMessage = {
+			id: userMessageId,
+			parentId: parentId,
+			childrenIds: [],
+			role: 'user',
+			content: userPrompt,
+			models: selectedModels
+		};
+
+		if (parentId !== null) {
+			history.messages[parentId].childrenIds = [
+				...history.messages[parentId].childrenIds,
+				userMessageId
+			];
+		}
+
+		history.messages[userMessageId] = userMessage;
+		history.currentId = userMessageId;
+
+		await tick();
+		await sendPrompt(userPrompt, userMessageId);
+	};
+
+	const regenerateResponse = async (message) => {
+		console.log('regenerateResponse');
+
+		if (history.currentId) {
+			let userMessage = history.messages[message.parentId];
+			let userPrompt = userMessage.content;
+
+			if ((userMessage?.models ?? [...selectedModels]).length == 1) {
+				// If user message has only one model selected, sendPrompt automatically selects it for regeneration
+				await sendPrompt(userPrompt, userMessage.id);
+			} else {
+				// If there are multiple models selected, use the model of the response message for regeneration
+				// e.g. many model chat
+				await sendPrompt(userPrompt, userMessage.id, {
+					modelId: message.model,
+					modelIdx: message.modelIdx
+				});
+			}
+		}
+	};
+
+	const continueResponse = async () => {
+		console.log('continueResponse');
+		const _chatId = JSON.parse(JSON.stringify($chatId));
+
+		if (history.currentId && history.messages[history.currentId].done == true) {
+			const responseMessage = history.messages[history.currentId];
+			responseMessage.done = false;
+			await tick();
+
+			const model = $models
+				.filter((m) => m.id === (responseMessage?.selectedModelId ?? responseMessage.model))
+				.at(0);
+
+			if (model) {
+				if (model?.owned_by === 'openai') {
+					await sendPromptOpenAI(
+						model,
+						history.messages[responseMessage.parentId].content,
+						responseMessage.id,
+						_chatId
+					);
+				} else
+					await sendPromptOllama(
+						model,
+						history.messages[responseMessage.parentId].content,
+						responseMessage.id,
+						_chatId
+					);
+			}
+		}
+	};
+
+	const mergeResponses = async (messageId, responses, _chatId) => {
+		console.log('mergeResponses', messageId, responses);
+		const message = history.messages[messageId];
+		const mergedResponse = {
+			status: true,
+			content: ''
+		};
+		message.merged = mergedResponse;
+		history.messages[messageId] = message;
+
+		try {
+			const [res, controller] = await generateMoACompletion(
+				localStorage.token,
+				message.model,
+				history.messages[message.parentId].content,
+				responses
+			);
+
+			if (res && res.ok && res.body) {
+				const textStream = await createOpenAITextStream(res.body, $settings.splitLargeChunks);
+				for await (const update of textStream) {
+					const { value, done, sources, error, usage } = update;
+					if (error || done) {
+						break;
+					}
+
+					if (mergedResponse.content == '' && value == '\n') {
+						continue;
+					} else {
+						mergedResponse.content += value;
+						history.messages[messageId] = message;
+					}
+
+					if (autoScroll) {
+						scrollToBottom();
+					}
+				}
+
+				await saveChatHandler(_chatId);
+			} else {
+				console.error(res);
+			}
+		} catch (e) {
+			console.error(e);
+		}
+	};
+
+	const generateChatTitle = async (messages) => {
+		const lastUserMessage = messages.filter((message) => message.role === 'user').at(-1);
+
+		if ($settings?.title?.auto ?? true) {
+			const modelId = selectedModels[0];
+
+			const title = await generateTitle(localStorage.token, modelId, messages, $chatId).catch(
+				(error) => {
+					console.error(error);
+					return lastUserMessage?.content ?? 'New Chat';
+				}
+			);
+
+			return title ? title : (lastUserMessage?.content ?? 'New Chat');
+		} else {
+			return lastUserMessage?.content ?? 'New Chat';
+		}
+	};
+
+	const setChatTitle = async (_chatId, title) => {
+		if (_chatId === $chatId) {
+			chatTitle.set(title);
+		}
+
+		if (!$temporaryChatEnabled) {
+			chat = await updateChatById(localStorage.token, _chatId, { title: title });
+
+			currentChatPage.set(1);
+			await chats.set(await getChatList(localStorage.token, $currentChatPage));
+		}
+	};
+
+	const setChatTags = async (messages) => {
+		if (!$temporaryChatEnabled) {
+			const currentTags = await getTagsById(localStorage.token, $chatId);
+			if (currentTags.length > 0) {
+				const res = await deleteTagsById(localStorage.token, $chatId);
+				if (res) {
+					allTags.set(await getAllTags(localStorage.token));
+				}
+			}
+
+			const lastMessage = messages.at(-1);
+			const modelId = selectedModels[0];
+
+			let generatedTags = await generateTags(localStorage.token, modelId, messages, $chatId).catch(
+				(error) => {
+					console.error(error);
+					return [];
+				}
+			);
+
+			generatedTags = generatedTags.filter(
+				(tag) => !currentTags.find((t) => t.id === tag.replaceAll(' ', '_').toLowerCase())
+			);
+			console.log(generatedTags);
+
+			for (const tag of generatedTags) {
+				await addTagById(localStorage.token, $chatId, tag);
+			}
+
+			chat = await getChatById(localStorage.token, $chatId);
+			allTags.set(await getAllTags(localStorage.token));
+		}
+	};
+
+	const getWebSearchResults = async (
+		model: string,
+		parentId: string,
+		responseMessageId: string
+	) => {
+		// TODO: move this to the backend
+		const responseMessage = history.messages[responseMessageId];
+		const userMessage = history.messages[parentId];
+		const messages = createMessagesList(history.currentId);
+
+		responseMessage.statusHistory = [
+			{
+				done: false,
+				action: 'web_search',
+				description: $i18n.t('Generating search query')
+			}
+		];
+		history.messages[responseMessageId] = responseMessage;
+
+		const prompt = userMessage.content;
+		let queries = await generateQueries(
+			localStorage.token,
+			model,
+			messages.filter((message) => message?.content?.trim()),
+			prompt
+		).catch((error) => {
+			console.log(error);
+			return [prompt];
+		});
+
+		if (queries.length === 0) {
+			responseMessage.statusHistory.push({
+				done: true,
+				error: true,
+				action: 'web_search',
+				description: $i18n.t('No search query generated')
+			});
+			history.messages[responseMessageId] = responseMessage;
+			return;
+		}
+
+		const searchQuery = queries[0];
+
+		responseMessage.statusHistory.push({
+			done: false,
+			action: 'web_search',
+			description: $i18n.t(`Searching "{{searchQuery}}"`, { searchQuery })
+		});
+		history.messages[responseMessageId] = responseMessage;
+
+		const results = await processWebSearch(localStorage.token, searchQuery).catch((error) => {
+			console.log(error);
+			toast.error(error);
+
+			return null;
+		});
+
+		if (results) {
+			responseMessage.statusHistory.push({
+				done: true,
+				action: 'web_search',
+				description: $i18n.t('Searched {{count}} sites', { count: results.filenames.length }),
+				query: searchQuery,
+				urls: results.filenames
+			});
+
+			if (responseMessage?.files ?? undefined === undefined) {
+				responseMessage.files = [];
+			}
+
+			responseMessage.files.push({
+				collection_name: results.collection_name,
+				name: searchQuery,
+				type: 'web_search_results',
+				urls: results.filenames
+			});
+			history.messages[responseMessageId] = responseMessage;
+		} else {
+			responseMessage.statusHistory.push({
+				done: true,
+				error: true,
+				action: 'web_search',
+				description: 'No search results found'
+			});
+			history.messages[responseMessageId] = responseMessage;
+		}
+	};
+
+	const initChatHandler = async () => {
+		if (!$temporaryChatEnabled) {
+			chat = await createNewChat(localStorage.token, {
+				id: $chatId,
+				title: $i18n.t('New Chat'),
+				models: selectedModels,
+				system: $settings.system ?? undefined,
+				params: params,
+				history: history,
+				messages: createMessagesList(history.currentId),
+				tags: [],
+				timestamp: Date.now()
+			});
+
+			currentChatPage.set(1);
+			await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			await chatId.set(chat.id);
+		} else {
+			await chatId.set('local');
+		}
+		await tick();
+	};
+
+	const saveChatHandler = async (_chatId) => {
+		if ($chatId == _chatId) {
+			if (!$temporaryChatEnabled) {
+				chat = await updateChatById(localStorage.token, _chatId, {
+					models: selectedModels,
+					history: history,
+					messages: createMessagesList(history.currentId),
+					params: params,
+					files: chatFiles
+				});
+
+				currentChatPage.set(1);
+				await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			}
+		}
+	};
+</script>
+
+<svelte:head>
+	<title>
+		{$chatTitle
+			? `${$chatTitle.length > 30 ? `${$chatTitle.slice(0, 30)}...` : $chatTitle} | ${$WEBUI_NAME}`
+			: `${$WEBUI_NAME}`}
+	</title>
+</svelte:head>
+
+<audio id="audioElement" src="" style="display: none;" />
+
+<EventConfirmDialog
+	bind:show={showEventConfirmation}
+	title={eventConfirmationTitle}
+	message={eventConfirmationMessage}
+	input={eventConfirmationInput}
+	inputPlaceholder={eventConfirmationInputPlaceholder}
+	inputValue={eventConfirmationInputValue}
+	on:confirm={(e) => {
+		if (e.detail) {
+			eventCallback(e.detail);
+		} else {
+			eventCallback(true);
+		}
+	}}
+	on:cancel={() => {
+		eventCallback(false);
+	}}
+/>
+
+{#if !chatIdProp || (loaded && chatIdProp)}
+	<div
+		class="h-screen max-h-[100dvh] {$showSidebar
+			? 'md:max-w-[calc(100%-260px)]'
+			: ''} w-full max-w-full flex flex-col"
+		id="chat-container"
+	>
+		{#if $settings?.backgroundImageUrl ?? null}
+			<div
+				class="absolute {$showSidebar
+					? 'md:max-w-[calc(100%-260px)] md:translate-x-[260px]'
+					: ''} top-0 left-0 w-full h-full bg-cover bg-center bg-no-repeat"
+				style="background-image: url({$settings.backgroundImageUrl})  "
+			/>
+
+			<div
+				class="absolute top-0 left-0 w-full h-full bg-gradient-to-t from-white to-white/85 dark:from-gray-900 dark:to-[#171717]/90 z-0"
+			/>
+		{/if}
+
+		<Navbar
+			bind:this={navbarElement}
+			chat={{
+				id: $chatId,
+				chat: {
+					title: $chatTitle,
+					models: selectedModels,
+					system: $settings.system ?? undefined,
+					params: params,
+					history: history,
+					timestamp: Date.now()
+				}
+			}}
+			title={$chatTitle}
+			bind:selectedModels
+			shareEnabled={!!history.currentId}
+			{initNewChat}
+		/>
+
+		<PaneGroup direction="horizontal" class="w-full h-full">
+			<Pane defaultSize={50} class="h-full flex w-full relative">
+				{#if $banners.length > 0 && !history.currentId && !$chatId && selectedModels.length <= 1}
+					<div class="absolute top-12 left-0 right-0 w-full z-30">
+						<div class=" flex flex-col gap-1 w-full">
+							{#each $banners.filter( (b) => (b.dismissible ? !JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]').includes(b.id) : true) ) as banner}
+								<Banner
+									{banner}
+									on:dismiss={(e) => {
+										const bannerId = e.detail;
+
+										localStorage.setItem(
+											'dismissedBannerIds',
+											JSON.stringify(
+												[
+													bannerId,
+													...JSON.parse(localStorage.getItem('dismissedBannerIds') ?? '[]')
+												].filter((id) => $banners.find((b) => b.id === id))
+											)
+										);
+									}}
+								/>
+							{/each}
+						</div>
+					</div>
+				{/if}
+
+				<div class="flex flex-col flex-auto z-10 w-full">
+					{#if $settings?.landingPageMode === 'chat' || createMessagesList(history.currentId).length > 0}
+						<div
+							class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
+							id="messages-container"
+							bind:this={messagesContainerElement}
+							on:scroll={(e) => {
+								autoScroll =
+									messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
+									messagesContainerElement.clientHeight + 5;
+							}}
+						>
+							<div class=" h-full w-full flex flex-col">
+								<Messages
+									chatId={$chatId}
+									bind:history
+									bind:autoScroll
+									bind:prompt
+									{selectedModels}
+									{sendPrompt}
+									{showMessage}
+									{submitMessage}
+									{continueResponse}
+									{regenerateResponse}
+									{mergeResponses}
+									{chatActionHandler}
+									bottomPadding={files.length > 0}
+								/>
+							</div>
+						</div>
+
+						<div class=" pb-[1rem]">
+							<MessageInput
+								{history}
+								{selectedModels}
+								bind:files
+								bind:prompt
+								bind:autoScroll
+								bind:selectedToolIds
+								bind:webSearchEnabled
+								bind:atSelectedModel
+								transparentBackground={$settings?.backgroundImageUrl ?? false}
+								{stopResponse}
+								{createMessagePair}
+								on:upload={async (e) => {
+									const { type, data } = e.detail;
+
+									if (type === 'web') {
+										await uploadWeb(data);
+									} else if (type === 'youtube') {
+										await uploadYoutubeTranscription(data);
+									}
+								}}
+								on:submit={async (e) => {
+									if (e.detail) {
+										await tick();
+										submitPrompt(
+											($settings?.richTextInput ?? true)
+												? e.detail.replaceAll('\n\n', '\n')
+												: e.detail
+										);
+									}
+								}}
+							/>
+
+							<div
+								class="absolute bottom-1 text-xs text-gray-500 text-center line-clamp-1 right-0 left-0"
+							>
+								<!-- {$i18n.t('LLMs can make mistakes. Verify important information.')} -->
+							</div>
+						</div>
+					{:else}
+						<div class="overflow-auto w-full h-full flex items-center">
+							<Placeholder
+								{history}
+								{selectedModels}
+								bind:files
+								bind:prompt
+								bind:autoScroll
+								bind:selectedToolIds
+								bind:webSearchEnabled
+								bind:atSelectedModel
+								transparentBackground={$settings?.backgroundImageUrl ?? false}
+								{stopResponse}
+								{createMessagePair}
+								on:upload={async (e) => {
+									const { type, data } = e.detail;
+
+									if (type === 'web') {
+										await uploadWeb(data);
+									} else if (type === 'youtube') {
+										await uploadYoutubeTranscription(data);
+									}
+								}}
+								on:submit={async (e) => {
+									if (e.detail) {
+										await tick();
+										submitPrompt(
+											($settings?.richTextInput ?? true)
+												? e.detail.replaceAll('\n\n', '\n')
+												: e.detail
+										);
+									}
+								}}
+							/>
+						</div>
+					{/if}
+				</div>
+			</Pane>
+
+			<ChatControls
+				bind:this={controlPaneComponent}
+				bind:history
+				bind:chatFiles
+				bind:params
+				bind:files
+				bind:pane={controlPane}
+				chatId={$chatId}
+				modelId={selectedModelIds?.at(0) ?? null}
+				models={selectedModelIds.reduce((a, e, i, arr) => {
+					const model = $models.find((m) => m.id === e);
+					if (model) {
+						return [...a, model];
+					}
+					return a;
+				}, [])}
+				{submitPrompt}
+				{stopResponse}
+				{showMessage}
+				{eventTarget}
+			/>
+		</PaneGroup>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/ChatControls.svelte b/src/lib/components/chat/ChatControls.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5b0135105a485dc2a49244d6feb2314022086057
--- /dev/null
+++ b/src/lib/components/chat/ChatControls.svelte
@@ -0,0 +1,282 @@
+<script lang="ts">
+	import { SvelteFlowProvider } from '@xyflow/svelte';
+	import { slide } from 'svelte/transition';
+	import { Pane, PaneResizer } from 'paneforge';
+
+	import { onDestroy, onMount, tick } from 'svelte';
+	import { mobile, showControls, showCallOverlay, showOverview, showArtifacts } from '$lib/stores';
+
+	import Modal from '../common/Modal.svelte';
+	import Controls from './Controls/Controls.svelte';
+	import CallOverlay from './MessageInput/CallOverlay.svelte';
+	import Drawer from '../common/Drawer.svelte';
+	import Overview from './Overview.svelte';
+	import EllipsisVertical from '../icons/EllipsisVertical.svelte';
+	import Artifacts from './Artifacts.svelte';
+	import { min } from '@floating-ui/utils';
+
+	export let history;
+	export let models = [];
+
+	export let chatId = null;
+
+	export let chatFiles = [];
+	export let params = {};
+
+	export let eventTarget: EventTarget;
+	export let submitPrompt: Function;
+	export let stopResponse: Function;
+	export let showMessage: Function;
+	export let files;
+	export let modelId;
+
+	export let pane;
+
+	let mediaQuery;
+	let largeScreen = false;
+	let dragged = false;
+
+	let minSize = 0;
+
+	export const openPane = () => {
+		if (parseInt(localStorage?.chatControlsSize)) {
+			pane.resize(parseInt(localStorage?.chatControlsSize));
+		} else {
+			pane.resize(minSize);
+		}
+	};
+
+	const handleMediaQuery = async (e) => {
+		if (e.matches) {
+			largeScreen = true;
+
+			if ($showCallOverlay) {
+				showCallOverlay.set(false);
+				await tick();
+				showCallOverlay.set(true);
+			}
+		} else {
+			largeScreen = false;
+
+			if ($showCallOverlay) {
+				showCallOverlay.set(false);
+				await tick();
+				showCallOverlay.set(true);
+			}
+			pane = null;
+		}
+	};
+
+	const onMouseDown = (event) => {
+		dragged = true;
+	};
+
+	const onMouseUp = (event) => {
+		dragged = false;
+	};
+
+	onMount(() => {
+		// listen to resize 1024px
+		mediaQuery = window.matchMedia('(min-width: 1024px)');
+
+		mediaQuery.addEventListener('change', handleMediaQuery);
+		handleMediaQuery(mediaQuery);
+
+		// Select the container element you want to observe
+		const container = document.getElementById('chat-container');
+
+		// initialize the minSize based on the container width
+		minSize = Math.floor((350 / container.clientWidth) * 100);
+
+		// Create a new ResizeObserver instance
+		const resizeObserver = new ResizeObserver((entries) => {
+			for (let entry of entries) {
+				const width = entry.contentRect.width;
+				// calculate the percentage of 200px
+				const percentage = (350 / width) * 100;
+				// set the minSize to the percentage, must be an integer
+				minSize = Math.floor(percentage);
+
+				if ($showControls) {
+					if (pane && pane.isExpanded() && pane.getSize() < minSize) {
+						pane.resize(minSize);
+					}
+				}
+			}
+		});
+
+		// Start observing the container's size changes
+		resizeObserver.observe(container);
+
+		document.addEventListener('mousedown', onMouseDown);
+		document.addEventListener('mouseup', onMouseUp);
+	});
+
+	onDestroy(() => {
+		showControls.set(false);
+
+		mediaQuery.removeEventListener('change', handleMediaQuery);
+		document.removeEventListener('mousedown', onMouseDown);
+		document.removeEventListener('mouseup', onMouseUp);
+	});
+
+	const closeHandler = () => {
+		showControls.set(false);
+		showOverview.set(false);
+		showArtifacts.set(false);
+
+		if ($showCallOverlay) {
+			showCallOverlay.set(false);
+		}
+	};
+
+	$: if (!chatId) {
+		closeHandler();
+	}
+</script>
+
+<SvelteFlowProvider>
+	{#if !largeScreen}
+		{#if $showControls}
+			<Drawer
+				show={$showControls}
+				on:close={() => {
+					showControls.set(false);
+				}}
+			>
+				<div
+					class=" {$showCallOverlay || $showOverview || $showArtifacts
+						? ' h-screen  w-screen'
+						: 'px-6 py-4'} h-full"
+				>
+					{#if $showCallOverlay}
+						<div
+							class=" h-full max-h-[100dvh] bg-white text-gray-700 dark:bg-black dark:text-gray-300 flex justify-center"
+						>
+							<CallOverlay
+								bind:files
+								{submitPrompt}
+								{stopResponse}
+								{modelId}
+								{chatId}
+								{eventTarget}
+								on:close={() => {
+									showControls.set(false);
+								}}
+							/>
+						</div>
+					{:else if $showArtifacts}
+						<Artifacts {history} />
+					{:else if $showOverview}
+						<Overview
+							{history}
+							on:nodeclick={(e) => {
+								showMessage(e.detail.node.data.message);
+							}}
+							on:close={() => {
+								showControls.set(false);
+							}}
+						/>
+					{:else}
+						<Controls
+							on:close={() => {
+								showControls.set(false);
+							}}
+							{models}
+							bind:chatFiles
+							bind:params
+						/>
+					{/if}
+				</div>
+			</Drawer>
+		{/if}
+	{:else}
+		<!-- if $showControls -->
+
+		{#if $showControls}
+			<PaneResizer class="relative flex w-2 items-center justify-center bg-background group">
+				<div class="z-10 flex h-7 w-5 items-center justify-center rounded-sm">
+					<EllipsisVertical className="size-4 invisible group-hover:visible" />
+				</div>
+			</PaneResizer>
+		{/if}
+
+		<Pane
+			bind:pane
+			defaultSize={0}
+			onResize={(size) => {
+				console.log('size', size, minSize);
+
+				if ($showControls && pane.isExpanded()) {
+					if (size < minSize) {
+						pane.resize(minSize);
+					}
+
+					if (size < minSize) {
+						localStorage.chatControlsSize = 0;
+					} else {
+						localStorage.chatControlsSize = size;
+					}
+				}
+			}}
+			onCollapse={() => {
+				showControls.set(false);
+			}}
+			collapsible={true}
+			class="pt-8"
+		>
+			{#if $showControls}
+				<div class="pr-4 pb-8 flex max-h-full min-h-full">
+					<div
+						class="w-full {($showOverview || $showArtifacts) && !$showCallOverlay
+							? ' '
+							: 'px-4 py-4 bg-white dark:shadow-lg dark:bg-gray-850  border border-gray-50 dark:border-gray-850'}  rounded-xl z-40 pointer-events-auto overflow-y-auto scrollbar-hidden"
+					>
+						{#if $showCallOverlay}
+							<div class="w-full h-full flex justify-center">
+								<CallOverlay
+									bind:files
+									{submitPrompt}
+									{stopResponse}
+									{modelId}
+									{chatId}
+									{eventTarget}
+									on:close={() => {
+										showControls.set(false);
+									}}
+								/>
+							</div>
+						{:else if $showArtifacts}
+							<Artifacts {history} overlay={dragged} />
+						{:else if $showOverview}
+							<Overview
+								{history}
+								on:nodeclick={(e) => {
+									if (e.detail.node.data.message.favorite) {
+										history.messages[e.detail.node.data.message.id].favorite = true;
+									} else {
+										history.messages[e.detail.node.data.message.id].favorite = null;
+									}
+
+									showMessage(e.detail.node.data.message);
+								}}
+								on:close={() => {
+									showControls.set(false);
+								}}
+							/>
+						{:else}
+							<Controls
+								on:close={() => {
+									showControls.set(false);
+								}}
+								{models}
+								bind:chatFiles
+								bind:params
+							/>
+						{/if}
+					</div>
+				</div>
+			{/if}
+		</Pane>
+	{/if}
+</SvelteFlowProvider>
diff --git a/src/lib/components/chat/ChatPlaceholder.svelte b/src/lib/components/chat/ChatPlaceholder.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e30213ebb89d0f42267cbbefdf674fbcc0eccf61
--- /dev/null
+++ b/src/lib/components/chat/ChatPlaceholder.svelte
@@ -0,0 +1,138 @@
+<script lang="ts">
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import { marked } from 'marked';
+
+	import { config, user, models as _models, temporaryChatEnabled } from '$lib/stores';
+	import { onMount, getContext } from 'svelte';
+
+	import { blur, fade } from 'svelte/transition';
+
+	import Suggestions from './Suggestions.svelte';
+	import { sanitizeResponseContent } from '$lib/utils';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import EyeSlash from '$lib/components/icons/EyeSlash.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let modelIds = [];
+	export let models = [];
+
+	export let submitPrompt;
+
+	let mounted = false;
+	let selectedModelIdx = 0;
+
+	$: if (modelIds.length > 0) {
+		selectedModelIdx = models.length - 1;
+	}
+
+	$: models = modelIds.map((id) => $_models.find((m) => m.id === id));
+
+	onMount(() => {
+		mounted = true;
+	});
+</script>
+
+{#key mounted}
+	<div class="m-auto w-full max-w-6xl px-8 lg:px-20">
+		<div class="flex justify-start">
+			<div class="flex -space-x-4 mb-0.5" in:fade={{ duration: 200 }}>
+				{#each models as model, modelIdx}
+					<button
+						on:click={() => {
+							selectedModelIdx = modelIdx;
+						}}
+					>
+						<Tooltip
+							content={marked.parse(
+								sanitizeResponseContent(models[selectedModelIdx]?.info?.meta?.description ?? '')
+							)}
+							placement="right"
+						>
+							<img
+								crossorigin="anonymous"
+								src={model?.info?.meta?.profile_image_url ??
+									($i18n.language === 'dg-DG'
+										? `/doge.png`
+										: `${WEBUI_BASE_URL}/static/favicon.png`)}
+								class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
+								alt="logo"
+								draggable="false"
+							/>
+						</Tooltip>
+					</button>
+				{/each}
+			</div>
+		</div>
+
+		{#if $temporaryChatEnabled}
+			<Tooltip
+				content="This chat won't appear in history and your messages will not be saved."
+				className="w-fit"
+				placement="top-start"
+			>
+				<div class="flex items-center gap-2 text-gray-500 font-medium text-lg my-2 w-fit">
+					<EyeSlash strokeWidth="2.5" className="size-5" /> Temporary Chat
+				</div>
+			</Tooltip>
+		{/if}
+
+		<div
+			class=" mt-2 mb-4 text-3xl text-gray-800 dark:text-gray-100 font-medium text-left flex items-center gap-4 font-primary"
+		>
+			<div>
+				<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
+					{#if models[selectedModelIdx]?.name}
+						{models[selectedModelIdx]?.name}
+					{:else}
+						{$i18n.t('Hello, {{name}}', { name: $user.name })}
+					{/if}
+				</div>
+
+				<div in:fade={{ duration: 200, delay: 200 }}>
+					{#if models[selectedModelIdx]?.info?.meta?.description ?? null}
+						<div
+							class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400 line-clamp-3 markdown"
+						>
+							{@html marked.parse(
+								sanitizeResponseContent(models[selectedModelIdx]?.info?.meta?.description)
+							)}
+						</div>
+						{#if models[selectedModelIdx]?.info?.meta?.user}
+							<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
+								By
+								{#if models[selectedModelIdx]?.info?.meta?.user.community}
+									<a
+										href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
+											.username}"
+										>{models[selectedModelIdx]?.info?.meta?.user.name
+											? models[selectedModelIdx]?.info?.meta?.user.name
+											: `@${models[selectedModelIdx]?.info?.meta?.user.username}`}</a
+									>
+								{:else}
+									{models[selectedModelIdx]?.info?.meta?.user.name}
+								{/if}
+							</div>
+						{/if}
+					{:else}
+						<div class=" font-medium text-gray-400 dark:text-gray-500 line-clamp-1 font-p">
+							{$i18n.t('How can I help you today?')}
+						</div>
+					{/if}
+				</div>
+			</div>
+		</div>
+
+		<div class=" w-full font-primary" in:fade={{ duration: 200, delay: 300 }}>
+			<Suggestions
+				className="grid grid-cols-2"
+				suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
+					$config?.default_prompt_suggestions ??
+					[]}
+				on:select={(e) => {
+					submitPrompt(e.detail);
+				}}
+			/>
+		</div>
+	</div>
+{/key}
diff --git a/src/lib/components/chat/Controls/Controls.svelte b/src/lib/components/chat/Controls/Controls.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4ae63f77e1a6822841edc99701cf055f66865318
--- /dev/null
+++ b/src/lib/components/chat/Controls/Controls.svelte
@@ -0,0 +1,93 @@
+<script lang="ts">
+	import { createEventDispatcher, getContext } from 'svelte';
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import XMark from '$lib/components/icons/XMark.svelte';
+	import AdvancedParams from '../Settings/Advanced/AdvancedParams.svelte';
+	import Valves from '$lib/components/chat/Controls/Valves.svelte';
+	import FileItem from '$lib/components/common/FileItem.svelte';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
+
+	import { user } from '$lib/stores';
+	export let models = [];
+	export let chatFiles = [];
+	export let params = {};
+
+	let showValves = false;
+</script>
+
+<div class=" dark:text-white">
+	<div class=" flex items-center justify-between dark:text-gray-100 mb-2">
+		<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Controls')}</div>
+		<button
+			class="self-center"
+			on:click={() => {
+				dispatch('close');
+			}}
+		>
+			<XMark className="size-3.5" />
+		</button>
+	</div>
+
+	<div class=" dark:text-gray-200 text-sm font-primary py-0.5 px-0.5">
+		{#if chatFiles.length > 0}
+			<Collapsible title={$i18n.t('Files')} open={true} buttonClassName="w-full">
+				<div class="flex flex-col gap-1 mt-1.5" slot="content">
+					{#each chatFiles as file, fileIdx}
+						<FileItem
+							className="w-full"
+							item={file}
+							edit={true}
+							url={file?.url ? file.url : null}
+							name={file.name}
+							type={file.type}
+							size={file?.size}
+							dismissible={true}
+							on:dismiss={() => {
+								// Remove the file from the chatFiles array
+
+								chatFiles.splice(fileIdx, 1);
+								chatFiles = chatFiles;
+							}}
+							on:click={() => {
+								console.log(file);
+							}}
+						/>
+					{/each}
+				</div>
+			</Collapsible>
+
+			<hr class="my-2 border-gray-50 dark:border-gray-700/10" />
+		{/if}
+
+		<Collapsible bind:open={showValves} title={$i18n.t('Valves')} buttonClassName="w-full">
+			<div class="text-sm" slot="content">
+				<Valves show={showValves} />
+			</div>
+		</Collapsible>
+
+		<hr class="my-2 border-gray-50 dark:border-gray-700/10" />
+
+		<Collapsible title={$i18n.t('System Prompt')} open={true} buttonClassName="w-full">
+			<div class="" slot="content">
+				<textarea
+					bind:value={params.system}
+					class="w-full text-xs py-1.5 bg-transparent outline-none resize-none"
+					rows="4"
+					placeholder={$i18n.t('Enter system prompt')}
+				/>
+			</div>
+		</Collapsible>
+
+		<hr class="my-2 border-gray-50 dark:border-gray-700/10" />
+
+		<Collapsible title={$i18n.t('Advanced Params')} open={true} buttonClassName="w-full">
+			<div class="text-sm mt-1.5" slot="content">
+				<div>
+					<AdvancedParams admin={$user?.role === 'admin'} bind:params />
+				</div>
+			</div>
+		</Collapsible>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Controls/Valves.svelte b/src/lib/components/chat/Controls/Valves.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..45e50408124c1bb5d301eabed9672575b728915f
--- /dev/null
+++ b/src/lib/components/chat/Controls/Valves.svelte
@@ -0,0 +1,213 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { config, functions, models, settings, tools, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+
+	import {
+		getUserValvesSpecById as getToolUserValvesSpecById,
+		getUserValvesById as getToolUserValvesById,
+		updateUserValvesById as updateToolUserValvesById,
+		getTools
+	} from '$lib/apis/tools';
+	import {
+		getUserValvesSpecById as getFunctionUserValvesSpecById,
+		getUserValvesById as getFunctionUserValvesById,
+		updateUserValvesById as updateFunctionUserValvesById,
+		getFunctions
+	} from '$lib/apis/functions';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Valves from '$lib/components/common/Valves.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+
+	let tab = 'tools';
+	let selectedId = '';
+
+	let loading = false;
+
+	let valvesSpec = null;
+	let valves = {};
+
+	let debounceTimer;
+
+	const debounceSubmitHandler = async () => {
+		if (debounceTimer) {
+			clearTimeout(debounceTimer);
+		}
+
+		// Set a new timer
+		debounceTimer = setTimeout(() => {
+			submitHandler();
+		}, 500); // 0.5 second debounce
+	};
+
+	const getUserValves = async () => {
+		loading = true;
+		if (tab === 'tools') {
+			valves = await getToolUserValvesById(localStorage.token, selectedId);
+			valvesSpec = await getToolUserValvesSpecById(localStorage.token, selectedId);
+		} else if (tab === 'functions') {
+			valves = await getFunctionUserValvesById(localStorage.token, selectedId);
+			valvesSpec = await getFunctionUserValvesSpecById(localStorage.token, selectedId);
+		}
+
+		if (valvesSpec) {
+			// Convert array to string
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? []).join(',');
+				}
+			}
+		}
+
+		loading = false;
+	};
+
+	const submitHandler = async () => {
+		if (valvesSpec) {
+			// Convert string to array
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim());
+				}
+			}
+
+			if (tab === 'tools') {
+				const res = await updateToolUserValvesById(localStorage.token, selectedId, valves).catch(
+					(error) => {
+						toast.error(error);
+						return null;
+					}
+				);
+
+				if (res) {
+					toast.success($i18n.t('Valves updated'));
+					valves = res;
+				}
+			} else if (tab === 'functions') {
+				const res = await updateFunctionUserValvesById(
+					localStorage.token,
+					selectedId,
+					valves
+				).catch((error) => {
+					toast.error(error);
+					return null;
+				});
+
+				if (res) {
+					toast.success($i18n.t('Valves updated'));
+					valves = res;
+				}
+			}
+		}
+	};
+
+	$: if (tab) {
+		selectedId = '';
+	}
+
+	$: if (selectedId) {
+		getUserValves();
+	}
+
+	$: if (show) {
+		init();
+	}
+
+	const init = async () => {
+		loading = true;
+
+		if ($functions === null) {
+			functions.set(await getFunctions(localStorage.token));
+		}
+		if ($tools === null) {
+			tools.set(await getTools(localStorage.token));
+		}
+
+		loading = false;
+	};
+</script>
+
+{#if show && !loading}
+	<form
+		class="flex flex-col h-full justify-between space-y-3 text-sm"
+		on:submit|preventDefault={() => {
+			submitHandler();
+			dispatch('save');
+		}}
+	>
+		<div class="flex flex-col">
+			<div class="space-y-1">
+				<div class="flex gap-2">
+					<div class="flex-1">
+						<select
+							class="  w-full rounded text-xs py-2 px-1 bg-transparent outline-none"
+							bind:value={tab}
+							placeholder="Select"
+						>
+							<option value="tools" class="bg-gray-100 dark:bg-gray-800">{$i18n.t('Tools')}</option>
+							<option value="functions" class="bg-gray-100 dark:bg-gray-800"
+								>{$i18n.t('Functions')}</option
+							>
+						</select>
+					</div>
+
+					<div class="flex-1">
+						<select
+							class="w-full rounded py-2 px-1 text-xs bg-transparent outline-none"
+							bind:value={selectedId}
+							on:change={async () => {
+								await tick();
+							}}
+						>
+							{#if tab === 'tools'}
+								<option value="" selected disabled class="bg-gray-100 dark:bg-gray-800"
+									>{$i18n.t('Select a tool')}</option
+								>
+
+								{#each $tools as tool, toolIdx}
+									<option value={tool.id} class="bg-gray-100 dark:bg-gray-800">{tool.name}</option>
+								{/each}
+							{:else if tab === 'functions'}
+								<option value="" selected disabled class="bg-gray-100 dark:bg-gray-800"
+									>{$i18n.t('Select a function')}</option
+								>
+
+								{#each $functions as func, funcIdx}
+									<option value={func.id} class="bg-gray-100 dark:bg-gray-800">{func.name}</option>
+								{/each}
+							{/if}
+						</select>
+					</div>
+				</div>
+			</div>
+
+			{#if selectedId}
+				<hr class="dark:border-gray-800 my-1 w-full" />
+
+				<div class="my-2 text-xs">
+					{#if !loading}
+						<Valves
+							{valvesSpec}
+							bind:valves
+							on:change={() => {
+								debounceSubmitHandler();
+							}}
+						/>
+					{:else}
+						<Spinner className="size-5" />
+					{/if}
+				</div>
+			{/if}
+		</div>
+	</form>
+{:else}
+	<Spinner className="size-4" />
+{/if}
diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..296cc793939a7109e1ab741050696a8814066828
--- /dev/null
+++ b/src/lib/components/chat/MessageInput.svelte
@@ -0,0 +1,1133 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { v4 as uuidv4 } from 'uuid';
+
+	import { onMount, tick, getContext, createEventDispatcher, onDestroy } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import {
+		type Model,
+		mobile,
+		settings,
+		showSidebar,
+		models,
+		config,
+		showCallOverlay,
+		tools,
+		user as _user,
+		showControls
+	} from '$lib/stores';
+
+	import { blobToFile, createMessagesList, findWordIndices } from '$lib/utils';
+	import { transcribeAudio } from '$lib/apis/audio';
+	import { uploadFile } from '$lib/apis/files';
+	import { getTools } from '$lib/apis/tools';
+
+	import { WEBUI_BASE_URL, WEBUI_API_BASE_URL, PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
+
+	import Tooltip from '../common/Tooltip.svelte';
+	import InputMenu from './MessageInput/InputMenu.svelte';
+	import Headphone from '../icons/Headphone.svelte';
+	import VoiceRecording from './MessageInput/VoiceRecording.svelte';
+	import FileItem from '../common/FileItem.svelte';
+	import FilesOverlay from './MessageInput/FilesOverlay.svelte';
+	import Commands from './MessageInput/Commands.svelte';
+	import XMark from '../icons/XMark.svelte';
+	import RichTextInput from '../common/RichTextInput.svelte';
+	import { generateAutoCompletion } from '$lib/apis';
+	import { error, text } from '@sveltejs/kit';
+
+	const i18n = getContext('i18n');
+
+	export let transparentBackground = false;
+
+	export let createMessagePair: Function;
+	export let stopResponse: Function;
+
+	export let autoScroll = false;
+
+	export let atSelectedModel: Model | undefined;
+	export let selectedModels: [''];
+
+	let selectedModelIds = [];
+	$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
+
+	export let history;
+
+	export let prompt = '';
+	export let files = [];
+
+	export let selectedToolIds = [];
+	export let webSearchEnabled = false;
+
+	let loaded = false;
+	let recording = false;
+
+	let chatInputContainerElement;
+	let chatInputElement;
+
+	let filesInputElement;
+	let commandsElement;
+
+	let inputFiles;
+	let dragged = false;
+
+	let user = null;
+	export let placeholder = '';
+
+	let visionCapableModels = [];
+	$: visionCapableModels = [...(atSelectedModel ? [atSelectedModel] : selectedModels)].filter(
+		(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true
+	);
+
+	const scrollToBottom = () => {
+		const element = document.getElementById('messages-container');
+		element.scrollTo({
+			top: element.scrollHeight,
+			behavior: 'smooth'
+		});
+	};
+
+	const uploadFileHandler = async (file, fullContext: boolean = false) => {
+		if ($_user?.role !== 'admin' && !($_user?.permissions?.chat?.file_upload ?? true)) {
+			toast.error($i18n.t('You do not have permission to upload files.'));
+			return null;
+		}
+
+		console.log(file);
+
+		const tempItemId = uuidv4();
+		const fileItem = {
+			type: 'file',
+			file: '',
+			id: null,
+			url: '',
+			name: file.name,
+			collection_name: '',
+			status: 'uploading',
+			size: file.size,
+			error: '',
+			itemId: tempItemId,
+			...(fullContext ? { context: 'full' } : {})
+		};
+
+		if (fileItem.size == 0) {
+			toast.error($i18n.t('You cannot upload an empty file.'));
+			return null;
+		}
+
+		files = [...files, fileItem];
+		// Check if the file is an audio file and transcribe/convert it to text file
+		if (['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/x-m4a'].includes(file['type'])) {
+			const res = await transcribeAudio(localStorage.token, file).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (res) {
+				console.log(res);
+				const blob = new Blob([res.text], { type: 'text/plain' });
+				file = blobToFile(blob, `${file.name}.txt`);
+
+				fileItem.name = file.name;
+				fileItem.size = file.size;
+			}
+		}
+
+		try {
+			// During the file upload, file content is automatically extracted.
+			const uploadedFile = await uploadFile(localStorage.token, file);
+
+			if (uploadedFile) {
+				if (uploadedFile.error) {
+					toast.warning(uploadedFile.error);
+				}
+
+				fileItem.status = 'uploaded';
+				fileItem.file = uploadedFile;
+				fileItem.id = uploadedFile.id;
+				fileItem.collection_name = uploadedFile?.meta?.collection_name;
+				fileItem.url = `${WEBUI_API_BASE_URL}/files/${uploadedFile.id}`;
+
+				files = files;
+			} else {
+				files = files.filter((item) => item?.itemId !== tempItemId);
+			}
+		} catch (e) {
+			toast.error(e);
+			files = files.filter((item) => item?.itemId !== tempItemId);
+		}
+	};
+
+	const inputFilesHandler = async (inputFiles) => {
+		inputFiles.forEach((file) => {
+			console.log(file, file.name.split('.').at(-1));
+
+			if (
+				($config?.file?.max_size ?? null) !== null &&
+				file.size > ($config?.file?.max_size ?? 0) * 1024 * 1024
+			) {
+				toast.error(
+					$i18n.t(`File size should not exceed {{maxSize}} MB.`, {
+						maxSize: $config?.file?.max_size
+					})
+				);
+				return;
+			}
+
+			if (['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])) {
+				if (visionCapableModels.length === 0) {
+					toast.error($i18n.t('Selected model(s) do not support image inputs'));
+					return;
+				}
+				let reader = new FileReader();
+				reader.onload = (event) => {
+					files = [
+						...files,
+						{
+							type: 'image',
+							url: `${event.target.result}`
+						}
+					];
+				};
+				reader.readAsDataURL(file);
+			} else {
+				uploadFileHandler(file);
+			}
+		});
+	};
+
+	const handleKeyDown = (event: KeyboardEvent) => {
+		if (event.key === 'Escape') {
+			console.log('Escape');
+			dragged = false;
+		}
+	};
+
+	const onDragOver = (e) => {
+		e.preventDefault();
+
+		// Check if a file is being dragged.
+		if (e.dataTransfer?.types?.includes('Files')) {
+			dragged = true;
+		} else {
+			dragged = false;
+		}
+	};
+
+	const onDragLeave = () => {
+		dragged = false;
+	};
+
+	const onDrop = async (e) => {
+		e.preventDefault();
+		console.log(e);
+
+		if (e.dataTransfer?.files) {
+			const inputFiles = Array.from(e.dataTransfer?.files);
+			if (inputFiles && inputFiles.length > 0) {
+				console.log(inputFiles);
+				inputFilesHandler(inputFiles);
+			}
+		}
+
+		dragged = false;
+	};
+
+	onMount(async () => {
+		loaded = true;
+
+		window.setTimeout(() => {
+			const chatInput = document.getElementById('chat-input');
+			chatInput?.focus();
+		}, 0);
+
+		window.addEventListener('keydown', handleKeyDown);
+
+		await tick();
+
+		const dropzoneElement = document.getElementById('chat-container');
+
+		dropzoneElement?.addEventListener('dragover', onDragOver);
+		dropzoneElement?.addEventListener('drop', onDrop);
+		dropzoneElement?.addEventListener('dragleave', onDragLeave);
+	});
+
+	onDestroy(() => {
+		console.log('destroy');
+		window.removeEventListener('keydown', handleKeyDown);
+
+		const dropzoneElement = document.getElementById('chat-container');
+
+		if (dropzoneElement) {
+			dropzoneElement?.removeEventListener('dragover', onDragOver);
+			dropzoneElement?.removeEventListener('drop', onDrop);
+			dropzoneElement?.removeEventListener('dragleave', onDragLeave);
+		}
+	});
+</script>
+
+<FilesOverlay show={dragged} />
+
+{#if loaded}
+	<div class="w-full font-primary">
+		<div class=" mx-auto inset-x-0 bg-transparent flex justify-center">
+			<div class="flex flex-col px-3 max-w-6xl w-full">
+				<div class="relative">
+					{#if autoScroll === false && history?.currentId}
+						<div
+							class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none"
+						>
+							<button
+								class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto"
+								on:click={() => {
+									autoScroll = true;
+									scrollToBottom();
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-5 h-5"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							</button>
+						</div>
+					{/if}
+				</div>
+
+				<div class="w-full relative">
+					{#if atSelectedModel !== undefined || selectedToolIds.length > 0 || webSearchEnabled}
+						<div
+							class="px-3 pb-0.5 pt-1.5 text-left w-full flex flex-col absolute bottom-0 left-0 right-0 bg-gradient-to-t from-white dark:from-gray-900 z-10"
+						>
+							{#if selectedToolIds.length > 0}
+								<div class="flex items-center justify-between w-full">
+									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
+										<div class="pl-1">
+											<span class="relative flex size-2">
+												<span
+													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-yellow-400 opacity-75"
+												/>
+												<span class="relative inline-flex rounded-full size-2 bg-yellow-500" />
+											</span>
+										</div>
+										<div class=" translate-y-[0.5px] text-ellipsis line-clamp-1 flex">
+											{#each selectedToolIds.map((id) => {
+												return $tools ? $tools.find((t) => t.id === id) : { id: id, name: id };
+											}) as tool, toolIdx (toolIdx)}
+												<Tooltip
+													content={tool?.meta?.description ?? ''}
+													className=" {toolIdx !== 0 ? 'pl-0.5' : ''} flex-shrink-0"
+													placement="top"
+												>
+													{tool.name}
+												</Tooltip>
+
+												{#if toolIdx !== selectedToolIds.length - 1}
+													<span>, </span>
+												{/if}
+											{/each}
+										</div>
+									</div>
+								</div>
+							{/if}
+
+							{#if webSearchEnabled}
+								<div class="flex items-center justify-between w-full">
+									<div class="flex items-center gap-2.5 text-sm dark:text-gray-500">
+										<div class="pl-1">
+											<span class="relative flex size-2">
+												<span
+													class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
+												/>
+												<span class="relative inline-flex rounded-full size-2 bg-green-500" />
+											</span>
+										</div>
+										<div class=" translate-y-[0.5px]">{$i18n.t('Search the web')}</div>
+									</div>
+								</div>
+							{/if}
+
+							{#if atSelectedModel !== undefined}
+								<div class="flex items-center justify-between w-full">
+									<div class="pl-[1px] flex items-center gap-2 text-sm dark:text-gray-500">
+										<img
+											crossorigin="anonymous"
+											alt="model profile"
+											class="size-3.5 max-w-[28px] object-cover rounded-full"
+											src={$models.find((model) => model.id === atSelectedModel.id)?.info?.meta
+												?.profile_image_url ??
+												($i18n.language === 'dg-DG'
+													? `/doge.png`
+													: `${WEBUI_BASE_URL}/static/favicon.png`)}
+										/>
+										<div class="translate-y-[0.5px]">
+											Talking to <span class=" font-medium">{atSelectedModel.name}</span>
+										</div>
+									</div>
+									<div>
+										<button
+											class="flex items-center dark:text-gray-500"
+											on:click={() => {
+												atSelectedModel = undefined;
+											}}
+										>
+											<XMark />
+										</button>
+									</div>
+								</div>
+							{/if}
+						</div>
+					{/if}
+
+					<Commands
+						bind:this={commandsElement}
+						bind:prompt
+						bind:files
+						on:upload={(e) => {
+							dispatch('upload', e.detail);
+						}}
+						on:select={(e) => {
+							const data = e.detail;
+
+							if (data?.type === 'model') {
+								atSelectedModel = data.data;
+							}
+
+							const chatInputElement = document.getElementById('chat-input');
+							chatInputElement?.focus();
+						}}
+					/>
+				</div>
+			</div>
+		</div>
+
+		<div class="{transparentBackground ? 'bg-transparent' : 'bg-white dark:bg-gray-900'} ">
+			<div class="max-w-6xl px-2.5 mx-auto inset-x-0">
+				<div class="">
+					<input
+						bind:this={filesInputElement}
+						bind:files={inputFiles}
+						type="file"
+						hidden
+						multiple
+						on:change={async () => {
+							if (inputFiles && inputFiles.length > 0) {
+								const _inputFiles = Array.from(inputFiles);
+								inputFilesHandler(_inputFiles);
+							} else {
+								toast.error($i18n.t(`File not found.`));
+							}
+
+							filesInputElement.value = '';
+						}}
+					/>
+
+					{#if recording}
+						<VoiceRecording
+							bind:recording
+							on:cancel={async () => {
+								recording = false;
+
+								await tick();
+								document.getElementById('chat-input')?.focus();
+							}}
+							on:confirm={async (e) => {
+								const { text, filename } = e.detail;
+								prompt = `${prompt}${text} `;
+
+								recording = false;
+
+								await tick();
+								document.getElementById('chat-input')?.focus();
+
+								if ($settings?.speechAutoSend ?? false) {
+									dispatch('submit', prompt);
+								}
+							}}
+						/>
+					{:else}
+						<form
+							class="w-full flex gap-1.5"
+							on:submit|preventDefault={() => {
+								// check if selectedModels support image input
+								dispatch('submit', prompt);
+							}}
+						>
+							<div
+								class="flex-1 flex flex-col relative w-full rounded-3xl px-1 bg-gray-50 dark:bg-gray-400/5 dark:text-gray-100"
+								dir={$settings?.chatDirection ?? 'LTR'}
+							>
+								{#if files.length > 0}
+									<div class="mx-1 mt-2.5 mb-1 flex flex-wrap gap-2">
+										{#each files as file, fileIdx}
+											{#if file.type === 'image'}
+												<div class=" relative group">
+													<div class="relative">
+														<img
+															src={file.url}
+															alt="input"
+															class=" h-16 w-16 rounded-xl object-cover"
+														/>
+														{#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length}
+															<Tooltip
+																className=" absolute top-1 left-1"
+																content={$i18n.t('{{ models }}', {
+																	models: [
+																		...(atSelectedModel ? [atSelectedModel] : selectedModels)
+																	]
+																		.filter((id) => !visionCapableModels.includes(id))
+																		.join(', ')
+																})}
+															>
+																<svg
+																	xmlns="http://www.w3.org/2000/svg"
+																	viewBox="0 0 24 24"
+																	fill="currentColor"
+																	class="size-4 fill-yellow-300"
+																>
+																	<path
+																		fill-rule="evenodd"
+																		d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
+																		clip-rule="evenodd"
+																	/>
+																</svg>
+															</Tooltip>
+														{/if}
+													</div>
+													<div class=" absolute -top-1 -right-1">
+														<button
+															class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
+															type="button"
+															on:click={() => {
+																files.splice(fileIdx, 1);
+																files = files;
+															}}
+														>
+															<svg
+																xmlns="http://www.w3.org/2000/svg"
+																viewBox="0 0 20 20"
+																fill="currentColor"
+																class="w-4 h-4"
+															>
+																<path
+																	d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+																/>
+															</svg>
+														</button>
+													</div>
+												</div>
+											{:else}
+												<FileItem
+													item={file}
+													name={file.name}
+													type={file.type}
+													size={file?.size}
+													loading={file.status === 'uploading'}
+													dismissible={true}
+													edit={true}
+													on:dismiss={() => {
+														files.splice(fileIdx, 1);
+														files = files;
+													}}
+													on:click={() => {
+														console.log(file);
+													}}
+												/>
+											{/if}
+										{/each}
+									</div>
+								{/if}
+
+								<div class=" flex">
+									<div class="ml-1 self-end mb-1.5 flex space-x-1">
+										<InputMenu
+											bind:webSearchEnabled
+											bind:selectedToolIds
+											uploadFilesHandler={() => {
+												filesInputElement.click();
+											}}
+											onClose={async () => {
+												await tick();
+
+												const chatInput = document.getElementById('chat-input');
+												chatInput?.focus();
+											}}
+										>
+											<button
+												class="bg-transparent hover:bg-white/80 text-gray-800 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-2 outline-none focus:outline-none"
+												type="button"
+												aria-label="More"
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													viewBox="0 0 20 20"
+													fill="currentColor"
+													class="size-5"
+												>
+													<path
+														d="M10.75 4.75a.75.75 0 0 0-1.5 0v4.5h-4.5a.75.75 0 0 0 0 1.5h4.5v4.5a.75.75 0 0 0 1.5 0v-4.5h4.5a.75.75 0 0 0 0-1.5h-4.5v-4.5Z"
+													/>
+												</svg>
+											</button>
+										</InputMenu>
+									</div>
+
+									{#if $settings?.richTextInput ?? true}
+										<div
+											class="scrollbar-hidden text-left bg-transparent dark:text-gray-100 outline-none w-full py-2.5 px-1 rounded-xl resize-none h-fit max-h-80 overflow-auto"
+										>
+											<RichTextInput
+												bind:this={chatInputElement}
+												bind:value={prompt}
+												id="chat-input"
+												messageInput={true}
+												shiftEnter={!$mobile ||
+													!(
+														'ontouchstart' in window ||
+														navigator.maxTouchPoints > 0 ||
+														navigator.msMaxTouchPoints > 0
+													)}
+												placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
+												largeTextAsFile={$settings?.largeTextAsFile ?? false}
+												autocomplete={true}
+												generateAutoCompletion={async (text) => {
+													if (selectedModelIds.length === 0 || !selectedModelIds.at(0)) {
+														toast.error($i18n.t('Please select a model first.'));
+													}
+
+													const res = await generateAutoCompletion(
+														localStorage.token,
+														selectedModelIds.at(0),
+														text,
+														history?.currentId
+															? createMessagesList(history, history.currentId)
+															: null
+													).catch((error) => {
+														console.log(error);
+
+														return null;
+													});
+
+													console.log(res);
+													return res;
+												}}
+												on:keydown={async (e) => {
+													e = e.detail.event;
+
+													const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
+													const commandsContainerElement =
+														document.getElementById('commands-container');
+
+													// Command/Ctrl + Shift + Enter to submit a message pair
+													if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) {
+														e.preventDefault();
+														createMessagePair(prompt);
+													}
+
+													// Check if Ctrl + R is pressed
+													if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') {
+														e.preventDefault();
+														console.log('regenerate');
+
+														const regenerateButton = [
+															...document.getElementsByClassName('regenerate-response-button')
+														]?.at(-1);
+
+														regenerateButton?.click();
+													}
+
+													if (prompt === '' && e.key == 'ArrowUp') {
+														e.preventDefault();
+
+														const userMessageElement = [
+															...document.getElementsByClassName('user-message')
+														]?.at(-1);
+
+														const editButton = [
+															...document.getElementsByClassName('edit-user-message-button')
+														]?.at(-1);
+
+														console.log(userMessageElement);
+
+														userMessageElement.scrollIntoView({ block: 'center' });
+														editButton?.click();
+													}
+
+													if (commandsContainerElement) {
+														if (commandsContainerElement && e.key === 'ArrowUp') {
+															e.preventDefault();
+															commandsElement.selectUp();
+
+															const commandOptionButton = [
+																...document.getElementsByClassName('selected-command-option-button')
+															]?.at(-1);
+															commandOptionButton.scrollIntoView({ block: 'center' });
+														}
+
+														if (commandsContainerElement && e.key === 'ArrowDown') {
+															e.preventDefault();
+															commandsElement.selectDown();
+
+															const commandOptionButton = [
+																...document.getElementsByClassName('selected-command-option-button')
+															]?.at(-1);
+															commandOptionButton.scrollIntoView({ block: 'center' });
+														}
+
+														if (commandsContainerElement && e.key === 'Tab') {
+															e.preventDefault();
+
+															const commandOptionButton = [
+																...document.getElementsByClassName('selected-command-option-button')
+															]?.at(-1);
+
+															commandOptionButton?.click();
+														}
+
+														if (commandsContainerElement && e.key === 'Enter') {
+															e.preventDefault();
+
+															const commandOptionButton = [
+																...document.getElementsByClassName('selected-command-option-button')
+															]?.at(-1);
+
+															if (commandOptionButton) {
+																commandOptionButton?.click();
+															} else {
+																document.getElementById('send-message-button')?.click();
+															}
+														}
+													} else {
+														if (
+															!$mobile ||
+															!(
+																'ontouchstart' in window ||
+																navigator.maxTouchPoints > 0 ||
+																navigator.msMaxTouchPoints > 0
+															)
+														) {
+															// Prevent Enter key from creating a new line
+															// Uses keyCode '13' for Enter key for chinese/japanese keyboards
+															if (e.keyCode === 13 && !e.shiftKey) {
+																e.preventDefault();
+															}
+
+															// Submit the prompt when Enter key is pressed
+															if (prompt !== '' && e.keyCode === 13 && !e.shiftKey) {
+																dispatch('submit', prompt);
+															}
+														}
+													}
+
+													if (e.key === 'Escape') {
+														console.log('Escape');
+														atSelectedModel = undefined;
+														selectedToolIds = [];
+														webSearchEnabled = false;
+													}
+												}}
+												on:paste={async (e) => {
+													e = e.detail.event;
+													console.log(e);
+
+													const clipboardData = e.clipboardData || window.clipboardData;
+
+													if (clipboardData && clipboardData.items) {
+														for (const item of clipboardData.items) {
+															if (item.type.indexOf('image') !== -1) {
+																const blob = item.getAsFile();
+																const reader = new FileReader();
+
+																reader.onload = function (e) {
+																	files = [
+																		...files,
+																		{
+																			type: 'image',
+																			url: `${e.target.result}`
+																		}
+																	];
+																};
+
+																reader.readAsDataURL(blob);
+															} else if (item.type === 'text/plain') {
+																if ($settings?.largeTextAsFile ?? false) {
+																	const text = clipboardData.getData('text/plain');
+
+																	if (text.length > PASTED_TEXT_CHARACTER_LIMIT) {
+																		e.preventDefault();
+																		const blob = new Blob([text], { type: 'text/plain' });
+																		const file = new File([blob], `Pasted_Text_${Date.now()}.txt`, {
+																			type: 'text/plain'
+																		});
+
+																		await uploadFileHandler(file, true);
+																	}
+																}
+															}
+														}
+													}
+												}}
+											/>
+										</div>
+									{:else}
+										<textarea
+											id="chat-input"
+											bind:this={chatInputElement}
+											class="scrollbar-hidden bg-gray-50 dark:bg-gray-850 dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px]"
+											placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
+											bind:value={prompt}
+											on:keypress={(e) => {
+												if (
+													!$mobile ||
+													!(
+														'ontouchstart' in window ||
+														navigator.maxTouchPoints > 0 ||
+														navigator.msMaxTouchPoints > 0
+													)
+												) {
+													// Prevent Enter key from creating a new line
+													if (e.key === 'Enter' && !e.shiftKey) {
+														e.preventDefault();
+													}
+
+													// Submit the prompt when Enter key is pressed
+													if (prompt !== '' && e.key === 'Enter' && !e.shiftKey) {
+														dispatch('submit', prompt);
+													}
+												}
+											}}
+											on:keydown={async (e) => {
+												const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
+												const commandsContainerElement =
+													document.getElementById('commands-container');
+
+												// Command/Ctrl + Shift + Enter to submit a message pair
+												if (isCtrlPressed && e.key === 'Enter' && e.shiftKey) {
+													e.preventDefault();
+													createMessagePair(prompt);
+												}
+
+												// Check if Ctrl + R is pressed
+												if (prompt === '' && isCtrlPressed && e.key.toLowerCase() === 'r') {
+													e.preventDefault();
+													console.log('regenerate');
+
+													const regenerateButton = [
+														...document.getElementsByClassName('regenerate-response-button')
+													]?.at(-1);
+
+													regenerateButton?.click();
+												}
+
+												if (prompt === '' && e.key == 'ArrowUp') {
+													e.preventDefault();
+
+													const userMessageElement = [
+														...document.getElementsByClassName('user-message')
+													]?.at(-1);
+
+													const editButton = [
+														...document.getElementsByClassName('edit-user-message-button')
+													]?.at(-1);
+
+													console.log(userMessageElement);
+
+													userMessageElement.scrollIntoView({ block: 'center' });
+													editButton?.click();
+												}
+
+												if (commandsContainerElement && e.key === 'ArrowUp') {
+													e.preventDefault();
+													commandsElement.selectUp();
+
+													const commandOptionButton = [
+														...document.getElementsByClassName('selected-command-option-button')
+													]?.at(-1);
+													commandOptionButton.scrollIntoView({ block: 'center' });
+												}
+
+												if (commandsContainerElement && e.key === 'ArrowDown') {
+													e.preventDefault();
+													commandsElement.selectDown();
+
+													const commandOptionButton = [
+														...document.getElementsByClassName('selected-command-option-button')
+													]?.at(-1);
+													commandOptionButton.scrollIntoView({ block: 'center' });
+												}
+
+												if (commandsContainerElement && e.key === 'Enter') {
+													e.preventDefault();
+
+													const commandOptionButton = [
+														...document.getElementsByClassName('selected-command-option-button')
+													]?.at(-1);
+
+													if (e.shiftKey) {
+														prompt = `${prompt}\n`;
+													} else if (commandOptionButton) {
+														commandOptionButton?.click();
+													} else {
+														document.getElementById('send-message-button')?.click();
+													}
+												}
+
+												if (commandsContainerElement && e.key === 'Tab') {
+													e.preventDefault();
+
+													const commandOptionButton = [
+														...document.getElementsByClassName('selected-command-option-button')
+													]?.at(-1);
+
+													commandOptionButton?.click();
+												} else if (e.key === 'Tab') {
+													const words = findWordIndices(prompt);
+
+													if (words.length > 0) {
+														const word = words.at(0);
+														const fullPrompt = prompt;
+
+														prompt = prompt.substring(0, word?.endIndex + 1);
+														await tick();
+
+														e.target.scrollTop = e.target.scrollHeight;
+														prompt = fullPrompt;
+														await tick();
+
+														e.preventDefault();
+														e.target.setSelectionRange(word?.startIndex, word.endIndex + 1);
+													}
+
+													e.target.style.height = '';
+													e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
+												}
+
+												if (e.key === 'Escape') {
+													console.log('Escape');
+													atSelectedModel = undefined;
+													selectedToolIds = [];
+													webSearchEnabled = false;
+												}
+											}}
+											rows="1"
+											on:input={async (e) => {
+												e.target.style.height = '';
+												e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
+											}}
+											on:focus={async (e) => {
+												e.target.style.height = '';
+												e.target.style.height = Math.min(e.target.scrollHeight, 320) + 'px';
+											}}
+											on:paste={async (e) => {
+												const clipboardData = e.clipboardData || window.clipboardData;
+
+												if (clipboardData && clipboardData.items) {
+													for (const item of clipboardData.items) {
+														if (item.type.indexOf('image') !== -1) {
+															const blob = item.getAsFile();
+															const reader = new FileReader();
+
+															reader.onload = function (e) {
+																files = [
+																	...files,
+																	{
+																		type: 'image',
+																		url: `${e.target.result}`
+																	}
+																];
+															};
+
+															reader.readAsDataURL(blob);
+														} else if (item.type === 'text/plain') {
+															if ($settings?.largeTextAsFile ?? false) {
+																const text = clipboardData.getData('text/plain');
+
+																if (text.length > PASTED_TEXT_CHARACTER_LIMIT) {
+																	e.preventDefault();
+																	const blob = new Blob([text], { type: 'text/plain' });
+																	const file = new File([blob], `Pasted_Text_${Date.now()}.txt`, {
+																		type: 'text/plain'
+																	});
+
+																	await uploadFileHandler(file, true);
+																}
+															}
+														}
+													}
+												}
+											}}
+										/>
+									{/if}
+
+									<div class="self-end mb-1.5 flex space-x-1 mr-1">
+										{#if !history?.currentId || history.messages[history.currentId]?.done == true}
+											<Tooltip content={$i18n.t('Record voice')}>
+												<button
+													id="voice-input-button"
+													class=" text-gray-600 dark:text-gray-300 hover:text-gray-700 dark:hover:text-gray-200 transition rounded-full p-1.5 mr-0.5 self-center"
+													type="button"
+													on:click={async () => {
+														try {
+															let stream = await navigator.mediaDevices
+																.getUserMedia({ audio: true })
+																.catch(function (err) {
+																	toast.error(
+																		$i18n.t(
+																			`Permission denied when accessing microphone: {{error}}`,
+																			{
+																				error: err
+																			}
+																		)
+																	);
+																	return null;
+																});
+
+															if (stream) {
+																recording = true;
+																const tracks = stream.getTracks();
+																tracks.forEach((track) => track.stop());
+															}
+															stream = null;
+														} catch {
+															toast.error($i18n.t('Permission denied when accessing microphone'));
+														}
+													}}
+													aria-label="Voice Input"
+												>
+													<svg
+														xmlns="http://www.w3.org/2000/svg"
+														viewBox="0 0 20 20"
+														fill="currentColor"
+														class="w-5 h-5 translate-y-[0.5px]"
+													>
+														<path d="M7 4a3 3 0 016 0v6a3 3 0 11-6 0V4z" />
+														<path
+															d="M5.5 9.643a.75.75 0 00-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 000 1.5h4.5a.75.75 0 000-1.5h-1.5v-1.546A6.001 6.001 0 0016 10v-.357a.75.75 0 00-1.5 0V10a4.5 4.5 0 01-9 0v-.357z"
+														/>
+													</svg>
+												</button>
+											</Tooltip>
+										{/if}
+
+										{#if !history.currentId || history.messages[history.currentId]?.done == true}
+											{#if prompt === ''}
+												<div class=" flex items-center">
+													<Tooltip content={$i18n.t('Call')}>
+														<button
+															class=" bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full p-2 self-center"
+															type="button"
+															on:click={async () => {
+																if (selectedModels.length > 1) {
+																	toast.error($i18n.t('Select only one model to call'));
+
+																	return;
+																}
+
+																if ($config.audio.stt.engine === 'web') {
+																	toast.error(
+																		$i18n.t(
+																			'Call feature is not supported when using Web STT engine'
+																		)
+																	);
+
+																	return;
+																}
+																// check if user has access to getUserMedia
+																try {
+																	let stream = await navigator.mediaDevices.getUserMedia({
+																		audio: true
+																	});
+																	// If the user grants the permission, proceed to show the call overlay
+
+																	if (stream) {
+																		const tracks = stream.getTracks();
+																		tracks.forEach((track) => track.stop());
+																	}
+
+																	stream = null;
+
+																	showCallOverlay.set(true);
+																	showControls.set(true);
+																} catch (err) {
+																	// If the user denies the permission or an error occurs, show an error message
+																	toast.error(
+																		$i18n.t('Permission denied when accessing media devices')
+																	);
+																}
+															}}
+															aria-label="Call"
+														>
+															<Headphone className="size-5" />
+														</button>
+													</Tooltip>
+												</div>
+											{:else}
+												<div class=" flex items-center">
+													<Tooltip content={$i18n.t('Send message')}>
+														<button
+															id="send-message-button"
+															class="{prompt !== ''
+																? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
+																: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 self-center"
+															type="submit"
+															disabled={prompt === ''}
+														>
+															<svg
+																xmlns="http://www.w3.org/2000/svg"
+																viewBox="0 0 16 16"
+																fill="currentColor"
+																class="size-6"
+															>
+																<path
+																	fill-rule="evenodd"
+																	d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
+																	clip-rule="evenodd"
+																/>
+															</svg>
+														</button>
+													</Tooltip>
+												</div>
+											{/if}
+										{:else}
+											<div class=" flex items-center">
+												<Tooltip content={$i18n.t('Stop')}>
+													<button
+														class="bg-white hover:bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800 transition rounded-full p-1.5"
+														on:click={() => {
+															stopResponse();
+														}}
+													>
+														<svg
+															xmlns="http://www.w3.org/2000/svg"
+															viewBox="0 0 24 24"
+															fill="currentColor"
+															class="size-6"
+														>
+															<path
+																fill-rule="evenodd"
+																d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm6-2.438c0-.724.588-1.312 1.313-1.312h4.874c.725 0 1.313.588 1.313 1.313v4.874c0 .725-.588 1.313-1.313 1.313H9.564a1.312 1.312 0 01-1.313-1.313V9.564z"
+																clip-rule="evenodd"
+															/>
+														</svg>
+													</button>
+												</Tooltip>
+											</div>
+										{/if}
+									</div>
+								</div>
+							</div>
+						</form>
+					{/if}
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/CallOverlay.svelte b/src/lib/components/chat/MessageInput/CallOverlay.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6f3b465a6c4424c5a1d70374590299c5b0a0805b
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/CallOverlay.svelte
@@ -0,0 +1,974 @@
+<script lang="ts">
+	import { config, models, settings, showCallOverlay } from '$lib/stores';
+	import { onMount, tick, getContext, onDestroy, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import { blobToFile } from '$lib/utils';
+	import { generateEmoji } from '$lib/apis';
+	import { synthesizeOpenAISpeech, transcribeAudio } from '$lib/apis/audio';
+
+	import { toast } from 'svelte-sonner';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import VideoInputMenu from './CallOverlay/VideoInputMenu.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let eventTarget: EventTarget;
+	export let submitPrompt: Function;
+	export let stopResponse: Function;
+	export let files;
+	export let chatId;
+	export let modelId;
+
+	let wakeLock = null;
+
+	let model = null;
+
+	let loading = false;
+	let confirmed = false;
+	let interrupted = false;
+	let assistantSpeaking = false;
+
+	let emoji = null;
+	let camera = false;
+	let cameraStream = null;
+
+	let chatStreaming = false;
+	let rmsLevel = 0;
+	let hasStartedSpeaking = false;
+	let mediaRecorder;
+	let audioStream = null;
+	let audioChunks = [];
+
+	let videoInputDevices = [];
+	let selectedVideoInputDeviceId = null;
+
+	const getVideoInputDevices = async () => {
+		const devices = await navigator.mediaDevices.enumerateDevices();
+		videoInputDevices = devices.filter((device) => device.kind === 'videoinput');
+
+		if (!!navigator.mediaDevices.getDisplayMedia) {
+			videoInputDevices = [
+				...videoInputDevices,
+				{
+					deviceId: 'screen',
+					label: 'Screen Share'
+				}
+			];
+		}
+
+		console.log(videoInputDevices);
+		if (selectedVideoInputDeviceId === null && videoInputDevices.length > 0) {
+			selectedVideoInputDeviceId = videoInputDevices[0].deviceId;
+		}
+	};
+
+	const startCamera = async () => {
+		await getVideoInputDevices();
+
+		if (cameraStream === null) {
+			camera = true;
+			await tick();
+			try {
+				await startVideoStream();
+			} catch (err) {
+				console.error('Error accessing webcam: ', err);
+			}
+		}
+	};
+
+	const startVideoStream = async () => {
+		const video = document.getElementById('camera-feed');
+		if (video) {
+			if (selectedVideoInputDeviceId === 'screen') {
+				cameraStream = await navigator.mediaDevices.getDisplayMedia({
+					video: {
+						cursor: 'always'
+					},
+					audio: false
+				});
+			} else {
+				cameraStream = await navigator.mediaDevices.getUserMedia({
+					video: {
+						deviceId: selectedVideoInputDeviceId ? { exact: selectedVideoInputDeviceId } : undefined
+					}
+				});
+			}
+
+			if (cameraStream) {
+				await getVideoInputDevices();
+				video.srcObject = cameraStream;
+				await video.play();
+			}
+		}
+	};
+
+	const stopVideoStream = async () => {
+		if (cameraStream) {
+			const tracks = cameraStream.getTracks();
+			tracks.forEach((track) => track.stop());
+		}
+
+		cameraStream = null;
+	};
+
+	const takeScreenshot = () => {
+		const video = document.getElementById('camera-feed');
+		const canvas = document.getElementById('camera-canvas');
+
+		if (!canvas) {
+			return;
+		}
+
+		const context = canvas.getContext('2d');
+
+		// Make the canvas match the video dimensions
+		canvas.width = video.videoWidth;
+		canvas.height = video.videoHeight;
+
+		// Draw the image from the video onto the canvas
+		context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
+
+		// Convert the canvas to a data base64 URL and console log it
+		const dataURL = canvas.toDataURL('image/png');
+		console.log(dataURL);
+
+		return dataURL;
+	};
+
+	const stopCamera = async () => {
+		await stopVideoStream();
+		camera = false;
+	};
+
+	const MIN_DECIBELS = -55;
+	const VISUALIZER_BUFFER_LENGTH = 300;
+
+	const transcribeHandler = async (audioBlob) => {
+		// Create a blob from the audio chunks
+
+		await tick();
+		const file = blobToFile(audioBlob, 'recording.wav');
+
+		const res = await transcribeAudio(localStorage.token, file).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			console.log(res.text);
+
+			if (res.text !== '') {
+				const _responses = await submitPrompt(res.text, { _raw: true });
+				console.log(_responses);
+			}
+		}
+	};
+
+	const stopRecordingCallback = async (_continue = true) => {
+		if ($showCallOverlay) {
+			console.log('%c%s', 'color: red; font-size: 20px;', '🚨 stopRecordingCallback 🚨');
+
+			// deep copy the audioChunks array
+			const _audioChunks = audioChunks.slice(0);
+
+			audioChunks = [];
+			mediaRecorder = false;
+
+			if (_continue) {
+				startRecording();
+			}
+
+			if (confirmed) {
+				loading = true;
+				emoji = null;
+
+				if (cameraStream) {
+					const imageUrl = takeScreenshot();
+
+					files = [
+						{
+							type: 'image',
+							url: imageUrl
+						}
+					];
+				}
+
+				const audioBlob = new Blob(_audioChunks, { type: 'audio/wav' });
+				await transcribeHandler(audioBlob);
+
+				confirmed = false;
+				loading = false;
+			}
+		} else {
+			audioChunks = [];
+			mediaRecorder = false;
+
+			if (audioStream) {
+				const tracks = audioStream.getTracks();
+				tracks.forEach((track) => track.stop());
+			}
+			audioStream = null;
+		}
+	};
+
+	const startRecording = async () => {
+		if ($showCallOverlay) {
+			if (!audioStream) {
+				audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
+			}
+			mediaRecorder = new MediaRecorder(audioStream);
+
+			mediaRecorder.onstart = () => {
+				console.log('Recording started');
+				audioChunks = [];
+				analyseAudio(audioStream);
+			};
+
+			mediaRecorder.ondataavailable = (event) => {
+				if (hasStartedSpeaking) {
+					audioChunks.push(event.data);
+				}
+			};
+
+			mediaRecorder.onstop = (e) => {
+				console.log('Recording stopped', audioStream, e);
+				stopRecordingCallback();
+			};
+
+			mediaRecorder.start();
+		}
+	};
+
+	const stopAudioStream = async () => {
+		try {
+			if (mediaRecorder) {
+				mediaRecorder.stop();
+			}
+		} catch (error) {
+			console.log('Error stopping audio stream:', error);
+		}
+
+		if (!audioStream) return;
+
+		audioStream.getAudioTracks().forEach(function (track) {
+			track.stop();
+		});
+
+		audioStream = null;
+	};
+
+	// Function to calculate the RMS level from time domain data
+	const calculateRMS = (data: Uint8Array) => {
+		let sumSquares = 0;
+		for (let i = 0; i < data.length; i++) {
+			const normalizedValue = (data[i] - 128) / 128; // Normalize the data
+			sumSquares += normalizedValue * normalizedValue;
+		}
+		return Math.sqrt(sumSquares / data.length);
+	};
+
+	const analyseAudio = (stream) => {
+		const audioContext = new AudioContext();
+		const audioStreamSource = audioContext.createMediaStreamSource(stream);
+
+		const analyser = audioContext.createAnalyser();
+		analyser.minDecibels = MIN_DECIBELS;
+		audioStreamSource.connect(analyser);
+
+		const bufferLength = analyser.frequencyBinCount;
+
+		const domainData = new Uint8Array(bufferLength);
+		const timeDomainData = new Uint8Array(analyser.fftSize);
+
+		let lastSoundTime = Date.now();
+		hasStartedSpeaking = false;
+
+		console.log('🔊 Sound detection started', lastSoundTime, hasStartedSpeaking);
+
+		const detectSound = () => {
+			const processFrame = () => {
+				if (!mediaRecorder || !$showCallOverlay) {
+					return;
+				}
+
+				if (assistantSpeaking && !($settings?.voiceInterruption ?? false)) {
+					// Mute the audio if the assistant is speaking
+					analyser.maxDecibels = 0;
+					analyser.minDecibels = -1;
+				} else {
+					analyser.minDecibels = MIN_DECIBELS;
+					analyser.maxDecibels = -30;
+				}
+
+				analyser.getByteTimeDomainData(timeDomainData);
+				analyser.getByteFrequencyData(domainData);
+
+				// Calculate RMS level from time domain data
+				rmsLevel = calculateRMS(timeDomainData);
+
+				// Check if initial speech/noise has started
+				const hasSound = domainData.some((value) => value > 0);
+				if (hasSound) {
+					// BIG RED TEXT
+					console.log('%c%s', 'color: red; font-size: 20px;', '🔊 Sound detected');
+
+					if (!hasStartedSpeaking) {
+						hasStartedSpeaking = true;
+						stopAllAudio();
+					}
+
+					lastSoundTime = Date.now();
+				}
+
+				// Start silence detection only after initial speech/noise has been detected
+				if (hasStartedSpeaking) {
+					if (Date.now() - lastSoundTime > 2000) {
+						confirmed = true;
+
+						if (mediaRecorder) {
+							console.log('%c%s', 'color: red; font-size: 20px;', '🔇 Silence detected');
+							mediaRecorder.stop();
+							return;
+						}
+					}
+				}
+
+				window.requestAnimationFrame(processFrame);
+			};
+
+			window.requestAnimationFrame(processFrame);
+		};
+
+		detectSound();
+	};
+
+	let finishedMessages = {};
+	let currentMessageId = null;
+	let currentUtterance = null;
+
+	const speakSpeechSynthesisHandler = (content) => {
+		if ($showCallOverlay) {
+			return new Promise((resolve) => {
+				let voices = [];
+				const getVoicesLoop = setInterval(async () => {
+					voices = await speechSynthesis.getVoices();
+					if (voices.length > 0) {
+						clearInterval(getVoicesLoop);
+
+						const voice =
+							voices
+								?.filter(
+									(v) => v.voiceURI === ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
+								)
+								?.at(0) ?? undefined;
+
+						currentUtterance = new SpeechSynthesisUtterance(content);
+						currentUtterance.rate = $settings.audio?.tts?.playbackRate ?? 1;
+
+						if (voice) {
+							currentUtterance.voice = voice;
+						}
+
+						speechSynthesis.speak(currentUtterance);
+						currentUtterance.onend = async (e) => {
+							await new Promise((r) => setTimeout(r, 200));
+							resolve(e);
+						};
+					}
+				}, 100);
+			});
+		} else {
+			return Promise.resolve();
+		}
+	};
+
+	const playAudio = (audio) => {
+		if ($showCallOverlay) {
+			return new Promise((resolve) => {
+				const audioElement = document.getElementById('audioElement') as HTMLAudioElement;
+
+				if (audioElement) {
+					audioElement.src = audio.src;
+					audioElement.muted = true;
+					audioElement.playbackRate = $settings.audio?.tts?.playbackRate ?? 1;
+
+					audioElement
+						.play()
+						.then(() => {
+							audioElement.muted = false;
+						})
+						.catch((error) => {
+							console.error(error);
+						});
+
+					audioElement.onended = async (e) => {
+						await new Promise((r) => setTimeout(r, 100));
+						resolve(e);
+					};
+				}
+			});
+		} else {
+			return Promise.resolve();
+		}
+	};
+
+	const stopAllAudio = async () => {
+		assistantSpeaking = false;
+		interrupted = true;
+
+		if (chatStreaming) {
+			stopResponse();
+		}
+
+		if (currentUtterance) {
+			speechSynthesis.cancel();
+			currentUtterance = null;
+		}
+
+		const audioElement = document.getElementById('audioElement');
+		if (audioElement) {
+			audioElement.muted = true;
+			audioElement.pause();
+			audioElement.currentTime = 0;
+		}
+	};
+
+	let audioAbortController = new AbortController();
+
+	// Audio cache map where key is the content and value is the Audio object.
+	const audioCache = new Map();
+	const emojiCache = new Map();
+
+	const fetchAudio = async (content) => {
+		if (!audioCache.has(content)) {
+			try {
+				// Set the emoji for the content if needed
+				if ($settings?.showEmojiInCall ?? false) {
+					const emoji = await generateEmoji(localStorage.token, modelId, content, chatId);
+					if (emoji) {
+						emojiCache.set(content, emoji);
+					}
+				}
+
+				if ($config.audio.tts.engine !== '') {
+					const res = await synthesizeOpenAISpeech(
+						localStorage.token,
+						$settings?.audio?.tts?.defaultVoice === $config.audio.tts.voice
+							? ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
+							: $config?.audio?.tts?.voice,
+						content
+					).catch((error) => {
+						console.error(error);
+						return null;
+					});
+
+					if (res) {
+						const blob = await res.blob();
+						const blobUrl = URL.createObjectURL(blob);
+						audioCache.set(content, new Audio(blobUrl));
+					}
+				} else {
+					audioCache.set(content, true);
+				}
+			} catch (error) {
+				console.error('Error synthesizing speech:', error);
+			}
+		}
+
+		return audioCache.get(content);
+	};
+
+	let messages = {};
+
+	const monitorAndPlayAudio = async (id, signal) => {
+		while (!signal.aborted) {
+			if (messages[id] && messages[id].length > 0) {
+				// Retrieve the next content string from the queue
+				const content = messages[id].shift(); // Dequeues the content for playing
+
+				if (audioCache.has(content)) {
+					// If content is available in the cache, play it
+
+					// Set the emoji for the content if available
+					if (($settings?.showEmojiInCall ?? false) && emojiCache.has(content)) {
+						emoji = emojiCache.get(content);
+					} else {
+						emoji = null;
+					}
+
+					if ($config.audio.tts.engine !== '') {
+						try {
+							console.log(
+								'%c%s',
+								'color: red; font-size: 20px;',
+								`Playing audio for content: ${content}`
+							);
+
+							const audio = audioCache.get(content);
+							await playAudio(audio); // Here ensure that playAudio is indeed correct method to execute
+							console.log(`Played audio for content: ${content}`);
+							await new Promise((resolve) => setTimeout(resolve, 200)); // Wait before retrying to reduce tight loop
+						} catch (error) {
+							console.error('Error playing audio:', error);
+						}
+					} else {
+						await speakSpeechSynthesisHandler(content);
+					}
+				} else {
+					// If not available in the cache, push it back to the queue and delay
+					messages[id].unshift(content); // Re-queue the content at the start
+					console.log(`Audio for "${content}" not yet available in the cache, re-queued...`);
+					await new Promise((resolve) => setTimeout(resolve, 200)); // Wait before retrying to reduce tight loop
+				}
+			} else if (finishedMessages[id] && messages[id] && messages[id].length === 0) {
+				// If the message is finished and there are no more messages to process, break the loop
+				assistantSpeaking = false;
+				break;
+			} else {
+				// No messages to process, sleep for a bit
+				await new Promise((resolve) => setTimeout(resolve, 200));
+			}
+		}
+		console.log(`Audio monitoring and playing stopped for message ID ${id}`);
+	};
+
+	const chatStartHandler = async (e) => {
+		const { id } = e.detail;
+
+		chatStreaming = true;
+
+		if (currentMessageId !== id) {
+			console.log(`Received chat start event for message ID ${id}`);
+
+			currentMessageId = id;
+			if (audioAbortController) {
+				audioAbortController.abort();
+			}
+			audioAbortController = new AbortController();
+
+			assistantSpeaking = true;
+			// Start monitoring and playing audio for the message ID
+			monitorAndPlayAudio(id, audioAbortController.signal);
+		}
+	};
+
+	const chatEventHandler = async (e) => {
+		const { id, content } = e.detail;
+		// "id" here is message id
+		// if "id" is not the same as "currentMessageId" then do not process
+		// "content" here is a sentence from the assistant,
+		// there will be many sentences for the same "id"
+
+		if (currentMessageId === id) {
+			console.log(`Received chat event for message ID ${id}: ${content}`);
+
+			try {
+				if (messages[id] === undefined) {
+					messages[id] = [content];
+				} else {
+					messages[id].push(content);
+				}
+
+				console.log(content);
+
+				fetchAudio(content);
+			} catch (error) {
+				console.error('Failed to fetch or play audio:', error);
+			}
+		}
+	};
+
+	const chatFinishHandler = async (e) => {
+		const { id, content } = e.detail;
+		// "content" here is the entire message from the assistant
+		finishedMessages[id] = true;
+
+		chatStreaming = false;
+	};
+
+	onMount(async () => {
+		const setWakeLock = async () => {
+			try {
+				wakeLock = await navigator.wakeLock.request('screen');
+			} catch (err) {
+				// The Wake Lock request has failed - usually system related, such as battery.
+				console.log(err);
+			}
+
+			if (wakeLock) {
+				// Add a listener to release the wake lock when the page is unloaded
+				wakeLock.addEventListener('release', () => {
+					// the wake lock has been released
+					console.log('Wake Lock released');
+				});
+			}
+		};
+
+		if ('wakeLock' in navigator) {
+			await setWakeLock();
+
+			document.addEventListener('visibilitychange', async () => {
+				// Re-request the wake lock if the document becomes visible
+				if (wakeLock !== null && document.visibilityState === 'visible') {
+					await setWakeLock();
+				}
+			});
+		}
+
+		model = $models.find((m) => m.id === modelId);
+
+		startRecording();
+
+		eventTarget.addEventListener('chat:start', chatStartHandler);
+		eventTarget.addEventListener('chat', chatEventHandler);
+		eventTarget.addEventListener('chat:finish', chatFinishHandler);
+
+		return async () => {
+			await stopAllAudio();
+
+			stopAudioStream();
+
+			eventTarget.removeEventListener('chat:start', chatStartHandler);
+			eventTarget.removeEventListener('chat', chatEventHandler);
+			eventTarget.removeEventListener('chat:finish', chatFinishHandler);
+
+			audioAbortController.abort();
+			await tick();
+
+			await stopAllAudio();
+
+			await stopRecordingCallback(false);
+			await stopCamera();
+		};
+	});
+
+	onDestroy(async () => {
+		await stopAllAudio();
+		await stopRecordingCallback(false);
+		await stopCamera();
+
+		await stopAudioStream();
+		eventTarget.removeEventListener('chat:start', chatStartHandler);
+		eventTarget.removeEventListener('chat', chatEventHandler);
+		eventTarget.removeEventListener('chat:finish', chatFinishHandler);
+		audioAbortController.abort();
+
+		await tick();
+
+		await stopAllAudio();
+	});
+</script>
+
+{#if $showCallOverlay}
+	<div class="max-w-lg w-full h-full max-h-[100dvh] flex flex-col justify-between p-3 md:p-6">
+		{#if camera}
+			<button
+				type="button"
+				class="flex justify-center items-center w-full h-20 min-h-20"
+				on:click={() => {
+					if (assistantSpeaking) {
+						stopAllAudio();
+					}
+				}}
+			>
+				{#if emoji}
+					<div
+						class="  transition-all rounded-full"
+						style="font-size:{rmsLevel * 100 > 4
+							? '4.5'
+							: rmsLevel * 100 > 2
+								? '4.25'
+								: rmsLevel * 100 > 1
+									? '3.75'
+									: '3.5'}rem;width: 100%; text-align:center;"
+					>
+						{emoji}
+					</div>
+				{:else if loading || assistantSpeaking}
+					<svg
+						class="size-12 text-gray-900 dark:text-gray-400"
+						viewBox="0 0 24 24"
+						fill="currentColor"
+						xmlns="http://www.w3.org/2000/svg"
+						><style>
+							.spinner_qM83 {
+								animation: spinner_8HQG 1.05s infinite;
+							}
+							.spinner_oXPr {
+								animation-delay: 0.1s;
+							}
+							.spinner_ZTLf {
+								animation-delay: 0.2s;
+							}
+							@keyframes spinner_8HQG {
+								0%,
+								57.14% {
+									animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+									transform: translate(0);
+								}
+								28.57% {
+									animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+									transform: translateY(-6px);
+								}
+								100% {
+									transform: translate(0);
+								}
+							}
+						</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
+							class="spinner_qM83 spinner_oXPr"
+							cx="12"
+							cy="12"
+							r="3"
+						/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
+					>
+				{:else}
+					<div
+						class=" {rmsLevel * 100 > 4
+							? ' size-[4.5rem]'
+							: rmsLevel * 100 > 2
+								? ' size-16'
+								: rmsLevel * 100 > 1
+									? 'size-14'
+									: 'size-12'}  transition-all rounded-full {(model?.info?.meta
+							?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
+							? ' bg-cover bg-center bg-no-repeat'
+							: 'bg-black dark:bg-white'}  bg-black dark:bg-white"
+						style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
+						'/static/favicon.png'
+							? `background-image: url('${model?.info?.meta?.profile_image_url}');`
+							: ''}
+					/>
+				{/if}
+				<!-- navbar -->
+			</button>
+		{/if}
+
+		<div class="flex justify-center items-center flex-1 h-full w-full max-h-full">
+			{#if !camera}
+				<button
+					type="button"
+					on:click={() => {
+						if (assistantSpeaking) {
+							stopAllAudio();
+						}
+					}}
+				>
+					{#if emoji}
+						<div
+							class="  transition-all rounded-full"
+							style="font-size:{rmsLevel * 100 > 4
+								? '13'
+								: rmsLevel * 100 > 2
+									? '12'
+									: rmsLevel * 100 > 1
+										? '11.5'
+										: '11'}rem;width:100%;text-align:center;"
+						>
+							{emoji}
+						</div>
+					{:else if loading || assistantSpeaking}
+						<svg
+							class="size-44 text-gray-900 dark:text-gray-400"
+							viewBox="0 0 24 24"
+							fill="currentColor"
+							xmlns="http://www.w3.org/2000/svg"
+							><style>
+								.spinner_qM83 {
+									animation: spinner_8HQG 1.05s infinite;
+								}
+								.spinner_oXPr {
+									animation-delay: 0.1s;
+								}
+								.spinner_ZTLf {
+									animation-delay: 0.2s;
+								}
+								@keyframes spinner_8HQG {
+									0%,
+									57.14% {
+										animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
+										transform: translate(0);
+									}
+									28.57% {
+										animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
+										transform: translateY(-6px);
+									}
+									100% {
+										transform: translate(0);
+									}
+								}
+							</style><circle class="spinner_qM83" cx="4" cy="12" r="3" /><circle
+								class="spinner_qM83 spinner_oXPr"
+								cx="12"
+								cy="12"
+								r="3"
+							/><circle class="spinner_qM83 spinner_ZTLf" cx="20" cy="12" r="3" /></svg
+						>
+					{:else}
+						<div
+							class=" {rmsLevel * 100 > 4
+								? ' size-52'
+								: rmsLevel * 100 > 2
+									? 'size-48'
+									: rmsLevel * 100 > 1
+										? 'size-44'
+										: 'size-40'}  transition-all rounded-full {(model?.info?.meta
+								?.profile_image_url ?? '/static/favicon.png') !== '/static/favicon.png'
+								? ' bg-cover bg-center bg-no-repeat'
+								: 'bg-black dark:bg-white'} "
+							style={(model?.info?.meta?.profile_image_url ?? '/static/favicon.png') !==
+							'/static/favicon.png'
+								? `background-image: url('${model?.info?.meta?.profile_image_url}');`
+								: ''}
+						/>
+					{/if}
+				</button>
+			{:else}
+				<div class="relative flex video-container w-full max-h-full pt-2 pb-4 md:py-6 px-2 h-full">
+					<video
+						id="camera-feed"
+						autoplay
+						class="rounded-2xl h-full min-w-full object-cover object-center"
+						playsinline
+					/>
+
+					<canvas id="camera-canvas" style="display:none;" />
+
+					<div class=" absolute top-4 md:top-8 left-4">
+						<button
+							type="button"
+							class="p-1.5 text-white cursor-pointer backdrop-blur-xl bg-black/10 rounded-full"
+							on:click={() => {
+								stopCamera();
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 16 16"
+								fill="currentColor"
+								class="size-6"
+							>
+								<path
+									d="M5.28 4.22a.75.75 0 0 0-1.06 1.06L6.94 8l-2.72 2.72a.75.75 0 1 0 1.06 1.06L8 9.06l2.72 2.72a.75.75 0 1 0 1.06-1.06L9.06 8l2.72-2.72a.75.75 0 0 0-1.06-1.06L8 6.94 5.28 4.22Z"
+								/>
+							</svg>
+						</button>
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<div class="flex justify-between items-center pb-2 w-full">
+			<div>
+				{#if camera}
+					<VideoInputMenu
+						devices={videoInputDevices}
+						on:change={async (e) => {
+							console.log(e.detail);
+							selectedVideoInputDeviceId = e.detail;
+							await stopVideoStream();
+							await startVideoStream();
+						}}
+					>
+						<button class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900" type="button">
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="size-5"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0V5.36l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389A5.5 5.5 0 0 1 13.89 6.11l.311.31h-2.432a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.219Z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</button>
+					</VideoInputMenu>
+				{:else}
+					<Tooltip content={$i18n.t('Camera')}>
+						<button
+							class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
+							type="button"
+							on:click={async () => {
+								await navigator.mediaDevices.getUserMedia({ video: true });
+								startCamera();
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								fill="none"
+								viewBox="0 0 24 24"
+								stroke-width="1.5"
+								stroke="currentColor"
+								class="size-5"
+							>
+								<path
+									stroke-linecap="round"
+									stroke-linejoin="round"
+									d="M6.827 6.175A2.31 2.31 0 0 1 5.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 0 0-1.134-.175 2.31 2.31 0 0 1-1.64-1.055l-.822-1.316a2.192 2.192 0 0 0-1.736-1.039 48.774 48.774 0 0 0-5.232 0 2.192 2.192 0 0 0-1.736 1.039l-.821 1.316Z"
+								/>
+								<path
+									stroke-linecap="round"
+									stroke-linejoin="round"
+									d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z"
+								/>
+							</svg>
+						</button>
+					</Tooltip>
+				{/if}
+			</div>
+
+			<div>
+				<button
+					type="button"
+					on:click={() => {
+						if (assistantSpeaking) {
+							stopAllAudio();
+						}
+					}}
+				>
+					<div class=" line-clamp-1 text-sm font-medium">
+						{#if loading}
+							{$i18n.t('Thinking...')}
+						{:else if assistantSpeaking}
+							{$i18n.t('Tap to interrupt')}
+						{:else}
+							{$i18n.t('Listening...')}
+						{/if}
+					</div>
+				</button>
+			</div>
+
+			<div>
+				<button
+					class=" p-3 rounded-full bg-gray-50 dark:bg-gray-900"
+					on:click={async () => {
+						await stopAudioStream();
+						await stopVideoStream();
+
+						console.log(audioStream);
+						console.log(cameraStream);
+
+						showCallOverlay.set(false);
+						dispatch('close');
+					}}
+					type="button"
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="size-5"
+					>
+						<path
+							d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"
+						/>
+					</svg>
+				</button>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/CallOverlay/VideoInputMenu.svelte b/src/lib/components/chat/MessageInput/CallOverlay/VideoInputMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..3b0cd0559272bcc2da2485eee0dbf68ae38408d8
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/CallOverlay/VideoInputMenu.svelte
@@ -0,0 +1,51 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+
+	export let onClose: Function = () => {};
+	export let devices: any;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<slot />
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[180px] rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-[9999] bg-white dark:bg-gray-900 dark:text-white shadow-sm"
+			sideOffset={6}
+			side="top"
+			align="start"
+			transition={flyAndScale}
+		>
+			{#each devices as device}
+				<DropdownMenu.Item
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+					on:click={() => {
+						dispatch('change', device.deviceId);
+					}}
+				>
+					<div class="flex items-center">
+						<div class=" line-clamp-1">
+							{device?.label ?? 'Camera'}
+						</div>
+					</div>
+				</DropdownMenu.Item>
+			{/each}
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/chat/MessageInput/Commands.svelte b/src/lib/components/chat/MessageInput/Commands.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a9c77ab7d81b6bb81276c81cbc1637ee4721e5c1
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/Commands.svelte
@@ -0,0 +1,120 @@
+<script>
+	import { createEventDispatcher, onMount } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	const dispatch = createEventDispatcher();
+
+	import { knowledge, prompts } from '$lib/stores';
+
+	import { removeLastWordFromString } from '$lib/utils';
+	import { getPrompts } from '$lib/apis/prompts';
+	import { getKnowledgeBases } from '$lib/apis/knowledge';
+
+	import Prompts from './Commands/Prompts.svelte';
+	import Knowledge from './Commands/Knowledge.svelte';
+	import Models from './Commands/Models.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+
+	export let prompt = '';
+	export let files = [];
+
+	let loading = false;
+	let commandElement = null;
+
+	export const selectUp = () => {
+		commandElement?.selectUp();
+	};
+
+	export const selectDown = () => {
+		commandElement?.selectDown();
+	};
+
+	let command = '';
+	$: command = prompt?.split('\n').pop()?.split(' ')?.pop() ?? '';
+
+	let show = false;
+	$: show = ['/', '#', '@'].includes(command?.charAt(0)) || '\\#' === command.slice(0, 2);
+
+	$: if (show) {
+		init();
+	}
+
+	const init = async () => {
+		loading = true;
+		await Promise.all([
+			(async () => {
+				prompts.set(await getPrompts(localStorage.token));
+			})(),
+			(async () => {
+				knowledge.set(await getKnowledgeBases(localStorage.token));
+			})()
+		]);
+		loading = false;
+	};
+</script>
+
+{#if show}
+	{#if !loading}
+		{#if command?.charAt(0) === '/'}
+			<Prompts bind:this={commandElement} bind:prompt bind:files {command} />
+		{:else if (command?.charAt(0) === '#' && command.startsWith('#') && !command.includes('# ')) || ('\\#' === command.slice(0, 2) && command.startsWith('#') && !command.includes('# '))}
+			<Knowledge
+				bind:this={commandElement}
+				bind:prompt
+				command={command.includes('\\#') ? command.slice(2) : command}
+				on:youtube={(e) => {
+					console.log(e);
+					dispatch('upload', {
+						type: 'youtube',
+						data: e.detail
+					});
+				}}
+				on:url={(e) => {
+					console.log(e);
+					dispatch('upload', {
+						type: 'web',
+						data: e.detail
+					});
+				}}
+				on:select={(e) => {
+					console.log(e);
+					files = [
+						...files,
+						{
+							...e.detail,
+							status: 'processed'
+						}
+					];
+
+					dispatch('select');
+				}}
+			/>
+		{:else if command?.charAt(0) === '@'}
+			<Models
+				bind:this={commandElement}
+				{command}
+				on:select={(e) => {
+					prompt = removeLastWordFromString(prompt, command);
+
+					dispatch('select', {
+						type: 'model',
+						data: e.detail
+					});
+				}}
+			/>
+		{/if}
+	{:else}
+		<div
+			id="commands-container"
+			class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
+		>
+			<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
+				<div
+					class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100"
+				>
+					<Spinner />
+				</div>
+			</div>
+		</div>
+	{/if}
+{/if}
diff --git a/src/lib/components/chat/MessageInput/Commands/Knowledge.svelte b/src/lib/components/chat/MessageInput/Commands/Knowledge.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c0e7ecaa8bd20aaedfd02c2956c0731e5bbd5f70
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/Commands/Knowledge.svelte
@@ -0,0 +1,321 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import Fuse from 'fuse.js';
+
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	dayjs.extend(relativeTime);
+
+	import { createEventDispatcher, tick, getContext, onMount } from 'svelte';
+	import { removeLastWordFromString, isValidHttpUrl } from '$lib/utils';
+	import { knowledge } from '$lib/stores';
+
+	const i18n = getContext('i18n');
+
+	export let prompt = '';
+	export let command = '';
+
+	const dispatch = createEventDispatcher();
+	let selectedIdx = 0;
+
+	let items = [];
+	let fuse = null;
+
+	let filteredItems = [];
+	$: if (fuse) {
+		filteredItems = command.slice(1)
+			? fuse.search(command).map((e) => {
+					return e.item;
+				})
+			: items;
+	}
+
+	$: if (command) {
+		selectedIdx = 0;
+	}
+
+	export const selectUp = () => {
+		selectedIdx = Math.max(0, selectedIdx - 1);
+	};
+
+	export const selectDown = () => {
+		selectedIdx = Math.min(selectedIdx + 1, filteredItems.length - 1);
+	};
+
+	const confirmSelect = async (item) => {
+		dispatch('select', item);
+
+		prompt = removeLastWordFromString(prompt, command);
+		const chatInputElement = document.getElementById('chat-input');
+
+		await tick();
+		chatInputElement?.focus();
+		await tick();
+	};
+
+	const confirmSelectWeb = async (url) => {
+		dispatch('url', url);
+
+		prompt = removeLastWordFromString(prompt, command);
+		const chatInputElement = document.getElementById('chat-input');
+
+		await tick();
+		chatInputElement?.focus();
+		await tick();
+	};
+
+	const confirmSelectYoutube = async (url) => {
+		dispatch('youtube', url);
+
+		prompt = removeLastWordFromString(prompt, command);
+		const chatInputElement = document.getElementById('chat-input');
+
+		await tick();
+		chatInputElement?.focus();
+		await tick();
+	};
+
+	onMount(() => {
+		let legacy_documents = $knowledge
+			.filter((item) => item?.meta?.document)
+			.map((item) => ({
+				...item,
+				type: 'file'
+			}));
+
+		let legacy_collections =
+			legacy_documents.length > 0
+				? [
+						{
+							name: 'All Documents',
+							legacy: true,
+							type: 'collection',
+							description: 'Deprecated (legacy collection), please create a new knowledge base.',
+							title: $i18n.t('All Documents'),
+							collection_names: legacy_documents.map((item) => item.id)
+						},
+
+						...legacy_documents
+							.reduce((a, item) => {
+								return [...new Set([...a, ...(item?.meta?.tags ?? []).map((tag) => tag.name)])];
+							}, [])
+							.map((tag) => ({
+								name: tag,
+								legacy: true,
+								type: 'collection',
+								description: 'Deprecated (legacy collection), please create a new knowledge base.',
+								collection_names: legacy_documents
+									.filter((item) => (item?.meta?.tags ?? []).map((tag) => tag.name).includes(tag))
+									.map((item) => item.id)
+							}))
+					]
+				: [];
+
+		let collections = $knowledge
+			.filter((item) => !item?.meta?.document)
+			.map((item) => ({
+				...item,
+				type: 'collection'
+			}));
+		let collection_files =
+			$knowledge.length > 0
+				? [
+						...$knowledge
+							.reduce((a, item) => {
+								return [
+									...new Set([
+										...a,
+										...(item?.files ?? []).map((file) => ({
+											...file,
+											collection: { name: item.name, description: item.description }
+										}))
+									])
+								];
+							}, [])
+							.map((file) => ({
+								...file,
+								name: file?.meta?.name,
+								description: `${file?.collection?.name} - ${file?.collection?.description}`,
+								type: 'file'
+							}))
+					]
+				: [];
+
+		items = [...collections, ...collection_files, ...legacy_collections, ...legacy_documents].map(
+			(item) => {
+				return {
+					...item,
+					...(item?.legacy || item?.meta?.legacy || item?.meta?.document ? { legacy: true } : {})
+				};
+			}
+		);
+
+		fuse = new Fuse(items, {
+			keys: ['name', 'description']
+		});
+	});
+</script>
+
+{#if filteredItems.length > 0 || prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
+	<div
+		id="commands-container"
+		class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
+	>
+		<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
+			<div
+				class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100"
+			>
+				<div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5 scrollbar-hidden">
+					{#each filteredItems as item, idx}
+						<button
+							class=" px-3 py-1.5 rounded-xl w-full text-left flex justify-between items-center {idx ===
+							selectedIdx
+								? ' bg-gray-50 dark:bg-gray-850 dark:text-gray-100 selected-command-option-button'
+								: ''}"
+							type="button"
+							on:click={() => {
+								console.log(item);
+								confirmSelect(item);
+							}}
+							on:mousemove={() => {
+								selectedIdx = idx;
+							}}
+						>
+							<div>
+								<div class=" font-medium text-black dark:text-gray-100 flex items-center gap-1">
+									{#if item.legacy}
+										<div
+											class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1 flex-shrink-0"
+										>
+											Legacy
+										</div>
+									{:else if item?.meta?.document}
+										<div
+											class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1 flex-shrink-0"
+										>
+											Document
+										</div>
+									{:else if item?.type === 'file'}
+										<div
+											class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1 flex-shrink-0"
+										>
+											File
+										</div>
+									{:else}
+										<div
+											class="bg-green-500/20 text-green-700 dark:text-green-200 rounded uppercase text-xs font-bold px-1 flex-shrink-0"
+										>
+											Collection
+										</div>
+									{/if}
+
+									<div class="line-clamp-1">
+										{item?.name}
+									</div>
+								</div>
+
+								<div class=" text-xs text-gray-600 dark:text-gray-100 line-clamp-1">
+									{item?.description}
+								</div>
+							</div>
+						</button>
+
+						<!-- <div slot="content" class=" pl-2 pt-1 flex flex-col gap-0.5">
+								{#if !item.legacy && (item?.files ?? []).length > 0}
+									{#each item?.files ?? [] as file, fileIdx}
+										<button
+											class=" px-3 py-1.5 rounded-xl w-full text-left flex justify-between items-center hover:bg-gray-50 dark:hover:bg-gray-850 dark:hover:text-gray-100 selected-command-option-button"
+											type="button"
+											on:click={() => {
+												console.log(file);
+											}}
+											on:mousemove={() => {
+												selectedIdx = idx;
+											}}
+										>
+											<div>
+												<div
+													class=" font-medium text-black dark:text-gray-100 flex items-center gap-1"
+												>
+													<div
+														class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1 flex-shrink-0"
+													>
+														File
+													</div>
+
+													<div class="line-clamp-1">
+														{file?.meta?.name}
+													</div>
+												</div>
+
+												<div class=" text-xs text-gray-600 dark:text-gray-100 line-clamp-1">
+													{$i18n.t('Updated')}
+													{dayjs(file.updated_at * 1000).fromNow()}
+												</div>
+											</div>
+										</button>
+									{/each}
+								{:else}
+									<div class=" text-gray-500 text-xs mt-1 mb-2">
+										{$i18n.t('No files found.')}
+									</div>
+								{/if}
+							</div> -->
+					{/each}
+
+					{#if prompt
+						.split(' ')
+						.some((s) => s.substring(1).startsWith('https://www.youtube.com') || s
+									.substring(1)
+									.startsWith('https://youtu.be'))}
+						<button
+							class="px-3 py-1.5 rounded-xl w-full text-left bg-gray-50 dark:bg-gray-850 dark:text-gray-100 selected-command-option-button"
+							type="button"
+							on:click={() => {
+								const url = prompt.split(' ')?.at(0)?.substring(1);
+								if (isValidHttpUrl(url)) {
+									confirmSelectYoutube(url);
+								} else {
+									toast.error(
+										$i18n.t(
+											'Oops! Looks like the URL is invalid. Please double-check and try again.'
+										)
+									);
+								}
+							}}
+						>
+							<div class=" font-medium text-black dark:text-gray-100 line-clamp-1">
+								{prompt.split(' ')?.at(0)?.substring(1)}
+							</div>
+
+							<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Youtube')}</div>
+						</button>
+					{:else if prompt.split(' ')?.at(0)?.substring(1).startsWith('http')}
+						<button
+							class="px-3 py-1.5 rounded-xl w-full text-left bg-gray-50 dark:bg-gray-850 dark:text-gray-100 selected-command-option-button"
+							type="button"
+							on:click={() => {
+								const url = prompt.split(' ')?.at(0)?.substring(1);
+								if (isValidHttpUrl(url)) {
+									confirmSelectWeb(url);
+								} else {
+									toast.error(
+										$i18n.t(
+											'Oops! Looks like the URL is invalid. Please double-check and try again.'
+										)
+									);
+								}
+							}}
+						>
+							<div class=" font-medium text-black dark:text-gray-100 line-clamp-1">
+								{prompt.split(' ')?.at(0)?.substring(1)}
+							</div>
+
+							<div class=" text-xs text-gray-600 line-clamp-1">{$i18n.t('Web')}</div>
+						</button>
+					{/if}
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/Commands/Models.svelte b/src/lib/components/chat/MessageInput/Commands/Models.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..690a3797d26e2b64927de62235096f638bf5e546
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/Commands/Models.svelte
@@ -0,0 +1,106 @@
+<script lang="ts">
+	import Fuse from 'fuse.js';
+
+	import { createEventDispatcher, onMount } from 'svelte';
+	import { tick, getContext } from 'svelte';
+
+	import { models } from '$lib/stores';
+
+	const i18n = getContext('i18n');
+
+	const dispatch = createEventDispatcher();
+
+	export let command = '';
+
+	let selectedIdx = 0;
+	let filteredItems = [];
+
+	let fuse = new Fuse(
+		$models
+			.filter((model) => !model?.info?.meta?.hidden)
+			.map((model) => {
+				const _item = {
+					...model,
+					modelName: model?.name,
+					tags: model?.info?.meta?.tags?.map((tag) => tag.name).join(' '),
+					desc: model?.info?.meta?.description
+				};
+				return _item;
+			}),
+		{
+			keys: ['value', 'tags', 'modelName'],
+			threshold: 0.3
+		}
+	);
+
+	$: filteredItems = command.slice(1)
+		? fuse.search(command).map((e) => {
+				return e.item;
+			})
+		: $models.filter((model) => !model?.info?.meta?.hidden);
+
+	$: if (command) {
+		selectedIdx = 0;
+	}
+
+	export const selectUp = () => {
+		selectedIdx = Math.max(0, selectedIdx - 1);
+	};
+
+	export const selectDown = () => {
+		selectedIdx = Math.min(selectedIdx + 1, filteredItems.length - 1);
+	};
+
+	const confirmSelect = async (model) => {
+		command = '';
+		dispatch('select', model);
+	};
+
+	onMount(async () => {
+		await tick();
+		const chatInputElement = document.getElementById('chat-input');
+		await tick();
+		chatInputElement?.focus();
+		await tick();
+	});
+</script>
+
+{#if filteredItems.length > 0}
+	<div
+		id="commands-container"
+		class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
+	>
+		<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
+			<div
+				class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100"
+			>
+				<div class="m-1 overflow-y-auto p-1 rounded-r-lg space-y-0.5 scrollbar-hidden">
+					{#each filteredItems as model, modelIdx}
+						<button
+							class="px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
+								? 'bg-gray-50 dark:bg-gray-850 selected-command-option-button'
+								: ''}"
+							type="button"
+							on:click={() => {
+								confirmSelect(model);
+							}}
+							on:mousemove={() => {
+								selectedIdx = modelIdx;
+							}}
+							on:focus={() => {}}
+						>
+							<div class="flex font-medium text-black dark:text-gray-100 line-clamp-1">
+								<img
+									src={model?.info?.meta?.profile_image_url ?? '/static/favicon.png'}
+									alt={model?.name ?? model.id}
+									class="rounded-full size-6 items-center mr-2"
+								/>
+								{model.name}
+							</div>
+						</button>
+					{/each}
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/Commands/Prompts.svelte b/src/lib/components/chat/MessageInput/Commands/Prompts.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6288912955f0cbeb05c3e5c3c2da9c3d5e86f214
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/Commands/Prompts.svelte
@@ -0,0 +1,201 @@
+<script lang="ts">
+	import { prompts, user } from '$lib/stores';
+	import {
+		findWordIndices,
+		getUserPosition,
+		getFormattedDate,
+		getFormattedTime,
+		getCurrentDateTime,
+		getUserTimezone,
+		getWeekday
+	} from '$lib/utils';
+	import { tick, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	export let files;
+
+	export let prompt = '';
+	export let command = '';
+
+	let selectedPromptIdx = 0;
+	let filteredPrompts = [];
+
+	$: filteredPrompts = $prompts
+		.filter((p) => p.command.toLowerCase().includes(command.toLowerCase()))
+		.sort((a, b) => a.title.localeCompare(b.title));
+
+	$: if (command) {
+		selectedPromptIdx = 0;
+	}
+
+	export const selectUp = () => {
+		selectedPromptIdx = Math.max(0, selectedPromptIdx - 1);
+	};
+
+	export const selectDown = () => {
+		selectedPromptIdx = Math.min(selectedPromptIdx + 1, filteredPrompts.length - 1);
+	};
+
+	const confirmPrompt = async (command) => {
+		let text = command.content;
+
+		if (command.content.includes('{{CLIPBOARD}}')) {
+			const clipboardText = await navigator.clipboard.readText().catch((err) => {
+				toast.error($i18n.t('Failed to read clipboard contents'));
+				return '{{CLIPBOARD}}';
+			});
+
+			const clipboardItems = await navigator.clipboard.read();
+
+			let imageUrl = null;
+			for (const item of clipboardItems) {
+				// Check for known image types
+				for (const type of item.types) {
+					if (type.startsWith('image/')) {
+						const blob = await item.getType(type);
+						imageUrl = URL.createObjectURL(blob);
+					}
+				}
+			}
+
+			if (imageUrl) {
+				files = [
+					...files,
+					{
+						type: 'image',
+						url: imageUrl
+					}
+				];
+			}
+
+			text = text.replaceAll('{{CLIPBOARD}}', clipboardText);
+		}
+
+		if (command.content.includes('{{USER_LOCATION}}')) {
+			const location = await getUserPosition();
+			text = text.replaceAll('{{USER_LOCATION}}', String(location));
+		}
+
+		if (command.content.includes('{{USER_NAME}}')) {
+			console.log($user);
+			const name = $user.name || 'User';
+			text = text.replaceAll('{{USER_NAME}}', name);
+		}
+
+		if (command.content.includes('{{USER_LANGUAGE}}')) {
+			const language = localStorage.getItem('locale') || 'en-US';
+			text = text.replaceAll('{{USER_LANGUAGE}}', language);
+		}
+
+		if (command.content.includes('{{CURRENT_DATE}}')) {
+			const date = getFormattedDate();
+			text = text.replaceAll('{{CURRENT_DATE}}', date);
+		}
+
+		if (command.content.includes('{{CURRENT_TIME}}')) {
+			const time = getFormattedTime();
+			text = text.replaceAll('{{CURRENT_TIME}}', time);
+		}
+
+		if (command.content.includes('{{CURRENT_DATETIME}}')) {
+			const dateTime = getCurrentDateTime();
+			text = text.replaceAll('{{CURRENT_DATETIME}}', dateTime);
+		}
+
+		if (command.content.includes('{{CURRENT_TIMEZONE}}')) {
+			const timezone = getUserTimezone();
+			text = text.replaceAll('{{CURRENT_TIMEZONE}}', timezone);
+		}
+
+		if (command.content.includes('{{CURRENT_WEEKDAY}}')) {
+			const weekday = getWeekday();
+			text = text.replaceAll('{{CURRENT_WEEKDAY}}', weekday);
+		}
+
+		prompt = text;
+
+		const chatInputContainerElement = document.getElementById('chat-input-container');
+		const chatInputElement = document.getElementById('chat-input');
+
+		await tick();
+		if (chatInputContainerElement) {
+			chatInputContainerElement.style.height = '';
+			chatInputContainerElement.style.height =
+				Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
+		}
+
+		await tick();
+		if (chatInputElement) {
+			chatInputElement.focus();
+			chatInputElement.dispatchEvent(new Event('input'));
+		}
+	};
+</script>
+
+{#if filteredPrompts.length > 0}
+	<div
+		id="commands-container"
+		class="px-2 mb-2 text-left w-full absolute bottom-0 left-0 right-0 z-10"
+	>
+		<div class="flex w-full rounded-xl border border-gray-50 dark:border-gray-850">
+			<div
+				class="max-h-60 flex flex-col w-full rounded-xl bg-white dark:bg-gray-900 dark:text-gray-100"
+			>
+				<div class="m-1 overflow-y-auto p-1 space-y-0.5 scrollbar-hidden">
+					{#each filteredPrompts as prompt, promptIdx}
+						<button
+							class=" px-3 py-1.5 rounded-xl w-full text-left {promptIdx === selectedPromptIdx
+								? '  bg-gray-50 dark:bg-gray-850 selected-command-option-button'
+								: ''}"
+							type="button"
+							on:click={() => {
+								confirmPrompt(prompt);
+							}}
+							on:mousemove={() => {
+								selectedPromptIdx = promptIdx;
+							}}
+							on:focus={() => {}}
+						>
+							<div class=" font-medium text-black dark:text-gray-100">
+								{prompt.command}
+							</div>
+
+							<div class=" text-xs text-gray-600 dark:text-gray-100">
+								{prompt.title}
+							</div>
+						</button>
+					{/each}
+				</div>
+
+				<div
+					class=" px-2 pt-0.5 pb-1 text-xs text-gray-600 dark:text-gray-100 bg-white dark:bg-gray-900 rounded-b-xl flex items-center space-x-1"
+				>
+					<div>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="w-3 h-3"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
+							/>
+						</svg>
+					</div>
+
+					<div class="line-clamp-1">
+						{$i18n.t(
+							'Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.'
+						)}
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/FilesOverlay.svelte b/src/lib/components/chat/MessageInput/FilesOverlay.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a8acc6a3e22309d9840331bd93e0c434e6745274
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/FilesOverlay.svelte
@@ -0,0 +1,35 @@
+<script lang="ts">
+	import { showSidebar } from '$lib/stores';
+	import AddFilesPlaceholder from '$lib/components/AddFilesPlaceholder.svelte';
+
+	export let show = false;
+	let overlayElement = null;
+
+	$: if (show && overlayElement) {
+		document.body.appendChild(overlayElement);
+		document.body.style.overflow = 'hidden';
+	} else if (overlayElement) {
+		document.body.removeChild(overlayElement);
+		document.body.style.overflow = 'unset';
+	}
+</script>
+
+{#if show}
+	<div
+		bind:this={overlayElement}
+		class="fixed {$showSidebar
+			? 'left-0 md:left-[260px] md:w-[calc(100%-260px)]'
+			: 'left-0'}  fixed top-0 right-0 bottom-0 w-full h-full flex z-[9999] touch-none pointer-events-none"
+		id="dropzone"
+		role="region"
+		aria-label="Drag and Drop Container"
+	>
+		<div class="absolute w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
+			<div class="m-auto pt-64 flex flex-col justify-center">
+				<div class="max-w-md">
+					<AddFilesPlaceholder />
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/MessageInput/InputMenu.svelte b/src/lib/components/chat/MessageInput/InputMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ffdb5897d3941f31170ba0de06c00f5269ca01ed
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/InputMenu.svelte
@@ -0,0 +1,141 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, onMount, tick } from 'svelte';
+
+	import { config, user, tools as _tools } from '$lib/stores';
+	import { getTools } from '$lib/apis/tools';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import DocumentArrowUpSolid from '$lib/components/icons/DocumentArrowUpSolid.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import GlobeAltSolid from '$lib/components/icons/GlobeAltSolid.svelte';
+	import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let uploadFilesHandler: Function;
+	export let selectedToolIds: string[] = [];
+
+	export let webSearchEnabled: boolean;
+	export let onClose: Function;
+
+	let tools = {};
+	let show = false;
+
+	$: if (show) {
+		init();
+	}
+
+	const init = async () => {
+		if ($_tools === null) {
+			await _tools.set(await getTools(localStorage.token));
+		}
+
+		tools = $_tools.reduce((a, tool, i, arr) => {
+			a[tool.id] = {
+				name: tool.name,
+				description: tool.meta.description,
+				enabled: selectedToolIds.includes(tool.id)
+			};
+			return a;
+		}, {});
+	};
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[200px] rounded-xl px-1 py-1  border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={15}
+			alignOffset={-8}
+			side="top"
+			align="start"
+			transition={flyAndScale}
+		>
+			{#if Object.keys(tools).length > 0}
+				<div class="  max-h-28 overflow-y-auto scrollbar-hidden">
+					{#each Object.keys(tools) as toolId}
+						<button
+							class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
+							on:click={() => {
+								tools[toolId].enabled = !tools[toolId].enabled;
+							}}
+						>
+							<div class="flex-1 truncate">
+								<Tooltip
+									content={tools[toolId]?.description ?? ''}
+									placement="top-start"
+									className="flex flex-1 gap-2 items-center"
+								>
+									<div class="flex-shrink-0">
+										<WrenchSolid />
+									</div>
+
+									<div class=" truncate">{tools[toolId].name}</div>
+								</Tooltip>
+							</div>
+
+							<div class=" flex-shrink-0">
+								<Switch
+									state={tools[toolId].enabled}
+									on:change={async (e) => {
+										const state = e.detail;
+										await tick();
+										if (state) {
+											selectedToolIds = [...selectedToolIds, toolId];
+										} else {
+											selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
+										}
+									}}
+								/>
+							</div>
+						</button>
+					{/each}
+				</div>
+
+				<hr class="border-black/5 dark:border-white/5 my-1" />
+			{/if}
+
+			{#if $config?.features?.enable_web_search}
+				<button
+					class="flex w-full justify-between gap-2 items-center px-3 py-2 text-sm font-medium cursor-pointer rounded-xl"
+					on:click={() => {
+						webSearchEnabled = !webSearchEnabled;
+					}}
+				>
+					<div class="flex-1 flex items-center gap-2">
+						<GlobeAltSolid />
+						<div class=" line-clamp-1">{$i18n.t('Web Search')}</div>
+					</div>
+
+					<Switch state={webSearchEnabled} />
+				</button>
+
+				<hr class="border-black/5 dark:border-white/5 my-1" />
+			{/if}
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-xl"
+				on:click={() => {
+					uploadFilesHandler();
+				}}
+			>
+				<DocumentArrowUpSolid />
+				<div class=" line-clamp-1">{$i18n.t('Upload Files')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/chat/MessageInput/VoiceRecording.svelte b/src/lib/components/chat/MessageInput/VoiceRecording.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8ba0e5bdb6bd23b1130aaddd49bf07e5f535b4bf
--- /dev/null
+++ b/src/lib/components/chat/MessageInput/VoiceRecording.svelte
@@ -0,0 +1,512 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher, tick, getContext, onMount, onDestroy } from 'svelte';
+	import { config, settings } from '$lib/stores';
+	import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
+
+	import { transcribeAudio } from '$lib/apis/audio';
+
+	const i18n = getContext('i18n');
+
+	const dispatch = createEventDispatcher();
+
+	export let recording = false;
+	export let className = ' p-2.5 w-full max-w-full';
+
+	let loading = false;
+	let confirmed = false;
+
+	let durationSeconds = 0;
+	let durationCounter = null;
+
+	let transcription = '';
+
+	const startDurationCounter = () => {
+		durationCounter = setInterval(() => {
+			durationSeconds++;
+		}, 1000);
+	};
+
+	const stopDurationCounter = () => {
+		clearInterval(durationCounter);
+		durationSeconds = 0;
+	};
+
+	$: if (recording) {
+		startRecording();
+	} else {
+		stopRecording();
+	}
+
+	const formatSeconds = (seconds) => {
+		const minutes = Math.floor(seconds / 60);
+		const remainingSeconds = seconds % 60;
+		const formattedSeconds = remainingSeconds < 10 ? `0${remainingSeconds}` : remainingSeconds;
+		return `${minutes}:${formattedSeconds}`;
+	};
+
+	let stream;
+	let speechRecognition;
+
+	let mediaRecorder;
+	let audioChunks = [];
+
+	const MIN_DECIBELS = -45;
+	let VISUALIZER_BUFFER_LENGTH = 300;
+
+	let visualizerData = Array(VISUALIZER_BUFFER_LENGTH).fill(0);
+
+	// Function to calculate the RMS level from time domain data
+	const calculateRMS = (data: Uint8Array) => {
+		let sumSquares = 0;
+		for (let i = 0; i < data.length; i++) {
+			const normalizedValue = (data[i] - 128) / 128; // Normalize the data
+			sumSquares += normalizedValue * normalizedValue;
+		}
+		return Math.sqrt(sumSquares / data.length);
+	};
+
+	const normalizeRMS = (rms) => {
+		rms = rms * 10;
+		const exp = 1.5; // Adjust exponent value; values greater than 1 expand larger numbers more and compress smaller numbers more
+		const scaledRMS = Math.pow(rms, exp);
+
+		// Scale between 0.01 (1%) and 1.0 (100%)
+		return Math.min(1.0, Math.max(0.01, scaledRMS));
+	};
+
+	const analyseAudio = (stream) => {
+		const audioContext = new AudioContext();
+		const audioStreamSource = audioContext.createMediaStreamSource(stream);
+
+		const analyser = audioContext.createAnalyser();
+		analyser.minDecibels = MIN_DECIBELS;
+		audioStreamSource.connect(analyser);
+
+		const bufferLength = analyser.frequencyBinCount;
+
+		const domainData = new Uint8Array(bufferLength);
+		const timeDomainData = new Uint8Array(analyser.fftSize);
+
+		let lastSoundTime = Date.now();
+
+		const detectSound = () => {
+			const processFrame = () => {
+				if (!recording || loading) return;
+
+				if (recording && !loading) {
+					analyser.getByteTimeDomainData(timeDomainData);
+					analyser.getByteFrequencyData(domainData);
+
+					// Calculate RMS level from time domain data
+					const rmsLevel = calculateRMS(timeDomainData);
+					// Push the calculated decibel level to visualizerData
+					visualizerData.push(normalizeRMS(rmsLevel));
+
+					// Ensure visualizerData array stays within the buffer length
+					if (visualizerData.length >= VISUALIZER_BUFFER_LENGTH) {
+						visualizerData.shift();
+					}
+
+					visualizerData = visualizerData;
+
+					// if (domainData.some((value) => value > 0)) {
+					// 	lastSoundTime = Date.now();
+					// }
+
+					// if (recording && Date.now() - lastSoundTime > 3000) {
+					// 	if ($settings?.speechAutoSend ?? false) {
+					// 		confirmRecording();
+					// 	}
+					// }
+				}
+
+				window.requestAnimationFrame(processFrame);
+			};
+
+			window.requestAnimationFrame(processFrame);
+		};
+
+		detectSound();
+	};
+
+	const transcribeHandler = async (audioBlob) => {
+		// Create a blob from the audio chunks
+
+		await tick();
+		const file = blobToFile(audioBlob, 'recording.wav');
+
+		const res = await transcribeAudio(localStorage.token, file).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			console.log(res);
+			dispatch('confirm', res);
+		}
+	};
+
+	const saveRecording = (blob) => {
+		const url = URL.createObjectURL(blob);
+		const a = document.createElement('a');
+		document.body.appendChild(a);
+		a.style = 'display: none';
+		a.href = url;
+		a.download = 'recording.wav';
+		a.click();
+		window.URL.revokeObjectURL(url);
+	};
+
+	const startRecording = async () => {
+		startDurationCounter();
+
+		stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+		mediaRecorder = new MediaRecorder(stream);
+		mediaRecorder.onstart = () => {
+			console.log('Recording started');
+			audioChunks = [];
+			analyseAudio(stream);
+		};
+		mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data);
+		mediaRecorder.onstop = async () => {
+			console.log('Recording stopped');
+			if ($config.audio.stt.engine === 'web' || ($settings?.audio?.stt?.engine ?? '') === 'web') {
+				audioChunks = [];
+			} else {
+				if (confirmed) {
+					const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
+
+					await transcribeHandler(audioBlob);
+
+					confirmed = false;
+					loading = false;
+				}
+				audioChunks = [];
+				recording = false;
+			}
+		};
+		mediaRecorder.start();
+		if ($config.audio.stt.engine === 'web' || ($settings?.audio?.stt?.engine ?? '') === 'web') {
+			if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
+				// Create a SpeechRecognition object
+				speechRecognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
+
+				// Set continuous to true for continuous recognition
+				speechRecognition.continuous = true;
+
+				// Set the timeout for turning off the recognition after inactivity (in milliseconds)
+				const inactivityTimeout = 2000; // 3 seconds
+
+				let timeoutId;
+				// Start recognition
+				speechRecognition.start();
+
+				// Event triggered when speech is recognized
+				speechRecognition.onresult = async (event) => {
+					// Clear the inactivity timeout
+					clearTimeout(timeoutId);
+
+					// Handle recognized speech
+					console.log(event);
+					const transcript = event.results[Object.keys(event.results).length - 1][0].transcript;
+
+					transcription = `${transcription}${transcript}`;
+
+					await tick();
+					document.getElementById('chat-input')?.focus();
+
+					// Restart the inactivity timeout
+					timeoutId = setTimeout(() => {
+						console.log('Speech recognition turned off due to inactivity.');
+						speechRecognition.stop();
+					}, inactivityTimeout);
+				};
+
+				// Event triggered when recognition is ended
+				speechRecognition.onend = function () {
+					// Restart recognition after it ends
+					console.log('recognition ended');
+
+					confirmRecording();
+					dispatch('confirm', { text: transcription });
+					confirmed = false;
+					loading = false;
+				};
+
+				// Event triggered when an error occurs
+				speechRecognition.onerror = function (event) {
+					console.log(event);
+					toast.error($i18n.t(`Speech recognition error: {{error}}`, { error: event.error }));
+					dispatch('cancel');
+
+					stopRecording();
+				};
+			}
+		}
+	};
+
+	const stopRecording = async () => {
+		if (recording && mediaRecorder) {
+			await mediaRecorder.stop();
+		}
+
+		if (speechRecognition) {
+			speechRecognition.stop();
+		}
+
+		stopDurationCounter();
+		audioChunks = [];
+
+		if (stream) {
+			const tracks = stream.getTracks();
+			tracks.forEach((track) => track.stop());
+		}
+
+		stream = null;
+	};
+
+	const confirmRecording = async () => {
+		loading = true;
+		confirmed = true;
+
+		if (recording && mediaRecorder) {
+			await mediaRecorder.stop();
+		}
+		clearInterval(durationCounter);
+
+		if (stream) {
+			const tracks = stream.getTracks();
+			tracks.forEach((track) => track.stop());
+		}
+
+		stream = null;
+	};
+
+	let resizeObserver;
+	let containerWidth;
+
+	let maxVisibleItems = 300;
+	$: maxVisibleItems = Math.floor(containerWidth / 5); // 2px width + 0.5px gap
+
+	onMount(() => {
+		// listen to width changes
+		resizeObserver = new ResizeObserver(() => {
+			VISUALIZER_BUFFER_LENGTH = Math.floor(window.innerWidth / 4);
+			if (visualizerData.length > VISUALIZER_BUFFER_LENGTH) {
+				visualizerData = visualizerData.slice(visualizerData.length - VISUALIZER_BUFFER_LENGTH);
+			} else {
+				visualizerData = Array(VISUALIZER_BUFFER_LENGTH - visualizerData.length)
+					.fill(0)
+					.concat(visualizerData);
+			}
+		});
+
+		resizeObserver.observe(document.body);
+	});
+
+	onDestroy(() => {
+		// remove resize observer
+		resizeObserver.disconnect();
+	});
+</script>
+
+<div
+	bind:clientWidth={containerWidth}
+	class="{loading
+		? ' bg-gray-100/50 dark:bg-gray-850/50'
+		: 'bg-indigo-300/10 dark:bg-indigo-500/10 '} rounded-full flex justify-between {className}"
+>
+	<div class="flex items-center mr-1">
+		<button
+			type="button"
+			class="p-1.5
+
+            {loading
+				? ' bg-gray-200 dark:bg-gray-700/50'
+				: 'bg-indigo-400/20 text-indigo-600 dark:text-indigo-300 '} 
+
+
+             rounded-full"
+			on:click={async () => {
+				stopRecording();
+				dispatch('cancel');
+			}}
+		>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				fill="none"
+				viewBox="0 0 24 24"
+				stroke-width="3"
+				stroke="currentColor"
+				class="size-4"
+			>
+				<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
+			</svg>
+		</button>
+	</div>
+
+	<div
+		class="flex flex-1 self-center items-center justify-between ml-2 mx-1 overflow-hidden h-6"
+		dir="rtl"
+	>
+		<div
+			class="flex items-center gap-0.5 h-6 w-full max-w-full overflow-hidden overflow-x-hidden flex-wrap"
+		>
+			{#each visualizerData.slice().reverse() as rms}
+				<div class="flex items-center h-full">
+					<div
+						class="w-[2px] flex-shrink-0
+                    
+                    {loading
+							? ' bg-gray-500 dark:bg-gray-400   '
+							: 'bg-indigo-500 dark:bg-indigo-400  '} 
+                    
+                    inline-block h-full"
+						style="height: {Math.min(100, Math.max(14, rms * 100))}%;"
+					/>
+				</div>
+			{/each}
+		</div>
+	</div>
+
+	<div class="flex">
+		<div class="  mx-1.5 pr-1 flex justify-center items-center">
+			<div
+				class="text-sm
+        
+        
+        {loading ? ' text-gray-500  dark:text-gray-400  ' : ' text-indigo-400 '} 
+       font-medium flex-1 mx-auto text-center"
+			>
+				{formatSeconds(durationSeconds)}
+			</div>
+		</div>
+
+		<div class="flex items-center">
+			{#if loading}
+				<div class=" text-gray-500 rounded-full cursor-not-allowed">
+					<svg
+						width="24"
+						height="24"
+						viewBox="0 0 24 24"
+						xmlns="http://www.w3.org/2000/svg"
+						fill="currentColor"
+						><style>
+							.spinner_OSmW {
+								transform-origin: center;
+								animation: spinner_T6mA 0.75s step-end infinite;
+							}
+							@keyframes spinner_T6mA {
+								8.3% {
+									transform: rotate(30deg);
+								}
+								16.6% {
+									transform: rotate(60deg);
+								}
+								25% {
+									transform: rotate(90deg);
+								}
+								33.3% {
+									transform: rotate(120deg);
+								}
+								41.6% {
+									transform: rotate(150deg);
+								}
+								50% {
+									transform: rotate(180deg);
+								}
+								58.3% {
+									transform: rotate(210deg);
+								}
+								66.6% {
+									transform: rotate(240deg);
+								}
+								75% {
+									transform: rotate(270deg);
+								}
+								83.3% {
+									transform: rotate(300deg);
+								}
+								91.6% {
+									transform: rotate(330deg);
+								}
+								100% {
+									transform: rotate(360deg);
+								}
+							}
+						</style><g class="spinner_OSmW"
+							><rect x="11" y="1" width="2" height="5" opacity=".14" /><rect
+								x="11"
+								y="1"
+								width="2"
+								height="5"
+								transform="rotate(30 12 12)"
+								opacity=".29"
+							/><rect
+								x="11"
+								y="1"
+								width="2"
+								height="5"
+								transform="rotate(60 12 12)"
+								opacity=".43"
+							/><rect
+								x="11"
+								y="1"
+								width="2"
+								height="5"
+								transform="rotate(90 12 12)"
+								opacity=".57"
+							/><rect
+								x="11"
+								y="1"
+								width="2"
+								height="5"
+								transform="rotate(120 12 12)"
+								opacity=".71"
+							/><rect
+								x="11"
+								y="1"
+								width="2"
+								height="5"
+								transform="rotate(150 12 12)"
+								opacity=".86"
+							/><rect x="11" y="1" width="2" height="5" transform="rotate(180 12 12)" /></g
+						></svg
+					>
+				</div>
+			{:else}
+				<button
+					type="button"
+					class="p-1.5 bg-indigo-500 text-white dark:bg-indigo-500 dark:text-blue-950 rounded-full"
+					on:click={async () => {
+						await confirmRecording();
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="2.5"
+						stroke="currentColor"
+						class="size-4"
+					>
+						<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
+					</svg>
+				</button>
+			{/if}
+		</div>
+	</div>
+</div>
+
+<style>
+	.visualizer {
+		display: flex;
+		height: 100%;
+	}
+
+	.visualizer-bar {
+		width: 2px;
+		background-color: #4a5aba; /* or whatever color you need */
+	}
+</style>
diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8a42dd7ad12c56183e6807613c41f62786b7c2b1
--- /dev/null
+++ b/src/lib/components/chat/Messages.svelte
@@ -0,0 +1,418 @@
+<script lang="ts">
+	import { v4 as uuidv4 } from 'uuid';
+	import { chats, config, settings, user as _user, mobile, currentChatPage } from '$lib/stores';
+	import { tick, getContext, onMount, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import { toast } from 'svelte-sonner';
+	import { getChatList, updateChatById } from '$lib/apis/chats';
+	import { copyToClipboard, findWordIndices } from '$lib/utils';
+
+	import Message from './Messages/Message.svelte';
+	import Loader from '../common/Loader.svelte';
+	import Spinner from '../common/Spinner.svelte';
+
+	import ChatPlaceholder from './ChatPlaceholder.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let chatId = '';
+	export let user = $_user;
+
+	export let prompt;
+	export let history = {};
+	export let selectedModels;
+
+	let messages = [];
+
+	export let sendPrompt: Function;
+	export let continueResponse: Function;
+	export let regenerateResponse: Function;
+	export let mergeResponses: Function;
+
+	export let chatActionHandler: Function;
+	export let showMessage: Function = () => {};
+	export let submitMessage: Function = () => {};
+
+	export let readOnly = false;
+
+	export let bottomPadding = false;
+	export let autoScroll;
+
+	let messagesCount = 20;
+	let messagesLoading = false;
+
+	const loadMoreMessages = async () => {
+		// scroll slightly down to disable continuous loading
+		const element = document.getElementById('messages-container');
+		element.scrollTop = element.scrollTop + 100;
+
+		messagesLoading = true;
+		messagesCount += 20;
+
+		await tick();
+
+		messagesLoading = false;
+	};
+
+	$: if (history.currentId) {
+		let _messages = [];
+
+		let message = history.messages[history.currentId];
+		while (message && _messages.length <= messagesCount) {
+			_messages.unshift({ ...message });
+			message = message.parentId !== null ? history.messages[message.parentId] : null;
+		}
+
+		messages = _messages;
+	} else {
+		messages = [];
+	}
+
+	$: if (autoScroll && bottomPadding) {
+		(async () => {
+			await tick();
+			scrollToBottom();
+		})();
+	}
+
+	const scrollToBottom = () => {
+		const element = document.getElementById('messages-container');
+		element.scrollTop = element.scrollHeight;
+	};
+
+	const updateChat = async () => {
+		history = history;
+		await tick();
+		await updateChatById(localStorage.token, chatId, {
+			history: history,
+			messages: messages
+		});
+
+		currentChatPage.set(1);
+		await chats.set(await getChatList(localStorage.token, $currentChatPage));
+	};
+
+	const showPreviousMessage = async (message) => {
+		if (message.parentId !== null) {
+			let messageId =
+				history.messages[message.parentId].childrenIds[
+					Math.max(history.messages[message.parentId].childrenIds.indexOf(message.id) - 1, 0)
+				];
+
+			if (message.id !== messageId) {
+				let messageChildrenIds = history.messages[messageId].childrenIds;
+
+				while (messageChildrenIds.length !== 0) {
+					messageId = messageChildrenIds.at(-1);
+					messageChildrenIds = history.messages[messageId].childrenIds;
+				}
+
+				history.currentId = messageId;
+			}
+		} else {
+			let childrenIds = Object.values(history.messages)
+				.filter((message) => message.parentId === null)
+				.map((message) => message.id);
+			let messageId = childrenIds[Math.max(childrenIds.indexOf(message.id) - 1, 0)];
+
+			if (message.id !== messageId) {
+				let messageChildrenIds = history.messages[messageId].childrenIds;
+
+				while (messageChildrenIds.length !== 0) {
+					messageId = messageChildrenIds.at(-1);
+					messageChildrenIds = history.messages[messageId].childrenIds;
+				}
+
+				history.currentId = messageId;
+			}
+		}
+
+		await tick();
+
+		if ($settings?.scrollOnBranchChange ?? true) {
+			const element = document.getElementById('messages-container');
+			autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
+
+			setTimeout(() => {
+				scrollToBottom();
+			}, 100);
+		}
+	};
+
+	const showNextMessage = async (message) => {
+		if (message.parentId !== null) {
+			let messageId =
+				history.messages[message.parentId].childrenIds[
+					Math.min(
+						history.messages[message.parentId].childrenIds.indexOf(message.id) + 1,
+						history.messages[message.parentId].childrenIds.length - 1
+					)
+				];
+
+			if (message.id !== messageId) {
+				let messageChildrenIds = history.messages[messageId].childrenIds;
+
+				while (messageChildrenIds.length !== 0) {
+					messageId = messageChildrenIds.at(-1);
+					messageChildrenIds = history.messages[messageId].childrenIds;
+				}
+
+				history.currentId = messageId;
+			}
+		} else {
+			let childrenIds = Object.values(history.messages)
+				.filter((message) => message.parentId === null)
+				.map((message) => message.id);
+			let messageId =
+				childrenIds[Math.min(childrenIds.indexOf(message.id) + 1, childrenIds.length - 1)];
+
+			if (message.id !== messageId) {
+				let messageChildrenIds = history.messages[messageId].childrenIds;
+
+				while (messageChildrenIds.length !== 0) {
+					messageId = messageChildrenIds.at(-1);
+					messageChildrenIds = history.messages[messageId].childrenIds;
+				}
+
+				history.currentId = messageId;
+			}
+		}
+
+		await tick();
+
+		if ($settings?.scrollOnBranchChange ?? true) {
+			const element = document.getElementById('messages-container');
+			autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
+
+			setTimeout(() => {
+				scrollToBottom();
+			}, 100);
+		}
+	};
+
+	const rateMessage = async (messageId, rating) => {
+		history.messages[messageId].annotation = {
+			...history.messages[messageId].annotation,
+			rating: rating
+		};
+
+		await updateChat();
+	};
+
+	const editMessage = async (messageId, content, submit = true) => {
+		if (history.messages[messageId].role === 'user') {
+			if (submit) {
+				// New user message
+				let userPrompt = content;
+				let userMessageId = uuidv4();
+
+				let userMessage = {
+					id: userMessageId,
+					parentId: history.messages[messageId].parentId,
+					childrenIds: [],
+					role: 'user',
+					content: userPrompt,
+					...(history.messages[messageId].files && { files: history.messages[messageId].files }),
+					models: selectedModels
+				};
+
+				let messageParentId = history.messages[messageId].parentId;
+
+				if (messageParentId !== null) {
+					history.messages[messageParentId].childrenIds = [
+						...history.messages[messageParentId].childrenIds,
+						userMessageId
+					];
+				}
+
+				history.messages[userMessageId] = userMessage;
+				history.currentId = userMessageId;
+
+				await tick();
+				await sendPrompt(userPrompt, userMessageId);
+			} else {
+				// Edit user message
+				history.messages[messageId].content = content;
+				await updateChat();
+			}
+		} else {
+			if (submit) {
+				// New response message
+				const responseMessageId = uuidv4();
+				const message = history.messages[messageId];
+				const parentId = message.parentId;
+
+				const responseMessage = {
+					...message,
+					id: responseMessageId,
+					parentId: parentId,
+					childrenIds: [],
+					content: content,
+					timestamp: Math.floor(Date.now() / 1000) // Unix epoch
+				};
+
+				history.messages[responseMessageId] = responseMessage;
+				history.currentId = responseMessageId;
+
+				// Append messageId to childrenIds of parent message
+				if (parentId !== null) {
+					history.messages[parentId].childrenIds = [
+						...history.messages[parentId].childrenIds,
+						responseMessageId
+					];
+				}
+
+				await updateChat();
+			} else {
+				// Edit response message
+				history.messages[messageId].originalContent = history.messages[messageId].content;
+				history.messages[messageId].content = content;
+				await updateChat();
+			}
+		}
+	};
+
+	const actionMessage = async (actionId, message, event = null) => {
+		await chatActionHandler(chatId, actionId, message.model, message.id, event);
+	};
+
+	const saveMessage = async (messageId, message) => {
+		history.messages[messageId] = message;
+		await updateChat();
+	};
+
+	const deleteMessage = async (messageId) => {
+		const messageToDelete = history.messages[messageId];
+		const parentMessageId = messageToDelete.parentId;
+		const childMessageIds = messageToDelete.childrenIds ?? [];
+
+		// Collect all grandchildren
+		const grandchildrenIds = childMessageIds.flatMap(
+			(childId) => history.messages[childId]?.childrenIds ?? []
+		);
+
+		// Update parent's children
+		if (parentMessageId && history.messages[parentMessageId]) {
+			history.messages[parentMessageId].childrenIds = [
+				...history.messages[parentMessageId].childrenIds.filter((id) => id !== messageId),
+				...grandchildrenIds
+			];
+		}
+
+		// Update grandchildren's parent
+		grandchildrenIds.forEach((grandchildId) => {
+			if (history.messages[grandchildId]) {
+				history.messages[grandchildId].parentId = parentMessageId;
+			}
+		});
+
+		// Delete the message and its children
+		[messageId, ...childMessageIds].forEach((id) => {
+			delete history.messages[id];
+		});
+
+		await tick();
+
+		showMessage({ id: parentMessageId });
+
+		// Update the chat
+		await updateChat();
+	};
+
+	const triggerScroll = () => {
+		if (autoScroll) {
+			const element = document.getElementById('messages-container');
+			autoScroll = element.scrollHeight - element.scrollTop <= element.clientHeight + 50;
+			setTimeout(() => {
+				scrollToBottom();
+			}, 100);
+		}
+	};
+</script>
+
+<div class="h-full flex pt-8">
+	{#if Object.keys(history?.messages ?? {}).length == 0}
+		<ChatPlaceholder
+			modelIds={selectedModels}
+			submitPrompt={async (p) => {
+				let text = p;
+
+				if (p.includes('{{CLIPBOARD}}')) {
+					const clipboardText = await navigator.clipboard.readText().catch((err) => {
+						toast.error($i18n.t('Failed to read clipboard contents'));
+						return '{{CLIPBOARD}}';
+					});
+
+					text = p.replaceAll('{{CLIPBOARD}}', clipboardText);
+				}
+
+				prompt = text;
+
+				await tick();
+
+				const chatInputContainerElement = document.getElementById('chat-input-container');
+				if (chatInputContainerElement) {
+					prompt = p;
+
+					chatInputContainerElement.style.height = '';
+					chatInputContainerElement.style.height =
+						Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
+					chatInputContainerElement.focus();
+				}
+
+				await tick();
+			}}
+		/>
+	{:else}
+		<div class="w-full pt-2">
+			{#key chatId}
+				<div class="w-full">
+					{#if messages.at(0)?.parentId !== null}
+						<Loader
+							on:visible={(e) => {
+								console.log('visible');
+								if (!messagesLoading) {
+									loadMoreMessages();
+								}
+							}}
+						>
+							<div class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2">
+								<Spinner className=" size-4" />
+								<div class=" ">Loading...</div>
+							</div>
+						</Loader>
+					{/if}
+
+					{#each messages as message, messageIdx (message.id)}
+						<Message
+							{chatId}
+							bind:history
+							messageId={message.id}
+							idx={messageIdx}
+							{user}
+							{showPreviousMessage}
+							{showNextMessage}
+							{updateChat}
+							{editMessage}
+							{deleteMessage}
+							{rateMessage}
+							{actionMessage}
+							{saveMessage}
+							{submitMessage}
+							{regenerateResponse}
+							{continueResponse}
+							{mergeResponses}
+							{triggerScroll}
+							{readOnly}
+						/>
+					{/each}
+				</div>
+				<div class="pb-12" />
+				{#if bottomPadding}
+					<div class="  pb-6" />
+				{/if}
+			{/key}
+		</div>
+	{/if}
+</div>
diff --git a/src/lib/components/chat/Messages/Citations.svelte b/src/lib/components/chat/Messages/Citations.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b434b15db1f2fa30830515b16743b9a2394df004
--- /dev/null
+++ b/src/lib/components/chat/Messages/Citations.svelte
@@ -0,0 +1,194 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import CitationsModal from './CitationsModal.svelte';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
+	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
+	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let sources = [];
+
+	let citations = [];
+	let showPercentage = false;
+	let showRelevance = true;
+
+	let showCitationModal = false;
+	let selectedCitation: any = null;
+	let isCollapsibleOpen = false;
+
+	function calculateShowRelevance(sources: any[]) {
+		const distances = sources.flatMap((citation) => citation.distances ?? []);
+		const inRange = distances.filter((d) => d !== undefined && d >= -1 && d <= 1).length;
+		const outOfRange = distances.filter((d) => d !== undefined && (d < -1 || d > 1)).length;
+
+		if (distances.length === 0) {
+			return false;
+		}
+
+		if (
+			(inRange === distances.length - 1 && outOfRange === 1) ||
+			(outOfRange === distances.length - 1 && inRange === 1)
+		) {
+			return false;
+		}
+
+		return true;
+	}
+
+	function shouldShowPercentage(sources: any[]) {
+		const distances = sources.flatMap((citation) => citation.distances ?? []);
+		return distances.every((d) => d !== undefined && d >= -1 && d <= 1);
+	}
+
+	$: {
+		citations = sources.reduce((acc, source) => {
+			if (Object.keys(source).length === 0) {
+				return acc;
+			}
+
+			source.document.forEach((document, index) => {
+				const metadata = source.metadata?.[index];
+				const distance = source.distances?.[index];
+
+				// Within the same citation there could be multiple documents
+				const id = metadata?.source ?? 'N/A';
+				let _source = source?.source;
+
+				if (metadata?.name) {
+					_source = { ..._source, name: metadata.name };
+				}
+
+				if (id.startsWith('http://') || id.startsWith('https://')) {
+					_source = { ..._source, name: id, url: id };
+				}
+
+				const existingSource = acc.find((item) => item.id === id);
+
+				if (existingSource) {
+					existingSource.document.push(document);
+					existingSource.metadata.push(metadata);
+					if (distance !== undefined) existingSource.distances.push(distance);
+				} else {
+					acc.push({
+						id: id,
+						source: _source,
+						document: [document],
+						metadata: metadata ? [metadata] : [],
+						distances: distance !== undefined ? [distance] : undefined
+					});
+				}
+			});
+			return acc;
+		}, []);
+
+		showRelevance = calculateShowRelevance(citations);
+		showPercentage = shouldShowPercentage(citations);
+	}
+</script>
+
+<CitationsModal
+	bind:show={showCitationModal}
+	citation={selectedCitation}
+	{showPercentage}
+	{showRelevance}
+/>
+
+{#if citations.length > 0}
+	<div class=" py-0.5 -mx-0.5 w-full flex gap-1 items-center flex-wrap">
+		{#if citations.length <= 3}
+			<div class="flex text-xs font-medium">
+				{#each citations as citation, idx}
+					<button
+						id={`source-${citation.source.name}`}
+						class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-white dark:bg-gray-900 rounded-xl max-w-96"
+						on:click={() => {
+							showCitationModal = true;
+							selectedCitation = citation;
+						}}
+					>
+						{#if citations.every((c) => c.distances !== undefined)}
+							<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
+								{idx + 1}
+							</div>
+						{/if}
+						<div
+							class="flex-1 mx-1 line-clamp-1 text-black/60 hover:text-black dark:text-white/60 dark:hover:text-white transition"
+						>
+							{citation.source.name}
+						</div>
+					</button>
+				{/each}
+			</div>
+		{:else}
+			<Collapsible bind:open={isCollapsibleOpen} className="w-full">
+				<div
+					class="flex items-center gap-2 text-gray-500 hover:text-gray-600 dark:hover:text-gray-400 transition cursor-pointer"
+				>
+					<div class="flex-grow flex items-center gap-1 overflow-hidden">
+						<span class="whitespace-nowrap hidden sm:inline">{$i18n.t('References from')}</span>
+						<div class="flex items-center">
+							<div class="flex text-xs font-medium items-center">
+								{#each citations.slice(0, 2) as citation, idx}
+									<button
+										class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
+										on:click={() => {
+											showCitationModal = true;
+											selectedCitation = citation;
+										}}
+										on:pointerup={(e) => {
+											e.stopPropagation();
+										}}
+									>
+										{#if citations.every((c) => c.distances !== undefined)}
+											<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
+												{idx + 1}
+											</div>
+										{/if}
+										<div class="flex-1 mx-1 line-clamp-1 truncate">
+											{citation.source.name}
+										</div>
+									</button>
+								{/each}
+							</div>
+						</div>
+						<div class="flex items-center gap-1 whitespace-nowrap">
+							<span class="hidden sm:inline">{$i18n.t('and')}</span>
+							{citations.length - 2}
+							<span>{$i18n.t('more')}</span>
+						</div>
+					</div>
+					<div class="flex-shrink-0">
+						{#if isCollapsibleOpen}
+							<ChevronUp strokeWidth="3.5" className="size-3.5" />
+						{:else}
+							<ChevronDown strokeWidth="3.5" className="size-3.5" />
+						{/if}
+					</div>
+				</div>
+				<div slot="content">
+					<div class="flex text-xs font-medium">
+						{#each citations as citation, idx}
+							<button
+								class="no-toggle outline-none flex dark:text-gray-300 p-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition rounded-xl max-w-96"
+								on:click={() => {
+									showCitationModal = true;
+									selectedCitation = citation;
+								}}
+							>
+								{#if citations.every((c) => c.distances !== undefined)}
+									<div class="bg-gray-50 dark:bg-gray-800 rounded-full size-4">
+										{idx + 1}
+									</div>
+								{/if}
+								<div class="flex-1 mx-1 line-clamp-1 truncate">
+									{citation.source.name}
+								</div>
+							</button>
+						{/each}
+					</div>
+				</div>
+			</Collapsible>
+		{/if}
+	</div>
+{/if}
diff --git a/src/lib/components/chat/Messages/CitationsModal.svelte b/src/lib/components/chat/Messages/CitationsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9c6b23ecdb76797eb87b9b6dd360c123a5a67272
--- /dev/null
+++ b/src/lib/components/chat/Messages/CitationsModal.svelte
@@ -0,0 +1,174 @@
+<script lang="ts">
+	import { getContext, onMount, tick } from 'svelte';
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+	export let citation;
+	export let showPercentage = false;
+	export let showRelevance = true;
+
+	let mergedDocuments = [];
+
+	function calculatePercentage(distance: number) {
+		if (distance < 0) return 0;
+		if (distance > 1) return 100;
+		return Math.round(distance * 10000) / 100;
+	}
+
+	function getRelevanceColor(percentage: number) {
+		if (percentage >= 80)
+			return 'bg-green-200 dark:bg-green-800 text-green-800 dark:text-green-200';
+		if (percentage >= 60)
+			return 'bg-yellow-200 dark:bg-yellow-800 text-yellow-800 dark:text-yellow-200';
+		if (percentage >= 40)
+			return 'bg-orange-200 dark:bg-orange-800 text-orange-800 dark:text-orange-200';
+		return 'bg-red-200 dark:bg-red-800 text-red-800 dark:text-red-200';
+	}
+
+	$: if (citation) {
+		mergedDocuments = citation.document?.map((c, i) => {
+			return {
+				source: citation.source,
+				document: c,
+				metadata: citation.metadata?.[i],
+				distance: citation.distances?.[i]
+			};
+		});
+		if (mergedDocuments.every((doc) => doc.distance !== undefined)) {
+			mergedDocuments = mergedDocuments.sort(
+				(a, b) => (b.distance ?? Infinity) - (a.distance ?? Infinity)
+			);
+		}
+	}
+</script>
+
+<Modal size="lg" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center capitalize">
+				{$i18n.t('Citation')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-6 pb-5 md:space-x-4">
+			<div
+				class="flex flex-col w-full dark:text-gray-200 overflow-y-scroll max-h-[22rem] scrollbar-hidden"
+			>
+				{#each mergedDocuments as document, documentIdx}
+					<div class="flex flex-col w-full">
+						<div class="text-sm font-medium dark:text-gray-300">
+							{$i18n.t('Source')}
+						</div>
+
+						{#if document.source?.name}
+							<Tooltip
+								className="w-fit"
+								content={$i18n.t('Open file')}
+								placement="top-start"
+								tippyOptions={{ duration: [500, 0] }}
+							>
+								<div class="text-sm dark:text-gray-400 flex items-center gap-2 w-fit">
+									<a
+										class="hover:text-gray-500 hover:dark:text-gray-100 underline flex-grow"
+										href={document?.metadata?.file_id
+											? `/api/v1/files/${document?.metadata?.file_id}/content${document?.metadata?.page !== undefined ? `#page=${document.metadata.page + 1}` : ''}`
+											: document.source?.url?.includes('http')
+												? document.source.url
+												: `#`}
+										target="_blank"
+									>
+										{document?.metadata?.name ?? document.source.name}
+									</a>
+									{#if document?.metadata?.page}
+										<span class="text-xs text-gray-500 dark:text-gray-400">
+											({$i18n.t('page')}
+											{document.metadata.page + 1})
+										</span>
+									{/if}
+								</div>
+							</Tooltip>
+							{#if showRelevance}
+								<div class="text-sm font-medium dark:text-gray-300 mt-2">
+									{$i18n.t('Relevance')}
+								</div>
+								{#if document.distance !== undefined}
+									<Tooltip
+										className="w-fit"
+										content={$i18n.t('Semantic distance to query')}
+										placement="top-start"
+										tippyOptions={{ duration: [500, 0] }}
+									>
+										<div class="text-sm my-1 dark:text-gray-400 flex items-center gap-2 w-fit">
+											{#if showPercentage}
+												{@const percentage = calculatePercentage(document.distance)}
+												<span class={`px-1 rounded font-medium ${getRelevanceColor(percentage)}`}>
+													{percentage.toFixed(2)}%
+												</span>
+												<span class="text-gray-500 dark:text-gray-500">
+													({document.distance.toFixed(4)})
+												</span>
+											{:else}
+												<span class="text-gray-500 dark:text-gray-500">
+													{document.distance.toFixed(4)}
+												</span>
+											{/if}
+										</div>
+									</Tooltip>
+								{:else}
+									<div class="text-sm dark:text-gray-400">
+										{$i18n.t('No distance available')}
+									</div>
+								{/if}
+							{/if}
+						{:else}
+							<div class="text-sm dark:text-gray-400">
+								{$i18n.t('No source available')}
+							</div>
+						{/if}
+					</div>
+					<div class="flex flex-col w-full">
+						<div class=" text-sm font-medium dark:text-gray-300 mt-2">
+							{$i18n.t('Content')}
+						</div>
+						{#if document.metadata?.html}
+							<iframe
+								class="w-full border-0 h-auto rounded-none"
+								sandbox="allow-scripts allow-forms allow-same-origin"
+								srcdoc={document.document}
+								title={$i18n.t('Content')}
+							></iframe>
+						{:else}
+							<pre class="text-sm dark:text-gray-400 whitespace-pre-line">
+                {document.document}
+              </pre>
+						{/if}
+					</div>
+
+					{#if documentIdx !== mergedDocuments.length - 1}
+						<hr class=" dark:border-gray-850 my-3" />
+					{/if}
+				{/each}
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Messages/CodeBlock.svelte b/src/lib/components/chat/Messages/CodeBlock.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..90e747ce9a0be9d042e2aedff870e98ce734e164
--- /dev/null
+++ b/src/lib/components/chat/Messages/CodeBlock.svelte
@@ -0,0 +1,395 @@
+<script lang="ts">
+	import hljs from 'highlight.js';
+	import { loadPyodide } from 'pyodide';
+	import mermaid from 'mermaid';
+
+	import { v4 as uuidv4 } from 'uuid';
+
+	import { getContext, getAllContexts, onMount, tick, createEventDispatcher } from 'svelte';
+	import { copyToClipboard } from '$lib/utils';
+
+	import 'highlight.js/styles/github-dark.min.css';
+
+	import PyodideWorker from '$lib/workers/pyodide.worker?worker';
+	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
+	import SvgPanZoom from '$lib/components/common/SVGPanZoom.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let id = '';
+
+	export let save = false;
+	export let run = true;
+
+	export let token;
+	export let lang = '';
+	export let code = '';
+
+	export let className = 'my-2';
+	export let editorClassName = '';
+	export let stickyButtonsClassName = 'top-8';
+
+	let _code = '';
+	$: if (code) {
+		updateCode();
+	}
+
+	const updateCode = () => {
+		_code = code;
+	};
+
+	let _token = null;
+
+	let mermaidHtml = null;
+
+	let highlightedCode = null;
+	let executing = false;
+
+	let stdout = null;
+	let stderr = null;
+	let result = null;
+
+	let copied = false;
+	let saved = false;
+
+	const saveCode = () => {
+		saved = true;
+
+		code = _code;
+		dispatch('save', code);
+
+		setTimeout(() => {
+			saved = false;
+		}, 1000);
+	};
+
+	const copyCode = async () => {
+		copied = true;
+		await copyToClipboard(code);
+
+		setTimeout(() => {
+			copied = false;
+		}, 1000);
+	};
+
+	const checkPythonCode = (str) => {
+		// Check if the string contains typical Python syntax characters
+		const pythonSyntax = [
+			'def ',
+			'else:',
+			'elif ',
+			'try:',
+			'except:',
+			'finally:',
+			'yield ',
+			'lambda ',
+			'assert ',
+			'nonlocal ',
+			'del ',
+			'True',
+			'False',
+			'None',
+			' and ',
+			' or ',
+			' not ',
+			' in ',
+			' is ',
+			' with '
+		];
+
+		for (let syntax of pythonSyntax) {
+			if (str.includes(syntax)) {
+				return true;
+			}
+		}
+
+		// If none of the above conditions met, it's probably not Python code
+		return false;
+	};
+
+	const executePython = async (code) => {
+		if (!code.includes('input') && !code.includes('matplotlib')) {
+			executePythonAsWorker(code);
+		} else {
+			result = null;
+			stdout = null;
+			stderr = null;
+
+			executing = true;
+
+			document.pyodideMplTarget = document.getElementById(`plt-canvas-${id}`);
+
+			let pyodide = await loadPyodide({
+				indexURL: '/pyodide/',
+				stdout: (text) => {
+					console.log('Python output:', text);
+
+					if (stdout) {
+						stdout += `${text}\n`;
+					} else {
+						stdout = `${text}\n`;
+					}
+				},
+				stderr: (text) => {
+					console.log('An error occurred:', text);
+					if (stderr) {
+						stderr += `${text}\n`;
+					} else {
+						stderr = `${text}\n`;
+					}
+				},
+				packages: ['micropip']
+			});
+
+			try {
+				const micropip = pyodide.pyimport('micropip');
+
+				// await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
+
+				let packages = [
+					code.includes('requests') ? 'requests' : null,
+					code.includes('bs4') ? 'beautifulsoup4' : null,
+					code.includes('numpy') ? 'numpy' : null,
+					code.includes('pandas') ? 'pandas' : null,
+					code.includes('matplotlib') ? 'matplotlib' : null,
+					code.includes('sklearn') ? 'scikit-learn' : null,
+					code.includes('scipy') ? 'scipy' : null,
+					code.includes('re') ? 'regex' : null,
+					code.includes('seaborn') ? 'seaborn' : null
+				].filter(Boolean);
+
+				console.log(packages);
+				await micropip.install(packages);
+
+				result = await pyodide.runPythonAsync(`from js import prompt
+def input(p):
+    return prompt(p)
+__builtins__.input = input`);
+
+				result = await pyodide.runPython(code);
+
+				if (!result) {
+					result = '[NO OUTPUT]';
+				}
+
+				console.log(result);
+				console.log(stdout);
+				console.log(stderr);
+
+				const pltCanvasElement = document.getElementById(`plt-canvas-${id}`);
+
+				if (pltCanvasElement?.innerHTML !== '') {
+					pltCanvasElement.classList.add('pt-4');
+				}
+			} catch (error) {
+				console.error('Error:', error);
+				stderr = error;
+			}
+
+			executing = false;
+		}
+	};
+
+	const executePythonAsWorker = async (code) => {
+		result = null;
+		stdout = null;
+		stderr = null;
+
+		executing = true;
+
+		let packages = [
+			code.includes('requests') ? 'requests' : null,
+			code.includes('bs4') ? 'beautifulsoup4' : null,
+			code.includes('numpy') ? 'numpy' : null,
+			code.includes('pandas') ? 'pandas' : null,
+			code.includes('sklearn') ? 'scikit-learn' : null,
+			code.includes('scipy') ? 'scipy' : null,
+			code.includes('re') ? 'regex' : null,
+			code.includes('seaborn') ? 'seaborn' : null
+		].filter(Boolean);
+
+		console.log(packages);
+
+		const pyodideWorker = new PyodideWorker();
+
+		pyodideWorker.postMessage({
+			id: id,
+			code: code,
+			packages: packages
+		});
+
+		setTimeout(() => {
+			if (executing) {
+				executing = false;
+				stderr = 'Execution Time Limit Exceeded';
+				pyodideWorker.terminate();
+			}
+		}, 60000);
+
+		pyodideWorker.onmessage = (event) => {
+			console.log('pyodideWorker.onmessage', event);
+			const { id, ...data } = event.data;
+
+			console.log(id, data);
+
+			data['stdout'] && (stdout = data['stdout']);
+			data['stderr'] && (stderr = data['stderr']);
+			data['result'] && (result = data['result']);
+
+			executing = false;
+		};
+
+		pyodideWorker.onerror = (event) => {
+			console.log('pyodideWorker.onerror', event);
+			executing = false;
+		};
+	};
+
+	let debounceTimeout;
+
+	const drawMermaidDiagram = async () => {
+		try {
+			if (await mermaid.parse(code)) {
+				const { svg } = await mermaid.render(`mermaid-${uuidv4()}`, code);
+				mermaidHtml = svg;
+			}
+		} catch (error) {
+			console.log('Error:', error);
+		}
+	};
+
+	const render = async () => {
+		if (lang === 'mermaid' && (token?.raw ?? '').slice(-4).includes('```')) {
+			(async () => {
+				await drawMermaidDiagram();
+			})();
+		}
+	};
+
+	$: if (token) {
+		if (JSON.stringify(token) !== JSON.stringify(_token)) {
+			_token = token;
+		}
+	}
+
+	$: if (_token) {
+		render();
+	}
+
+	$: dispatch('code', { lang, code });
+
+	onMount(async () => {
+		console.log('codeblock', lang, code);
+
+		if (lang) {
+			dispatch('code', { lang, code });
+		}
+		if (document.documentElement.classList.contains('dark')) {
+			mermaid.initialize({
+				startOnLoad: true,
+				theme: 'dark',
+				securityLevel: 'loose'
+			});
+		} else {
+			mermaid.initialize({
+				startOnLoad: true,
+				theme: 'default',
+				securityLevel: 'loose'
+			});
+		}
+	});
+</script>
+
+<div>
+	<div class="relative {className} flex flex-col rounded-lg" dir="ltr">
+		{#if lang === 'mermaid'}
+			{#if mermaidHtml}
+				<SvgPanZoom
+					className=" border border-gray-50 dark:border-gray-850 rounded-lg max-h-fit overflow-hidden"
+					svg={mermaidHtml}
+					content={_token.text}
+				/>
+			{:else}
+				<pre class="mermaid">{code}</pre>
+			{/if}
+		{:else}
+			<div class="text-text-300 absolute pl-4 py-1.5 text-xs font-medium dark:text-white">
+				{lang}
+			</div>
+
+			<div
+				class="sticky {stickyButtonsClassName} mb-1 py-1 pr-2.5 flex items-center justify-end z-10 text-xs text-black dark:text-white"
+			>
+				<div class="flex items-center gap-0.5 translate-y-[1px]">
+					{#if lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code))}
+						{#if executing}
+							<div class="run-code-button bg-none border-none p-1 cursor-not-allowed">Running</div>
+						{:else if run}
+							<button
+								class="run-code-button bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
+								on:click={async () => {
+									code = _code;
+									await tick();
+									executePython(code);
+								}}>{$i18n.t('Run')}</button
+							>
+						{/if}
+					{/if}
+
+					{#if save}
+						<button
+							class="save-code-button bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
+							on:click={saveCode}
+						>
+							{saved ? $i18n.t('Saved') : $i18n.t('Save')}
+						</button>
+					{/if}
+
+					<button
+						class="copy-code-button bg-none border-none bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-md px-1.5 py-0.5"
+						on:click={copyCode}>{copied ? $i18n.t('Copied') : $i18n.t('Copy')}</button
+					>
+				</div>
+			</div>
+
+			<div
+				class="language-{lang} rounded-t-lg -mt-8 {editorClassName
+					? editorClassName
+					: executing || stdout || stderr || result
+						? ''
+						: 'rounded-b-lg'} overflow-hidden"
+			>
+				<div class=" pt-7 bg-gray-50 dark:bg-gray-850"></div>
+				<CodeEditor
+					value={code}
+					{id}
+					{lang}
+					on:save={() => {
+						saveCode();
+					}}
+					on:change={(e) => {
+						_code = e.detail.value;
+					}}
+				/>
+			</div>
+
+			<div
+				id="plt-canvas-{id}"
+				class="bg-[#202123] text-white max-w-full overflow-x-auto scrollbar-hidden"
+			/>
+
+			{#if executing}
+				<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
+					<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
+					<div class="text-sm">Running...</div>
+				</div>
+			{:else if stdout || stderr || result}
+				<div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
+					<div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
+					<div class="text-sm">{stdout || stderr || result}</div>
+				</div>
+			{/if}
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/chat/Messages/CodeExecutionModal.svelte b/src/lib/components/chat/Messages/CodeExecutionModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1be9391d805a601917e749d62809ec6b9a8adedc
--- /dev/null
+++ b/src/lib/components/chat/Messages/CodeExecutionModal.svelte
@@ -0,0 +1,118 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import CodeBlock from './CodeBlock.svelte';
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	const i18n = getContext('i18n');
+
+	export let show = false;
+	export let codeExecution = null;
+</script>
+
+<Modal size="lg" bind:show>
+	<div>
+		<div class="flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class="text-lg font-medium self-center flex flex-col gap-0.5 capitalize">
+				{#if codeExecution?.result}
+					<div>
+						{#if codeExecution.result?.error}
+							<Badge type="error" content="error" />
+						{:else if codeExecution.result?.output}
+							<Badge type="success" content="success" />
+						{:else}
+							<Badge type="warning" content="incomplete" />
+						{/if}
+					</div>
+				{/if}
+
+				<div class="flex gap-2 items-center">
+					{#if !codeExecution?.result}
+						<div>
+							<Spinner className="size-4" />
+						</div>
+					{/if}
+
+					<div>
+						{#if codeExecution?.name}
+							{$i18n.t('Code execution')}: {codeExecution?.name}
+						{:else}
+							{$i18n.t('Code execution')}
+						{/if}
+					</div>
+				</div>
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+					codeExecution = null;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pb-5">
+			<div
+				class="flex flex-col w-full dark:text-gray-200 overflow-y-scroll max-h-[22rem] scrollbar-hidden"
+			>
+				<div class="flex flex-col w-full">
+					<CodeBlock
+						id="code-exec-{codeExecution?.id}-code"
+						lang={codeExecution?.language ?? ''}
+						code={codeExecution?.code ?? ''}
+						className=""
+						editorClassName={codeExecution?.result &&
+						(codeExecution?.result?.error || codeExecution?.result?.output)
+							? 'rounded-b-none'
+							: ''}
+						stickyButtonsClassName="top-0"
+						run={false}
+					/>
+				</div>
+
+				{#if codeExecution?.result && (codeExecution?.result?.error || codeExecution?.result?.output)}
+					<div class="dark:bg-[#202123] dark:text-white px-4 py-4 rounded-b-lg flex flex-col gap-3">
+						{#if codeExecution?.result?.error}
+							<div>
+								<div class=" text-gray-500 text-xs mb-1">{$i18n.t('ERROR')}</div>
+								<div class="text-sm">{codeExecution?.result?.error}</div>
+							</div>
+						{/if}
+						{#if codeExecution?.result?.output}
+							<div>
+								<div class=" text-gray-500 text-xs mb-1">{$i18n.t('OUTPUT')}</div>
+								<div class="text-sm">{codeExecution?.result?.output}</div>
+							</div>
+						{/if}
+					</div>
+				{/if}
+				{#if codeExecution?.result?.files && codeExecution?.result?.files.length > 0}
+					<div class="flex flex-col w-full">
+						<hr class=" dark:border-gray-850 my-2" />
+						<div class=" text-sm font-medium dark:text-gray-300">
+							{$i18n.t('Files')}
+						</div>
+						<ul class="mt-1 list-disc pl-4 text-xs">
+							{#each codeExecution?.result?.files as file}
+								<li>
+									<a href={file.url} target="_blank">{file.name}</a>
+								</li>
+							{/each}
+						</ul>
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Messages/CodeExecutions.svelte b/src/lib/components/chat/Messages/CodeExecutions.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2c53eaeb3898576cf083c865d356203f61b6f2ce
--- /dev/null
+++ b/src/lib/components/chat/Messages/CodeExecutions.svelte
@@ -0,0 +1,80 @@
+<script lang="ts">
+	import CodeExecutionModal from './CodeExecutionModal.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Check from '$lib/components/icons/Check.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
+	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+
+	export let codeExecutions = [];
+
+	let selectedCodeExecution = null;
+	let showCodeExecutionModal = false;
+
+	$: if (codeExecutions) {
+		updateSelectedCodeExecution();
+	}
+
+	const updateSelectedCodeExecution = () => {
+		if (selectedCodeExecution) {
+			selectedCodeExecution = codeExecutions.find(
+				(execution) => execution.id === selectedCodeExecution.id
+			);
+		}
+	};
+</script>
+
+<CodeExecutionModal bind:show={showCodeExecutionModal} codeExecution={selectedCodeExecution} />
+
+{#if codeExecutions.length > 0}
+	<div class="mt-1 mb-2 w-full flex gap-1 items-center flex-wrap">
+		{#each codeExecutions as execution (execution.id)}
+			<div class="flex gap-1 text-xs font-semibold">
+				<button
+					class="flex dark:text-gray-300 py-1 px-1 bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-xl max-w-96"
+					on:click={() => {
+						selectedCodeExecution = execution;
+						showCodeExecutionModal = true;
+					}}
+				>
+					<div
+						class="bg-white dark:bg-gray-700 rounded-full size-4 flex items-center justify-center"
+					>
+						{#if execution?.result}
+							{#if execution.result?.error}
+								<XMark />
+							{:else if execution.result?.output}
+								<Check strokeWidth="3" className="size-3" />
+							{:else}
+								<EllipsisHorizontal />
+							{/if}
+						{:else}
+							<Spinner className="size-4" />
+						{/if}
+					</div>
+					<div
+						class="flex-1 mx-2 line-clamp-1 code-execution-name {execution?.result ? '' : 'pulse'}"
+					>
+						{execution.name}
+					</div>
+				</button>
+			</div>
+		{/each}
+	</div>
+{/if}
+
+<style>
+	@keyframes pulse {
+		0%,
+		100% {
+			opacity: 1;
+		}
+		50% {
+			opacity: 0.6;
+		}
+	}
+
+	.pulse {
+		opacity: 1;
+		animation: pulse 1.5s ease;
+	}
+</style>
diff --git a/src/lib/components/chat/Messages/ContentRenderer.svelte b/src/lib/components/chat/Messages/ContentRenderer.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d77a9b6efb3f0d38ee5c020c807cc84d78ecba56
--- /dev/null
+++ b/src/lib/components/chat/Messages/ContentRenderer.svelte
@@ -0,0 +1,261 @@
+<script>
+	import { onDestroy, onMount, tick, getContext, createEventDispatcher } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import Markdown from './Markdown.svelte';
+	import LightBlub from '$lib/components/icons/LightBlub.svelte';
+	import { chatId, mobile, showArtifacts, showControls, showOverview } from '$lib/stores';
+	import ChatBubble from '$lib/components/icons/ChatBubble.svelte';
+	import { stringify } from 'postcss';
+
+	export let id;
+	export let content;
+	export let model = null;
+	export let sources = null;
+
+	export let save = false;
+	export let floatingButtons = true;
+	export let onSourceClick = () => {};
+
+	let contentContainerElement;
+	let buttonsContainerElement;
+
+	let selectedText = '';
+	let floatingInput = false;
+	let floatingInputValue = '';
+
+	const updateButtonPosition = (event) => {
+		if (
+			!contentContainerElement?.contains(event.target) &&
+			!buttonsContainerElement?.contains(event.target)
+		) {
+			closeFloatingButtons();
+			return;
+		}
+
+		setTimeout(async () => {
+			await tick();
+
+			if (!contentContainerElement?.contains(event.target)) return;
+
+			let selection = window.getSelection();
+
+			if (selection.toString().trim().length > 0) {
+				floatingInput = false;
+				const range = selection.getRangeAt(0);
+				const rect = range.getBoundingClientRect();
+
+				const parentRect = contentContainerElement.getBoundingClientRect();
+
+				// Adjust based on parent rect
+				const top = rect.bottom - parentRect.top;
+				const left = rect.left - parentRect.left;
+
+				if (buttonsContainerElement) {
+					buttonsContainerElement.style.display = 'block';
+
+					// Calculate space available on the right
+					const spaceOnRight = parentRect.width - (left + buttonsContainerElement.offsetWidth);
+
+					let thirdScreenWidth = window.innerWidth / 3;
+
+					if (spaceOnRight < thirdScreenWidth) {
+						const right = parentRect.right - rect.right;
+						buttonsContainerElement.style.right = `${right}px`;
+						buttonsContainerElement.style.left = 'auto'; // Reset left
+					} else {
+						// Enough space, position using 'left'
+						buttonsContainerElement.style.left = `${left}px`;
+						buttonsContainerElement.style.right = 'auto'; // Reset right
+					}
+
+					buttonsContainerElement.style.top = `${top + 5}px`; // +5 to add some spacing
+				}
+			} else {
+				closeFloatingButtons();
+			}
+		}, 0);
+	};
+
+	const closeFloatingButtons = () => {
+		if (buttonsContainerElement) {
+			buttonsContainerElement.style.display = 'none';
+			selectedText = '';
+			floatingInput = false;
+			floatingInputValue = '';
+		}
+	};
+
+	const selectAskHandler = () => {
+		dispatch('select', {
+			type: 'ask',
+			content: selectedText,
+			input: floatingInputValue
+		});
+
+		floatingInput = false;
+		floatingInputValue = '';
+		selectedText = '';
+
+		// Clear selection
+		window.getSelection().removeAllRanges();
+		buttonsContainerElement.style.display = 'none';
+	};
+
+	const keydownHandler = (e) => {
+		if (e.key === 'Escape') {
+			closeFloatingButtons();
+		}
+	};
+
+	onMount(() => {
+		if (floatingButtons) {
+			contentContainerElement?.addEventListener('mouseup', updateButtonPosition);
+			document.addEventListener('mouseup', updateButtonPosition);
+			document.addEventListener('keydown', keydownHandler);
+		}
+	});
+
+	onDestroy(() => {
+		if (floatingButtons) {
+			contentContainerElement?.removeEventListener('mouseup', updateButtonPosition);
+			document.removeEventListener('mouseup', updateButtonPosition);
+			document.removeEventListener('keydown', keydownHandler);
+		}
+	});
+</script>
+
+<div bind:this={contentContainerElement}>
+	<Markdown
+		{id}
+		{content}
+		{model}
+		{save}
+		sourceIds={(sources ?? []).reduce((acc, s) => {
+			let ids = [];
+			s.document.forEach((document, index) => {
+				const metadata = s.metadata?.[index];
+				const id = metadata?.source ?? 'N/A';
+
+				if (metadata?.name) {
+					ids.push(metadata.name);
+					return ids;
+				}
+
+				if (id.startsWith('http://') || id.startsWith('https://')) {
+					ids.push(id);
+				} else {
+					ids.push(s?.source?.name ?? id);
+				}
+
+				return ids;
+			});
+
+			acc = [...acc, ...ids];
+
+			// remove duplicates
+			return acc.filter((item, index) => acc.indexOf(item) === index);
+		}, [])}
+		{onSourceClick}
+		on:update={(e) => {
+			dispatch('update', e.detail);
+		}}
+		on:code={(e) => {
+			const { lang, code } = e.detail;
+
+			if (
+				(['html', 'svg'].includes(lang) || (lang === 'xml' && code.includes('svg'))) &&
+				!$mobile &&
+				$chatId
+			) {
+				showArtifacts.set(true);
+				showControls.set(true);
+			}
+		}}
+	/>
+</div>
+
+{#if floatingButtons}
+	<div
+		bind:this={buttonsContainerElement}
+		class="absolute rounded-lg mt-1 text-xs z-[9999]"
+		style="display: none"
+	>
+		{#if !floatingInput}
+			<div
+				class="flex flex-row gap-0.5 shrink-0 p-1 bg-white dark:bg-gray-850 dark:text-gray-100 text-medium rounded-lg shadow-xl"
+			>
+				<button
+					class="px-1 hover:bg-gray-50 dark:hover:bg-gray-800 rounded flex items-center gap-1 min-w-fit"
+					on:click={() => {
+						selectedText = window.getSelection().toString();
+						floatingInput = true;
+					}}
+				>
+					<ChatBubble className="size-3 shrink-0" />
+
+					<div class="shrink-0">Ask</div>
+				</button>
+				<button
+					class="px-1 hover:bg-gray-50 dark:hover:bg-gray-800 rounded flex items-center gap-1 min-w-fit"
+					on:click={() => {
+						const selection = window.getSelection();
+						dispatch('select', {
+							type: 'explain',
+							content: selection.toString()
+						});
+
+						// Clear selection
+						selection.removeAllRanges();
+						buttonsContainerElement.style.display = 'none';
+					}}
+				>
+					<LightBlub className="size-3 shrink-0" />
+
+					<div class="shrink-0">Explain</div>
+				</button>
+			</div>
+		{:else}
+			<div
+				class="py-1 flex dark:text-gray-100 bg-gray-50 dark:bg-gray-800 border dark:border-gray-800 w-72 rounded-full shadow-xl"
+			>
+				<input
+					type="text"
+					class="ml-5 bg-transparent outline-none w-full flex-1 text-sm"
+					placeholder={$i18n.t('Ask a question')}
+					bind:value={floatingInputValue}
+					on:keydown={(e) => {
+						if (e.key === 'Enter') {
+							selectAskHandler();
+						}
+					}}
+				/>
+
+				<div class="ml-1 mr-2">
+					<button
+						class="{floatingInputValue !== ''
+							? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
+							: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 m-0.5 self-center"
+						on:click={() => {
+							selectAskHandler();
+						}}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="size-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</button>
+				</div>
+			</div>
+		{/if}
+	</div>
+{/if}
diff --git a/src/lib/components/chat/Messages/Error.svelte b/src/lib/components/chat/Messages/Error.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cc71036632159942cd54b30505bd27a27bcca13a
--- /dev/null
+++ b/src/lib/components/chat/Messages/Error.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	import Info from '$lib/components/icons/Info.svelte';
+
+	export let content = '';
+</script>
+
+<div class="flex my-2 gap-2.5 border px-4 py-3 border-red-600/10 bg-red-600/10 rounded-lg">
+	<div class=" self-start mt-0.5">
+		<Info className="size-5 text-red-700 dark:text-red-400" />
+	</div>
+
+	<div class=" self-center text-sm">
+		{typeof content === 'string' ? content : JSON.stringify(content)}
+	</div>
+</div>
diff --git a/src/lib/components/chat/Messages/Markdown.svelte b/src/lib/components/chat/Messages/Markdown.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..99ad7719033b95eff1dbf4043658fad2d9f6afbf
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown.svelte
@@ -0,0 +1,53 @@
+<script>
+	import { marked } from 'marked';
+	import { replaceTokens, processResponseContent } from '$lib/utils';
+	import { user } from '$lib/stores';
+
+	import markedExtension from '$lib/utils/marked/extension';
+	import markedKatexExtension from '$lib/utils/marked/katex-extension';
+
+	import MarkdownTokens from './Markdown/MarkdownTokens.svelte';
+	import { createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	export let id;
+	export let content;
+	export let model = null;
+	export let save = false;
+
+	export let sourceIds = [];
+	export let onSourceClick = () => {};
+
+	let tokens = [];
+
+	const options = {
+		throwOnError: false
+	};
+
+	marked.use(markedKatexExtension(options));
+	marked.use(markedExtension(options));
+
+	$: (async () => {
+		if (content) {
+			tokens = marked.lexer(
+				replaceTokens(processResponseContent(content), sourceIds, model?.name, $user?.name)
+			);
+		}
+	})();
+</script>
+
+{#key id}
+	<MarkdownTokens
+		{tokens}
+		{id}
+		{save}
+		{onSourceClick}
+		on:update={(e) => {
+			dispatch('update', e.detail);
+		}}
+		on:code={(e) => {
+			dispatch('code', e.detail);
+		}}
+	/>
+{/key}
diff --git a/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte b/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4dfb9f2c5bf86f0c2f2353ced968a48d22a87df1
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown/KatexRenderer.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	import katex from 'katex';
+	import 'katex/contrib/mhchem';
+	import 'katex/dist/katex.min.css';
+
+	export let content: string;
+	export let displayMode: boolean = false;
+</script>
+
+{@html katex.renderToString(content, { displayMode, throwOnError: false })}
diff --git a/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte b/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8e6500dcceffdf4e3b6709841b7c34637f5efdab
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte
@@ -0,0 +1,85 @@
+<script lang="ts">
+	import DOMPurify from 'dompurify';
+	import { toast } from 'svelte-sonner';
+
+	import type { Token } from 'marked';
+	import { getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import { copyToClipboard, revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
+
+	import Image from '$lib/components/common/Image.svelte';
+	import KatexRenderer from './KatexRenderer.svelte';
+	import Source from './Source.svelte';
+
+	export let id: string;
+	export let tokens: Token[];
+	export let onSourceClick: Function = () => {};
+</script>
+
+{#each tokens as token}
+	{#if token.type === 'escape'}
+		{unescapeHtml(token.text)}
+	{:else if token.type === 'html'}
+		{@const html = DOMPurify.sanitize(token.text)}
+		{#if html && html.includes('<video')}
+			{@html html}
+		{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
+			{@html `${token.text}`}
+		{:else if token.text.includes(`<source_id`)}
+			<Source {token} onClick={onSourceClick} />
+		{:else}
+			{token.text}
+		{/if}
+	{:else if token.type === 'link'}
+		{#if token.tokens}
+			<a href={token.href} target="_blank" rel="nofollow" title={token.title}>
+				<svelte:self id={`${id}-a`} tokens={token.tokens} {onSourceClick} />
+			</a>
+		{:else}
+			<a href={token.href} target="_blank" rel="nofollow" title={token.title}>{token.text}</a>
+		{/if}
+	{:else if token.type === 'image'}
+		<Image src={token.href} alt={token.text} />
+	{:else if token.type === 'strong'}
+		<strong>
+			<svelte:self id={`${id}-strong`} tokens={token.tokens} {onSourceClick} />
+		</strong>
+	{:else if token.type === 'em'}
+		<em>
+			<svelte:self id={`${id}-em`} tokens={token.tokens} {onSourceClick} />
+		</em>
+	{:else if token.type === 'codespan'}
+		<!-- svelte-ignore a11y-click-events-have-key-events -->
+		<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
+		<code
+			class="codespan cursor-pointer"
+			on:click={() => {
+				copyToClipboard(unescapeHtml(token.text));
+				toast.success($i18n.t('Copied to clipboard'));
+			}}>{unescapeHtml(token.text)}</code
+		>
+	{:else if token.type === 'br'}
+		<br />
+	{:else if token.type === 'del'}
+		<del>
+			<svelte:self id={`${id}-del`} tokens={token.tokens} {onSourceClick} />
+		</del>
+	{:else if token.type === 'inlineKatex'}
+		{#if token.text}
+			<KatexRenderer content={revertSanitizedResponseContent(token.text)} displayMode={false} />
+		{/if}
+	{:else if token.type === 'iframe'}
+		<iframe
+			src="{WEBUI_BASE_URL}/api/v1/files/{token.fileId}/content"
+			title={token.fileId}
+			width="100%"
+			frameborder="0"
+			onload="this.style.height=(this.contentWindow.document.body.scrollHeight+20)+'px';"
+		></iframe>
+	{:else if token.type === 'text'}
+		{token.raw}
+	{/if}
+{/each}
diff --git a/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte b/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d4486d7e777fbb1f773e80d4cc49217670d7c9ba
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown/MarkdownTokens.svelte
@@ -0,0 +1,254 @@
+<script lang="ts">
+	import DOMPurify from 'dompurify';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { marked, type Token } from 'marked';
+	import { revertSanitizedResponseContent, unescapeHtml } from '$lib/utils';
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import CodeBlock from '$lib/components/chat/Messages/CodeBlock.svelte';
+	import MarkdownInlineTokens from '$lib/components/chat/Messages/Markdown/MarkdownInlineTokens.svelte';
+	import KatexRenderer from './KatexRenderer.svelte';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	export let id: string;
+	export let tokens: Token[];
+	export let top = true;
+
+	export let save = false;
+	export let onSourceClick: Function = () => {};
+
+	const headerComponent = (depth: number) => {
+		return 'h' + depth;
+	};
+
+	const exportTableToCSVHandler = (token, tokenIdx = 0) => {
+		console.log('Exporting table to CSV');
+
+		// Create an array for rows that will hold the mapped cell text.
+		const rows = token.rows.map((row) =>
+			row.map((cell) => cell.tokens.map((token) => token.text).join(''))
+		);
+
+		// Join the rows using commas (,) as the separator and rows using newline (\n).
+		const csvContent = rows.map((row) => row.join(',')).join('\n');
+
+		// Log rows and CSV content to ensure everything is correct.
+		console.log(rows);
+		console.log(csvContent);
+
+		// To handle Unicode characters, you need to prefix the data with a BOM:
+		const bom = '\uFEFF'; // BOM for UTF-8
+
+		// Create a new Blob prefixed with the BOM to ensure proper Unicode encoding.
+		const blob = new Blob([bom + csvContent], { type: 'text/csv;charset=UTF-8' });
+
+		// Use FileSaver.js's saveAs function to save the generated CSV file.
+		saveAs(blob, `table-${id}-${tokenIdx}.csv`);
+	};
+</script>
+
+<!-- {JSON.stringify(tokens)} -->
+{#each tokens as token, tokenIdx (tokenIdx)}
+	{#if token.type === 'hr'}
+		<hr class=" border-gray-50 dark:border-gray-850" />
+	{:else if token.type === 'heading'}
+		<svelte:element this={headerComponent(token.depth)}>
+			<MarkdownInlineTokens id={`${id}-${tokenIdx}-h`} tokens={token.tokens} {onSourceClick} />
+		</svelte:element>
+	{:else if token.type === 'code'}
+		{#if token.raw.includes('```')}
+			<CodeBlock
+				id={`${id}-${tokenIdx}`}
+				{token}
+				lang={token?.lang ?? ''}
+				code={revertSanitizedResponseContent(token?.text ?? '')}
+				{save}
+				on:code={(e) => {
+					dispatch('code', e.detail);
+				}}
+				on:save={(e) => {
+					dispatch('update', {
+						raw: token.raw,
+						oldContent: token.text,
+						newContent: e.detail
+					});
+				}}
+			/>
+		{:else}
+			{token.text}
+		{/if}
+	{:else if token.type === 'table'}
+		<div class="relative w-full group">
+			<div class="scrollbar-hidden relative overflow-x-auto max-w-full rounded-lg">
+				<table
+					class=" w-full text-sm text-left text-gray-500 dark:text-gray-400 max-w-full rounded-xl"
+				>
+					<thead
+						class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 border-none"
+					>
+						<tr class="">
+							{#each token.header as header, headerIdx}
+								<th
+									scope="col"
+									class="!px-3 !py-1.5 cursor-pointer select-none border border-gray-50 dark:border-gray-850"
+									style={token.align[headerIdx] ? '' : `text-align: ${token.align[headerIdx]}`}
+								>
+									<div class="flex flex-col gap-1.5 text-left">
+										<div class="flex-shrink-0 break-normal">
+											<MarkdownInlineTokens
+												id={`${id}-${tokenIdx}-header-${headerIdx}`}
+												tokens={header.tokens}
+												{onSourceClick}
+											/>
+										</div>
+									</div>
+								</th>
+							{/each}
+						</tr>
+					</thead>
+					<tbody>
+						{#each token.rows as row, rowIdx}
+							<tr class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs">
+								{#each row ?? [] as cell, cellIdx}
+									<td
+										class="!px-3 !py-1.5 text-gray-900 dark:text-white w-max border border-gray-50 dark:border-gray-850"
+										style={token.align[cellIdx] ? '' : `text-align: ${token.align[cellIdx]}`}
+									>
+										<div class="flex flex-col break-normal">
+											<MarkdownInlineTokens
+												id={`${id}-${tokenIdx}-row-${rowIdx}-${cellIdx}`}
+												tokens={cell.tokens}
+												{onSourceClick}
+											/>
+										</div>
+									</td>
+								{/each}
+							</tr>
+						{/each}
+					</tbody>
+				</table>
+			</div>
+
+			<div class=" absolute top-1 right-1.5 z-20 invisible group-hover:visible">
+				<Tooltip content={$i18n.t('Export to CSV')}>
+					<button
+						class="p-1 rounded-lg bg-transparent transition"
+						on:click={(e) => {
+							e.stopPropagation();
+							exportTableToCSVHandler(token, tokenIdx);
+						}}
+					>
+						<ArrowDownTray className=" size-3.5" strokeWidth="1.5" />
+					</button>
+				</Tooltip>
+			</div>
+		</div>
+	{:else if token.type === 'blockquote'}
+		<blockquote>
+			<svelte:self id={`${id}-${tokenIdx}`} tokens={token.tokens} />
+		</blockquote>
+	{:else if token.type === 'list'}
+		{#if token.ordered}
+			<ol start={token.start || 1}>
+				{#each token.items as item, itemIdx}
+					<li>
+						<svelte:self
+							id={`${id}-${tokenIdx}-${itemIdx}`}
+							tokens={item.tokens}
+							top={token.loose}
+						/>
+					</li>
+				{/each}
+			</ol>
+		{:else}
+			<ul>
+				{#each token.items as item, itemIdx}
+					<li>
+						<svelte:self
+							id={`${id}-${tokenIdx}-${itemIdx}`}
+							tokens={item.tokens}
+							top={token.loose}
+						/>
+					</li>
+				{/each}
+			</ul>
+		{/if}
+	{:else if token.type === 'details'}
+		<Collapsible title={token.summary} className="w-fit space-y-1">
+			<div class=" mb-1.5" slot="content">
+				<svelte:self id={`${id}-${tokenIdx}-d`} tokens={marked.lexer(token.text)} />
+			</div>
+		</Collapsible>
+	{:else if token.type === 'html'}
+		{@const html = DOMPurify.sanitize(token.text)}
+		{#if html && html.includes('<video')}
+			{@html html}
+		{:else if token.text.includes(`<iframe src="${WEBUI_BASE_URL}/api/v1/files/`)}
+			{@html `${token.text}`}
+		{:else}
+			{token.text}
+		{/if}
+	{:else if token.type === 'iframe'}
+		<iframe
+			src="{WEBUI_BASE_URL}/api/v1/files/{token.fileId}/content"
+			title={token.fileId}
+			width="100%"
+			frameborder="0"
+			onload="this.style.height=(this.contentWindow.document.body.scrollHeight+20)+'px';"
+		></iframe>
+	{:else if token.type === 'paragraph'}
+		<p>
+			<MarkdownInlineTokens
+				id={`${id}-${tokenIdx}-p`}
+				tokens={token.tokens ?? []}
+				{onSourceClick}
+			/>
+		</p>
+	{:else if token.type === 'text'}
+		{#if top}
+			<p>
+				{#if token.tokens}
+					<MarkdownInlineTokens id={`${id}-${tokenIdx}-t`} tokens={token.tokens} {onSourceClick} />
+				{:else}
+					{unescapeHtml(token.text)}
+				{/if}
+			</p>
+		{:else if token.tokens}
+			<MarkdownInlineTokens
+				id={`${id}-${tokenIdx}-p`}
+				tokens={token.tokens ?? []}
+				{onSourceClick}
+			/>
+		{:else}
+			{unescapeHtml(token.text)}
+		{/if}
+	{:else if token.type === 'inlineKatex'}
+		{#if token.text}
+			<KatexRenderer
+				content={revertSanitizedResponseContent(token.text)}
+				displayMode={token?.displayMode ?? false}
+			/>
+		{/if}
+	{:else if token.type === 'blockKatex'}
+		{#if token.text}
+			<KatexRenderer
+				content={revertSanitizedResponseContent(token.text)}
+				displayMode={token?.displayMode ?? false}
+			/>
+		{/if}
+	{:else if token.type === 'space'}
+		<div class="my-2" />
+	{:else}
+		{console.log('Unknown token', token)}
+	{/if}
+{/each}
diff --git a/src/lib/components/chat/Messages/Markdown/Source.svelte b/src/lib/components/chat/Messages/Markdown/Source.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5ff1b8eecb151d5a9999a998bba205eb1c0cde4a
--- /dev/null
+++ b/src/lib/components/chat/Messages/Markdown/Source.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+	export let token;
+	export let onClick: Function = () => {};
+
+	let id = '';
+	function extractDataAttribute(input) {
+		// Use a regular expression to extract the value of the `data` attribute
+		const match = input.match(/data="([^"]*)"/);
+		// Check if a match was found and return the first captured group
+		return match ? match[1] : null;
+	}
+
+	$: id = extractDataAttribute(token.text);
+</script>
+
+<button
+	class="text-xs font-medium w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/60 dark:hover:text-white bg-gray-50 text-black/60 hover:text-black transition rounded-lg"
+	on:click={() => {
+		onClick(id);
+	}}
+>
+	<span class="line-clamp-1">
+		{id}
+	</span>
+</button>
diff --git a/src/lib/components/chat/Messages/Message.svelte b/src/lib/components/chat/Messages/Message.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..aeee99612e84af7e7944defc44dc49b6631654c6
--- /dev/null
+++ b/src/lib/components/chat/Messages/Message.svelte
@@ -0,0 +1,104 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { tick, getContext, onMount, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import { settings } from '$lib/stores';
+	import { copyToClipboard } from '$lib/utils';
+
+	import MultiResponseMessages from './MultiResponseMessages.svelte';
+	import ResponseMessage from './ResponseMessage.svelte';
+	import UserMessage from './UserMessage.svelte';
+
+	export let chatId;
+	export let idx = 0;
+
+	export let history;
+	export let messageId;
+
+	export let user;
+
+	export let showPreviousMessage;
+	export let showNextMessage;
+	export let updateChat;
+
+	export let editMessage;
+	export let saveMessage;
+	export let deleteMessage;
+	export let rateMessage;
+	export let actionMessage;
+	export let submitMessage;
+
+	export let regenerateResponse;
+	export let continueResponse;
+	export let mergeResponses;
+
+	export let triggerScroll;
+	export let readOnly = false;
+</script>
+
+<div
+	class="flex flex-col justify-between px-5 mb-3 w-full {($settings?.widescreenMode ?? null)
+		? 'max-w-full'
+		: 'max-w-5xl'} mx-auto rounded-lg group"
+>
+	{#if history.messages[messageId]}
+		{#if history.messages[messageId].role === 'user'}
+			<UserMessage
+				{user}
+				{history}
+				{messageId}
+				isFirstMessage={idx === 0}
+				siblings={history.messages[messageId].parentId !== null
+					? (history.messages[history.messages[messageId].parentId]?.childrenIds ?? [])
+					: (Object.values(history.messages)
+							.filter((message) => message.parentId === null)
+							.map((message) => message.id) ?? [])}
+				{showPreviousMessage}
+				{showNextMessage}
+				{editMessage}
+				{deleteMessage}
+				{readOnly}
+			/>
+		{:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1}
+			<ResponseMessage
+				{chatId}
+				{history}
+				{messageId}
+				isLastMessage={messageId === history.currentId}
+				siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
+				{showPreviousMessage}
+				{showNextMessage}
+				{updateChat}
+				{editMessage}
+				{saveMessage}
+				{rateMessage}
+				{actionMessage}
+				{submitMessage}
+				{continueResponse}
+				{regenerateResponse}
+				{readOnly}
+			/>
+		{:else}
+			<MultiResponseMessages
+				bind:history
+				{chatId}
+				{messageId}
+				isLastMessage={messageId === history?.currentId}
+				{updateChat}
+				{editMessage}
+				{saveMessage}
+				{rateMessage}
+				{actionMessage}
+				{submitMessage}
+				{continueResponse}
+				{regenerateResponse}
+				{mergeResponses}
+				{triggerScroll}
+				{readOnly}
+			/>
+		{/if}
+	{/if}
+</div>
diff --git a/src/lib/components/chat/Messages/MultiResponseMessages.svelte b/src/lib/components/chat/Messages/MultiResponseMessages.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..11497a70e0429f301edbb9d1e4bf6c6c38a1db82
--- /dev/null
+++ b/src/lib/components/chat/Messages/MultiResponseMessages.svelte
@@ -0,0 +1,302 @@
+<script lang="ts">
+	import dayjs from 'dayjs';
+	import { onMount, tick, getContext } from 'svelte';
+	import { createEventDispatcher } from 'svelte';
+
+	import { mobile, settings } from '$lib/stores';
+
+	import { generateMoACompletion } from '$lib/apis';
+	import { updateChatById } from '$lib/apis/chats';
+	import { createOpenAITextStream } from '$lib/apis/streaming';
+
+	import ResponseMessage from './ResponseMessage.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Merge from '$lib/components/icons/Merge.svelte';
+
+	import Markdown from './Markdown.svelte';
+	import Name from './Name.svelte';
+	import Skeleton from './Skeleton.svelte';
+	const i18n = getContext('i18n');
+
+	export let chatId;
+	export let history;
+	export let messageId;
+
+	export let isLastMessage;
+	export let readOnly = false;
+
+	export let updateChat: Function;
+	export let editMessage: Function;
+	export let saveMessage: Function;
+	export let rateMessage: Function;
+	export let actionMessage: Function;
+
+	export let submitMessage: Function;
+	export let continueResponse: Function;
+	export let regenerateResponse: Function;
+	export let mergeResponses: Function;
+
+	export let triggerScroll: Function;
+
+	const dispatch = createEventDispatcher();
+
+	let currentMessageId;
+	let parentMessage;
+	let groupedMessageIds = {};
+	let groupedMessageIdsIdx = {};
+
+	let message = JSON.parse(JSON.stringify(history.messages[messageId]));
+	$: if (history.messages) {
+		if (JSON.stringify(message) !== JSON.stringify(history.messages[messageId])) {
+			message = JSON.parse(JSON.stringify(history.messages[messageId]));
+		}
+	}
+
+	const showPreviousMessage = async (modelIdx) => {
+		groupedMessageIdsIdx[modelIdx] = Math.max(0, groupedMessageIdsIdx[modelIdx] - 1);
+
+		let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]];
+		console.log(messageId);
+
+		let messageChildrenIds = history.messages[messageId].childrenIds;
+
+		while (messageChildrenIds.length !== 0) {
+			messageId = messageChildrenIds.at(-1);
+			messageChildrenIds = history.messages[messageId].childrenIds;
+		}
+
+		history.currentId = messageId;
+
+		await tick();
+		await updateChat();
+		triggerScroll();
+	};
+
+	const showNextMessage = async (modelIdx) => {
+		groupedMessageIdsIdx[modelIdx] = Math.min(
+			groupedMessageIds[modelIdx].messageIds.length - 1,
+			groupedMessageIdsIdx[modelIdx] + 1
+		);
+
+		let messageId = groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]];
+		console.log(messageId);
+
+		let messageChildrenIds = history.messages[messageId].childrenIds;
+
+		while (messageChildrenIds.length !== 0) {
+			messageId = messageChildrenIds.at(-1);
+			messageChildrenIds = history.messages[messageId].childrenIds;
+		}
+
+		history.currentId = messageId;
+
+		await tick();
+		await updateChat();
+		triggerScroll();
+	};
+
+	const initHandler = async () => {
+		console.log('multiresponse:initHandler');
+		await tick();
+
+		currentMessageId = messageId;
+		parentMessage = history.messages[messageId].parentId
+			? history.messages[history.messages[messageId].parentId]
+			: null;
+
+		groupedMessageIds = parentMessage?.models.reduce((a, model, modelIdx) => {
+			// Find all messages that are children of the parent message and have the same model
+			let modelMessageIds = parentMessage?.childrenIds
+				.map((id) => history.messages[id])
+				.filter((m) => m?.modelIdx === modelIdx)
+				.map((m) => m.id);
+
+			// Legacy support for messages that don't have a modelIdx
+			// Find all messages that are children of the parent message and have the same model
+			if (modelMessageIds.length === 0) {
+				let modelMessages = parentMessage?.childrenIds
+					.map((id) => history.messages[id])
+					.filter((m) => m?.model === model);
+
+				modelMessages.forEach((m) => {
+					m.modelIdx = modelIdx;
+				});
+
+				modelMessageIds = modelMessages.map((m) => m.id);
+			}
+
+			return {
+				...a,
+				[modelIdx]: { messageIds: modelMessageIds }
+			};
+		}, {});
+
+		groupedMessageIdsIdx = parentMessage?.models.reduce((a, model, modelIdx) => {
+			const idx = groupedMessageIds[modelIdx].messageIds.findIndex((id) => id === messageId);
+			if (idx !== -1) {
+				return {
+					...a,
+					[modelIdx]: idx
+				};
+			} else {
+				return {
+					...a,
+					[modelIdx]: groupedMessageIds[modelIdx].messageIds.length - 1
+				};
+			}
+		}, {});
+
+		console.log(groupedMessageIds, groupedMessageIdsIdx);
+
+		await tick();
+	};
+
+	const mergeResponsesHandler = async () => {
+		const responses = Object.keys(groupedMessageIds).map((modelIdx) => {
+			const { messageIds } = groupedMessageIds[modelIdx];
+			const messageId = messageIds[groupedMessageIdsIdx[modelIdx]];
+
+			return history.messages[messageId].content;
+		});
+		mergeResponses(messageId, responses, chatId);
+	};
+
+	onMount(async () => {
+		await initHandler();
+		await tick();
+
+		const messageElement = document.getElementById(`message-${messageId}`);
+		if (messageElement) {
+			messageElement.scrollIntoView({ block: 'start' });
+		}
+	});
+</script>
+
+{#if parentMessage}
+	<div>
+		<div
+			class="flex snap-x snap-mandatory overflow-x-auto scrollbar-hidden"
+			id="responses-container-{chatId}-{parentMessage.id}"
+		>
+			{#each Object.keys(groupedMessageIds) as modelIdx}
+				{#if groupedMessageIdsIdx[modelIdx] !== undefined && groupedMessageIds[modelIdx].messageIds.length > 0}
+					<!-- svelte-ignore a11y-no-static-element-interactions -->
+					<!-- svelte-ignore a11y-click-events-have-key-events -->
+					{@const _messageId =
+						groupedMessageIds[modelIdx].messageIds[groupedMessageIdsIdx[modelIdx]]}
+
+					<div
+						class=" snap-center w-full max-w-full m-1 border {history.messages[messageId]
+							?.modelIdx == modelIdx
+							? `border-gray-100 dark:border-gray-800 border-[1.5px] ${
+									$mobile ? 'min-w-full' : 'min-w-80'
+								}`
+							: `border-gray-50 dark:border-gray-850 border-dashed ${
+									$mobile ? 'min-w-full' : 'min-w-80'
+								}`} transition-all p-5 rounded-2xl"
+						on:click={async () => {
+							if (messageId != _messageId) {
+								let currentMessageId = _messageId;
+								let messageChildrenIds = history.messages[currentMessageId].childrenIds;
+								while (messageChildrenIds.length !== 0) {
+									currentMessageId = messageChildrenIds.at(-1);
+									messageChildrenIds = history.messages[currentMessageId].childrenIds;
+								}
+								history.currentId = currentMessageId;
+
+								await tick();
+								await updateChat();
+								triggerScroll();
+							}
+						}}
+					>
+						{#key history.currentId}
+							{#if message}
+								<ResponseMessage
+									{chatId}
+									{history}
+									messageId={_messageId}
+									isLastMessage={true}
+									siblings={groupedMessageIds[modelIdx].messageIds}
+									showPreviousMessage={() => showPreviousMessage(modelIdx)}
+									showNextMessage={() => showNextMessage(modelIdx)}
+									{updateChat}
+									{editMessage}
+									{saveMessage}
+									{rateMessage}
+									{actionMessage}
+									{submitMessage}
+									{continueResponse}
+									regenerateResponse={async (message) => {
+										regenerateResponse(message);
+										await tick();
+										groupedMessageIdsIdx[modelIdx] =
+											groupedMessageIds[modelIdx].messageIds.length - 1;
+									}}
+									{readOnly}
+								/>
+							{/if}
+						{/key}
+					</div>
+				{/if}
+			{/each}
+		</div>
+
+		{#if !readOnly}
+			{#if !Object.keys(groupedMessageIds).find((modelIdx) => {
+				const { messageIds } = groupedMessageIds[modelIdx];
+				const _messageId = messageIds[groupedMessageIdsIdx[modelIdx]];
+				return !history.messages[_messageId]?.done ?? false;
+			})}
+				<div class="flex justify-end">
+					<div class="w-full">
+						{#if history.messages[messageId]?.merged?.status}
+							{@const message = history.messages[messageId]?.merged}
+
+							<div class="w-full rounded-xl pl-5 pr-2 py-2">
+								<Name>
+									Merged Response
+
+									{#if message.timestamp}
+										<span
+											class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium uppercase ml-0.5 -mt-0.5"
+										>
+											{dayjs(message.timestamp * 1000).format($i18n.t('h:mm a'))}
+										</span>
+									{/if}
+								</Name>
+
+								<div class="mt-1 markdown-prose w-full min-w-full">
+									{#if (message?.content ?? '') === ''}
+										<Skeleton />
+									{:else}
+										<Markdown id={`merged`} content={message.content ?? ''} />
+									{/if}
+								</div>
+							</div>
+						{/if}
+					</div>
+
+					{#if isLastMessage}
+						<div class=" flex-shrink-0 text-gray-600 dark:text-gray-500 mt-1">
+							<Tooltip content={$i18n.t('Merge Responses')} placement="bottom">
+								<button
+									type="button"
+									id="merge-response-button"
+									class="{true
+										? 'visible'
+										: 'invisible group-hover:visible'} p-1 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
+									on:click={() => {
+										mergeResponsesHandler();
+									}}
+								>
+									<Merge className=" size-5 " />
+								</button>
+							</Tooltip>
+						</div>
+					{/if}
+				</div>
+			{/if}
+		{/if}
+	</div>
+{/if}
diff --git a/src/lib/components/chat/Messages/Name.svelte b/src/lib/components/chat/Messages/Name.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..53879b24adeb3a8b86ec8e956cf36cd6d7e1dfab
--- /dev/null
+++ b/src/lib/components/chat/Messages/Name.svelte
@@ -0,0 +1,3 @@
+<div class=" self-center font-semibold mb-0.5 line-clamp-1 contents">
+	<slot />
+</div>
diff --git a/src/lib/components/chat/Messages/ProfileImage.svelte b/src/lib/components/chat/Messages/ProfileImage.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..dce2385a5c5c29bb33bf112833d617f184b26e3c
--- /dev/null
+++ b/src/lib/components/chat/Messages/ProfileImage.svelte
@@ -0,0 +1,21 @@
+<script lang="ts">
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	export let className = 'size-8';
+	export let src = `${WEBUI_BASE_URL}/static/favicon.png`;
+</script>
+
+<img
+	crossorigin="anonymous"
+	src={src === ''
+		? `${WEBUI_BASE_URL}/static/favicon.png`
+		: src.startsWith(WEBUI_BASE_URL) ||
+			  src.startsWith('https://www.gravatar.com/avatar/') ||
+			  src.startsWith('data:') ||
+			  src.startsWith('/')
+			? src
+			: `/user.png`}
+	class=" {className} object-cover rounded-full -translate-y-[1px]"
+	alt="profile"
+	draggable="false"
+/>
diff --git a/src/lib/components/chat/Messages/RateComment.svelte b/src/lib/components/chat/Messages/RateComment.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c774738c50f3b2d849a1d8bf85f8256e01abfc94
--- /dev/null
+++ b/src/lib/components/chat/Messages/RateComment.svelte
@@ -0,0 +1,249 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	import { config, models } from '$lib/stores';
+	import Tags from '$lib/components/common/Tags.svelte';
+
+	const i18n = getContext('i18n');
+
+	const dispatch = createEventDispatcher();
+
+	export let message;
+	export let show = false;
+
+	let LIKE_REASONS = [
+		'accurate_information',
+		'followed_instructions_perfectly',
+		'showcased_creativity',
+		'positive_attitude',
+		'attention_to_detail',
+		'thorough_explanation',
+		'other'
+	];
+	let DISLIKE_REASONS = [
+		'dont_like_the_style',
+		'too_verbose',
+		'not_helpful',
+		'not_factually_correct',
+		'didnt_fully_follow_instructions',
+		'refused_when_it_shouldnt_have',
+		'being_lazy',
+		'other'
+	];
+
+	let tags = [];
+
+	let reasons = [];
+	let selectedReason = null;
+	let comment = '';
+
+	let detailedRating = null;
+	let selectedModel = null;
+
+	$: if (message?.annotation?.rating === 1) {
+		reasons = LIKE_REASONS;
+	} else if (message?.annotation?.rating === -1) {
+		reasons = DISLIKE_REASONS;
+	}
+
+	$: if (message) {
+		init();
+	}
+
+	const init = () => {
+		selectedReason = message?.annotation?.reason ?? '';
+		comment = message?.annotation?.comment ?? '';
+		tags = (message?.annotation?.tags ?? []).map((tag) => ({
+			name: tag
+		}));
+		detailedRating = message?.annotation?.details?.rating ?? null;
+	};
+
+	onMount(() => {
+		if (message?.arena) {
+			selectedModel = $models.find((m) => m.id === message.selectedModelId);
+			toast.success(
+				$i18n.t('This response was generated by "{{model}}"', {
+					model: selectedModel ? (selectedModel?.name ?? selectedModel.id) : message.selectedModelId
+				})
+			);
+		}
+	});
+
+	const saveHandler = () => {
+		console.log('saveHandler');
+		// if (!selectedReason) {
+		// 	toast.error($i18n.t('Please select a reason'));
+		// 	return;
+		// }
+
+		dispatch('save', {
+			reason: selectedReason,
+			comment: comment,
+			tags: tags.map((tag) => tag.name),
+			details: {
+				rating: detailedRating
+			}
+		});
+
+		toast.success($i18n.t('Thanks for your feedback!'));
+		show = false;
+	};
+</script>
+
+{#if message?.arena}
+	<div class="text-xs font-medium pt-1.5 -mb-0.5">
+		{$i18n.t('This response was generated by "{{model}}"', {
+			model: selectedModel ? (selectedModel?.name ?? selectedModel.id) : message.selectedModelId
+		})}
+	</div>
+{/if}
+
+<div
+	class=" my-2.5 rounded-xl px-4 py-3 border border-gray-50 dark:border-gray-850"
+	id="message-feedback-{message.id}"
+>
+	<div class="flex justify-between items-center">
+		<div class="text-sm font-medium">{$i18n.t('How would you rate this response?')}</div>
+
+		<!-- <div class=" text-sm">{$i18n.t('Tell us more:')}</div> -->
+
+		<button
+			on:click={() => {
+				show = false;
+			}}
+		>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				fill="none"
+				viewBox="0 0 24 24"
+				stroke-width="1.5"
+				stroke="currentColor"
+				class="size-4"
+			>
+				<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
+			</svg>
+		</button>
+	</div>
+
+	<div class="w-full flex justify-center">
+		<div class=" relative w-fit">
+			<div class="mt-1.5 w-fit flex gap-1 pb-5">
+				<!-- 1-10 scale -->
+				{#each Array.from({ length: 10 }).map((_, i) => i + 1) as rating}
+					<button
+						class="size-7 text-sm border border-gray-50 dark:border-gray-850 hover:bg-gray-50 dark:hover:bg-gray-850 {detailedRating ===
+						rating
+							? 'bg-gray-100 dark:bg-gray-800'
+							: ''} transition rounded-full disabled:cursor-not-allowed disabled:text-gray-500 disabled:bg-white disabled:dark:bg-gray-900"
+						on:click={() => {
+							detailedRating = rating;
+						}}
+						disabled={message?.annotation?.rating === -1 ? rating > 5 : rating < 6}
+					>
+						{rating}
+					</button>
+				{/each}
+			</div>
+
+			<div class="absolute bottom-0 left-0 right-0 flex justify-between text-xs">
+				<div>
+					1 - {$i18n.t('Awful')}
+				</div>
+
+				<div>
+					10 - {$i18n.t('Amazing')}
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div>
+		{#if reasons.length > 0}
+			<div class="text-sm mt-1.5 font-medium">{$i18n.t('Why?')}</div>
+
+			<div class="flex flex-wrap gap-1.5 text-sm mt-1.5">
+				{#each reasons as reason}
+					<button
+						class="px-3 py-0.5 border border-gray-50 dark:border-gray-850 hover:bg-gray-50 dark:hover:bg-gray-850 {selectedReason ===
+						reason
+							? 'bg-gray-100 dark:bg-gray-800'
+							: ''} transition rounded-xl"
+						on:click={() => {
+							selectedReason = reason;
+						}}
+					>
+						{#if reason === 'accurate_information'}
+							{$i18n.t('Accurate information')}
+						{:else if reason === 'followed_instructions_perfectly'}
+							{$i18n.t('Followed instructions perfectly')}
+						{:else if reason === 'showcased_creativity'}
+							{$i18n.t('Showcased creativity')}
+						{:else if reason === 'positive_attitude'}
+							{$i18n.t('Positive attitude')}
+						{:else if reason === 'attention_to_detail'}
+							{$i18n.t('Attention to detail')}
+						{:else if reason === 'thorough_explanation'}
+							{$i18n.t('Thorough explanation')}
+						{:else if reason === 'dont_like_the_style'}
+							{$i18n.t("Don't like the style")}
+						{:else if reason === 'too_verbose'}
+							{$i18n.t('Too verbose')}
+						{:else if reason === 'not_helpful'}
+							{$i18n.t('Not helpful')}
+						{:else if reason === 'not_factually_correct'}
+							{$i18n.t('Not factually correct')}
+						{:else if reason === 'didnt_fully_follow_instructions'}
+							{$i18n.t("Didn't fully follow instructions")}
+						{:else if reason === 'refused_when_it_shouldnt_have'}
+							{$i18n.t("Refused when it shouldn't have")}
+						{:else if reason === 'being_lazy'}
+							{$i18n.t('Being lazy')}
+						{:else if reason === 'other'}
+							{$i18n.t('Other')}
+						{:else}
+							{reason}
+						{/if}
+					</button>
+				{/each}
+			</div>
+		{/if}
+	</div>
+
+	<div class="mt-2">
+		<textarea
+			bind:value={comment}
+			class="w-full text-sm px-1 py-2 bg-transparent outline-none resize-none rounded-xl"
+			placeholder={$i18n.t('Feel free to add specific details')}
+			rows="3"
+		/>
+	</div>
+
+	<div class="mt-2 gap-1.5 flex justify-between">
+		<div class="flex items-end group">
+			<Tags
+				{tags}
+				on:delete={(e) => {
+					tags = tags.filter(
+						(tag) =>
+							tag.name.replaceAll(' ', '_').toLowerCase() !==
+							e.detail.replaceAll(' ', '_').toLowerCase()
+					);
+				}}
+				on:add={(e) => {
+					tags = [...tags, { name: e.detail }];
+				}}
+			/>
+		</div>
+
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			on:click={() => {
+				saveHandler();
+			}}
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Messages/ResponseMessage.svelte b/src/lib/components/chat/Messages/ResponseMessage.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2e883df93ddbe6c9de6af0874a0f5bb9a0b60e31
--- /dev/null
+++ b/src/lib/components/chat/Messages/ResponseMessage.svelte
@@ -0,0 +1,1267 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext<Writable<i18nType>>('i18n');
+
+	const dispatch = createEventDispatcher();
+
+	import { config, models, settings, user } from '$lib/stores';
+	import { synthesizeOpenAISpeech } from '$lib/apis/audio';
+	import { imageGenerations } from '$lib/apis/images';
+	import {
+		copyToClipboard as _copyToClipboard,
+		approximateToHumanReadable,
+		getMessageContentParts,
+		sanitizeResponseContent,
+		createMessagesList
+	} from '$lib/utils';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import Name from './Name.svelte';
+	import ProfileImage from './ProfileImage.svelte';
+	import Skeleton from './Skeleton.svelte';
+	import Image from '$lib/components/common/Image.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import RateComment from './RateComment.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import WebSearchResults from './ResponseMessage/WebSearchResults.svelte';
+	import Sparkles from '$lib/components/icons/Sparkles.svelte';
+	import Error from './Error.svelte';
+	import Citations from './Citations.svelte';
+	import CodeExecutions from './CodeExecutions.svelte';
+
+	import type { Writable } from 'svelte/store';
+	import type { i18n as i18nType } from 'i18next';
+	import ContentRenderer from './ContentRenderer.svelte';
+	import { createNewFeedback, getFeedbackById, updateFeedbackById } from '$lib/apis/evaluations';
+	import { getChatById } from '$lib/apis/chats';
+	import { generateTags } from '$lib/apis';
+
+	interface MessageType {
+		id: string;
+		model: string;
+		content: string;
+		files?: { type: string; url: string }[];
+		timestamp: number;
+		role: string;
+		statusHistory?: {
+			done: boolean;
+			action: string;
+			description: string;
+			urls?: string[];
+			query?: string;
+		}[];
+		status?: {
+			done: boolean;
+			action: string;
+			description: string;
+			urls?: string[];
+			query?: string;
+		};
+		done: boolean;
+		error?: boolean | { content: string };
+		sources?: string[];
+		code_executions?: {
+			uuid: string;
+			name: string;
+			code: string;
+			language?: string;
+			result?: {
+				error?: string;
+				output?: string;
+				files?: { name: string; url: string }[];
+			};
+		}[];
+		info?: {
+			openai?: boolean;
+			prompt_tokens?: number;
+			completion_tokens?: number;
+			total_tokens?: number;
+			eval_count?: number;
+			eval_duration?: number;
+			prompt_eval_count?: number;
+			prompt_eval_duration?: number;
+			total_duration?: number;
+			load_duration?: number;
+			usage?: unknown;
+		};
+		annotation?: { type: string; rating: number };
+	}
+
+	export let chatId = '';
+	export let history;
+	export let messageId;
+
+	let message: MessageType = JSON.parse(JSON.stringify(history.messages[messageId]));
+	$: if (history.messages) {
+		if (JSON.stringify(message) !== JSON.stringify(history.messages[messageId])) {
+			message = JSON.parse(JSON.stringify(history.messages[messageId]));
+		}
+	}
+
+	export let siblings;
+
+	export let showPreviousMessage: Function;
+	export let showNextMessage: Function;
+
+	export let updateChat: Function;
+	export let editMessage: Function;
+	export let saveMessage: Function;
+	export let rateMessage: Function;
+	export let actionMessage: Function;
+
+	export let submitMessage: Function;
+	export let continueResponse: Function;
+	export let regenerateResponse: Function;
+
+	export let isLastMessage = true;
+	export let readOnly = false;
+
+	let model = null;
+	$: model = $models.find((m) => m.id === message.model);
+
+	let edit = false;
+	let editedContent = '';
+	let editTextAreaElement: HTMLTextAreaElement;
+
+	let audioParts: Record<number, HTMLAudioElement | null> = {};
+	let speaking = false;
+	let speakingIdx: number | undefined;
+
+	let loadingSpeech = false;
+	let generatingImage = false;
+
+	let showRateComment = false;
+
+	const copyToClipboard = async (text) => {
+		const res = await _copyToClipboard(text);
+		if (res) {
+			toast.success($i18n.t('Copying to clipboard was successful!'));
+		}
+	};
+
+	const playAudio = (idx: number) => {
+		return new Promise<void>((res) => {
+			speakingIdx = idx;
+			const audio = audioParts[idx];
+
+			if (!audio) {
+				return res();
+			}
+
+			audio.play();
+			audio.onended = async () => {
+				await new Promise((r) => setTimeout(r, 300));
+
+				if (Object.keys(audioParts).length - 1 === idx) {
+					speaking = false;
+				}
+
+				res();
+			};
+		});
+	};
+
+	const toggleSpeakMessage = async () => {
+		if (speaking) {
+			try {
+				speechSynthesis.cancel();
+
+				if (speakingIdx !== undefined && audioParts[speakingIdx]) {
+					audioParts[speakingIdx]!.pause();
+					audioParts[speakingIdx]!.currentTime = 0;
+				}
+			} catch {}
+
+			speaking = false;
+			speakingIdx = undefined;
+			return;
+		}
+
+		if (!(message?.content ?? '').trim().length) {
+			toast.info($i18n.t('No content to speak'));
+			return;
+		}
+
+		speaking = true;
+
+		if ($config.audio.tts.engine !== '') {
+			loadingSpeech = true;
+
+			const messageContentParts: string[] = getMessageContentParts(
+				message.content,
+				$config?.audio?.tts?.split_on ?? 'punctuation'
+			);
+
+			if (!messageContentParts.length) {
+				console.log('No content to speak');
+				toast.info($i18n.t('No content to speak'));
+
+				speaking = false;
+				loadingSpeech = false;
+				return;
+			}
+
+			console.debug('Prepared message content for TTS', messageContentParts);
+
+			audioParts = messageContentParts.reduce(
+				(acc, _sentence, idx) => {
+					acc[idx] = null;
+					return acc;
+				},
+				{} as typeof audioParts
+			);
+
+			let lastPlayedAudioPromise = Promise.resolve(); // Initialize a promise that resolves immediately
+
+			for (const [idx, sentence] of messageContentParts.entries()) {
+				const res = await synthesizeOpenAISpeech(
+					localStorage.token,
+					$settings?.audio?.tts?.defaultVoice === $config.audio.tts.voice
+						? ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
+						: $config?.audio?.tts?.voice,
+					sentence
+				).catch((error) => {
+					console.error(error);
+					toast.error(error);
+
+					speaking = false;
+					loadingSpeech = false;
+				});
+
+				if (res) {
+					const blob = await res.blob();
+					const blobUrl = URL.createObjectURL(blob);
+					const audio = new Audio(blobUrl);
+					audio.playbackRate = $settings.audio?.tts?.playbackRate ?? 1;
+
+					audioParts[idx] = audio;
+					loadingSpeech = false;
+					lastPlayedAudioPromise = lastPlayedAudioPromise.then(() => playAudio(idx));
+				}
+			}
+		} else {
+			let voices = [];
+			const getVoicesLoop = setInterval(() => {
+				voices = speechSynthesis.getVoices();
+				if (voices.length > 0) {
+					clearInterval(getVoicesLoop);
+
+					const voice =
+						voices
+							?.filter(
+								(v) => v.voiceURI === ($settings?.audio?.tts?.voice ?? $config?.audio?.tts?.voice)
+							)
+							?.at(0) ?? undefined;
+
+					console.log(voice);
+
+					const speak = new SpeechSynthesisUtterance(message.content);
+					speak.rate = $settings.audio?.tts?.playbackRate ?? 1;
+
+					console.log(speak);
+
+					speak.onend = () => {
+						speaking = false;
+						if ($settings.conversationMode) {
+							document.getElementById('voice-input-button')?.click();
+						}
+					};
+
+					if (voice) {
+						speak.voice = voice;
+					}
+
+					speechSynthesis.speak(speak);
+				}
+			}, 100);
+		}
+	};
+
+	const editMessageHandler = async () => {
+		edit = true;
+		editedContent = message.content;
+
+		await tick();
+
+		editTextAreaElement.style.height = '';
+		editTextAreaElement.style.height = `${editTextAreaElement.scrollHeight}px`;
+	};
+
+	const editMessageConfirmHandler = async () => {
+		editMessage(message.id, editedContent ? editedContent : '', false);
+
+		edit = false;
+		editedContent = '';
+
+		await tick();
+	};
+
+	const saveAsCopyHandler = async () => {
+		editMessage(message.id, editedContent ? editedContent : '');
+
+		edit = false;
+		editedContent = '';
+
+		await tick();
+	};
+
+	const cancelEditMessage = async () => {
+		edit = false;
+		editedContent = '';
+		await tick();
+	};
+
+	const generateImage = async (message: MessageType) => {
+		generatingImage = true;
+		const res = await imageGenerations(localStorage.token, message.content).catch((error) => {
+			toast.error(error);
+		});
+		console.log(res);
+
+		if (res) {
+			const files = res.map((image) => ({
+				type: 'image',
+				url: `${image.url}`
+			}));
+
+			saveMessage(message.id, {
+				...message,
+				files: files
+			});
+		}
+
+		generatingImage = false;
+	};
+
+	let feedbackLoading = false;
+
+	const feedbackHandler = async (rating: number | null = null, details: object | null = null) => {
+		feedbackLoading = true;
+		console.log('Feedback', rating, details);
+
+		const updatedMessage = {
+			...message,
+			annotation: {
+				...(message?.annotation ?? {}),
+				...(rating !== null ? { rating: rating } : {}),
+				...(details ? details : {})
+			}
+		};
+
+		const chat = await getChatById(localStorage.token, chatId).catch((error) => {
+			toast.error(error);
+		});
+		if (!chat) {
+			return;
+		}
+
+		const messages = createMessagesList(history, message.id);
+
+		let feedbackItem = {
+			type: 'rating',
+			data: {
+				...(updatedMessage?.annotation ? updatedMessage.annotation : {}),
+				model_id: message?.selectedModelId ?? message.model,
+				...(history.messages[message.parentId].childrenIds.length > 1
+					? {
+							sibling_model_ids: history.messages[message.parentId].childrenIds
+								.filter((id) => id !== message.id)
+								.map((id) => history.messages[id]?.selectedModelId ?? history.messages[id].model)
+						}
+					: {})
+			},
+			meta: {
+				arena: message ? message.arena : false,
+				model_id: message.model,
+				message_id: message.id,
+				message_index: messages.length,
+				chat_id: chatId
+			},
+			snapshot: {
+				chat: chat
+			}
+		};
+
+		const baseModels = [
+			feedbackItem.data.model_id,
+			...(feedbackItem.data.sibling_model_ids ?? [])
+		].reduce((acc, modelId) => {
+			const model = $models.find((m) => m.id === modelId);
+			if (model) {
+				acc[model.id] = model?.info?.base_model_id ?? null;
+			} else {
+				// Log or handle cases where corresponding model is not found
+				console.warn(`Model with ID ${modelId} not found`);
+			}
+			return acc;
+		}, {});
+		feedbackItem.meta.base_models = baseModels;
+
+		let feedback = null;
+		if (message?.feedbackId) {
+			feedback = await updateFeedbackById(
+				localStorage.token,
+				message.feedbackId,
+				feedbackItem
+			).catch((error) => {
+				toast.error(error);
+			});
+		} else {
+			feedback = await createNewFeedback(localStorage.token, feedbackItem).catch((error) => {
+				toast.error(error);
+			});
+
+			if (feedback) {
+				updatedMessage.feedbackId = feedback.id;
+			}
+		}
+
+		console.log(updatedMessage);
+		saveMessage(message.id, updatedMessage);
+
+		await tick();
+
+		if (!details) {
+			showRateComment = true;
+
+			if (!updatedMessage.annotation?.tags) {
+				// attempt to generate tags
+				const tags = await generateTags(localStorage.token, message.model, messages, chatId).catch(
+					(error) => {
+						console.error(error);
+						return [];
+					}
+				);
+				console.log(tags);
+
+				if (tags) {
+					updatedMessage.annotation.tags = tags;
+					feedbackItem.data.tags = tags;
+
+					saveMessage(message.id, updatedMessage);
+					await updateFeedbackById(
+						localStorage.token,
+						updatedMessage.feedbackId,
+						feedbackItem
+					).catch((error) => {
+						toast.error(error);
+					});
+				}
+			}
+		}
+
+		feedbackLoading = false;
+	};
+
+	$: if (!edit) {
+		(async () => {
+			await tick();
+		})();
+	}
+
+	onMount(async () => {
+		// console.log('ResponseMessage mounted');
+
+		await tick();
+	});
+</script>
+
+{#key message.id}
+	<div
+		class=" flex w-full message-{message.id}"
+		id="message-{message.id}"
+		dir={$settings.chatDirection}
+	>
+		<div class={`flex-shrink-0 ${($settings?.chatDirection ?? 'LTR') === 'LTR' ? 'mr-3' : 'ml-3'}`}>
+			<ProfileImage
+				src={model?.info?.meta?.profile_image_url ??
+					($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
+				className={'size-8'}
+			/>
+		</div>
+
+		<div class="flex-auto w-0 pl-1">
+			<Name>
+				{model?.name ?? message.model}
+
+				{#if message.timestamp}
+					<span
+						class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium uppercase ml-0.5 -mt-0.5"
+					>
+						{dayjs(message.timestamp * 1000).format($i18n.t('h:mm a'))}
+					</span>
+				{/if}
+			</Name>
+
+			<div>
+				{#if message?.files && message.files?.filter((f) => f.type === 'image').length > 0}
+					<div class="my-2.5 w-full flex overflow-x-auto gap-2 flex-wrap">
+						{#each message.files as file}
+							<div>
+								{#if file.type === 'image'}
+									<Image src={file.url} alt={message.content} />
+								{/if}
+							</div>
+						{/each}
+					</div>
+				{/if}
+
+				<div class="chat-{message.role} w-full min-w-full markdown-prose">
+					<div>
+						{#if (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length > 0}
+							{@const status = (
+								message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]
+							).at(-1)}
+							<div class="status-description flex items-center gap-2 py-0.5">
+								{#if status?.done === false}
+									<div class="">
+										<Spinner className="size-4" />
+									</div>
+								{/if}
+
+								{#if status?.action === 'web_search' && status?.urls}
+									<WebSearchResults {status}>
+										<div class="flex flex-col justify-center -space-y-0.5">
+											<div
+												class="{status?.done === false
+													? 'shimmer'
+													: ''} text-base line-clamp-1 text-wrap"
+											>
+												{status?.description}
+											</div>
+										</div>
+									</WebSearchResults>
+								{:else}
+									<div class="flex flex-col justify-center -space-y-0.5">
+										<div
+											class="{status?.done === false
+												? 'shimmer'
+												: ''} text-gray-500 dark:text-gray-500 text-base line-clamp-1 text-wrap"
+										>
+											{status?.description}
+										</div>
+									</div>
+								{/if}
+							</div>
+						{/if}
+
+						{#if edit === true}
+							<div class="w-full bg-gray-50 dark:bg-gray-800 rounded-3xl px-5 py-3 my-2">
+								<textarea
+									id="message-edit-{message.id}"
+									bind:this={editTextAreaElement}
+									class=" bg-transparent outline-none w-full resize-none"
+									bind:value={editedContent}
+									on:input={(e) => {
+										e.target.style.height = '';
+										e.target.style.height = `${e.target.scrollHeight}px`;
+									}}
+									on:keydown={(e) => {
+										if (e.key === 'Escape') {
+											document.getElementById('close-edit-message-button')?.click();
+										}
+
+										const isCmdOrCtrlPressed = e.metaKey || e.ctrlKey;
+										const isEnterPressed = e.key === 'Enter';
+
+										if (isCmdOrCtrlPressed && isEnterPressed) {
+											document.getElementById('confirm-edit-message-button')?.click();
+										}
+									}}
+								/>
+
+								<div class=" mt-2 mb-1 flex justify-between text-sm font-medium">
+									<div>
+										<button
+											id="save-new-message-button"
+											class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
+											on:click={() => {
+												saveAsCopyHandler();
+											}}
+										>
+											{$i18n.t('Save As Copy')}
+										</button>
+									</div>
+
+									<div class="flex space-x-1.5">
+										<button
+											id="close-edit-message-button"
+											class="px-4 py-2 bg-white dark:bg-gray-900 hover:bg-gray-100 text-gray-800 dark:text-gray-100 transition rounded-3xl"
+											on:click={() => {
+												cancelEditMessage();
+											}}
+										>
+											{$i18n.t('Cancel')}
+										</button>
+
+										<button
+											id="confirm-edit-message-button"
+											class=" px-4 py-2 bg-gray-900 dark:bg-white hover:bg-gray-850 text-gray-100 dark:text-gray-800 transition rounded-3xl"
+											on:click={() => {
+												editMessageConfirmHandler();
+											}}
+										>
+											{$i18n.t('Save')}
+										</button>
+									</div>
+								</div>
+							</div>
+						{:else}
+							<div class="w-full flex flex-col relative" id="response-content-container">
+								{#if message.content === '' && !message.error}
+									<Skeleton />
+								{:else if message.content && message.error !== true}
+									<!-- always show message contents even if there's an error -->
+									<!-- unless message.error === true which is legacy error handling, where the error message is stored in message.content -->
+									<ContentRenderer
+										id={message.id}
+										content={message.content}
+										sources={message.sources}
+										floatingButtons={message?.done}
+										save={!readOnly}
+										{model}
+										onSourceClick={(e) => {
+											console.log(e);
+											const sourceButton = document.getElementById(`source-${e}`);
+
+											if (sourceButton) {
+												sourceButton.click();
+											}
+										}}
+										on:update={(e) => {
+											const { raw, oldContent, newContent } = e.detail;
+
+											history.messages[message.id].content = history.messages[
+												message.id
+											].content.replace(raw, raw.replace(oldContent, newContent));
+
+											updateChat();
+										}}
+										on:select={(e) => {
+											const { type, content } = e.detail;
+
+											if (type === 'explain') {
+												submitMessage(
+													message.id,
+													`Explain this section to me in more detail\n\n\`\`\`\n${content}\n\`\`\``
+												);
+											} else if (type === 'ask') {
+												const input = e.detail?.input ?? '';
+												submitMessage(message.id, `\`\`\`\n${content}\n\`\`\`\n${input}`);
+											}
+										}}
+									/>
+								{/if}
+
+								{#if message?.error}
+									<Error content={message?.error?.content ?? message.content} />
+								{/if}
+
+								{#if (message?.sources || message?.citations) && (model?.info?.meta?.capabilities?.citations ?? true)}
+									<Citations sources={message?.sources ?? message?.citations} />
+								{/if}
+
+								{#if message.code_executions}
+									<CodeExecutions codeExecutions={message.code_executions} />
+								{/if}
+							</div>
+						{/if}
+					</div>
+				</div>
+
+				{#if !edit}
+					{#if message.done || siblings.length > 1}
+						<div
+							class=" flex justify-start overflow-x-auto buttons text-gray-600 dark:text-gray-500 mt-0.5"
+						>
+							{#if siblings.length > 1}
+								<div class="flex self-center min-w-fit" dir="ltr">
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showPreviousMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M15.75 19.5 8.25 12l7.5-7.5"
+											/>
+										</svg>
+									</button>
+
+									<div
+										class="text-sm tracking-widest font-semibold self-center dark:text-gray-100 min-w-fit"
+									>
+										{siblings.indexOf(message.id) + 1}/{siblings.length}
+									</div>
+
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showNextMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="m8.25 4.5 7.5 7.5-7.5 7.5"
+											/>
+										</svg>
+									</button>
+								</div>
+							{/if}
+
+							{#if message.done}
+								{#if !readOnly}
+									{#if $user.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
+										<Tooltip content={$i18n.t('Edit')} placement="bottom">
+											<button
+												class="{isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
+												on:click={() => {
+													editMessageHandler();
+												}}
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													fill="none"
+													viewBox="0 0 24 24"
+													stroke-width="2.3"
+													stroke="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+									{/if}
+								{/if}
+
+								<Tooltip content={$i18n.t('Copy')} placement="bottom">
+									<button
+										class="{isLastMessage
+											? 'visible'
+											: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition copy-response-button"
+										on:click={() => {
+											copyToClipboard(message.content);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke-width="2.3"
+											stroke="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
+											/>
+										</svg>
+									</button>
+								</Tooltip>
+
+								<Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
+									<button
+										id="speak-button-{message.id}"
+										class="{isLastMessage
+											? 'visible'
+											: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
+										on:click={() => {
+											if (!loadingSpeech) {
+												toggleSpeakMessage();
+											}
+										}}
+									>
+										{#if loadingSpeech}
+											<svg
+												class=" w-4 h-4"
+												fill="currentColor"
+												viewBox="0 0 24 24"
+												xmlns="http://www.w3.org/2000/svg"
+											>
+												<style>
+													.spinner_S1WN {
+														animation: spinner_MGfb 0.8s linear infinite;
+														animation-delay: -0.8s;
+													}
+
+													.spinner_Km9P {
+														animation-delay: -0.65s;
+													}
+
+													.spinner_JApP {
+														animation-delay: -0.5s;
+													}
+
+													@keyframes spinner_MGfb {
+														93.75%,
+														100% {
+															opacity: 0.2;
+														}
+													}
+												</style>
+												<circle class="spinner_S1WN" cx="4" cy="12" r="3" />
+												<circle class="spinner_S1WN spinner_Km9P" cx="12" cy="12" r="3" />
+												<circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" />
+											</svg>
+										{:else if speaking}
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												fill="none"
+												viewBox="0 0 24 24"
+												stroke-width="2.3"
+												stroke="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													d="M17.25 9.75 19.5 12m0 0 2.25 2.25M19.5 12l2.25-2.25M19.5 12l-2.25 2.25m-10.5-6 4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z"
+												/>
+											</svg>
+										{:else}
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												fill="none"
+												viewBox="0 0 24 24"
+												stroke-width="2.3"
+												stroke="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
+												/>
+											</svg>
+										{/if}
+									</button>
+								</Tooltip>
+
+								{#if $config?.features.enable_image_generation && !readOnly}
+									<Tooltip content={$i18n.t('Generate Image')} placement="bottom">
+										<button
+											class="{isLastMessage
+												? 'visible'
+												: 'invisible group-hover:visible'}  p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
+											on:click={() => {
+												if (!generatingImage) {
+													generateImage(message);
+												}
+											}}
+										>
+											{#if generatingImage}
+												<svg
+													class=" w-4 h-4"
+													fill="currentColor"
+													viewBox="0 0 24 24"
+													xmlns="http://www.w3.org/2000/svg"
+												>
+													<style>
+														.spinner_S1WN {
+															animation: spinner_MGfb 0.8s linear infinite;
+															animation-delay: -0.8s;
+														}
+
+														.spinner_Km9P {
+															animation-delay: -0.65s;
+														}
+
+														.spinner_JApP {
+															animation-delay: -0.5s;
+														}
+
+														@keyframes spinner_MGfb {
+															93.75%,
+															100% {
+																opacity: 0.2;
+															}
+														}
+													</style>
+													<circle class="spinner_S1WN" cx="4" cy="12" r="3" />
+													<circle class="spinner_S1WN spinner_Km9P" cx="12" cy="12" r="3" />
+													<circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3" />
+												</svg>
+											{:else}
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													fill="none"
+													viewBox="0 0 24 24"
+													stroke-width="2.3"
+													stroke="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
+													/>
+												</svg>
+											{/if}
+										</button>
+									</Tooltip>
+								{/if}
+
+								{#if message.info}
+									<Tooltip
+										content={message.info.openai
+											? message.info.usage
+												? `<pre>${sanitizeResponseContent(
+														JSON.stringify(message.info.usage, null, 2)
+															.replace(/"([^(")"]+)":/g, '$1:')
+															.slice(1, -1)
+															.split('\n')
+															.map((line) => line.slice(2))
+															.map((line) => (line.endsWith(',') ? line.slice(0, -1) : line))
+															.join('\n')
+													)}</pre>`
+												: `prompt_tokens: ${message.info.prompt_tokens ?? 'N/A'}<br/>
+													completion_tokens: ${message.info.completion_tokens ?? 'N/A'}<br/>
+													total_tokens: ${message.info.total_tokens ?? 'N/A'}`
+											: `response_token/s: ${
+													`${
+														Math.round(
+															((message.info.eval_count ?? 0) /
+																((message.info.eval_duration ?? 0) / 1000000000)) *
+																100
+														) / 100
+													} tokens` ?? 'N/A'
+												}<br/>
+					prompt_token/s: ${
+						Math.round(
+							((message.info.prompt_eval_count ?? 0) /
+								((message.info.prompt_eval_duration ?? 0) / 1000000000)) *
+								100
+						) / 100 ?? 'N/A'
+					} tokens<br/>
+		            total_duration: ${
+									Math.round(((message.info.total_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A'
+								}ms<br/>
+		            load_duration: ${
+									Math.round(((message.info.load_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A'
+								}ms<br/>
+		            prompt_eval_count: ${message.info.prompt_eval_count ?? 'N/A'}<br/>
+		            prompt_eval_duration: ${
+									Math.round(((message.info.prompt_eval_duration ?? 0) / 1000000) * 100) / 100 ??
+									'N/A'
+								}ms<br/>
+		            eval_count: ${message.info.eval_count ?? 'N/A'}<br/>
+		            eval_duration: ${
+									Math.round(((message.info.eval_duration ?? 0) / 1000000) * 100) / 100 ?? 'N/A'
+								}ms<br/>
+		            approximate_total: ${approximateToHumanReadable(message.info.total_duration ?? 0)}`}
+										placement="top"
+									>
+										<Tooltip content={$i18n.t('Generation Info')} placement="bottom">
+											<button
+												class=" {isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
+												on:click={() => {
+													console.log(message);
+												}}
+												id="info-{message.id}"
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													fill="none"
+													viewBox="0 0 24 24"
+													stroke-width="2.3"
+													stroke="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+									</Tooltip>
+								{/if}
+
+								{#if !readOnly}
+									{#if $config?.features.enable_message_rating ?? true}
+										<Tooltip content={$i18n.t('Good Response')} placement="bottom">
+											<button
+												class="{isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
+													message?.annotation?.rating ?? ''
+												).toString() === '1'
+													? 'bg-gray-100 dark:bg-gray-800'
+													: ''} dark:hover:text-white hover:text-black transition disabled:cursor-progress disabled:hover:bg-transparent"
+												disabled={feedbackLoading}
+												on:click={async () => {
+													await feedbackHandler(1);
+													window.setTimeout(() => {
+														document
+															.getElementById(`message-feedback-${message.id}`)
+															?.scrollIntoView();
+													}, 0);
+												}}
+											>
+												<svg
+													stroke="currentColor"
+													fill="none"
+													stroke-width="2.3"
+													viewBox="0 0 24 24"
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													class="w-4 h-4"
+													xmlns="http://www.w3.org/2000/svg"
+												>
+													<path
+														d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+
+										<Tooltip content={$i18n.t('Bad Response')} placement="bottom">
+											<button
+												class="{isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
+													message?.annotation?.rating ?? ''
+												).toString() === '-1'
+													? 'bg-gray-100 dark:bg-gray-800'
+													: ''} dark:hover:text-white hover:text-black transition disabled:cursor-progress disabled:hover:bg-transparent"
+												disabled={feedbackLoading}
+												on:click={async () => {
+													await feedbackHandler(-1);
+													window.setTimeout(() => {
+														document
+															.getElementById(`message-feedback-${message.id}`)
+															?.scrollIntoView();
+													}, 0);
+												}}
+											>
+												<svg
+													stroke="currentColor"
+													fill="none"
+													stroke-width="2.3"
+													viewBox="0 0 24 24"
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													class="w-4 h-4"
+													xmlns="http://www.w3.org/2000/svg"
+												>
+													<path
+														d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+									{/if}
+
+									{#if isLastMessage}
+										<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
+											<button
+												type="button"
+												id="continue-response-button"
+												class="{isLastMessage
+													? 'visible'
+													: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
+												on:click={() => {
+													continueResponse();
+												}}
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													fill="none"
+													viewBox="0 0 24 24"
+													stroke-width="2.3"
+													stroke="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
+													/>
+													<path
+														stroke-linecap="round"
+														stroke-linejoin="round"
+														d="M15.91 11.672a.375.375 0 0 1 0 .656l-5.603 3.113a.375.375 0 0 1-.557-.328V8.887c0-.286.307-.466.557-.327l5.603 3.112Z"
+													/>
+												</svg>
+											</button>
+										</Tooltip>
+									{/if}
+
+									<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
+										<button
+											type="button"
+											class="{isLastMessage
+												? 'visible'
+												: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
+											on:click={() => {
+												showRateComment = false;
+												regenerateResponse(message);
+
+												(model?.actions ?? []).forEach((action) => {
+													dispatch('action', {
+														id: action.id,
+														event: {
+															id: 'regenerate-response',
+															data: {
+																messageId: message.id
+															}
+														}
+													});
+												});
+											}}
+										>
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												fill="none"
+												viewBox="0 0 24 24"
+												stroke-width="2.3"
+												stroke="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
+												/>
+											</svg>
+										</button>
+									</Tooltip>
+
+									{#if isLastMessage}
+										{#each model?.actions ?? [] as action}
+											<Tooltip content={action.name} placement="bottom">
+												<button
+													type="button"
+													class="{isLastMessage
+														? 'visible'
+														: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
+													on:click={() => {
+														actionMessage(action.id, message);
+													}}
+												>
+													{#if action.icon_url}
+														<img
+															src={action.icon_url}
+															class="w-4 h-4 {action.icon_url.includes('svg')
+																? 'dark:invert-[80%]'
+																: ''}"
+															style="fill: currentColor;"
+															alt={action.name}
+														/>
+													{:else}
+														<Sparkles strokeWidth="2.1" className="size-4" />
+													{/if}
+												</button>
+											</Tooltip>
+										{/each}
+									{/if}
+								{/if}
+							{/if}
+						</div>
+					{/if}
+
+					{#if message.done && showRateComment}
+						<RateComment
+							bind:message
+							bind:show={showRateComment}
+							on:save={async (e) => {
+								await feedbackHandler(null, {
+									...e.detail
+								});
+							}}
+						/>
+					{/if}
+				{/if}
+			</div>
+		</div>
+	</div>
+{/key}
+
+<style>
+	.buttons::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.buttons {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	@keyframes shimmer {
+		0% {
+			background-position: 200% 0;
+		}
+		100% {
+			background-position: -200% 0;
+		}
+	}
+
+	.shimmer {
+		background: linear-gradient(90deg, #9a9b9e 25%, #2a2929 50%, #9a9b9e 75%);
+		background-size: 200% 100%;
+		background-clip: text;
+		-webkit-background-clip: text;
+		-webkit-text-fill-color: transparent;
+		animation: shimmer 4s linear infinite;
+		color: #818286; /* Fallback color */
+	}
+
+	:global(.dark) .shimmer {
+		background: linear-gradient(90deg, #818286 25%, #eae5e5 50%, #818286 75%);
+		background-size: 200% 100%;
+		background-clip: text;
+		-webkit-background-clip: text;
+		-webkit-text-fill-color: transparent;
+		animation: shimmer 4s linear infinite;
+		color: #a1a3a7; /* Darker fallback color for dark mode */
+	}
+
+	@keyframes smoothFadeIn {
+		0% {
+			opacity: 0;
+			transform: translateY(-10px);
+		}
+		100% {
+			opacity: 1;
+			transform: translateY(0);
+		}
+	}
+
+	.status-description {
+		animation: smoothFadeIn 0.2s forwards;
+	}
+</style>
diff --git a/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bb1a5a17d23a181c450b9ca0185d1a86907d246f
--- /dev/null
+++ b/src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte
@@ -0,0 +1,93 @@
+<script lang="ts">
+	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
+	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
+	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
+
+	export let status = { urls: [], query: '' };
+	let state = false;
+</script>
+
+<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 mb-1.5"
+		slot="content"
+	>
+		{#if status?.query}
+			<a
+				href="https://www.google.com/search?q={status.query}"
+				target="_blank"
+				class="flex w-full items-center p-3 px-4 border-b border-gray-300/30 dark:border-gray-700/50 group/item justify-between font-normal text-gray-800 dark:text-gray-300 no-underline"
+			>
+				<div class="flex gap-2 items-center">
+					<MagnifyingGlass />
+
+					<div class=" line-clamp-1">
+						{status.query}
+					</div>
+				</div>
+
+				<div
+					class=" ml-1 text-white dark:text-gray-900 group-hover/item:text-gray-600 dark:group-hover/item:text-white transition"
+				>
+					<!--  -->
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+			</a>
+		{/if}
+
+		{#each status.urls as url, urlIdx}
+			<a
+				href={url}
+				target="_blank"
+				class="flex w-full items-center p-3 px-4 {urlIdx === status.urls.length - 1
+					? ''
+					: 'border-b border-gray-300/30 dark:border-gray-700/50'} group/item justify-between font-normal text-gray-800 dark:text-gray-300"
+			>
+				<div class=" line-clamp-1">
+					{url}
+				</div>
+
+				<div
+					class=" ml-1 text-white dark:text-gray-900 group-hover/item:text-gray-600 dark:group-hover/item:text-white transition"
+				>
+					<!--  -->
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+			</a>
+		{/each}
+	</div>
+</Collapsible>
diff --git a/src/lib/components/chat/Messages/Skeleton.svelte b/src/lib/components/chat/Messages/Skeleton.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..364d0ecf7fcb62571629d4d1ce61d23722fdc3cf
--- /dev/null
+++ b/src/lib/components/chat/Messages/Skeleton.svelte
@@ -0,0 +1,19 @@
+<div class="w-full mt-2 mb-2">
+	<div class="animate-pulse flex w-full">
+		<div class="space-y-2 w-full">
+			<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded mr-14" />
+
+			<div class="grid grid-cols-3 gap-4">
+				<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-2" />
+				<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1" />
+			</div>
+			<div class="grid grid-cols-4 gap-4">
+				<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1" />
+				<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-2" />
+				<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded col-span-1 mr-4" />
+			</div>
+
+			<div class="h-2 bg-gray-200 dark:bg-gray-600 rounded" />
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Messages/UserMessage.svelte b/src/lib/components/chat/Messages/UserMessage.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7f9e78226f166ccc65b7de90849aad25cd962345
--- /dev/null
+++ b/src/lib/components/chat/Messages/UserMessage.svelte
@@ -0,0 +1,415 @@
+<script lang="ts">
+	import dayjs from 'dayjs';
+	import { toast } from 'svelte-sonner';
+	import { tick, getContext, onMount } from 'svelte';
+
+	import { models, settings } from '$lib/stores';
+	import { user as _user } from '$lib/stores';
+	import { copyToClipboard as _copyToClipboard } from '$lib/utils';
+
+	import Name from './Name.svelte';
+	import ProfileImage from './ProfileImage.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import FileItem from '$lib/components/common/FileItem.svelte';
+	import Markdown from './Markdown.svelte';
+	import Image from '$lib/components/common/Image.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let user;
+
+	export let history;
+	export let messageId;
+
+	export let siblings;
+
+	export let showPreviousMessage: Function;
+	export let showNextMessage: Function;
+
+	export let editMessage: Function;
+	export let deleteMessage: Function;
+
+	export let isFirstMessage: boolean;
+	export let readOnly: boolean;
+
+	let edit = false;
+	let editedContent = '';
+	let messageEditTextAreaElement: HTMLTextAreaElement;
+
+	let message = JSON.parse(JSON.stringify(history.messages[messageId]));
+	$: if (history.messages) {
+		if (JSON.stringify(message) !== JSON.stringify(history.messages[messageId])) {
+			message = JSON.parse(JSON.stringify(history.messages[messageId]));
+		}
+	}
+
+	const copyToClipboard = async (text) => {
+		const res = await _copyToClipboard(text);
+		if (res) {
+			toast.success($i18n.t('Copying to clipboard was successful!'));
+		}
+	};
+
+	const editMessageHandler = async () => {
+		edit = true;
+		editedContent = message.content;
+
+		await tick();
+
+		messageEditTextAreaElement.style.height = '';
+		messageEditTextAreaElement.style.height = `${messageEditTextAreaElement.scrollHeight}px`;
+
+		messageEditTextAreaElement?.focus();
+	};
+
+	const editMessageConfirmHandler = async (submit = true) => {
+		editMessage(message.id, editedContent, submit);
+
+		edit = false;
+		editedContent = '';
+	};
+
+	const cancelEditMessage = () => {
+		edit = false;
+		editedContent = '';
+	};
+
+	const deleteMessageHandler = async () => {
+		deleteMessage(message.id);
+	};
+
+	onMount(() => {
+		// console.log('UserMessage mounted');
+	});
+</script>
+
+<div class=" flex w-full user-message" dir={$settings.chatDirection} id="message-{message.id}">
+	{#if !($settings?.chatBubble ?? true)}
+		<div class={`flex-shrink-0 ${($settings?.chatDirection ?? 'LTR') === 'LTR' ? 'mr-3' : 'ml-3'}`}>
+			<ProfileImage
+				src={message.user
+					? ($models.find((m) => m.id === message.user)?.info?.meta?.profile_image_url ??
+						'/user.png')
+					: (user?.profile_image_url ?? '/user.png')}
+				className={'size-8'}
+			/>
+		</div>
+	{/if}
+	<div class="flex-auto w-0 max-w-full pl-1">
+		{#if !($settings?.chatBubble ?? true)}
+			<div>
+				<Name>
+					{#if message.user}
+						{$i18n.t('You')}
+						<span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
+					{:else if $settings.showUsername || $_user.name !== user.name}
+						{user.name}
+					{:else}
+						{$i18n.t('You')}
+					{/if}
+
+					{#if message.timestamp}
+						<span
+							class=" invisible group-hover:visible text-gray-400 text-xs font-medium uppercase ml-0.5 -mt-0.5"
+						>
+							{dayjs(message.timestamp * 1000).format($i18n.t('h:mm a'))}
+						</span>
+					{/if}
+				</Name>
+			</div>
+		{/if}
+
+		<div class="chat-{message.role} w-full min-w-full markdown-prose">
+			{#if message.files}
+				<div class="mt-2.5 mb-1 w-full flex flex-col justify-end overflow-x-auto gap-1 flex-wrap">
+					{#each message.files as file}
+						<div class={($settings?.chatBubble ?? true) ? 'self-end' : ''}>
+							{#if file.type === 'image'}
+								<Image src={file.url} imageClassName=" max-h-96 rounded-lg" />
+							{:else}
+								<FileItem
+									item={file}
+									url={file.url}
+									name={file.name}
+									type={file.type}
+									size={file?.size}
+									colorClassName="bg-white dark:bg-gray-850 "
+								/>
+							{/if}
+						</div>
+					{/each}
+				</div>
+			{/if}
+
+			{#if edit === true}
+				<div class=" w-full bg-gray-50 dark:bg-gray-800 rounded-3xl px-5 py-3 mb-2">
+					<div class="max-h-96 overflow-auto">
+						<textarea
+							id="message-edit-{message.id}"
+							bind:this={messageEditTextAreaElement}
+							class=" bg-transparent outline-none w-full resize-none"
+							bind:value={editedContent}
+							on:input={(e) => {
+								e.target.style.height = '';
+								e.target.style.height = `${e.target.scrollHeight}px`;
+							}}
+							on:keydown={(e) => {
+								if (e.key === 'Escape') {
+									document.getElementById('close-edit-message-button')?.click();
+								}
+
+								const isCmdOrCtrlPressed = e.metaKey || e.ctrlKey;
+								const isEnterPressed = e.key === 'Enter';
+
+								if (isCmdOrCtrlPressed && isEnterPressed) {
+									document.getElementById('confirm-edit-message-button')?.click();
+								}
+							}}
+						/>
+					</div>
+
+					<div class=" mt-2 mb-1 flex justify-between text-sm font-medium">
+						<div>
+							<button
+								id="save-edit-message-button"
+								class=" px-4 py-2 bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 border dark:border-gray-700 text-gray-700 dark:text-gray-200 transition rounded-3xl"
+								on:click={() => {
+									editMessageConfirmHandler(false);
+								}}
+							>
+								{$i18n.t('Save')}
+							</button>
+						</div>
+
+						<div class="flex space-x-1.5">
+							<button
+								id="close-edit-message-button"
+								class="px-4 py-2 bg-white dark:bg-gray-900 hover:bg-gray-100 text-gray-800 dark:text-gray-100 transition rounded-3xl"
+								on:click={() => {
+									cancelEditMessage();
+								}}
+							>
+								{$i18n.t('Cancel')}
+							</button>
+
+							<button
+								id="confirm-edit-message-button"
+								class=" px-4 py-2 bg-gray-900 dark:bg-white hover:bg-gray-850 text-gray-100 dark:text-gray-800 transition rounded-3xl"
+								on:click={() => {
+									editMessageConfirmHandler();
+								}}
+							>
+								{$i18n.t('Send')}
+							</button>
+						</div>
+					</div>
+				</div>
+			{:else}
+				<div class="w-full">
+					<div class="flex {($settings?.chatBubble ?? true) ? 'justify-end pb-1' : 'w-full'}">
+						<div
+							class="rounded-3xl {($settings?.chatBubble ?? true)
+								? `max-w-[90%] px-5 py-2  bg-gray-50 dark:bg-gray-850 ${
+										message.files ? 'rounded-tr-lg' : ''
+									}`
+								: ' w-full'}"
+						>
+							{#if message.content}
+								<Markdown id={message.id} content={message.content} />
+							{/if}
+						</div>
+					</div>
+
+					<div
+						class=" flex {($settings?.chatBubble ?? true)
+							? 'justify-end'
+							: ''}  text-gray-600 dark:text-gray-500"
+					>
+						{#if !($settings?.chatBubble ?? true)}
+							{#if siblings.length > 1}
+								<div class="flex self-center" dir="ltr">
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showPreviousMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M15.75 19.5 8.25 12l7.5-7.5"
+											/>
+										</svg>
+									</button>
+
+									<div class="text-sm tracking-widest font-semibold self-center dark:text-gray-100">
+										{siblings.indexOf(message.id) + 1}/{siblings.length}
+									</div>
+
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showNextMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="m8.25 4.5 7.5 7.5-7.5 7.5"
+											/>
+										</svg>
+									</button>
+								</div>
+							{/if}
+						{/if}
+						{#if !readOnly}
+							<Tooltip content={$i18n.t('Edit')} placement="bottom">
+								<button
+									class="invisible group-hover:visible p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition edit-user-message-button"
+									on:click={() => {
+										editMessageHandler();
+									}}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="2.3"
+										stroke="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+						{/if}
+
+						<Tooltip content={$i18n.t('Copy')} placement="bottom">
+							<button
+								class="invisible group-hover:visible p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
+								on:click={() => {
+									copyToClipboard(message.content);
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="2.3"
+									stroke="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
+									/>
+								</svg>
+							</button>
+						</Tooltip>
+
+						{#if !isFirstMessage && !readOnly}
+							<Tooltip content={$i18n.t('Delete')} placement="bottom">
+								<button
+									class="invisible group-hover:visible p-1 rounded dark:hover:text-white hover:text-black transition"
+									on:click={() => {
+										deleteMessageHandler();
+									}}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="2"
+										stroke="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+						{/if}
+
+						{#if $settings?.chatBubble ?? true}
+							{#if siblings.length > 1}
+								<div class="flex self-center" dir="ltr">
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showPreviousMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M15.75 19.5 8.25 12l7.5-7.5"
+											/>
+										</svg>
+									</button>
+
+									<div class="text-sm tracking-widest font-semibold self-center dark:text-gray-100">
+										{siblings.indexOf(message.id) + 1}/{siblings.length}
+									</div>
+
+									<button
+										class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
+										on:click={() => {
+											showNextMessage(message);
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke="currentColor"
+											stroke-width="2.5"
+											class="size-3.5"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="m8.25 4.5 7.5 7.5-7.5 7.5"
+											/>
+										</svg>
+									</button>
+								</div>
+							{/if}
+						{/if}
+					</div>
+				</div>
+			{/if}
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/chat/ModelSelector.svelte b/src/lib/components/chat/ModelSelector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9b77cd8ce298b70f17ee5607baa0e7e9f1f8c8c4
--- /dev/null
+++ b/src/lib/components/chat/ModelSelector.svelte
@@ -0,0 +1,117 @@
+<script lang="ts">
+	import { models, showSettings, settings, user, mobile, config } from '$lib/stores';
+	import { onMount, tick, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import Selector from './ModelSelector/Selector.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+
+	import { updateUserSettings } from '$lib/apis/users';
+	const i18n = getContext('i18n');
+
+	export let selectedModels = [''];
+	export let disabled = false;
+
+	export let showSetDefault = true;
+
+	const saveDefaultModel = async () => {
+		const hasEmptyModel = selectedModels.filter((it) => it === '');
+		if (hasEmptyModel.length) {
+			toast.error($i18n.t('Choose a model before saving...'));
+			return;
+		}
+		settings.set({ ...$settings, models: selectedModels });
+		await updateUserSettings(localStorage.token, { ui: $settings });
+
+		toast.success($i18n.t('Default model updated'));
+	};
+
+	$: if (selectedModels.length > 0 && $models.length > 0) {
+		selectedModels = selectedModels.map((model) =>
+			$models.map((m) => m.id).includes(model) ? model : ''
+		);
+	}
+</script>
+
+<div class="flex flex-col w-full items-start">
+	{#each selectedModels as selectedModel, selectedModelIdx}
+		<div class="flex w-full max-w-fit">
+			<div class="overflow-hidden w-full">
+				<div class="mr-1 max-w-full">
+					<Selector
+						id={`${selectedModelIdx}`}
+						placeholder={$i18n.t('Select a model')}
+						items={$models.map((model) => ({
+							value: model.id,
+							label: model.name,
+							model: model
+						}))}
+						showTemporaryChatControl={$user.role === 'user'
+							? ($user?.permissions?.chat?.temporary ?? true)
+							: true}
+						bind:value={selectedModel}
+					/>
+				</div>
+			</div>
+
+			{#if selectedModelIdx === 0}
+				<div
+					class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
+				>
+					<Tooltip content={$i18n.t('Add Model')}>
+						<button
+							class=" "
+							{disabled}
+							on:click={() => {
+								selectedModels = [...selectedModels, ''];
+							}}
+							aria-label="Add Model"
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								fill="none"
+								viewBox="0 0 24 24"
+								stroke-width="2"
+								stroke="currentColor"
+								class="size-3.5"
+							>
+								<path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m6-6H6" />
+							</svg>
+						</button>
+					</Tooltip>
+				</div>
+			{:else}
+				<div
+					class="  self-center mx-1 disabled:text-gray-600 disabled:hover:text-gray-600 -translate-y-[0.5px]"
+				>
+					<Tooltip content={$i18n.t('Remove Model')}>
+						<button
+							{disabled}
+							on:click={() => {
+								selectedModels.splice(selectedModelIdx, 1);
+								selectedModels = selectedModels;
+							}}
+							aria-label="Remove Model"
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								fill="none"
+								viewBox="0 0 24 24"
+								stroke-width="2"
+								stroke="currentColor"
+								class="size-3"
+							>
+								<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 12h-15" />
+							</svg>
+						</button>
+					</Tooltip>
+				</div>
+			{/if}
+		</div>
+	{/each}
+</div>
+
+{#if showSetDefault}
+	<div class=" absolute text-left mt-[1px] ml-1 text-[0.7rem] text-gray-500 font-primary">
+		<button on:click={saveDefaultModel}> {$i18n.t('Set as default')}</button>
+	</div>
+{/if}
diff --git a/src/lib/components/chat/ModelSelector/Selector.svelte b/src/lib/components/chat/ModelSelector/Selector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9e6b1d0fe31186bab8244a345dc54ac279f58c36
--- /dev/null
+++ b/src/lib/components/chat/ModelSelector/Selector.svelte
@@ -0,0 +1,588 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { marked } from 'marked';
+	import Fuse from 'fuse.js';
+
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+
+	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
+	import Check from '$lib/components/icons/Check.svelte';
+	import Search from '$lib/components/icons/Search.svelte';
+
+	import { deleteModel, getOllamaVersion, pullModel } from '$lib/apis/ollama';
+
+	import { user, MODEL_DOWNLOAD_POOL, models, mobile, temporaryChatEnabled } from '$lib/stores';
+	import { toast } from 'svelte-sonner';
+	import { capitalizeFirstLetter, sanitizeResponseContent, splitStream } from '$lib/utils';
+	import { getModels } from '$lib/apis';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import ChatBubbleOval from '$lib/components/icons/ChatBubbleOval.svelte';
+	import { goto } from '$app/navigation';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let id = '';
+	export let value = '';
+	export let placeholder = 'Select a model';
+	export let searchEnabled = true;
+	export let searchPlaceholder = $i18n.t('Search a model');
+
+	export let showTemporaryChatControl = false;
+
+	export let items: {
+		label: string;
+		value: string;
+		model: Model;
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
+		[key: string]: any;
+	}[] = [];
+
+	export let className = 'w-[32rem]';
+	export let triggerClassName = 'text-lg';
+
+	let show = false;
+
+	let selectedModel = '';
+	$: selectedModel = items.find((item) => item.value === value) ?? '';
+
+	let searchValue = '';
+	let ollamaVersion = null;
+
+	let selectedModelIdx = 0;
+
+	const fuse = new Fuse(
+		items.map((item) => {
+			const _item = {
+				...item,
+				modelName: item.model?.name,
+				tags: item.model?.info?.meta?.tags?.map((tag) => tag.name).join(' '),
+				desc: item.model?.info?.meta?.description
+			};
+			return _item;
+		}),
+		{
+			keys: ['value', 'tags', 'modelName'],
+			threshold: 0.4
+		}
+	);
+
+	$: filteredItems = searchValue
+		? fuse.search(searchValue).map((e) => {
+				return e.item;
+			})
+		: items;
+
+	const pullModelHandler = async () => {
+		const sanitizedModelTag = searchValue.trim().replace(/^ollama\s+(run|pull)\s+/, '');
+
+		console.log($MODEL_DOWNLOAD_POOL);
+		if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag]) {
+			toast.error(
+				$i18n.t(`Model '{{modelTag}}' is already in queue for downloading.`, {
+					modelTag: sanitizedModelTag
+				})
+			);
+			return;
+		}
+		if (Object.keys($MODEL_DOWNLOAD_POOL).length === 3) {
+			toast.error(
+				$i18n.t('Maximum of 3 models can be downloaded simultaneously. Please try again later.')
+			);
+			return;
+		}
+
+		const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch(
+			(error) => {
+				toast.error(error);
+				return null;
+			}
+		);
+
+		if (res) {
+			const reader = res.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL,
+				[sanitizedModelTag]: {
+					...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+					abortController: controller,
+					reader,
+					done: false
+				}
+			});
+
+			while (true) {
+				try {
+					const { value, done } = await reader.read();
+					if (done) break;
+
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							let data = JSON.parse(line);
+							console.log(data);
+							if (data.error) {
+								throw data.error;
+							}
+							if (data.detail) {
+								throw data.detail;
+							}
+
+							if (data.status) {
+								if (data.digest) {
+									let downloadProgress = 0;
+									if (data.completed) {
+										downloadProgress = Math.round((data.completed / data.total) * 1000) / 10;
+									} else {
+										downloadProgress = 100;
+									}
+
+									MODEL_DOWNLOAD_POOL.set({
+										...$MODEL_DOWNLOAD_POOL,
+										[sanitizedModelTag]: {
+											...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+											pullProgress: downloadProgress,
+											digest: data.digest
+										}
+									});
+								} else {
+									toast.success(data.status);
+
+									MODEL_DOWNLOAD_POOL.set({
+										...$MODEL_DOWNLOAD_POOL,
+										[sanitizedModelTag]: {
+											...$MODEL_DOWNLOAD_POOL[sanitizedModelTag],
+											done: data.status === 'success'
+										}
+									});
+								}
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+					if (typeof error !== 'string') {
+						error = error.message;
+					}
+
+					toast.error(error);
+					// opts.callback({ success: false, error, modelName: opts.modelName });
+					break;
+				}
+			}
+
+			if ($MODEL_DOWNLOAD_POOL[sanitizedModelTag].done) {
+				toast.success(
+					$i18n.t(`Model '{{modelName}}' has been successfully downloaded.`, {
+						modelName: sanitizedModelTag
+					})
+				);
+
+				models.set(await getModels(localStorage.token));
+			} else {
+				toast.error($i18n.t('Download canceled'));
+			}
+
+			delete $MODEL_DOWNLOAD_POOL[sanitizedModelTag];
+
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL
+			});
+		}
+	};
+
+	onMount(async () => {
+		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
+	});
+
+	const cancelModelPullHandler = async (model: string) => {
+		const { reader, abortController } = $MODEL_DOWNLOAD_POOL[model];
+		if (abortController) {
+			abortController.abort();
+		}
+		if (reader) {
+			await reader.cancel();
+			delete $MODEL_DOWNLOAD_POOL[model];
+			MODEL_DOWNLOAD_POOL.set({
+				...$MODEL_DOWNLOAD_POOL
+			});
+			await deleteModel(localStorage.token, model);
+			toast.success(`${model} download has been canceled`);
+		}
+	};
+</script>
+
+<DropdownMenu.Root
+	bind:open={show}
+	onOpenChange={async () => {
+		searchValue = '';
+		selectedModelIdx = 0;
+		window.setTimeout(() => document.getElementById('model-search-input')?.focus(), 0);
+	}}
+	closeFocus={false}
+>
+	<DropdownMenu.Trigger
+		class="relative w-full font-primary"
+		aria-label={placeholder}
+		id="model-selector-{id}-button"
+	>
+		<div
+			class="flex w-full text-left px-0.5 outline-none bg-transparent truncate {triggerClassName} justify-between font-medium placeholder-gray-400 focus:outline-none"
+		>
+			{#if selectedModel}
+				{selectedModel.label}
+			{:else}
+				{placeholder}
+			{/if}
+			<ChevronDown className=" self-center ml-2 size-3" strokeWidth="2.5" />
+		</div>
+	</DropdownMenu.Trigger>
+
+	<DropdownMenu.Content
+		class=" z-40 {$mobile
+			? `w-full`
+			: `${className}`} max-w-[calc(100vw-1rem)] justify-start rounded-xl  bg-white dark:bg-gray-850 dark:text-white shadow-lg  outline-none"
+		transition={flyAndScale}
+		side={$mobile ? 'bottom' : 'bottom-start'}
+		sideOffset={3}
+	>
+		<slot>
+			{#if searchEnabled}
+				<div class="flex items-center gap-2.5 px-5 mt-3.5 mb-3">
+					<Search className="size-4" strokeWidth="2.5" />
+
+					<input
+						id="model-search-input"
+						bind:value={searchValue}
+						class="w-full text-sm bg-transparent outline-none"
+						placeholder={searchPlaceholder}
+						autocomplete="off"
+						on:keydown={(e) => {
+							if (e.code === 'Enter' && filteredItems.length > 0) {
+								value = filteredItems[selectedModelIdx].value;
+								show = false;
+								return; // dont need to scroll on selection
+							} else if (e.code === 'ArrowDown') {
+								selectedModelIdx = Math.min(selectedModelIdx + 1, filteredItems.length - 1);
+							} else if (e.code === 'ArrowUp') {
+								selectedModelIdx = Math.max(selectedModelIdx - 1, 0);
+							} else {
+								// if the user types something, reset to the top selection.
+								selectedModelIdx = 0;
+							}
+
+							const item = document.querySelector(`[data-arrow-selected="true"]`);
+							item?.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
+						}}
+					/>
+				</div>
+
+				<hr class="border-gray-50 dark:border-gray-800" />
+			{/if}
+
+			<div class="px-3 my-2 max-h-64 overflow-y-auto scrollbar-hidden group">
+				{#each filteredItems as item, index}
+					<button
+						aria-label="model-item"
+						class="flex w-full text-left font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted {index ===
+						selectedModelIdx
+							? 'bg-gray-100 dark:bg-gray-800 group-hover:bg-transparent'
+							: ''}"
+						data-arrow-selected={index === selectedModelIdx}
+						on:click={() => {
+							value = item.value;
+							selectedModelIdx = index;
+
+							show = false;
+						}}
+					>
+						<div class="flex flex-col">
+							{#if $mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
+								<div class="flex gap-0.5 self-start h-full mb-1.5 -translate-x-1">
+									{#each item.model?.info?.meta.tags as tag}
+										<div
+											class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+										>
+											{tag.name}
+										</div>
+									{/each}
+								</div>
+							{/if}
+							<div class="flex items-center gap-2">
+								<div class="flex items-center min-w-fit">
+									<div class="line-clamp-1">
+										<div class="flex items-center min-w-fit">
+											<Tooltip
+												content={$user?.role === 'admin' ? (item?.value ?? '') : ''}
+												placement="top-start"
+											>
+												<img
+													src={item.model?.info?.meta?.profile_image_url ?? '/static/favicon.png'}
+													alt="Model"
+													class="rounded-full size-5 flex items-center mr-2"
+												/>
+												{item.label}
+											</Tooltip>
+										</div>
+									</div>
+									{#if item.model.owned_by === 'ollama' && (item.model.ollama?.details?.parameter_size ?? '') !== ''}
+										<div class="flex ml-1 items-center translate-y-[0.5px]">
+											<Tooltip
+												content={`${
+													item.model.ollama?.details?.quantization_level
+														? item.model.ollama?.details?.quantization_level + ' '
+														: ''
+												}${
+													item.model.ollama?.size
+														? `(${(item.model.ollama?.size / 1024 ** 3).toFixed(1)}GB)`
+														: ''
+												}`}
+												className="self-end"
+											>
+												<span
+													class=" text-xs font-medium text-gray-600 dark:text-gray-400 line-clamp-1"
+													>{item.model.ollama?.details?.parameter_size ?? ''}</span
+												>
+											</Tooltip>
+										</div>
+									{/if}
+								</div>
+
+								<!-- {JSON.stringify(item.info)} -->
+
+								{#if item.model.owned_by === 'openai'}
+									<Tooltip content={`${'External'}`}>
+										<div class="translate-y-[1px]">
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												viewBox="0 0 16 16"
+												fill="currentColor"
+												class="size-3"
+											>
+												<path
+													fill-rule="evenodd"
+													d="M8.914 6.025a.75.75 0 0 1 1.06 0 3.5 3.5 0 0 1 0 4.95l-2 2a3.5 3.5 0 0 1-5.396-4.402.75.75 0 0 1 1.251.827 2 2 0 0 0 3.085 2.514l2-2a2 2 0 0 0 0-2.828.75.75 0 0 1 0-1.06Z"
+													clip-rule="evenodd"
+												/>
+												<path
+													fill-rule="evenodd"
+													d="M7.086 9.975a.75.75 0 0 1-1.06 0 3.5 3.5 0 0 1 0-4.95l2-2a3.5 3.5 0 0 1 5.396 4.402.75.75 0 0 1-1.251-.827 2 2 0 0 0-3.085-2.514l-2 2a2 2 0 0 0 0 2.828.75.75 0 0 1 0 1.06Z"
+													clip-rule="evenodd"
+												/>
+											</svg>
+										</div>
+									</Tooltip>
+								{/if}
+
+								{#if item.model?.info?.meta?.description}
+									<Tooltip
+										content={`${marked.parse(
+											sanitizeResponseContent(item.model?.info?.meta?.description).replaceAll(
+												'\n',
+												'<br>'
+											)
+										)}`}
+									>
+										<div class=" translate-y-[1px]">
+											<svg
+												xmlns="http://www.w3.org/2000/svg"
+												fill="none"
+												viewBox="0 0 24 24"
+												stroke-width="1.5"
+												stroke="currentColor"
+												class="w-4 h-4"
+											>
+												<path
+													stroke-linecap="round"
+													stroke-linejoin="round"
+													d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
+												/>
+											</svg>
+										</div>
+									</Tooltip>
+								{/if}
+
+								{#if !$mobile && (item?.model?.info?.meta?.tags ?? []).length > 0}
+									<div class="flex gap-0.5 self-center items-center h-full translate-y-[0.5px]">
+										{#each item.model?.info?.meta.tags as tag}
+											<Tooltip content={tag.name}>
+												<div
+													class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+												>
+													{tag.name}
+												</div>
+											</Tooltip>
+										{/each}
+									</div>
+								{/if}
+							</div>
+						</div>
+
+						{#if value === item.value}
+							<div class="ml-auto pl-2 pr-2 md:pr-0">
+								<Check />
+							</div>
+						{/if}
+					</button>
+				{:else}
+					<div>
+						<div class="block px-3 py-2 text-sm text-gray-700 dark:text-gray-100">
+							{$i18n.t('No results found')}
+						</div>
+					</div>
+				{/each}
+
+				{#if !(searchValue.trim() in $MODEL_DOWNLOAD_POOL) && searchValue && ollamaVersion && $user.role === 'admin'}
+					<Tooltip
+						content={$i18n.t(`Pull "{{searchValue}}" from Ollama.com`, {
+							searchValue: searchValue
+						})}
+						placement="top-start"
+					>
+						<button
+							class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
+							on:click={() => {
+								pullModelHandler();
+							}}
+						>
+							<div class=" truncate">
+								{$i18n.t(`Pull "{{searchValue}}" from Ollama.com`, { searchValue: searchValue })}
+							</div>
+						</button>
+					</Tooltip>
+				{/if}
+
+				{#each Object.keys($MODEL_DOWNLOAD_POOL) as model}
+					<div
+						class="flex w-full justify-between font-medium select-none rounded-button py-2 pl-3 pr-1.5 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
+					>
+						<div class="flex">
+							<div class="-ml-2 mr-2.5 translate-y-0.5">
+								<svg
+									class="size-4"
+									viewBox="0 0 24 24"
+									fill="currentColor"
+									xmlns="http://www.w3.org/2000/svg"
+									><style>
+										.spinner_ajPY {
+											transform-origin: center;
+											animation: spinner_AtaB 0.75s infinite linear;
+										}
+										@keyframes spinner_AtaB {
+											100% {
+												transform: rotate(360deg);
+											}
+										}
+									</style><path
+										d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+										opacity=".25"
+									/><path
+										d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+										class="spinner_ajPY"
+									/></svg
+								>
+							</div>
+
+							<div class="flex flex-col self-start">
+								<div class="flex gap-1">
+									<div class="line-clamp-1">
+										Downloading "{model}"
+									</div>
+
+									<div class="flex-shrink-0">
+										{'pullProgress' in $MODEL_DOWNLOAD_POOL[model]
+											? `(${$MODEL_DOWNLOAD_POOL[model].pullProgress}%)`
+											: ''}
+									</div>
+								</div>
+
+								{#if 'digest' in $MODEL_DOWNLOAD_POOL[model] && $MODEL_DOWNLOAD_POOL[model].digest}
+									<div class="-mt-1 h-fit text-[0.7rem] dark:text-gray-500 line-clamp-1">
+										{$MODEL_DOWNLOAD_POOL[model].digest}
+									</div>
+								{/if}
+							</div>
+						</div>
+
+						<div class="mr-2 ml-1 translate-y-0.5">
+							<Tooltip content={$i18n.t('Cancel')}>
+								<button
+									class="text-gray-800 dark:text-gray-100"
+									on:click={() => {
+										cancelModelPullHandler(model);
+									}}
+								>
+									<svg
+										class="w-4 h-4 text-gray-800 dark:text-white"
+										aria-hidden="true"
+										xmlns="http://www.w3.org/2000/svg"
+										width="24"
+										height="24"
+										fill="currentColor"
+										viewBox="0 0 24 24"
+									>
+										<path
+											stroke="currentColor"
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											stroke-width="2"
+											d="M6 18 17.94 6M18 18 6.06 6"
+										/>
+									</svg>
+								</button>
+							</Tooltip>
+						</div>
+					</div>
+				{/each}
+			</div>
+
+			{#if showTemporaryChatControl}
+				<hr class="border-gray-50 dark:border-gray-800" />
+
+				<div class="flex items-center mx-2 my-2">
+					<button
+						class="flex justify-between w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 px-3 text-sm text-gray-700 dark:text-gray-100 outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
+						on:click={async () => {
+							temporaryChatEnabled.set(!$temporaryChatEnabled);
+							await goto('/');
+							const newChatButton = document.getElementById('new-chat-button');
+							setTimeout(() => {
+								newChatButton?.click();
+							}, 0);
+
+							// add 'temporary-chat=true' to the URL
+							if ($temporaryChatEnabled) {
+								history.replaceState(null, '', '?temporary-chat=true');
+							} else {
+								history.replaceState(null, '', location.pathname);
+							}
+
+							show = false;
+						}}
+					>
+						<div class="flex gap-2.5 items-center">
+							<ChatBubbleOval className="size-4" strokeWidth="2.5" />
+
+							{$i18n.t(`Temporary Chat`)}
+						</div>
+
+						<div>
+							<Switch state={$temporaryChatEnabled} />
+						</div>
+					</button>
+				</div>
+			{/if}
+
+			<div class="hidden w-[42rem]" />
+			<div class="hidden w-[32rem]" />
+		</slot>
+	</DropdownMenu.Content>
+</DropdownMenu.Root>
diff --git a/src/lib/components/chat/Overview.svelte b/src/lib/components/chat/Overview.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5592e013999569e7f95e7e6d1317dfdfb4c5a013
--- /dev/null
+++ b/src/lib/components/chat/Overview.svelte
@@ -0,0 +1,199 @@
+<script lang="ts">
+	import { getContext, createEventDispatcher, onDestroy } from 'svelte';
+	import { useSvelteFlow, useNodesInitialized, useStore } from '@xyflow/svelte';
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import { onMount, tick } from 'svelte';
+
+	import { writable } from 'svelte/store';
+	import { models, showOverview, theme, user } from '$lib/stores';
+
+	import '@xyflow/svelte/dist/style.css';
+
+	import CustomNode from './Overview/Node.svelte';
+	import Flow from './Overview/Flow.svelte';
+	import XMark from '../icons/XMark.svelte';
+	import ArrowLeft from '../icons/ArrowLeft.svelte';
+
+	const { width, height } = useStore();
+
+	const { fitView, getViewport } = useSvelteFlow();
+	const nodesInitialized = useNodesInitialized();
+
+	export let history;
+
+	let selectedMessageId = null;
+
+	const nodes = writable([]);
+	const edges = writable([]);
+
+	const nodeTypes = {
+		custom: CustomNode
+	};
+
+	$: if (history) {
+		drawFlow();
+	}
+
+	$: if (history && history.currentId) {
+		focusNode();
+	}
+
+	const focusNode = async () => {
+		if (selectedMessageId === null) {
+			await fitView({ nodes: [{ id: history.currentId }] });
+		} else {
+			await fitView({ nodes: [{ id: selectedMessageId }] });
+		}
+
+		selectedMessageId = null;
+	};
+
+	const drawFlow = async () => {
+		const nodeList = [];
+		const edgeList = [];
+		const levelOffset = 150; // Vertical spacing between layers
+		const siblingOffset = 250; // Horizontal spacing between nodes at the same layer
+
+		// Map to keep track of node positions at each level
+		let positionMap = new Map();
+
+		// Helper function to truncate labels
+		function createLabel(content) {
+			const maxLength = 100;
+			return content.length > maxLength ? content.substr(0, maxLength) + '...' : content;
+		}
+
+		// Create nodes and map children to ensure alignment in width
+		let layerWidths = {}; // Track widths of each layer
+
+		Object.keys(history.messages).forEach((id) => {
+			const message = history.messages[id];
+			const level = message.parentId ? (positionMap.get(message.parentId)?.level ?? -1) + 1 : 0;
+			if (!layerWidths[level]) layerWidths[level] = 0;
+
+			positionMap.set(id, {
+				id: message.id,
+				level,
+				position: layerWidths[level]++
+			});
+		});
+
+		// Adjust positions based on siblings count to centralize vertical spacing
+		Object.keys(history.messages).forEach((id) => {
+			const pos = positionMap.get(id);
+			const xOffset = pos.position * siblingOffset;
+			const y = pos.level * levelOffset;
+			const x = xOffset;
+
+			nodeList.push({
+				id: pos.id,
+				type: 'custom',
+				data: {
+					user: $user,
+					message: history.messages[id],
+					model: $models.find((model) => model.id === history.messages[id].model)
+				},
+				position: { x, y }
+			});
+
+			// Create edges
+			const parentId = history.messages[id].parentId;
+			if (parentId) {
+				edgeList.push({
+					id: parentId + '-' + pos.id,
+					source: parentId,
+					target: pos.id,
+					selectable: false,
+					class: ' dark:fill-gray-300 fill-gray-300',
+					type: 'smoothstep',
+					animated: history.currentId === id || recurseCheckChild(id, history.currentId)
+				});
+			}
+		});
+
+		await edges.set([...edgeList]);
+		await nodes.set([...nodeList]);
+	};
+
+	const recurseCheckChild = (nodeId, currentId) => {
+		const node = history.messages[nodeId];
+		return (
+			node.childrenIds &&
+			node.childrenIds.some((id) => id === currentId || recurseCheckChild(id, currentId))
+		);
+	};
+
+	onMount(() => {
+		drawFlow();
+
+		nodesInitialized.subscribe(async (initialized) => {
+			if (initialized) {
+				await tick();
+				const res = await fitView({ nodes: [{ id: history.currentId }] });
+			}
+		});
+
+		width.subscribe((value) => {
+			if (value) {
+				// fitView();
+				fitView({ nodes: [{ id: history.currentId }] });
+			}
+		});
+
+		height.subscribe((value) => {
+			if (value) {
+				// fitView();
+				fitView({ nodes: [{ id: history.currentId }] });
+			}
+		});
+	});
+
+	onDestroy(() => {
+		console.log('Overview destroyed');
+
+		nodes.set([]);
+		edges.set([]);
+	});
+</script>
+
+<div class="w-full h-full relative">
+	<div class=" absolute z-50 w-full flex justify-between dark:text-gray-100 px-4 py-3.5">
+		<div class="flex items-center gap-2.5">
+			<button
+				class="self-center p-0.5"
+				on:click={() => {
+					showOverview.set(false);
+				}}
+			>
+				<ArrowLeft className="size-3.5" />
+			</button>
+			<div class=" text-lg font-medium self-center font-primary">{$i18n.t('Chat Overview')}</div>
+		</div>
+		<button
+			class="self-center p-0.5"
+			on:click={() => {
+				dispatch('close');
+				showOverview.set(false);
+			}}
+		>
+			<XMark className="size-3.5" />
+		</button>
+	</div>
+
+	{#if $nodes.length > 0}
+		<Flow
+			{nodes}
+			{nodeTypes}
+			{edges}
+			on:nodeclick={(e) => {
+				console.log(e.detail.node.data);
+				dispatch('nodeclick', e.detail);
+				selectedMessageId = e.detail.node.data.message.id;
+				fitView({ nodes: [{ id: selectedMessageId }] });
+			}}
+		/>
+	{/if}
+</div>
diff --git a/src/lib/components/chat/Overview/Flow.svelte b/src/lib/components/chat/Overview/Flow.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f7ff307567e4a97462c5fba341479be6b9e965a2
--- /dev/null
+++ b/src/lib/components/chat/Overview/Flow.svelte
@@ -0,0 +1,36 @@
+<script>
+	import { createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import { theme } from '$lib/stores';
+	import { Background, Controls, SvelteFlow, BackgroundVariant } from '@xyflow/svelte';
+
+	export let nodes;
+	export let nodeTypes;
+	export let edges;
+</script>
+
+<SvelteFlow
+	{nodes}
+	{nodeTypes}
+	{edges}
+	fitView
+	minZoom={0.001}
+	colorMode={$theme.includes('dark')
+		? 'dark'
+		: $theme === 'system'
+			? window.matchMedia('(prefers-color-scheme: dark)').matches
+				? 'dark'
+				: 'light'
+			: 'light'}
+	nodesConnectable={false}
+	nodesDraggable={false}
+	on:nodeclick={(e) => dispatch('nodeclick', e.detail)}
+	oninit={() => {
+		console.log('Flow initialized');
+	}}
+>
+	<Controls showLock={false} />
+	<Background variant={BackgroundVariant.Dots} />
+</SvelteFlow>
diff --git a/src/lib/components/chat/Overview/Node.svelte b/src/lib/components/chat/Overview/Node.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..45afa222a52a83cf0b982e4dbb463fe38b3bf12e
--- /dev/null
+++ b/src/lib/components/chat/Overview/Node.svelte
@@ -0,0 +1,82 @@
+<script lang="ts">
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import { Handle, Position, type NodeProps } from '@xyflow/svelte';
+
+	import ProfileImage from '../Messages/ProfileImage.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Heart from '$lib/components/icons/Heart.svelte';
+
+	type $$Props = NodeProps;
+	export let data: $$Props['data'];
+</script>
+
+<div
+	class="px-4 py-3 shadow-md rounded-xl dark:bg-black bg-white border dark:border-gray-900 w-60 h-20 group"
+>
+	<Tooltip
+		content={data?.message?.error ? data.message.error.content : data.message.content}
+		class="w-full"
+		allowHTML={false}
+	>
+		{#if data.message.role === 'user'}
+			<div class="flex w-full">
+				<ProfileImage
+					src={data.user?.profile_image_url ?? '/user.png'}
+					className={'size-5 -translate-y-[1px]'}
+				/>
+				<div class="ml-2">
+					<div class=" flex justify-between items-center">
+						<div class="text-xs text-black dark:text-white font-medium line-clamp-1">
+							{data?.user?.name ?? 'User'}
+						</div>
+					</div>
+
+					{#if data?.message?.error}
+						<div class="text-red-500 line-clamp-2 text-xs mt-0.5">{data.message.error.content}</div>
+					{:else}
+						<div class="text-gray-500 line-clamp-2 text-xs mt-0.5">{data.message.content}</div>
+					{/if}
+				</div>
+			</div>
+		{:else}
+			<div class="flex w-full">
+				<ProfileImage
+					src={data?.model?.info?.meta?.profile_image_url ?? ''}
+					className={'size-5 -translate-y-[1px]'}
+				/>
+
+				<div class="ml-2">
+					<div class=" flex justify-between items-center">
+						<div class="text-xs text-black dark:text-white font-medium line-clamp-1">
+							{data?.model?.name ?? data?.message?.model ?? 'Assistant'}
+						</div>
+
+						<button
+							class={data?.message?.favorite ? '' : 'invisible group-hover:visible'}
+							on:click={() => {
+								data.message.favorite = !(data?.message?.favorite ?? false);
+							}}
+						>
+							<Heart
+								className="size-3 {data?.message?.favorite
+									? 'fill-red-500 stroke-red-500'
+									: 'hover:fill-red-500 hover:stroke-red-500'} "
+								strokeWidth="2.5"
+							/>
+						</button>
+					</div>
+
+					{#if data?.message?.error}
+						<div class="text-red-500 line-clamp-2 text-xs mt-0.5">
+							{data.message.error.content}
+						</div>
+					{:else}
+						<div class="text-gray-500 line-clamp-2 text-xs mt-0.5">{data.message.content}</div>
+					{/if}
+				</div>
+			</div>
+		{/if}
+	</Tooltip>
+	<Handle type="target" position={Position.Top} class="w-2 rounded-full dark:bg-gray-900" />
+	<Handle type="source" position={Position.Bottom} class="w-2 rounded-full dark:bg-gray-900" />
+</div>
diff --git a/src/lib/components/chat/Placeholder.svelte b/src/lib/components/chat/Placeholder.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6782ea628b551fafd4c5da9522b96e81c6c55070
--- /dev/null
+++ b/src/lib/components/chat/Placeholder.svelte
@@ -0,0 +1,225 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { marked } from 'marked';
+
+	import { onMount, getContext, tick, createEventDispatcher } from 'svelte';
+	import { blur, fade } from 'svelte/transition';
+
+	const dispatch = createEventDispatcher();
+
+	import { config, user, models as _models, temporaryChatEnabled } from '$lib/stores';
+	import { sanitizeResponseContent, findWordIndices } from '$lib/utils';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import Suggestions from './Suggestions.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import EyeSlash from '$lib/components/icons/EyeSlash.svelte';
+	import MessageInput from './MessageInput.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let transparentBackground = false;
+
+	export let createMessagePair: Function;
+	export let stopResponse: Function;
+
+	export let autoScroll = false;
+
+	export let atSelectedModel: Model | undefined;
+	export let selectedModels: [''];
+
+	export let history;
+
+	export let prompt = '';
+	export let files = [];
+
+	export let selectedToolIds = [];
+	export let webSearchEnabled = false;
+
+	let models = [];
+
+	const selectSuggestionPrompt = async (p) => {
+		let text = p;
+
+		if (p.includes('{{CLIPBOARD}}')) {
+			const clipboardText = await navigator.clipboard.readText().catch((err) => {
+				toast.error($i18n.t('Failed to read clipboard contents'));
+				return '{{CLIPBOARD}}';
+			});
+
+			text = p.replaceAll('{{CLIPBOARD}}', clipboardText);
+
+			console.log('Clipboard text:', clipboardText, text);
+		}
+
+		prompt = text;
+
+		console.log(prompt);
+		await tick();
+
+		const chatInputContainerElement = document.getElementById('chat-input-container');
+		const chatInputElement = document.getElementById('chat-input');
+
+		if (chatInputContainerElement) {
+			chatInputContainerElement.style.height = '';
+			chatInputContainerElement.style.height =
+				Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
+		}
+
+		await tick();
+		if (chatInputElement) {
+			chatInputElement.focus();
+			chatInputElement.dispatchEvent(new Event('input'));
+		}
+
+		await tick();
+	};
+
+	let selectedModelIdx = 0;
+
+	$: if (selectedModels.length > 0) {
+		selectedModelIdx = models.length - 1;
+	}
+
+	$: models = selectedModels.map((id) => $_models.find((m) => m.id === id));
+
+	onMount(() => {});
+</script>
+
+<div class="m-auto w-full max-w-6xl px-2 xl:px-20 translate-y-6 py-24 text-center">
+	{#if $temporaryChatEnabled}
+		<Tooltip
+			content="This chat won't appear in history and your messages will not be saved."
+			className="w-full flex justify-center mb-0.5"
+			placement="top"
+		>
+			<div class="flex items-center gap-2 text-gray-500 font-medium text-lg my-2 w-fit">
+				<EyeSlash strokeWidth="2.5" className="size-5" /> Temporary Chat
+			</div>
+		</Tooltip>
+	{/if}
+
+	<div
+		class="w-full text-3xl text-gray-800 dark:text-gray-100 font-medium text-center flex items-center gap-4 font-primary"
+	>
+		<div class="w-full flex flex-col justify-center items-center">
+			<div class="flex flex-row justify-center gap-3 sm:gap-3.5 w-fit px-5">
+				<div class="flex flex-shrink-0 justify-center">
+					<div class="flex -space-x-4 mb-0.5" in:fade={{ duration: 100 }}>
+						{#each models as model, modelIdx}
+							<Tooltip
+								content={(models[modelIdx]?.info?.meta?.tags ?? [])
+									.map((tag) => tag.name.toUpperCase())
+									.join(', ')}
+								placement="top"
+							>
+								<button
+									on:click={() => {
+										selectedModelIdx = modelIdx;
+									}}
+								>
+									<img
+										crossorigin="anonymous"
+										src={model?.info?.meta?.profile_image_url ??
+											($i18n.language === 'dg-DG'
+												? `/doge.png`
+												: `${WEBUI_BASE_URL}/static/favicon.png`)}
+										class=" size-9 sm:size-10 rounded-full border-[1px] border-gray-200 dark:border-none"
+										alt="logo"
+										draggable="false"
+									/>
+								</button>
+							</Tooltip>
+						{/each}
+					</div>
+				</div>
+
+				<div class=" text-3xl sm:text-4xl line-clamp-1" in:fade={{ duration: 100 }}>
+					{#if models[selectedModelIdx]?.name}
+						{models[selectedModelIdx]?.name}
+					{:else}
+						{$i18n.t('Hello, {{name}}', { name: $user.name })}
+					{/if}
+				</div>
+			</div>
+
+			<div class="flex mt-1 mb-2">
+				<div in:fade={{ duration: 100, delay: 50 }}>
+					{#if models[selectedModelIdx]?.info?.meta?.description ?? null}
+						<Tooltip
+							className=" w-fit"
+							content={marked.parse(
+								sanitizeResponseContent(models[selectedModelIdx]?.info?.meta?.description ?? '')
+							)}
+							placement="top"
+						>
+							<div
+								class="mt-0.5 px-2 text-sm font-normal text-gray-500 dark:text-gray-400 line-clamp-2 max-w-xl markdown"
+							>
+								{@html marked.parse(
+									sanitizeResponseContent(models[selectedModelIdx]?.info?.meta?.description)
+								)}
+							</div>
+						</Tooltip>
+
+						{#if models[selectedModelIdx]?.info?.meta?.user}
+							<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
+								By
+								{#if models[selectedModelIdx]?.info?.meta?.user.community}
+									<a
+										href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
+											.username}"
+										>{models[selectedModelIdx]?.info?.meta?.user.name
+											? models[selectedModelIdx]?.info?.meta?.user.name
+											: `@${models[selectedModelIdx]?.info?.meta?.user.username}`}</a
+									>
+								{:else}
+									{models[selectedModelIdx]?.info?.meta?.user.name}
+								{/if}
+							</div>
+						{/if}
+					{/if}
+				</div>
+			</div>
+
+			<div
+				class="text-base font-normal xl:translate-x-6 md:max-w-3xl w-full py-3 {atSelectedModel
+					? 'mt-2'
+					: ''}"
+			>
+				<MessageInput
+					{history}
+					{selectedModels}
+					bind:files
+					bind:prompt
+					bind:autoScroll
+					bind:selectedToolIds
+					bind:webSearchEnabled
+					bind:atSelectedModel
+					{transparentBackground}
+					{stopResponse}
+					{createMessagePair}
+					placeholder={$i18n.t('How can I help you today?')}
+					on:upload={(e) => {
+						dispatch('upload', e.detail);
+					}}
+					on:submit={(e) => {
+						dispatch('submit', e.detail);
+					}}
+				/>
+			</div>
+		</div>
+	</div>
+	<div class="mx-auto max-w-2xl font-primary" in:fade={{ duration: 200, delay: 200 }}>
+		<div class="mx-5">
+			<Suggestions
+				suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
+					$config?.default_prompt_suggestions ??
+					[]}
+				on:select={(e) => {
+					selectSuggestionPrompt(e.detail);
+				}}
+			/>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Settings/About.svelte b/src/lib/components/chat/Settings/About.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9cf05aa750afbc27ec5ded35d2b58b6c6cdf3baa
--- /dev/null
+++ b/src/lib/components/chat/Settings/About.svelte
@@ -0,0 +1,144 @@
+<script lang="ts">
+	import { getVersionUpdates } from '$lib/apis';
+	import { getOllamaVersion } from '$lib/apis/ollama';
+	import { WEBUI_BUILD_HASH, WEBUI_VERSION } from '$lib/constants';
+	import { WEBUI_NAME, config, showChangelog } from '$lib/stores';
+	import { compareVersion } from '$lib/utils';
+	import { onMount, getContext } from 'svelte';
+
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	let ollamaVersion = '';
+
+	let updateAvailable = null;
+	let version = {
+		current: '',
+		latest: ''
+	};
+
+	const checkForVersionUpdates = async () => {
+		updateAvailable = null;
+		version = await getVersionUpdates(localStorage.token).catch((error) => {
+			return {
+				current: WEBUI_VERSION,
+				latest: WEBUI_VERSION
+			};
+		});
+
+		console.log(version);
+
+		updateAvailable = compareVersion(version.latest, version.current);
+		console.log(updateAvailable);
+	};
+
+	onMount(async () => {
+		ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => {
+			return '';
+		});
+
+		checkForVersionUpdates();
+	});
+</script>
+
+<div class="flex flex-col h-full justify-between space-y-3 text-sm mb-6">
+	<div class=" space-y-3 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div>
+			<div class=" mb-2.5 text-sm font-medium flex space-x-2 items-center">
+				<div>
+					{$WEBUI_NAME}
+					{$i18n.t('Version')}
+				</div>
+			</div>
+			<div class="flex w-full justify-between items-center">
+				<div class="flex flex-col text-xs text-gray-700 dark:text-gray-200">
+					<div class="flex gap-1">
+						<Tooltip content={WEBUI_BUILD_HASH}>
+							v{WEBUI_VERSION}
+						</Tooltip>
+
+						<a
+							href="https://github.com/open-webui/open-webui/releases/tag/v{version.latest}"
+							target="_blank"
+						>
+							{updateAvailable === null
+								? $i18n.t('Checking for updates...')
+								: updateAvailable
+									? `(v${version.latest} ${$i18n.t('available!')})`
+									: $i18n.t('(latest)')}
+						</a>
+					</div>
+
+					<button
+						class=" underline flex items-center space-x-1 text-xs text-gray-500 dark:text-gray-500"
+						on:click={() => {
+							showChangelog.set(true);
+						}}
+					>
+						<div>{$i18n.t("See what's new")}</div>
+					</button>
+				</div>
+
+				<button
+					class=" text-xs px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-850 dark:hover:bg-gray-800 transition rounded-lg font-medium"
+					on:click={() => {
+						checkForVersionUpdates();
+					}}
+				>
+					{$i18n.t('Check for updates')}
+				</button>
+			</div>
+		</div>
+
+		{#if ollamaVersion}
+			<hr class=" dark:border-gray-850" />
+
+			<div>
+				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Ollama Version')}</div>
+				<div class="flex w-full">
+					<div class="flex-1 text-xs text-gray-700 dark:text-gray-200">
+						{ollamaVersion ?? 'N/A'}
+					</div>
+				</div>
+			</div>
+		{/if}
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class="flex space-x-1">
+			<a href="https://discord.gg/5rJgQTnV4s" target="_blank">
+				<img
+					alt="Discord"
+					src="https://img.shields.io/badge/Discord-Open_WebUI-blue?logo=discord&logoColor=white"
+				/>
+			</a>
+
+			<a href="https://twitter.com/OpenWebUI" target="_blank">
+				<img
+					alt="X (formerly Twitter) Follow"
+					src="https://img.shields.io/twitter/follow/OpenWebUI"
+				/>
+			</a>
+
+			<a href="https://github.com/open-webui/open-webui" target="_blank">
+				<img
+					alt="Github Repo"
+					src="https://img.shields.io/github/stars/open-webui/open-webui?style=social&label=Star us on Github"
+				/>
+			</a>
+		</div>
+
+		<div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
+			{#if !$WEBUI_NAME.includes('Open WebUI')}
+				<span class=" text-gray-500 dark:text-gray-300 font-medium">{$WEBUI_NAME}</span> -
+			{/if}
+			{$i18n.t('Created by')}
+			<a
+				class=" text-gray-500 dark:text-gray-300 font-medium"
+				href="https://github.com/tjbck"
+				target="_blank">Timothy J. Baek</a
+			>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Settings/Account.svelte b/src/lib/components/chat/Settings/Account.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..70c53977ef785b0c67c4a89904da89bc28c99c23
--- /dev/null
+++ b/src/lib/components/chat/Settings/Account.svelte
@@ -0,0 +1,412 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext } from 'svelte';
+
+	import { user, config } from '$lib/stores';
+	import { updateUserProfile, createAPIKey, getAPIKey } from '$lib/apis/auths';
+
+	import UpdatePassword from './Account/UpdatePassword.svelte';
+	import { getGravatarUrl } from '$lib/apis/utils';
+	import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
+	import { copyToClipboard } from '$lib/utils';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let saveHandler: Function;
+
+	let profileImageUrl = '';
+	let name = '';
+
+	let showAPIKeys = false;
+
+	let JWTTokenCopied = false;
+
+	let APIKey = '';
+	let APIKeyCopied = false;
+	let profileImageInputElement: HTMLInputElement;
+
+	const submitHandler = async () => {
+		if (name !== $user.name) {
+			if (profileImageUrl === generateInitialsImage($user.name) || profileImageUrl === '') {
+				profileImageUrl = generateInitialsImage(name);
+			}
+		}
+
+		const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch(
+			(error) => {
+				toast.error(error);
+			}
+		);
+
+		if (updatedUser) {
+			await user.set(updatedUser);
+			return true;
+		}
+		return false;
+	};
+
+	const createAPIKeyHandler = async () => {
+		APIKey = await createAPIKey(localStorage.token);
+		if (APIKey) {
+			toast.success($i18n.t('API Key created.'));
+		} else {
+			toast.error($i18n.t('Failed to create API Key.'));
+		}
+	};
+
+	onMount(async () => {
+		name = $user.name;
+		profileImageUrl = $user.profile_image_url;
+
+		APIKey = await getAPIKey(localStorage.token).catch((error) => {
+			console.log(error);
+			return '';
+		});
+	});
+</script>
+
+<div class="flex flex-col h-full justify-between text-sm">
+	<div class=" space-y-3 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<input
+			id="profile-image-input"
+			bind:this={profileImageInputElement}
+			type="file"
+			hidden
+			accept="image/*"
+			on:change={(e) => {
+				const files = profileImageInputElement.files ?? [];
+				let reader = new FileReader();
+				reader.onload = (event) => {
+					let originalImageUrl = `${event.target.result}`;
+
+					const img = new Image();
+					img.src = originalImageUrl;
+
+					img.onload = function () {
+						const canvas = document.createElement('canvas');
+						const ctx = canvas.getContext('2d');
+
+						// Calculate the aspect ratio of the image
+						const aspectRatio = img.width / img.height;
+
+						// Calculate the new width and height to fit within 250x250
+						let newWidth, newHeight;
+						if (aspectRatio > 1) {
+							newWidth = 250 * aspectRatio;
+							newHeight = 250;
+						} else {
+							newWidth = 250;
+							newHeight = 250 / aspectRatio;
+						}
+
+						// Set the canvas size
+						canvas.width = 250;
+						canvas.height = 250;
+
+						// Calculate the position to center the image
+						const offsetX = (250 - newWidth) / 2;
+						const offsetY = (250 - newHeight) / 2;
+
+						// Draw the image on the canvas
+						ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
+
+						// Get the base64 representation of the compressed image
+						const compressedSrc = canvas.toDataURL('image/jpeg');
+
+						// Display the compressed image
+						profileImageUrl = compressedSrc;
+
+						profileImageInputElement.files = null;
+					};
+				};
+
+				if (
+					files.length > 0 &&
+					['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(files[0]['type'])
+				) {
+					reader.readAsDataURL(files[0]);
+				}
+			}}
+		/>
+
+		<div class="space-y-1">
+			<!-- <div class=" text-sm font-medium">{$i18n.t('Account')}</div> -->
+
+			<div class="flex space-x-5">
+				<div class="flex flex-col">
+					<div class="self-center mt-2">
+						<button
+							class="relative rounded-full dark:bg-gray-700"
+							type="button"
+							on:click={() => {
+								profileImageInputElement.click();
+							}}
+						>
+							<img
+								src={profileImageUrl !== '' ? profileImageUrl : generateInitialsImage(name)}
+								alt="profile"
+								class=" rounded-full size-16 object-cover"
+							/>
+
+							<div
+								class="absolute flex justify-center rounded-full bottom-0 left-0 right-0 top-0 h-full w-full overflow-hidden bg-gray-700 bg-fixed opacity-0 transition duration-300 ease-in-out hover:opacity-50"
+							>
+								<div class="my-auto text-gray-100">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-5 h-5"
+									>
+										<path
+											d="m2.695 14.762-1.262 3.155a.5.5 0 0 0 .65.65l3.155-1.262a4 4 0 0 0 1.343-.886L17.5 5.501a2.121 2.121 0 0 0-3-3L3.58 13.419a4 4 0 0 0-.885 1.343Z"
+										/>
+									</svg>
+								</div>
+							</div>
+						</button>
+					</div>
+				</div>
+
+				<div class="flex-1 flex flex-col self-center gap-0.5">
+					<div class=" mb-0.5 text-sm font-medium">{$i18n.t('Profile Image')}</div>
+
+					<div>
+						<button
+							class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
+							on:click={async () => {
+								if (canvasPixelTest()) {
+									profileImageUrl = generateInitialsImage(name);
+								} else {
+									toast.info(
+										$i18n.t(
+											'Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.'
+										),
+										{
+											duration: 1000 * 10
+										}
+									);
+								}
+							}}>{$i18n.t('Use Initials')}</button
+						>
+
+						<button
+							class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
+							on:click={async () => {
+								const url = await getGravatarUrl($user.email);
+
+								profileImageUrl = url;
+							}}>{$i18n.t('Use Gravatar')}</button
+						>
+
+						<button
+							class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-lg px-2 py-1"
+							on:click={async () => {
+								profileImageUrl = '/user.png';
+							}}>{$i18n.t('Remove')}</button
+						>
+					</div>
+				</div>
+			</div>
+
+			<div class="pt-0.5">
+				<div class="flex flex-col w-full">
+					<div class=" mb-1 text-xs font-medium">{$i18n.t('Name')}</div>
+
+					<div class="flex-1">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							bind:value={name}
+							required
+						/>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class="py-0.5">
+			<UpdatePassword />
+		</div>
+
+		<hr class=" dark:border-gray-850 my-4" />
+
+		<div class="flex justify-between items-center text-sm">
+			<div class="  font-medium">{$i18n.t('API keys')}</div>
+			<button
+				class=" text-xs font-medium text-gray-500"
+				type="button"
+				on:click={() => {
+					showAPIKeys = !showAPIKeys;
+				}}>{showAPIKeys ? $i18n.t('Hide') : $i18n.t('Show')}</button
+			>
+		</div>
+
+		{#if showAPIKeys}
+			<div class="flex flex-col gap-4">
+				<div class="justify-between w-full">
+					<div class="flex justify-between w-full">
+						<div class="self-center text-xs font-medium">{$i18n.t('JWT Token')}</div>
+					</div>
+
+					<div class="flex mt-2">
+						<SensitiveInput value={localStorage.token} readOnly={true} />
+
+						<button
+							class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
+							on:click={() => {
+								copyToClipboard(localStorage.token);
+								JWTTokenCopied = true;
+								setTimeout(() => {
+									JWTTokenCopied = false;
+								}, 2000);
+							}}
+						>
+							{#if JWTTokenCopied}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 20 20"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							{:else}
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M11.986 3H12a2 2 0 0 1 2 2v6a2 2 0 0 1-1.5 1.937V7A2.5 2.5 0 0 0 10 4.5H4.063A2 2 0 0 1 6 3h.014A2.25 2.25 0 0 1 8.25 1h1.5a2.25 2.25 0 0 1 2.236 2ZM10.5 4v-.75a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0-.75.75V4h3Z"
+										clip-rule="evenodd"
+									/>
+									<path
+										fill-rule="evenodd"
+										d="M3 6a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3Zm1.75 2.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5ZM4 11.75a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 0 1.5h-3.5a.75.75 0 0 1-.75-.75Z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							{/if}
+						</button>
+					</div>
+				</div>
+				{#if $config?.features?.enable_api_key ?? true}
+					<div class="justify-between w-full">
+						<div class="flex justify-between w-full">
+							<div class="self-center text-xs font-medium">{$i18n.t('API Key')}</div>
+						</div>
+						<div class="flex mt-2">
+							{#if APIKey}
+								<SensitiveInput value={APIKey} readOnly={true} />
+
+								<button
+									class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
+									on:click={() => {
+										copyToClipboard(APIKey);
+										APIKeyCopied = true;
+										setTimeout(() => {
+											APIKeyCopied = false;
+										}, 2000);
+									}}
+								>
+									{#if APIKeyCopied}
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 20 20"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									{:else}
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="w-4 h-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M11.986 3H12a2 2 0 0 1 2 2v6a2 2 0 0 1-1.5 1.937V7A2.5 2.5 0 0 0 10 4.5H4.063A2 2 0 0 1 6 3h.014A2.25 2.25 0 0 1 8.25 1h1.5a2.25 2.25 0 0 1 2.236 2ZM10.5 4v-.75a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0-.75.75V4h3Z"
+												clip-rule="evenodd"
+											/>
+											<path
+												fill-rule="evenodd"
+												d="M3 6a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H3Zm1.75 2.5a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5h-3.5ZM4 11.75a.75.75 0 0 1 .75-.75h3.5a.75.75 0 0 1 0 1.5h-3.5a.75.75 0 0 1-.75-.75Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									{/if}
+								</button>
+
+								<Tooltip content={$i18n.t('Create new key')}>
+									<button
+										class=" px-1.5 py-1 dark:hover:bg-gray-850transition rounded-lg"
+										on:click={() => {
+											createAPIKeyHandler();
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke-width="2"
+											stroke="currentColor"
+											class="size-4"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
+											/>
+										</svg>
+									</button>
+								</Tooltip>
+							{:else}
+								<button
+									class="flex gap-1.5 items-center font-medium px-3.5 py-1.5 rounded-lg bg-gray-100/70 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-850 transition"
+									on:click={() => {
+										createAPIKeyHandler();
+									}}
+								>
+									<Plus strokeWidth="2" className=" size-3.5" />
+
+									{$i18n.t('Create new secret key')}</button
+								>
+							{/if}
+						</div>
+					</div>
+				{/if}
+			</div>
+		{/if}
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			on:click={async () => {
+				const res = await submitHandler();
+
+				if (res) {
+					saveHandler();
+				}
+			}}
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Settings/Account/UpdatePassword.svelte b/src/lib/components/chat/Settings/Account/UpdatePassword.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..175ee61ebbed6d21c0bd6fb650e7cc04bd3b3176
--- /dev/null
+++ b/src/lib/components/chat/Settings/Account/UpdatePassword.svelte
@@ -0,0 +1,109 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import { updateUserPassword } from '$lib/apis/auths';
+
+	const i18n = getContext('i18n');
+
+	let show = false;
+	let currentPassword = '';
+	let newPassword = '';
+	let newPasswordConfirm = '';
+
+	const updatePasswordHandler = async () => {
+		if (newPassword === newPasswordConfirm) {
+			const res = await updateUserPassword(localStorage.token, currentPassword, newPassword).catch(
+				(error) => {
+					toast.error(error);
+					return null;
+				}
+			);
+
+			if (res) {
+				toast.success($i18n.t('Successfully updated.'));
+			}
+
+			currentPassword = '';
+			newPassword = '';
+			newPasswordConfirm = '';
+		} else {
+			toast.error(
+				`The passwords you entered don't quite match. Please double-check and try again.`
+			);
+			newPassword = '';
+			newPasswordConfirm = '';
+		}
+	};
+</script>
+
+<form
+	class="flex flex-col text-sm"
+	on:submit|preventDefault={() => {
+		updatePasswordHandler();
+	}}
+>
+	<div class="flex justify-between items-center text-sm">
+		<div class="  font-medium">{$i18n.t('Change Password')}</div>
+		<button
+			class=" text-xs font-medium text-gray-500"
+			type="button"
+			on:click={() => {
+				show = !show;
+			}}>{show ? $i18n.t('Hide') : $i18n.t('Show')}</button
+		>
+	</div>
+
+	{#if show}
+		<div class=" py-2.5 space-y-1.5">
+			<div class="flex flex-col w-full">
+				<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Current Password')}</div>
+
+				<div class="flex-1">
+					<input
+						class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
+						type="password"
+						bind:value={currentPassword}
+						autocomplete="current-password"
+						required
+					/>
+				</div>
+			</div>
+
+			<div class="flex flex-col w-full">
+				<div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
+
+				<div class="flex-1">
+					<input
+						class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
+						type="password"
+						bind:value={newPassword}
+						autocomplete="new-password"
+						required
+					/>
+				</div>
+			</div>
+
+			<div class="flex flex-col w-full">
+				<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Confirm Password')}</div>
+
+				<div class="flex-1">
+					<input
+						class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
+						type="password"
+						bind:value={newPasswordConfirm}
+						autocomplete="off"
+						required
+					/>
+				</div>
+			</div>
+		</div>
+
+		<div class="mt-3 flex justify-end">
+			<button
+				class=" px-4 py-2 text-xs bg-gray-800 hover:bg-gray-900 dark:bg-gray-700 dark:hover:bg-gray-800 text-gray-100 transition rounded-md font-medium"
+			>
+				{$i18n.t('Update password')}
+			</button>
+		</div>
+	{/if}
+</form>
diff --git a/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte b/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e692f1530997bdb518dba4a4730c332bc0f2353b
--- /dev/null
+++ b/src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
@@ -0,0 +1,1175 @@
+<script lang="ts">
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let admin = false;
+
+	export let params = {
+		// Advanced
+		stream_response: null, // Set stream responses for this model individually
+		seed: null,
+		stop: null,
+		temperature: null,
+		frequency_penalty: null,
+		repeat_last_n: null,
+		mirostat: null,
+		mirostat_eta: null,
+		mirostat_tau: null,
+		top_k: null,
+		top_p: null,
+		min_p: null,
+		tfs_z: null,
+		num_ctx: null,
+		num_batch: null,
+		num_keep: null,
+		max_tokens: null,
+		use_mmap: null,
+		use_mlock: null,
+		num_thread: null,
+		num_gpu: null,
+		template: null
+	};
+
+	let customFieldName = '';
+	let customFieldValue = '';
+
+	$: if (params) {
+		dispatch('change', params);
+	}
+</script>
+
+<div class=" space-y-1 text-xs pb-safe-bottom">
+	<div>
+		<Tooltip
+			content={$i18n.t(
+				'When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Stream Chat Response')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						params.stream_response =
+							(params?.stream_response ?? null) === null
+								? true
+								: params.stream_response
+									? false
+									: null;
+					}}
+					type="button"
+				>
+					{#if params.stream_response === true}
+						<span class="ml-2 self-center">{$i18n.t('On')}</span>
+					{:else if params.stream_response === false}
+						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Seed')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.seed = (params?.seed ?? null) === null ? 0 : null;
+					}}
+				>
+					{#if (params?.seed ?? null) === null}
+						<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
+					{:else}
+						<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.seed ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+						type="number"
+						placeholder={$i18n.t('Enter Seed')}
+						bind:value={params.seed}
+						autocomplete="off"
+						min="0"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Stop Sequence')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.stop = (params?.stop ?? null) === null ? '' : null;
+					}}
+				>
+					{#if (params?.stop ?? null) === null}
+						<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
+					{:else}
+						<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.stop ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+						type="text"
+						placeholder={$i18n.t('Enter stop sequence')}
+						bind:value={params.stop}
+						autocomplete="off"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Temperature')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.temperature = (params?.temperature ?? null) === null ? 0.8 : null;
+					}}
+				>
+					{#if (params?.temperature ?? null) === null}
+						<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
+					{:else}
+						<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.temperature ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="1"
+						step="0.05"
+						bind:value={params.temperature}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.temperature}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="1"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Mirostat')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.mirostat = (params?.mirostat ?? null) === null ? 0 : null;
+					}}
+				>
+					{#if (params?.mirostat ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.mirostat ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="2"
+						step="1"
+						bind:value={params.mirostat}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.mirostat}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="2"
+						step="1"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Mirostat Eta')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.mirostat_eta = (params?.mirostat_eta ?? null) === null ? 0.1 : null;
+					}}
+				>
+					{#if (params?.mirostat_eta ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.mirostat_eta ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="1"
+						step="0.05"
+						bind:value={params.mirostat_eta}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.mirostat_eta}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="1"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Mirostat Tau')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.mirostat_tau = (params?.mirostat_tau ?? null) === null ? 5.0 : null;
+					}}
+				>
+					{#if (params?.mirostat_tau ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.mirostat_tau ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="10"
+						step="0.5"
+						bind:value={params.mirostat_tau}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.mirostat_tau}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="10"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Top K')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.top_k = (params?.top_k ?? null) === null ? 40 : null;
+					}}
+				>
+					{#if (params?.top_k ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.top_k ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="100"
+						step="0.5"
+						bind:value={params.top_k}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.top_k}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="100"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Top P')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.top_p = (params?.top_p ?? null) === null ? 0.9 : null;
+					}}
+				>
+					{#if (params?.top_p ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.top_p ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="1"
+						step="0.05"
+						bind:value={params.top_p}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.top_p}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="1"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Min P')}
+				</div>
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.min_p = (params?.min_p ?? null) === null ? 0.0 : null;
+					}}
+				>
+					{#if (params?.min_p ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.min_p ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="1"
+						step="0.05"
+						bind:value={params.min_p}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.min_p}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="1"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Frequency Penalty')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.frequency_penalty = (params?.frequency_penalty ?? null) === null ? 1.1 : null;
+					}}
+				>
+					{#if (params?.frequency_penalty ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.frequency_penalty ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="2"
+						step="0.05"
+						bind:value={params.frequency_penalty}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.frequency_penalty}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="2"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Repeat Last N')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.repeat_last_n = (params?.repeat_last_n ?? null) === null ? 64 : null;
+					}}
+				>
+					{#if (params?.repeat_last_n ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.repeat_last_n ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="-1"
+						max="128"
+						step="1"
+						bind:value={params.repeat_last_n}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.repeat_last_n}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="-1"
+						max="128"
+						step="1"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Tfs Z')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.tfs_z = (params?.tfs_z ?? null) === null ? 1 : null;
+					}}
+				>
+					{#if (params?.tfs_z ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.tfs_z ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="0"
+						max="2"
+						step="0.05"
+						bind:value={params.tfs_z}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.tfs_z}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="0"
+						max="2"
+						step="any"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'Sets the size of the context window used to generate the next token. (Default: 2048)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Context Length')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.num_ctx = (params?.num_ctx ?? null) === null ? 2048 : null;
+					}}
+				>
+					{#if (params?.num_ctx ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.num_ctx ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="-1"
+						max="10240000"
+						step="1"
+						bind:value={params.num_ctx}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div class="">
+					<input
+						bind:value={params.num_ctx}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="-1"
+						step="1"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Batch Size (num_batch)')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.num_batch = (params?.num_batch ?? null) === null ? 512 : null;
+					}}
+				>
+					{#if (params?.num_batch ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.num_batch ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="256"
+						max="8192"
+						step="256"
+						bind:value={params.num_batch}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.num_batch}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="256"
+						step="256"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Tokens To Keep On Context Refresh (num_keep)')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
+					}}
+				>
+					{#if (params?.num_keep ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.num_keep ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="-1"
+						max="10240000"
+						step="1"
+						bind:value={params.num_keep}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div class="">
+					<input
+						bind:value={params.num_keep}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="-1"
+						step="1"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class=" py-0.5 w-full justify-between">
+		<Tooltip
+			content={$i18n.t(
+				'This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)'
+			)}
+			placement="top-start"
+			className="inline-tooltip"
+		>
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Max Tokens (num_predict)')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null;
+					}}
+				>
+					{#if (params?.max_tokens ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+		</Tooltip>
+
+		{#if (params?.max_tokens ?? null) !== null}
+			<div class="flex mt-0.5 space-x-2">
+				<div class=" flex-1">
+					<input
+						id="steps-range"
+						type="range"
+						min="-2"
+						max="131072"
+						step="1"
+						bind:value={params.max_tokens}
+						class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+					/>
+				</div>
+				<div>
+					<input
+						bind:value={params.max_tokens}
+						type="number"
+						class=" bg-transparent text-center w-14"
+						min="-2"
+						step="1"
+					/>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	{#if admin}
+		<div class=" py-0.5 w-full justify-between">
+			<Tooltip
+				content={$i18n.t(
+					'Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.'
+				)}
+				placement="top-start"
+				className="inline-tooltip"
+			>
+				<div class="flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('use_mmap (Ollama)')}
+					</div>
+					<button
+						class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+						type="button"
+						on:click={() => {
+							params.use_mmap = (params?.use_mmap ?? null) === null ? true : null;
+						}}
+					>
+						{#if (params?.use_mmap ?? null) === null}
+							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+						{/if}
+					</button>
+				</div>
+			</Tooltip>
+
+			{#if (params?.use_mmap ?? null) !== null}
+				<div class="flex justify-between items-center mt-1">
+					<div class="text-xs text-gray-500">
+						{params.use_mmap ? 'Enabled' : 'Disabled'}
+					</div>
+					<div class=" pr-2">
+						<Switch bind:state={params.use_mmap} />
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<div class=" py-0.5 w-full justify-between">
+			<Tooltip
+				content={$i18n.t(
+					"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access."
+				)}
+				placement="top-start"
+				className="inline-tooltip"
+			>
+				<div class="flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('use_mlock (Ollama)')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+						type="button"
+						on:click={() => {
+							params.use_mlock = (params?.use_mlock ?? null) === null ? true : null;
+						}}
+					>
+						{#if (params?.use_mlock ?? null) === null}
+							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+						{/if}
+					</button>
+				</div>
+			</Tooltip>
+
+			{#if (params?.use_mlock ?? null) !== null}
+				<div class="flex justify-between items-center mt-1">
+					<div class="text-xs text-gray-500">
+						{params.use_mlock ? 'Enabled' : 'Disabled'}
+					</div>
+
+					<div class=" pr-2">
+						<Switch bind:state={params.use_mlock} />
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<div class=" py-0.5 w-full justify-between">
+			<Tooltip
+				content={$i18n.t(
+					'Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.'
+				)}
+				placement="top-start"
+				className="inline-tooltip"
+			>
+				<div class="flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('num_thread (Ollama)')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+						type="button"
+						on:click={() => {
+							params.num_thread = (params?.num_thread ?? null) === null ? 2 : null;
+						}}
+					>
+						{#if (params?.num_thread ?? null) === null}
+							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+						{/if}
+					</button>
+				</div>
+			</Tooltip>
+
+			{#if (params?.num_thread ?? null) !== null}
+				<div class="flex mt-0.5 space-x-2">
+					<div class=" flex-1">
+						<input
+							id="steps-range"
+							type="range"
+							min="1"
+							max="256"
+							step="1"
+							bind:value={params.num_thread}
+							class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+						/>
+					</div>
+					<div class="">
+						<input
+							bind:value={params.num_thread}
+							type="number"
+							class=" bg-transparent text-center w-14"
+							min="1"
+							max="256"
+							step="1"
+						/>
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<div class=" py-0.5 w-full justify-between">
+			<Tooltip
+				content={$i18n.t(
+					'Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.'
+				)}
+				placement="top-start"
+				className="inline-tooltip"
+			>
+				<div class="flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">
+						{$i18n.t('num_gpu (Ollama)')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+						type="button"
+						on:click={() => {
+							params.num_gpu = (params?.num_gpu ?? null) === null ? 0 : null;
+						}}
+					>
+						{#if (params?.num_gpu ?? null) === null}
+							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+						{/if}
+					</button>
+				</div>
+			</Tooltip>
+
+			{#if (params?.num_gpu ?? null) !== null}
+				<div class="flex mt-0.5 space-x-2">
+					<div class=" flex-1">
+						<input
+							id="steps-range"
+							type="range"
+							min="0"
+							max="256"
+							step="1"
+							bind:value={params.num_gpu}
+							class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
+						/>
+					</div>
+					<div class="">
+						<input
+							bind:value={params.num_gpu}
+							type="number"
+							class=" bg-transparent text-center w-14"
+							min="0"
+							max="256"
+							step="1"
+						/>
+					</div>
+				</div>
+			{/if}
+		</div>
+
+		<!-- <div class=" py-0.5 w-full justify-between">
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition flex-shrink-0 outline-none"
+					type="button"
+					on:click={() => {
+						params.template = (params?.template ?? null) === null ? '' : null;
+					}}
+				>
+					{#if (params?.template ?? null) === null}
+						<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+					{/if}
+				</button>
+			</div>
+
+			{#if (params?.template ?? null) !== null}
+				<div class="flex mt-0.5 space-x-2">
+					<div class=" flex-1">
+						<textarea
+							class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
+							placeholder={$i18n.t('Write your model template content here')}
+							rows="4"
+							bind:value={params.template}
+						/>
+					</div>
+				</div>
+			{/if}
+		</div> -->
+	{/if}
+</div>
diff --git a/src/lib/components/chat/Settings/Audio.svelte b/src/lib/components/chat/Settings/Audio.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e56fe056a4821026b7d6a39244bc6dc1804b8780
--- /dev/null
+++ b/src/lib/components/chat/Settings/Audio.svelte
@@ -0,0 +1,242 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+
+	import { user, settings, config } from '$lib/stores';
+	import { getVoices as _getVoices } from '$lib/apis/audio';
+
+	import Switch from '$lib/components/common/Switch.svelte';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let saveSettings: Function;
+
+	// Audio
+	let conversationMode = false;
+	let speechAutoSend = false;
+	let responseAutoPlayback = false;
+	let nonLocalVoices = false;
+
+	let STTEngine = '';
+
+	let voices = [];
+	let voice = '';
+
+	// Audio speed control
+	let playbackRate = 1;
+	const speedOptions = [2, 1.75, 1.5, 1.25, 1, 0.75, 0.5];
+
+	const getVoices = async () => {
+		if ($config.audio.tts.engine === '') {
+			const getVoicesLoop = setInterval(async () => {
+				voices = await speechSynthesis.getVoices();
+
+				// do your loop
+				if (voices.length > 0) {
+					clearInterval(getVoicesLoop);
+				}
+			}, 100);
+		} else {
+			const res = await _getVoices(localStorage.token).catch((e) => {
+				toast.error(e);
+			});
+
+			if (res) {
+				console.log(res);
+				voices = res.voices;
+			}
+		}
+	};
+
+	const toggleResponseAutoPlayback = async () => {
+		responseAutoPlayback = !responseAutoPlayback;
+		saveSettings({ responseAutoPlayback: responseAutoPlayback });
+	};
+
+	const toggleSpeechAutoSend = async () => {
+		speechAutoSend = !speechAutoSend;
+		saveSettings({ speechAutoSend: speechAutoSend });
+	};
+
+	onMount(async () => {
+		playbackRate = $settings.audio?.tts?.playbackRate ?? 1;
+		conversationMode = $settings.conversationMode ?? false;
+		speechAutoSend = $settings.speechAutoSend ?? false;
+		responseAutoPlayback = $settings.responseAutoPlayback ?? false;
+
+		STTEngine = $settings?.audio?.stt?.engine ?? '';
+
+		if ($settings?.audio?.tts?.defaultVoice === $config.audio.tts.voice) {
+			voice = $settings?.audio?.tts?.voice ?? $config.audio.tts.voice ?? '';
+		} else {
+			voice = $config.audio.tts.voice ?? '';
+		}
+
+		nonLocalVoices = $settings.audio?.tts?.nonLocalVoices ?? false;
+
+		await getVoices();
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={async () => {
+		saveSettings({
+			audio: {
+				stt: {
+					engine: STTEngine !== '' ? STTEngine : undefined
+				},
+				tts: {
+					playbackRate: playbackRate,
+					voice: voice !== '' ? voice : undefined,
+					defaultVoice: $config?.audio?.tts?.voice ?? '',
+					nonLocalVoices: $config.audio.tts.engine === '' ? nonLocalVoices : undefined
+				}
+			}
+		});
+		dispatch('save');
+	}}
+>
+	<div class=" space-y-3 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div>
+			<div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div>
+
+			{#if $config.audio.stt.engine !== 'web'}
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div>
+					<div class="flex items-center relative">
+						<select
+							class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+							bind:value={STTEngine}
+							placeholder="Select an engine"
+						>
+							<option value="">{$i18n.t('Default')}</option>
+							<option value="web">{$i18n.t('Web API')}</option>
+						</select>
+					</div>
+				</div>
+			{/if}
+
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{$i18n.t('Instant Auto-Send After Voice Transcription')}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						toggleSpeechAutoSend();
+					}}
+					type="button"
+				>
+					{#if speechAutoSend === true}
+						<span class="ml-2 self-center">{$i18n.t('On')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+					{/if}
+				</button>
+			</div>
+		</div>
+
+		<div>
+			<div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div>
+
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Auto-playback response')}</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					on:click={() => {
+						toggleResponseAutoPlayback();
+					}}
+					type="button"
+				>
+					{#if responseAutoPlayback === true}
+						<span class="ml-2 self-center">{$i18n.t('On')}</span>
+					{:else}
+						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+					{/if}
+				</button>
+			</div>
+
+			<div class=" py-0.5 flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Speech Playback Speed')}</div>
+
+				<div class="flex items-center relative">
+					<select
+						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
+						bind:value={playbackRate}
+					>
+						{#each speedOptions as option}
+							<option value={option} selected={playbackRate === option}>{option}x</option>
+						{/each}
+					</select>
+				</div>
+			</div>
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		{#if $config.audio.tts.engine === ''}
+			<div>
+				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
+				<div class="flex w-full">
+					<div class="flex-1">
+						<select
+							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+							bind:value={voice}
+						>
+							<option value="" selected={voice !== ''}>{$i18n.t('Default')}</option>
+							{#each voices.filter((v) => nonLocalVoices || v.localService === true) as _voice}
+								<option
+									value={_voice.name}
+									class="bg-gray-100 dark:bg-gray-700"
+									selected={voice === _voice.name}>{_voice.name}</option
+								>
+							{/each}
+						</select>
+					</div>
+				</div>
+				<div class="flex items-center justify-between my-1.5">
+					<div class="text-xs">
+						{$i18n.t('Allow non-local voices')}
+					</div>
+
+					<div class="mt-1">
+						<Switch bind:state={nonLocalVoices} />
+					</div>
+				</div>
+			</div>
+		{:else if $config.audio.tts.engine !== ''}
+			<div>
+				<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
+				<div class="flex w-full">
+					<div class="flex-1">
+						<input
+							list="voice-list"
+							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+							bind:value={voice}
+							placeholder="Select a voice"
+						/>
+
+						<datalist id="voice-list">
+							{#each voices as voice}
+								<option value={voice.id}>{voice.name}</option>
+							{/each}
+						</datalist>
+					</div>
+				</div>
+			</div>
+		{/if}
+	</div>
+
+	<div class="flex justify-end text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/chat/Settings/Chats.svelte b/src/lib/components/chat/Settings/Chats.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9944266a9469ac4715d25f2183eea4c2ff7d2293
--- /dev/null
+++ b/src/lib/components/chat/Settings/Chats.svelte
@@ -0,0 +1,332 @@
+<script lang="ts">
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { chats, user, settings, scrollPaginationEnabled, currentChatPage } from '$lib/stores';
+
+	import {
+		archiveAllChats,
+		createNewChat,
+		deleteAllChats,
+		getAllChats,
+		getAllUserChats,
+		getChatList
+	} from '$lib/apis/chats';
+	import { getImportOrigin, convertOpenAIChats } from '$lib/utils';
+	import { onMount, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	export let saveSettings: Function;
+
+	// Chats
+	let importFiles;
+
+	let showArchiveConfirm = false;
+	let showDeleteConfirm = false;
+
+	let chatImportInputElement: HTMLInputElement;
+
+	$: if (importFiles) {
+		console.log(importFiles);
+
+		let reader = new FileReader();
+		reader.onload = (event) => {
+			let chats = JSON.parse(event.target.result);
+			console.log(chats);
+			if (getImportOrigin(chats) == 'openai') {
+				try {
+					chats = convertOpenAIChats(chats);
+				} catch (error) {
+					console.log('Unable to import chats:', error);
+				}
+			}
+			importChats(chats);
+		};
+
+		if (importFiles.length > 0) {
+			reader.readAsText(importFiles[0]);
+		}
+	}
+
+	const importChats = async (_chats) => {
+		for (const chat of _chats) {
+			console.log(chat);
+
+			if (chat.chat) {
+				await createNewChat(localStorage.token, chat.chat);
+			} else {
+				await createNewChat(localStorage.token, chat);
+			}
+		}
+
+		currentChatPage.set(1);
+		await chats.set(await getChatList(localStorage.token, $currentChatPage));
+		scrollPaginationEnabled.set(true);
+	};
+
+	const exportChats = async () => {
+		let blob = new Blob([JSON.stringify(await getAllChats(localStorage.token))], {
+			type: 'application/json'
+		});
+		saveAs(blob, `chat-export-${Date.now()}.json`);
+	};
+
+	const archiveAllChatsHandler = async () => {
+		await goto('/');
+		await archiveAllChats(localStorage.token).catch((error) => {
+			toast.error(error);
+		});
+
+		currentChatPage.set(1);
+		await chats.set(await getChatList(localStorage.token, $currentChatPage));
+		scrollPaginationEnabled.set(true);
+	};
+
+	const deleteAllChatsHandler = async () => {
+		await goto('/');
+		await deleteAllChats(localStorage.token).catch((error) => {
+			toast.error(error);
+		});
+
+		currentChatPage.set(1);
+		await chats.set(await getChatList(localStorage.token, $currentChatPage));
+		scrollPaginationEnabled.set(true);
+	};
+</script>
+
+<div class="flex flex-col h-full justify-between space-y-3 text-sm">
+	<div class=" space-y-2 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div class="flex flex-col">
+			<input
+				id="chat-import-input"
+				bind:this={chatImportInputElement}
+				bind:files={importFiles}
+				type="file"
+				accept=".json"
+				hidden
+			/>
+			<button
+				class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={() => {
+					chatImportInputElement.click();
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">{$i18n.t('Import Chats')}</div>
+			</button>
+			<button
+				class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+				on:click={() => {
+					exportChats();
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 16 16"
+						fill="currentColor"
+						class="w-4 h-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center text-sm font-medium">{$i18n.t('Export Chats')}</div>
+			</button>
+		</div>
+
+		<hr class=" dark:border-gray-850" />
+
+		<div class="flex flex-col">
+			{#if showArchiveConfirm}
+				<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
+					<div class="flex items-center space-x-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+							<path
+								fill-rule="evenodd"
+								d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+						<span>{$i18n.t('Are you sure?')}</span>
+					</div>
+
+					<div class="flex space-x-1.5 items-center">
+						<button
+							class="hover:text-white transition"
+							on:click={() => {
+								archiveAllChatsHandler();
+								showArchiveConfirm = false;
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</button>
+						<button
+							class="hover:text-white transition"
+							on:click={() => {
+								showArchiveConfirm = false;
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+								/>
+							</svg>
+						</button>
+					</div>
+				</div>
+			{:else}
+				<button
+					class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+					on:click={() => {
+						showArchiveConfirm = true;
+					}}
+				>
+					<div class=" self-center mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 24 24"
+							fill="currentColor"
+							class="size-4"
+						>
+							<path
+								d="M3.375 3C2.339 3 1.5 3.84 1.5 4.875v.75c0 1.036.84 1.875 1.875 1.875h17.25c1.035 0 1.875-.84 1.875-1.875v-.75C22.5 3.839 21.66 3 20.625 3H3.375Z"
+							/>
+							<path
+								fill-rule="evenodd"
+								d="m3.087 9 .54 9.176A3 3 0 0 0 6.62 21h10.757a3 3 0 0 0 2.995-2.824L20.913 9H3.087Zm6.163 3.75A.75.75 0 0 1 10 12h4a.75.75 0 0 1 0 1.5h-4a.75.75 0 0 1-.75-.75Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center text-sm font-medium">{$i18n.t('Archive All Chats')}</div>
+				</button>
+			{/if}
+
+			{#if showDeleteConfirm}
+				<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
+					<div class="flex items-center space-x-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
+							<path
+								fill-rule="evenodd"
+								d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+						<span>{$i18n.t('Are you sure?')}</span>
+					</div>
+
+					<div class="flex space-x-1.5 items-center">
+						<button
+							class="hover:text-white transition"
+							on:click={() => {
+								deleteAllChatsHandler();
+								showDeleteConfirm = false;
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									fill-rule="evenodd"
+									d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
+									clip-rule="evenodd"
+								/>
+							</svg>
+						</button>
+						<button
+							class="hover:text-white transition"
+							on:click={() => {
+								showDeleteConfirm = false;
+							}}
+						>
+							<svg
+								xmlns="http://www.w3.org/2000/svg"
+								viewBox="0 0 20 20"
+								fill="currentColor"
+								class="w-4 h-4"
+							>
+								<path
+									d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+								/>
+							</svg>
+						</button>
+					</div>
+				</div>
+			{:else}
+				<button
+					class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
+					on:click={() => {
+						showDeleteConfirm = true;
+					}}
+				>
+					<div class=" self-center mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm7 7a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1 0-1.5h4.5A.75.75 0 0 1 11 9Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center text-sm font-medium">{$i18n.t('Delete All Chats')}</div>
+				</button>
+			{/if}
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Settings/General.svelte b/src/lib/components/chat/Settings/General.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..694648206f4eb58b8ef2ea252ac8f0ae9d09dfe2
--- /dev/null
+++ b/src/lib/components/chat/Settings/General.svelte
@@ -0,0 +1,363 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	import { getLanguages } from '$lib/i18n';
+	const dispatch = createEventDispatcher();
+
+	import { models, settings, theme, user } from '$lib/stores';
+
+	const i18n = getContext('i18n');
+
+	import AdvancedParams from './Advanced/AdvancedParams.svelte';
+
+	export let saveSettings: Function;
+	export let getModels: Function;
+
+	// General
+	let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light', 'oled-dark'];
+	let selectedTheme = 'system';
+
+	let languages: Awaited<ReturnType<typeof getLanguages>> = [];
+	let lang = $i18n.language;
+	let notificationEnabled = false;
+	let system = '';
+
+	let showAdvanced = false;
+
+	const toggleNotification = async () => {
+		const permission = await Notification.requestPermission();
+
+		if (permission === 'granted') {
+			notificationEnabled = !notificationEnabled;
+			saveSettings({ notificationEnabled: notificationEnabled });
+		} else {
+			toast.error(
+				$i18n.t(
+					'Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.'
+				)
+			);
+		}
+	};
+
+	// Advanced
+	let requestFormat = '';
+	let keepAlive: string | null = null;
+
+	let params = {
+		// Advanced
+		stream_response: null,
+		seed: null,
+		temperature: null,
+		frequency_penalty: null,
+		repeat_last_n: null,
+		mirostat: null,
+		mirostat_eta: null,
+		mirostat_tau: null,
+		top_k: null,
+		top_p: null,
+		min_p: null,
+		stop: null,
+		tfs_z: null,
+		num_ctx: null,
+		num_batch: null,
+		num_keep: null,
+		max_tokens: null,
+		num_gpu: null
+	};
+
+	const toggleRequestFormat = async () => {
+		if (requestFormat === '') {
+			requestFormat = 'json';
+		} else {
+			requestFormat = '';
+		}
+
+		saveSettings({ requestFormat: requestFormat !== '' ? requestFormat : undefined });
+	};
+
+	onMount(async () => {
+		selectedTheme = localStorage.theme ?? 'system';
+
+		languages = await getLanguages();
+
+		notificationEnabled = $settings.notificationEnabled ?? false;
+		system = $settings.system ?? '';
+
+		requestFormat = $settings.requestFormat ?? '';
+		keepAlive = $settings.keepAlive ?? null;
+
+		params = { ...params, ...$settings.params };
+		params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null;
+	});
+
+	const applyTheme = (_theme: string) => {
+		let themeToApply = _theme === 'oled-dark' ? 'dark' : _theme;
+
+		if (_theme === 'system') {
+			themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+		}
+
+		if (themeToApply === 'dark' && !_theme.includes('oled')) {
+			document.documentElement.style.setProperty('--color-gray-800', '#333');
+			document.documentElement.style.setProperty('--color-gray-850', '#262626');
+			document.documentElement.style.setProperty('--color-gray-900', '#171717');
+			document.documentElement.style.setProperty('--color-gray-950', '#0d0d0d');
+		}
+
+		themes
+			.filter((e) => e !== themeToApply)
+			.forEach((e) => {
+				e.split(' ').forEach((e) => {
+					document.documentElement.classList.remove(e);
+				});
+			});
+
+		themeToApply.split(' ').forEach((e) => {
+			document.documentElement.classList.add(e);
+		});
+
+		const metaThemeColor = document.querySelector('meta[name="theme-color"]');
+		if (metaThemeColor) {
+			if (_theme.includes('system')) {
+				const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
+					? 'dark'
+					: 'light';
+				console.log('Setting system meta theme color: ' + systemTheme);
+				metaThemeColor.setAttribute('content', systemTheme === 'light' ? '#ffffff' : '#171717');
+			} else {
+				console.log('Setting meta theme color: ' + _theme);
+				metaThemeColor.setAttribute(
+					'content',
+					_theme === 'dark'
+						? '#171717'
+						: _theme === 'oled-dark'
+							? '#000000'
+							: _theme === 'her'
+								? '#983724'
+								: '#ffffff'
+				);
+			}
+		}
+
+		console.log(_theme);
+	};
+
+	const themeChangeHandler = (_theme: string) => {
+		theme.set(_theme);
+		localStorage.setItem('theme', _theme);
+		if (_theme.includes('oled')) {
+			document.documentElement.style.setProperty('--color-gray-800', '#101010');
+			document.documentElement.style.setProperty('--color-gray-850', '#050505');
+			document.documentElement.style.setProperty('--color-gray-900', '#000000');
+			document.documentElement.style.setProperty('--color-gray-950', '#000000');
+			document.documentElement.classList.add('dark');
+		}
+		applyTheme(_theme);
+	};
+</script>
+
+<div class="flex flex-col h-full justify-between text-sm">
+	<div class="  overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div class="">
+			<div class=" mb-1 text-sm font-medium">{$i18n.t('WebUI Settings')}</div>
+
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Theme')}</div>
+				<div class="flex items-center relative">
+					<select
+						class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
+						bind:value={selectedTheme}
+						placeholder="Select a theme"
+						on:change={() => themeChangeHandler(selectedTheme)}
+					>
+						<option value="system">⚙️ {$i18n.t('System')}</option>
+						<option value="dark">🌑 {$i18n.t('Dark')}</option>
+						<option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option>
+						<option value="light">☀️ {$i18n.t('Light')}</option>
+						<option value="her">🌷 Her</option>
+						<!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
+						<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> -->
+					</select>
+				</div>
+			</div>
+
+			<div class=" flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">{$i18n.t('Language')}</div>
+				<div class="flex items-center relative">
+					<select
+						class=" dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
+						bind:value={lang}
+						placeholder="Select a language"
+						on:change={(e) => {
+							$i18n.changeLanguage(lang);
+						}}
+					>
+						{#each languages as language}
+							<option value={language['code']}>{language['title']}</option>
+						{/each}
+					</select>
+				</div>
+			</div>
+			{#if $i18n.language === 'en-US'}
+				<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
+					Couldn't find your language?
+					<a
+						class=" text-gray-300 font-medium underline"
+						href="https://github.com/open-webui/open-webui/blob/main/docs/CONTRIBUTING.md#-translations-and-internationalization"
+						target="_blank"
+					>
+						Help us translate Open WebUI!
+					</a>
+				</div>
+			{/if}
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs font-medium">{$i18n.t('Notifications')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleNotification();
+						}}
+						type="button"
+					>
+						{#if notificationEnabled === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+		</div>
+
+		<hr class=" dark:border-gray-850 my-3" />
+
+		<div>
+			<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
+			<textarea
+				bind:value={system}
+				class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
+				rows="4"
+			/>
+		</div>
+
+		<div class="mt-2 space-y-3 pr-1.5">
+			<div class="flex justify-between items-center text-sm">
+				<div class="  font-medium">{$i18n.t('Advanced Parameters')}</div>
+				<button
+					class=" text-xs font-medium text-gray-500"
+					type="button"
+					on:click={() => {
+						showAdvanced = !showAdvanced;
+					}}>{showAdvanced ? $i18n.t('Hide') : $i18n.t('Show')}</button
+				>
+			</div>
+
+			{#if showAdvanced}
+				<AdvancedParams admin={$user?.role === 'admin'} bind:params />
+				<hr class=" dark:border-gray-850" />
+
+				<div class=" py-1 w-full justify-between">
+					<div class="flex w-full justify-between">
+						<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							type="button"
+							on:click={() => {
+								keepAlive = keepAlive === null ? '5m' : null;
+							}}
+						>
+							{#if keepAlive === null}
+								<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
+							{:else}
+								<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+							{/if}
+						</button>
+					</div>
+
+					{#if keepAlive !== null}
+						<div class="flex mt-1 space-x-2">
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
+								type="text"
+								placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
+								bind:value={keepAlive}
+							/>
+						</div>
+					{/if}
+				</div>
+
+				<div>
+					<div class=" py-1 flex w-full justify-between">
+						<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							on:click={() => {
+								toggleRequestFormat();
+							}}
+						>
+							{#if requestFormat === ''}
+								<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
+							{:else if requestFormat === 'json'}
+								<!-- <svg
+                            xmlns="http://www.w3.org/2000/svg"
+                            viewBox="0 0 20 20"
+                            fill="currentColor"
+                            class="w-4 h-4 self-center"
+                        >
+                            <path
+                                d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
+                            />
+                        </svg> -->
+								<span class="ml-2 self-center"> {$i18n.t('JSON')} </span>
+							{/if}
+						</button>
+					</div>
+				</div>
+			{/if}
+		</div>
+	</div>
+
+	<div class="flex justify-end pt-3 text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			on:click={() => {
+				saveSettings({
+					system: system !== '' ? system : undefined,
+					params: {
+						stream_response: params.stream_response !== null ? params.stream_response : undefined,
+						seed: (params.seed !== null ? params.seed : undefined) ?? undefined,
+						stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined,
+						temperature: params.temperature !== null ? params.temperature : undefined,
+						frequency_penalty:
+							params.frequency_penalty !== null ? params.frequency_penalty : undefined,
+						repeat_last_n: params.repeat_last_n !== null ? params.repeat_last_n : undefined,
+						mirostat: params.mirostat !== null ? params.mirostat : undefined,
+						mirostat_eta: params.mirostat_eta !== null ? params.mirostat_eta : undefined,
+						mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined,
+						top_k: params.top_k !== null ? params.top_k : undefined,
+						top_p: params.top_p !== null ? params.top_p : undefined,
+						min_p: params.min_p !== null ? params.min_p : undefined,
+						tfs_z: params.tfs_z !== null ? params.tfs_z : undefined,
+						num_ctx: params.num_ctx !== null ? params.num_ctx : undefined,
+						num_batch: params.num_batch !== null ? params.num_batch : undefined,
+						num_keep: params.num_keep !== null ? params.num_keep : undefined,
+						max_tokens: params.max_tokens !== null ? params.max_tokens : undefined,
+						use_mmap: params.use_mmap !== null ? params.use_mmap : undefined,
+						use_mlock: params.use_mlock !== null ? params.use_mlock : undefined,
+						num_thread: params.num_thread !== null ? params.num_thread : undefined,
+						num_gpu: params.num_gpu !== null ? params.num_gpu : undefined
+					},
+					keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
+				});
+				dispatch('save');
+			}}
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</div>
diff --git a/src/lib/components/chat/Settings/Interface.svelte b/src/lib/components/chat/Settings/Interface.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1098458b24d30c2aec5cbdd54520764dc83acf9f
--- /dev/null
+++ b/src/lib/components/chat/Settings/Interface.svelte
@@ -0,0 +1,676 @@
+<script lang="ts">
+	import { getBackendConfig } from '$lib/apis';
+	import { setDefaultPromptSuggestions } from '$lib/apis/configs';
+	import { config, models, settings, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { updateUserInfo } from '$lib/apis/users';
+	import { getUserPosition } from '$lib/utils';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let saveSettings: Function;
+
+	let backgroundImageUrl = null;
+	let inputFiles = null;
+	let filesInputElement;
+
+	// Addons
+	let titleAutoGenerate = true;
+	let autoTags = true;
+
+	let responseAutoCopy = false;
+	let widescreenMode = false;
+	let splitLargeChunks = false;
+	let scrollOnBranchChange = true;
+	let userLocation = false;
+
+	// Interface
+	let defaultModelId = '';
+	let showUsername = false;
+	let richTextInput = true;
+	let largeTextAsFile = false;
+
+	let landingPageMode = '';
+	let chatBubble = true;
+	let chatDirection: 'LTR' | 'RTL' = 'LTR';
+
+	// Admin - Show Update Available Toast
+	let showUpdateToast = true;
+	let showChangelog = true;
+
+	let showEmojiInCall = false;
+	let voiceInterruption = false;
+	let hapticFeedback = false;
+
+	const toggleSplitLargeChunks = async () => {
+		splitLargeChunks = !splitLargeChunks;
+		saveSettings({ splitLargeChunks: splitLargeChunks });
+	};
+
+	const togglesScrollOnBranchChange = async () => {
+		scrollOnBranchChange = !scrollOnBranchChange;
+		saveSettings({ scrollOnBranchChange: scrollOnBranchChange });
+	};
+
+	const toggleWidescreenMode = async () => {
+		widescreenMode = !widescreenMode;
+		saveSettings({ widescreenMode: widescreenMode });
+	};
+
+	const toggleChatBubble = async () => {
+		chatBubble = !chatBubble;
+		saveSettings({ chatBubble: chatBubble });
+	};
+
+	const toggleLandingPageMode = async () => {
+		landingPageMode = landingPageMode === '' ? 'chat' : '';
+		saveSettings({ landingPageMode: landingPageMode });
+	};
+
+	const toggleShowUpdateToast = async () => {
+		showUpdateToast = !showUpdateToast;
+		saveSettings({ showUpdateToast: showUpdateToast });
+	};
+
+	const toggleShowChangelog = async () => {
+		showChangelog = !showChangelog;
+		saveSettings({ showChangelog: showChangelog });
+	};
+
+	const toggleShowUsername = async () => {
+		showUsername = !showUsername;
+		saveSettings({ showUsername: showUsername });
+	};
+
+	const toggleEmojiInCall = async () => {
+		showEmojiInCall = !showEmojiInCall;
+		saveSettings({ showEmojiInCall: showEmojiInCall });
+	};
+
+	const toggleVoiceInterruption = async () => {
+		voiceInterruption = !voiceInterruption;
+		saveSettings({ voiceInterruption: voiceInterruption });
+	};
+
+	const toggleHapticFeedback = async () => {
+		hapticFeedback = !hapticFeedback;
+		saveSettings({ hapticFeedback: hapticFeedback });
+	};
+
+	const toggleUserLocation = async () => {
+		userLocation = !userLocation;
+
+		if (userLocation) {
+			const position = await getUserPosition().catch((error) => {
+				toast.error(error.message);
+				return null;
+			});
+
+			if (position) {
+				await updateUserInfo(localStorage.token, { location: position });
+				toast.success($i18n.t('User location successfully retrieved.'));
+			} else {
+				userLocation = false;
+			}
+		}
+
+		saveSettings({ userLocation });
+	};
+
+	const toggleTitleAutoGenerate = async () => {
+		titleAutoGenerate = !titleAutoGenerate;
+		saveSettings({
+			title: {
+				...$settings.title,
+				auto: titleAutoGenerate
+			}
+		});
+	};
+
+	const toggleAutoTags = async () => {
+		autoTags = !autoTags;
+		saveSettings({ autoTags });
+	};
+
+	const toggleRichTextInput = async () => {
+		richTextInput = !richTextInput;
+		saveSettings({ richTextInput });
+	};
+
+	const toggleLargeTextAsFile = async () => {
+		largeTextAsFile = !largeTextAsFile;
+		saveSettings({ largeTextAsFile });
+	};
+
+	const toggleResponseAutoCopy = async () => {
+		const permission = await navigator.clipboard
+			.readText()
+			.then(() => {
+				return 'granted';
+			})
+			.catch(() => {
+				return '';
+			});
+
+		console.log(permission);
+
+		if (permission === 'granted') {
+			responseAutoCopy = !responseAutoCopy;
+			saveSettings({ responseAutoCopy: responseAutoCopy });
+		} else {
+			toast.error(
+				$i18n.t(
+					'Clipboard write permission denied. Please check your browser settings to grant the necessary access.'
+				)
+			);
+		}
+	};
+
+	const toggleChangeChatDirection = async () => {
+		chatDirection = chatDirection === 'LTR' ? 'RTL' : 'LTR';
+		saveSettings({ chatDirection });
+	};
+
+	const updateInterfaceHandler = async () => {
+		saveSettings({
+			models: [defaultModelId]
+		});
+	};
+
+	onMount(async () => {
+		titleAutoGenerate = $settings?.title?.auto ?? true;
+		autoTags = $settings.autoTags ?? true;
+
+		responseAutoCopy = $settings.responseAutoCopy ?? false;
+
+		showUsername = $settings.showUsername ?? false;
+		showUpdateToast = $settings.showUpdateToast ?? true;
+		showChangelog = $settings.showChangelog ?? true;
+
+		showEmojiInCall = $settings.showEmojiInCall ?? false;
+		voiceInterruption = $settings.voiceInterruption ?? false;
+
+		richTextInput = $settings.richTextInput ?? true;
+		largeTextAsFile = $settings.largeTextAsFile ?? false;
+
+		landingPageMode = $settings.landingPageMode ?? '';
+		chatBubble = $settings.chatBubble ?? true;
+		widescreenMode = $settings.widescreenMode ?? false;
+		splitLargeChunks = $settings.splitLargeChunks ?? false;
+		scrollOnBranchChange = $settings.scrollOnBranchChange ?? true;
+		chatDirection = $settings.chatDirection ?? 'LTR';
+		userLocation = $settings.userLocation ?? false;
+
+		hapticFeedback = $settings.hapticFeedback ?? false;
+
+		defaultModelId = $settings?.models?.at(0) ?? '';
+		if ($config?.default_models) {
+			defaultModelId = $config.default_models.split(',')[0];
+		}
+
+		backgroundImageUrl = $settings.backgroundImageUrl ?? null;
+	});
+</script>
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={() => {
+		updateInterfaceHandler();
+		dispatch('save');
+	}}
+>
+	<input
+		bind:this={filesInputElement}
+		bind:files={inputFiles}
+		type="file"
+		hidden
+		accept="image/*"
+		on:change={() => {
+			let reader = new FileReader();
+			reader.onload = (event) => {
+				let originalImageUrl = `${event.target.result}`;
+
+				backgroundImageUrl = originalImageUrl;
+				saveSettings({ backgroundImageUrl });
+			};
+
+			if (
+				inputFiles &&
+				inputFiles.length > 0 &&
+				['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(inputFiles[0]['type'])
+			) {
+				reader.readAsDataURL(inputFiles[0]);
+			} else {
+				console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
+				inputFiles = null;
+			}
+		}}
+	/>
+
+	<div class=" space-y-3 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div>
+			<div class=" mb-1.5 text-sm font-medium">{$i18n.t('UI')}</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Landing Page Mode')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleLandingPageMode();
+						}}
+						type="button"
+					>
+						{#if landingPageMode === ''}
+							<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Chat')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Chat Bubble UI')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleChatBubble();
+						}}
+						type="button"
+					>
+						{#if chatBubble === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			{#if !$settings.chatBubble}
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs">
+							{$i18n.t('Display the username instead of You in the Chat')}
+						</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							on:click={() => {
+								toggleShowUsername();
+							}}
+							type="button"
+						>
+							{#if showUsername === true}
+								<span class="ml-2 self-center">{$i18n.t('On')}</span>
+							{:else}
+								<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+							{/if}
+						</button>
+					</div>
+				</div>
+			{/if}
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Widescreen Mode')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleWidescreenMode();
+						}}
+						type="button"
+					>
+						{#if widescreenMode === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Chat direction')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={toggleChangeChatDirection}
+						type="button"
+					>
+						{#if chatDirection === 'LTR'}
+							<span class="ml-2 self-center">{$i18n.t('LTR')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('RTL')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			{#if $user.role === 'admin'}
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs">
+							{$i18n.t('Toast notifications for new updates')}
+						</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							on:click={() => {
+								toggleShowUpdateToast();
+							}}
+							type="button"
+						>
+							{#if showUpdateToast === true}
+								<span class="ml-2 self-center">{$i18n.t('On')}</span>
+							{:else}
+								<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+							{/if}
+						</button>
+					</div>
+				</div>
+
+				<div>
+					<div class=" py-0.5 flex w-full justify-between">
+						<div class=" self-center text-xs">
+							{$i18n.t(`Show "What's New" modal on login`)}
+						</div>
+
+						<button
+							class="p-1 px-3 text-xs flex rounded transition"
+							on:click={() => {
+								toggleShowChangelog();
+							}}
+							type="button"
+						>
+							{#if showChangelog === true}
+								<span class="ml-2 self-center">{$i18n.t('On')}</span>
+							{:else}
+								<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+							{/if}
+						</button>
+					</div>
+				</div>
+			{/if}
+
+			<div class=" my-1.5 text-sm font-medium">{$i18n.t('Chat')}</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Title Auto-Generation')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleTitleAutoGenerate();
+						}}
+						type="button"
+					>
+						{#if titleAutoGenerate === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Chat Tags Auto-Generation')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleAutoTags();
+						}}
+						type="button"
+					>
+						{#if autoTags === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Auto-Copy Response to Clipboard')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleResponseAutoCopy();
+						}}
+						type="button"
+					>
+						{#if responseAutoCopy === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Rich Text Input for Chat')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleRichTextInput();
+						}}
+						type="button"
+					>
+						{#if richTextInput === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Paste Large Text as File')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleLargeTextAsFile();
+						}}
+						type="button"
+					>
+						{#if largeTextAsFile === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Chat Background Image')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							if (backgroundImageUrl !== null) {
+								backgroundImageUrl = null;
+								saveSettings({ backgroundImageUrl });
+							} else {
+								filesInputElement.click();
+							}
+						}}
+						type="button"
+					>
+						{#if backgroundImageUrl !== null}
+							<span class="ml-2 self-center">{$i18n.t('Reset')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Upload')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Allow User Location')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleUserLocation();
+						}}
+						type="button"
+					>
+						{#if userLocation === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Haptic Feedback')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleHapticFeedback();
+						}}
+						type="button"
+					>
+						{#if hapticFeedback === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Fluidly stream large external response chunks')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleSplitLargeChunks();
+						}}
+						type="button"
+					>
+						{#if splitLargeChunks === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">
+						{$i18n.t('Scroll to bottom when switching between branches')}
+					</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							togglesScrollOnBranchChange();
+						}}
+						type="button"
+					>
+						{#if scrollOnBranchChange === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div class=" my-1.5 text-sm font-medium">{$i18n.t('Voice')}</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Allow Voice Interruption in Call')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleVoiceInterruption();
+						}}
+						type="button"
+					>
+						{#if voiceInterruption === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class=" py-0.5 flex w-full justify-between">
+					<div class=" self-center text-xs">{$i18n.t('Display Emoji in Call')}</div>
+
+					<button
+						class="p-1 px-3 text-xs flex rounded transition"
+						on:click={() => {
+							toggleEmojiInCall();
+						}}
+						type="button"
+					>
+						{#if showEmojiInCall === true}
+							<span class="ml-2 self-center">{$i18n.t('On')}</span>
+						{:else}
+							<span class="ml-2 self-center">{$i18n.t('Off')}</span>
+						{/if}
+					</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class="flex justify-end text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/chat/Settings/Personalization.svelte b/src/lib/components/chat/Settings/Personalization.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2f0eb9212f668b07cd8ec5db7030d8b451d7db81
--- /dev/null
+++ b/src/lib/components/chat/Settings/Personalization.svelte
@@ -0,0 +1,98 @@
+<script lang="ts">
+	import { getBackendConfig } from '$lib/apis';
+	import { setDefaultPromptSuggestions } from '$lib/apis/configs';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import { config, models, settings, user } from '$lib/stores';
+	import { createEventDispatcher, onMount, getContext, tick } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import ManageModal from './Personalization/ManageModal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let saveSettings: Function;
+
+	let showManageModal = false;
+
+	// Addons
+	let enableMemory = false;
+
+	onMount(async () => {
+		enableMemory = $settings?.memory ?? false;
+	});
+</script>
+
+<ManageModal bind:show={showManageModal} />
+
+<form
+	class="flex flex-col h-full justify-between space-y-3 text-sm"
+	on:submit|preventDefault={() => {
+		dispatch('save');
+	}}
+>
+	<div class="py-1 overflow-y-scroll max-h-[28rem] lg:max-h-full">
+		<div>
+			<div class="flex items-center justify-between mb-1">
+				<Tooltip
+					content={$i18n.t(
+						'This is an experimental feature, it may not function as expected and is subject to change at any time.'
+					)}
+				>
+					<div class="text-sm font-medium">
+						{$i18n.t('Memory')}
+
+						<span class=" text-xs text-gray-500">({$i18n.t('Experimental')})</span>
+					</div>
+				</Tooltip>
+
+				<div class="">
+					<Switch
+						bind:state={enableMemory}
+						on:change={async () => {
+							saveSettings({ memory: enableMemory });
+						}}
+					/>
+				</div>
+			</div>
+		</div>
+
+		<div class="text-xs text-gray-600 dark:text-gray-400">
+			<div>
+				{$i18n.t(
+					"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you."
+				)}
+			</div>
+
+			<!-- <div class="mt-3">
+				To understand what LLM remembers or teach it something new, just chat with it:
+
+				<div>- “Remember that I like concise responses.”</div>
+				<div>- “I just got a puppy!”</div>
+				<div>- “What do you remember about me?”</div>
+				<div>- “Where did we leave off on my last project?”</div>
+			</div> -->
+		</div>
+
+		<div class="mt-3 mb-1 ml-1">
+			<button
+				type="button"
+				class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
+				on:click={() => {
+					showManageModal = true;
+				}}
+			>
+				{$i18n.t('Manage')}
+			</button>
+		</div>
+	</div>
+
+	<div class="flex justify-end text-sm font-medium">
+		<button
+			class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+			type="submit"
+		>
+			{$i18n.t('Save')}
+		</button>
+	</div>
+</form>
diff --git a/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte b/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6e16576b0c3b883304bcad5c7c663e0154446ca2
--- /dev/null
+++ b/src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte
@@ -0,0 +1,126 @@
+<script>
+	import { createEventDispatcher, getContext } from 'svelte';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import { addNewMemory, updateMemoryById } from '$lib/apis/memories';
+	import { toast } from 'svelte-sonner';
+
+	const dispatch = createEventDispatcher();
+
+	export let show;
+	const i18n = getContext('i18n');
+
+	let loading = false;
+	let content = '';
+
+	const submitHandler = async () => {
+		loading = true;
+
+		const res = await addNewMemory(localStorage.token, content).catch((error) => {
+			toast.error(error);
+
+			return null;
+		});
+
+		if (res) {
+			console.log(res);
+			toast.success($i18n.t('Memory added successfully'));
+			content = '';
+			show = false;
+			dispatch('save');
+		}
+
+		loading = false;
+	};
+</script>
+
+<Modal bind:show size="sm">
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">
+				{$i18n.t('Add Memory')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="">
+						<textarea
+							bind:value={content}
+							class=" bg-transparent w-full text-sm resize-none rounded-xl p-3 outline outline-1 outline-gray-100 dark:outline-gray-800"
+							rows="3"
+							placeholder={$i18n.t('Enter a detail about yourself for your LLMs to recall')}
+						/>
+
+						<div class="text-xs text-gray-500">
+							ⓘ {$i18n.t('Refer to yourself as "User" (e.g., "User is learning Spanish")')}
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-1 text-sm font-medium">
+						<button
+							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-3xl flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Add')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte b/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..773309ff9e12db5d4d53c1714a5819ea6b52786b
--- /dev/null
+++ b/src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte
@@ -0,0 +1,136 @@
+<script>
+	import { createEventDispatcher, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	import { updateMemoryById } from '$lib/apis/memories';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	export let show;
+	export let memory = {};
+
+	const i18n = getContext('i18n');
+
+	let loading = false;
+	let content = '';
+
+	$: if (show) {
+		setContent();
+	}
+
+	const setContent = () => {
+		content = memory.content;
+	};
+
+	const submitHandler = async () => {
+		loading = true;
+
+		const res = await updateMemoryById(localStorage.token, memory.id, content).catch((error) => {
+			toast.error(error);
+
+			return null;
+		});
+
+		if (res) {
+			console.log(res);
+			toast.success($i18n.t('Memory updated successfully'));
+			dispatch('save');
+			show = false;
+		}
+
+		loading = false;
+	};
+</script>
+
+<Modal bind:show size="sm">
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">
+				{$i18n.t('Edit Memory')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="">
+						<textarea
+							bind:value={content}
+							class=" bg-transparent w-full text-sm resize-none rounded-xl p-3 outline outline-1 outline-gray-100 dark:outline-gray-800"
+							rows="3"
+							placeholder={$i18n.t('Enter a detail about yourself for your LLMs to recall')}
+						/>
+
+						<div class="text-xs text-gray-500">
+							ⓘ {$i18n.t('Refer to yourself as "User" (e.g., "User is learning Spanish")')}
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-1 text-sm font-medium">
+						<button
+							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-3xl flex flex-row space-x-1 items-center {loading
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={loading}
+						>
+							{$i18n.t('Update')}
+
+							{#if loading}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Settings/Personalization/ManageModal.svelte b/src/lib/components/chat/Settings/Personalization/ManageModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..92b4936e91b4ca8eb75e5f7ed990ebc1d2cdc0fe
--- /dev/null
+++ b/src/lib/components/chat/Settings/Personalization/ManageModal.svelte
@@ -0,0 +1,208 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import AddMemoryModal from './AddMemoryModal.svelte';
+	import { deleteMemoriesByUserId, deleteMemoryById, getMemories } from '$lib/apis/memories';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { error } from '@sveltejs/kit';
+	import EditMemoryModal from './EditMemoryModal.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+
+	let memories = [];
+	let loading = true;
+
+	let showAddMemoryModal = false;
+	let showEditMemoryModal = false;
+
+	let selectedMemory = null;
+
+	$: if (show && memories.length === 0 && loading) {
+		(async () => {
+			memories = await getMemories(localStorage.token);
+			loading = false;
+		})();
+	}
+</script>
+
+<Modal size="xl" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Memory')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col w-full px-5 pb-5 dark:text-gray-200">
+			<div
+				class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6 h-[28rem] max-h-screen outline outline-1 rounded-xl outline-gray-100 dark:outline-gray-800 mb-4 mt-1"
+			>
+				{#if memories.length > 0}
+					<div class="text-left text-sm w-full mb-4 overflow-y-scroll">
+						<div class="relative overflow-x-auto">
+							<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
+								<thead
+									class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-800"
+								>
+									<tr>
+										<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
+										<th scope="col" class="px-3 py-2 hidden md:flex">
+											{$i18n.t('Last Modified')}
+										</th>
+										<th scope="col" class="px-3 py-2 text-right" />
+									</tr>
+								</thead>
+								<tbody>
+									{#each memories as memory}
+										<tr class="border-b dark:border-gray-800 items-center">
+											<td class="px-3 py-1">
+												<div class="line-clamp-1">
+													{memory.content}
+												</div>
+											</td>
+											<td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
+												<div class="my-auto whitespace-nowrap">
+													{dayjs(memory.updated_at * 1000).format(
+														$i18n.t('MMMM DD, YYYY hh:mm:ss A')
+													)}
+												</div>
+											</td>
+											<td class="px-3 py-1">
+												<div class="flex justify-end w-full">
+													<Tooltip content="Edit">
+														<button
+															class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+															on:click={() => {
+																selectedMemory = memory;
+																showEditMemoryModal = true;
+															}}
+														>
+															<svg
+																xmlns="http://www.w3.org/2000/svg"
+																fill="none"
+																viewBox="0 0 24 24"
+																stroke-width="1.5"
+																stroke="currentColor"
+																class="w-4 h-4 s-FoVA_WMOgxUD"
+																><path
+																	stroke-linecap="round"
+																	stroke-linejoin="round"
+																	d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
+																	class="s-FoVA_WMOgxUD"
+																/></svg
+															>
+														</button>
+													</Tooltip>
+
+													<Tooltip content="Delete">
+														<button
+															class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+															on:click={async () => {
+																const res = await deleteMemoryById(
+																	localStorage.token,
+																	memory.id
+																).catch((error) => {
+																	toast.error(error);
+																	return null;
+																});
+
+																if (res) {
+																	toast.success($i18n.t('Memory deleted successfully'));
+																	memories = await getMemories(localStorage.token);
+																}
+															}}
+														>
+															<svg
+																xmlns="http://www.w3.org/2000/svg"
+																fill="none"
+																viewBox="0 0 24 24"
+																stroke-width="1.5"
+																stroke="currentColor"
+																class="w-4 h-4"
+															>
+																<path
+																	stroke-linecap="round"
+																	stroke-linejoin="round"
+																	d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+																/>
+															</svg>
+														</button>
+													</Tooltip>
+												</div>
+											</td>
+										</tr>
+									{/each}
+								</tbody>
+							</table>
+						</div>
+					</div>
+				{:else}
+					<div class="text-center flex h-full text-sm w-full">
+						<div class=" my-auto pb-10 px-4 w-full text-gray-500">
+							{$i18n.t('Memories accessible by LLMs will be shown here.')}
+						</div>
+					</div>
+				{/if}
+			</div>
+			<div class="flex text-sm font-medium gap-1.5">
+				<button
+					class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
+					on:click={() => {
+						showAddMemoryModal = true;
+					}}>{$i18n.t('Add Memory')}</button
+				>
+				<button
+					class=" px-3.5 py-1.5 font-medium text-red-500 hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-red-300 dark:outline-red-800 rounded-3xl"
+					on:click={async () => {
+						const res = await deleteMemoriesByUserId(localStorage.token).catch((error) => {
+							toast.error(error);
+							return null;
+						});
+
+						if (res) {
+							toast.success($i18n.t('Memory cleared successfully'));
+							memories = [];
+						}
+					}}>{$i18n.t('Clear memory')}</button
+				>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<AddMemoryModal
+	bind:show={showAddMemoryModal}
+	on:save={async () => {
+		memories = await getMemories(localStorage.token);
+	}}
+/>
+
+<EditMemoryModal
+	bind:show={showEditMemoryModal}
+	memory={selectedMemory}
+	on:save={async () => {
+		memories = await getMemories(localStorage.token);
+	}}
+/>
diff --git a/src/lib/components/chat/SettingsModal.svelte b/src/lib/components/chat/SettingsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2c381a62fe9af9dce3062f363ac4602397b3efc6
--- /dev/null
+++ b/src/lib/components/chat/SettingsModal.svelte
@@ -0,0 +1,672 @@
+<script lang="ts">
+	import { getContext, tick } from 'svelte';
+	import { toast } from 'svelte-sonner';
+	import { models, settings, user } from '$lib/stores';
+	import { updateUserSettings } from '$lib/apis/users';
+	import { getModels as _getModels } from '$lib/apis';
+	import { goto } from '$app/navigation';
+
+	import Modal from '../common/Modal.svelte';
+	import Account from './Settings/Account.svelte';
+	import About from './Settings/About.svelte';
+	import General from './Settings/General.svelte';
+	import Interface from './Settings/Interface.svelte';
+	import Audio from './Settings/Audio.svelte';
+	import Chats from './Settings/Chats.svelte';
+	import User from '../icons/User.svelte';
+	import Personalization from './Settings/Personalization.svelte';
+	import SearchInput from '../layout/Sidebar/SearchInput.svelte';
+	import Search from '../icons/Search.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+
+	interface SettingsTab {
+		id: string;
+		title: string;
+		keywords: string[];
+	}
+
+	const searchData: SettingsTab[] = [
+		{
+			id: 'general',
+			title: 'General',
+			keywords: [
+				'general',
+				'theme',
+				'language',
+				'notifications',
+				'system',
+				'systemprompt',
+				'prompt',
+				'advanced',
+				'settings',
+				'defaultsettings',
+				'configuration',
+				'systemsettings',
+				'notificationsettings',
+				'systempromptconfig',
+				'languageoptions',
+				'defaultparameters',
+				'systemparameters'
+			]
+		},
+		{
+			id: 'interface',
+			title: 'Interface',
+			keywords: [
+				'defaultmodel',
+				'selectmodel',
+				'ui',
+				'userinterface',
+				'display',
+				'layout',
+				'design',
+				'landingpage',
+				'landingpagemode',
+				'default',
+				'chat',
+				'chatbubble',
+				'chatui',
+				'username',
+				'showusername',
+				'displayusername',
+				'widescreen',
+				'widescreenmode',
+				'fullscreen',
+				'expandmode',
+				'chatdirection',
+				'lefttoright',
+				'ltr',
+				'righttoleft',
+				'rtl',
+				'notifications',
+				'toast',
+				'toastnotifications',
+				'largechunks',
+				'streamlargechunks',
+				'scroll',
+				'scrollonbranchchange',
+				'scrollbehavior',
+				'richtext',
+				'richtextinput',
+				'background',
+				'chatbackground',
+				'chatbackgroundimage',
+				'backgroundimage',
+				'uploadbackground',
+				'resetbackground',
+				'titleautogen',
+				'titleautogeneration',
+				'autotitle',
+				'chattags',
+				'autochattags',
+				'responseautocopy',
+				'clipboard',
+				'location',
+				'userlocation',
+				'userlocationaccess',
+				'haptic',
+				'hapticfeedback',
+				'vibration',
+				'voice',
+				'voicecontrol',
+				'voiceinterruption',
+				'call',
+				'emojis',
+				'displayemoji',
+				'save',
+				'interfaceoptions',
+				'interfacecustomization'
+			]
+		},
+		{
+			id: 'personalization',
+			title: 'Personalization',
+			keywords: [
+				'personalization',
+				'memory',
+				'personalize',
+				'preferences',
+				'profile',
+				'personalsettings',
+				'customsettings',
+				'userpreferences',
+				'accountpreferences'
+			]
+		},
+		{
+			id: 'audio',
+			title: 'Audio',
+			keywords: [
+				'audio',
+				'sound',
+				'soundsettings',
+				'audiocontrol',
+				'volume',
+				'speech',
+				'speechrecognition',
+				'stt',
+				'speechtotext',
+				'tts',
+				'texttospeech',
+				'playback',
+				'playbackspeed',
+				'voiceplayback',
+				'speechplayback',
+				'audiooutput',
+				'speechengine',
+				'voicecontrol',
+				'audioplayback',
+				'transcription',
+				'autotranscribe',
+				'autosend',
+				'speechsettings',
+				'audiovoice',
+				'voiceoptions',
+				'setvoice',
+				'nonlocalvoices',
+				'savesettings',
+				'audioconfig',
+				'speechconfig',
+				'voicerecognition',
+				'speechsynthesis',
+				'speechmode',
+				'voicespeed',
+				'speechrate',
+				'speechspeed',
+				'audioinput',
+				'audiofeatures',
+				'voicemodes'
+			]
+		},
+		{
+			id: 'chats',
+			title: 'Chats',
+			keywords: [
+				'chat',
+				'messages',
+				'conversations',
+				'chatsettings',
+				'history',
+				'chathistory',
+				'messagehistory',
+				'messagearchive',
+				'convo',
+				'chats',
+				'conversationhistory',
+				'exportmessages',
+				'chatactivity'
+			]
+		},
+		{
+			id: 'account',
+			title: 'Account',
+			keywords: [
+				'account',
+				'profile',
+				'security',
+				'privacy',
+				'settings',
+				'login',
+				'useraccount',
+				'userdata',
+				'api',
+				'apikey',
+				'userprofile',
+				'profiledetails',
+				'accountsettings',
+				'accountpreferences',
+				'securitysettings',
+				'privacysettings'
+			]
+		},
+		{
+			id: 'admin',
+			title: 'Admin',
+			keywords: [
+				'admin',
+				'administrator',
+				'adminsettings',
+				'adminpanel',
+				'systemadmin',
+				'administratoraccess',
+				'systemcontrol',
+				'manage',
+				'management',
+				'admincontrols',
+				'adminfeatures',
+				'usercontrol',
+				'arenamodel',
+				'evaluations',
+				'websearch',
+				'database',
+				'pipelines',
+				'images',
+				'audio',
+				'documents',
+				'rag',
+				'models',
+				'ollama',
+				'openai',
+				'users'
+			]
+		},
+		{
+			id: 'about',
+			title: 'About',
+			keywords: [
+				'about',
+				'info',
+				'information',
+				'version',
+				'documentation',
+				'help',
+				'support',
+				'details',
+				'aboutus',
+				'softwareinfo',
+				'timothyjaeryangbaek',
+				'openwebui',
+				'release',
+				'updates',
+				'updateinfo',
+				'versioninfo',
+				'aboutapp',
+				'terms',
+				'termsandconditions',
+				'contact',
+				'aboutpage'
+			]
+		}
+	];
+
+	let search = '';
+	let visibleTabs = searchData.map((tab) => tab.id);
+	let searchDebounceTimeout;
+
+	const searchSettings = (query: string): string[] => {
+		const lowerCaseQuery = query.toLowerCase().trim();
+		return searchData
+			.filter(
+				(tab) =>
+					tab.title.toLowerCase().includes(lowerCaseQuery) ||
+					tab.keywords.some((keyword) => keyword.includes(lowerCaseQuery))
+			)
+			.map((tab) => tab.id);
+	};
+
+	const searchDebounceHandler = () => {
+		clearTimeout(searchDebounceTimeout);
+		searchDebounceTimeout = setTimeout(() => {
+			visibleTabs = searchSettings(search);
+			if (visibleTabs.length > 0 && !visibleTabs.includes(selectedTab)) {
+				selectedTab = visibleTabs[0];
+			}
+		}, 100);
+	};
+
+	const saveSettings = async (updated) => {
+		console.log(updated);
+		await settings.set({ ...$settings, ...updated });
+		await models.set(await getModels());
+		await updateUserSettings(localStorage.token, { ui: $settings });
+	};
+
+	const getModels = async () => {
+		return await _getModels(localStorage.token);
+	};
+
+	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>
+
+<Modal size="xl" bind:show>
+	<div class="text-gray-700 dark:text-gray-100">
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Settings')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-4 pt-1 pb-4 md:space-x-4">
+			<div
+				id="settings-tabs-container"
+				class="tabs flex flex-row overflow-x-auto gap-2.5 md:gap-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-sm font-medium text-left mb-1 md:mb-0 -translate-y-1"
+			>
+				<div class="hidden md:flex w-full rounded-xl -mb-1 px-0.5 gap-2" id="settings-search">
+					<div class="self-center rounded-l-xl bg-transparent">
+						<Search className="size-3.5" />
+					</div>
+					<input
+						class="w-full py-1.5 text-sm bg-transparent dark:text-gray-300 outline-none"
+						bind:value={search}
+						on:input={searchDebounceHandler}
+						placeholder={$i18n.t('Search')}
+					/>
+				</div>
+
+				{#if visibleTabs.length > 0}
+					{#each visibleTabs as tabId (tabId)}
+						{#if tabId === 'general'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'general'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'general';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M8.34 1.804A1 1 0 019.32 1h1.36a1 1 0 01.98.804l.295 1.473c.497.144.971.342 1.416.587l1.25-.834a1 1 0 011.262.125l.962.962a1 1 0 01.125 1.262l-.834 1.25c.245.445.443.919.587 1.416l1.473.294a1 1 0 01.804.98v1.361a1 1 0 01-.804.98l-1.473.295a6.95 6.95 0 01-.587 1.416l.834 1.25a1 1 0 01-.125 1.262l-.962.962a1 1 0 01-1.262.125l-1.25-.834a6.953 6.953 0 01-1.416.587l-.294 1.473a1 1 0 01-.98.804H9.32a1 1 0 01-.98-.804l-.295-1.473a6.957 6.957 0 01-1.416-.587l-1.25.834a1 1 0 01-1.262-.125l-.962-.962a1 1 0 01-.125-1.262l.834-1.25a6.957 6.957 0 01-.587-1.416l-1.473-.294A1 1 0 011 10.68V9.32a1 1 0 01.804-.98l1.473-.295c.144-.497.342-.971.587-1.416l-.834-1.25a1 1 0 01.125-1.262l.962-.962A1 1 0 015.38 3.03l1.25.834a6.957 6.957 0 011.416-.587l.294-1.473zM13 10a3 3 0 11-6 0 3 3 0 016 0z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('General')}</div>
+							</button>
+						{:else if tabId === 'interface'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'interface'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'interface';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 16 16"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M2 4.25A2.25 2.25 0 0 1 4.25 2h7.5A2.25 2.25 0 0 1 14 4.25v5.5A2.25 2.25 0 0 1 11.75 12h-1.312c.1.128.21.248.328.36a.75.75 0 0 1 .234.545v.345a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-.345a.75.75 0 0 1 .234-.545c.118-.111.228-.232.328-.36H4.25A2.25 2.25 0 0 1 2 9.75v-5.5Zm2.25-.75a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h7.5a.75.75 0 0 0 .75-.75v-4.5a.75.75 0 0 0-.75-.75h-7.5Z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('Interface')}</div>
+							</button>
+						{:else if tabId === 'personalization'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'personalization'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'personalization';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<User />
+								</div>
+								<div class=" self-center">{$i18n.t('Personalization')}</div>
+							</button>
+						{:else if tabId === 'audio'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'audio'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'audio';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 16 16"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											d="M7.557 2.066A.75.75 0 0 1 8 2.75v10.5a.75.75 0 0 1-1.248.56L3.59 11H2a1 1 0 0 1-1-1V6a1 1 0 0 1 1-1h1.59l3.162-2.81a.75.75 0 0 1 .805-.124ZM12.95 3.05a.75.75 0 1 0-1.06 1.06 5.5 5.5 0 0 1 0 7.78.75.75 0 1 0 1.06 1.06 7 7 0 0 0 0-9.9Z"
+										/>
+										<path
+											d="M10.828 5.172a.75.75 0 1 0-1.06 1.06 2.5 2.5 0 0 1 0 3.536.75.75 0 1 0 1.06 1.06 4 4 0 0 0 0-5.656Z"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('Audio')}</div>
+							</button>
+						{:else if tabId === 'chats'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'chats'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'chats';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 16 16"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M8 2C4.262 2 1 4.57 1 8c0 1.86.98 3.486 2.455 4.566a3.472 3.472 0 0 1-.469 1.26.75.75 0 0 0 .713 1.14 6.961 6.961 0 0 0 3.06-1.06c.403.062.818.094 1.241.094 3.738 0 7-2.57 7-6s-3.262-6-7-6ZM5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Zm7-1a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM8 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('Chats')}</div>
+							</button>
+						{:else if tabId === 'account'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'account'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'account';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 16 16"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-5-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 9c-1.825 0-3.422.977-4.295 2.437A5.49 5.49 0 0 0 8 13.5a5.49 5.49 0 0 0 4.294-2.063A4.997 4.997 0 0 0 8 9Z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('Account')}</div>
+							</button>
+						{:else if tabId === 'about'}
+							<button
+								class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+								'about'
+									? ''
+									: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+								on:click={() => {
+									selectedTab = 'about';
+								}}
+							>
+								<div class=" self-center mr-2">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<div class=" self-center">{$i18n.t('About')}</div>
+							</button>
+						{:else if tabId === 'admin'}
+							{#if $user.role === 'admin'}
+								<button
+									class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
+									'admin'
+										? ''
+										: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
+									on:click={async () => {
+										await goto('/admin/settings');
+										show = false;
+									}}
+								>
+									<div class=" self-center mr-2">
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 24 24"
+											fill="currentColor"
+											class="size-4"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M4.5 3.75a3 3 0 0 0-3 3v10.5a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V6.75a3 3 0 0 0-3-3h-15Zm4.125 3a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Zm-3.873 8.703a4.126 4.126 0 0 1 7.746 0 .75.75 0 0 1-.351.92 7.47 7.47 0 0 1-3.522.877 7.47 7.47 0 0 1-3.522-.877.75.75 0 0 1-.351-.92ZM15 8.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15ZM14.25 12a.75.75 0 0 1 .75-.75h3.75a.75.75 0 0 1 0 1.5H15a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</div>
+									<div class=" self-center">{$i18n.t('Admin Settings')}</div>
+								</button>
+							{/if}
+						{/if}
+					{/each}
+				{:else}
+					<div class="text-center text-gray-500 mt-4">
+						{$i18n.t('No results found')}
+					</div>
+				{/if}
+			</div>
+			<div class="flex-1 md:min-h-[32rem] max-h-[32rem]">
+				{#if selectedTab === 'general'}
+					<General
+						{getModels}
+						{saveSettings}
+						on:save={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
+				{:else if selectedTab === 'interface'}
+					<Interface
+						{saveSettings}
+						on:save={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
+				{:else if selectedTab === 'personalization'}
+					<Personalization
+						{saveSettings}
+						on:save={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
+				{:else if selectedTab === 'audio'}
+					<Audio
+						{saveSettings}
+						on:save={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
+				{:else if selectedTab === 'chats'}
+					<Chats {saveSettings} />
+				{:else if selectedTab === 'account'}
+					<Account
+						saveHandler={() => {
+							toast.success($i18n.t('Settings saved successfully!'));
+						}}
+					/>
+				{:else if selectedTab === 'about'}
+					<About />
+				{/if}
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/chat/ShareChatModal.svelte b/src/lib/components/chat/ShareChatModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f7cc6d6be52127472076b869282ffa7518f74a88
--- /dev/null
+++ b/src/lib/components/chat/ShareChatModal.svelte
@@ -0,0 +1,202 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+	import { models, config } from '$lib/stores';
+
+	import { toast } from 'svelte-sonner';
+	import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
+	import { copyToClipboard } from '$lib/utils';
+
+	import Modal from '../common/Modal.svelte';
+	import Link from '../icons/Link.svelte';
+
+	export let chatId;
+
+	let chat = null;
+	let shareUrl = null;
+	const i18n = getContext('i18n');
+
+	const shareLocalChat = async () => {
+		const _chat = chat;
+
+		const sharedChat = await shareChatById(localStorage.token, chatId);
+		shareUrl = `${window.location.origin}/s/${sharedChat.id}`;
+		console.log(shareUrl);
+		chat = await getChatById(localStorage.token, chatId);
+
+		return shareUrl;
+	};
+
+	const shareChat = async () => {
+		const _chat = chat.chat;
+		console.log('share', _chat);
+
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+		const url = 'https://openwebui.com';
+		// const url = 'http://localhost:5173';
+
+		const tab = await window.open(`${url}/chats/upload`, '_blank');
+		window.addEventListener(
+			'message',
+			(event) => {
+				if (event.origin !== url) return;
+				if (event.data === 'loaded') {
+					tab.postMessage(
+						JSON.stringify({
+							chat: _chat,
+							models: $models.filter((m) => _chat.models.includes(m.id))
+						}),
+						'*'
+					);
+				}
+			},
+			false
+		);
+	};
+
+	export let show = false;
+
+	const isDifferentChat = (_chat) => {
+		if (!chat) {
+			return true;
+		}
+		if (!_chat) {
+			return false;
+		}
+		return chat.id !== _chat.id || chat.share_id !== _chat.share_id;
+	};
+
+	$: if (show) {
+		(async () => {
+			if (chatId) {
+				const _chat = await getChatById(localStorage.token, chatId);
+				if (isDifferentChat(_chat)) {
+					chat = _chat;
+				}
+			} else {
+				chat = null;
+				console.log(chat);
+			}
+		})();
+	}
+</script>
+
+<Modal bind:show size="md">
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-0.5">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Share Chat')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		{#if chat}
+			<div class="px-5 pt-4 pb-5 w-full flex flex-col justify-center">
+				<div class=" text-sm dark:text-gray-300 mb-1">
+					{#if chat.share_id}
+						<a href="/s/{chat.share_id}" target="_blank"
+							>{$i18n.t('You have shared this chat')}
+							<span class=" underline">{$i18n.t('before')}</span>.</a
+						>
+						{$i18n.t('Click here to')}
+						<button
+							class="underline"
+							on:click={async () => {
+								const res = await deleteSharedChatById(localStorage.token, chatId);
+
+								if (res) {
+									chat = await getChatById(localStorage.token, chatId);
+								}
+							}}
+							>{$i18n.t('delete this link')}
+						</button>
+						{$i18n.t('and create a new shared link.')}
+					{:else}
+						{$i18n.t(
+							"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat."
+						)}
+					{/if}
+				</div>
+
+				<div class="flex justify-end">
+					<div class="flex flex-col items-end space-x-1 mt-1.5">
+						<div class="flex gap-1">
+							{#if $config?.features.enable_community_sharing}
+								<button
+									class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white"
+									type="button"
+									on:click={() => {
+										shareChat();
+										show = false;
+									}}
+								>
+									{$i18n.t('Share to OpenWebUI Community')}
+								</button>
+							{/if}
+
+							<button
+								class=" self-center flex items-center gap-1 px-3.5 py-2 rounded-xl text-sm font-medium bg-emerald-600 hover:bg-emerald-500 text-white"
+								type="button"
+								id="copy-and-share-chat-button"
+								on:click={async () => {
+									const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
+
+									if (isSafari) {
+										// Oh, Safari, you're so special, let's give you some extra love and attention
+										console.log('isSafari');
+
+										const getUrlPromise = async () => {
+											const url = await shareLocalChat();
+											return new Blob([url], { type: 'text/plain' });
+										};
+
+										navigator.clipboard
+											.write([
+												new ClipboardItem({
+													'text/plain': getUrlPromise()
+												})
+											])
+											.then(() => {
+												console.log('Async: Copying to clipboard was successful!');
+												return true;
+											})
+											.catch((error) => {
+												console.error('Async: Could not copy text: ', error);
+												return false;
+											});
+									} else {
+										copyToClipboard(await shareLocalChat());
+									}
+
+									toast.success($i18n.t('Copied shared chat URL to clipboard!'));
+									show = false;
+								}}
+							>
+								<Link />
+
+								{#if chat.share_id}
+									{$i18n.t('Update and Copy Link')}
+								{:else}
+									{$i18n.t('Copy Link')}
+								{/if}
+							</button>
+						</div>
+					</div>
+				</div>
+			</div>
+		{/if}
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/ShortcutsModal.svelte b/src/lib/components/chat/ShortcutsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7a0ec21ab0f4d23f0a101dc7ba47b2e23e40b35f
--- /dev/null
+++ b/src/lib/components/chat/ShortcutsModal.svelte
@@ -0,0 +1,287 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import Modal from '../common/Modal.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+</script>
+
+<Modal bind:show>
+	<div class="text-gray-700 dark:text-gray-100">
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Keyboard shortcuts')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<div class="flex flex-col space-y-3 w-full self-start">
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Open new chat')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								O
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Focus chat input')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Esc
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Copy last code block')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								;
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Copy last response')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								C
+							</div>
+						</div>
+					</div>
+				</div>
+
+				<div class="flex flex-col space-y-3 w-full self-start">
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Toggle settings')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								.
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Toggle sidebar')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								S
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Delete chat')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Shift
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								⌫/Delete
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">{$i18n.t('Show shortcuts')}</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								Ctrl/⌘
+							</div>
+
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								/
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class=" flex justify-between dark:text-gray-300 px-5">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Input commands')}</div>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full p-5 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<div class="flex flex-col space-y-3 w-full self-start">
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">
+							{$i18n.t('Attach file')}
+						</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								#
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">
+							{$i18n.t('Add custom prompt')}
+						</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								/
+							</div>
+						</div>
+					</div>
+
+					<div class="w-full flex justify-between items-center">
+						<div class=" text-sm">
+							{$i18n.t('Select model')}
+						</div>
+
+						<div class="flex space-x-1 text-xs">
+							<div
+								class=" h-fit py-1 px-2 flex items-center justify-center rounded border border-black/10 capitalize text-gray-600 dark:border-white/10 dark:text-gray-300"
+							>
+								@
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/chat/Suggestions.svelte b/src/lib/components/chat/Suggestions.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a73d7961715d7b1b61a97ea8e93ef6008e253eaa
--- /dev/null
+++ b/src/lib/components/chat/Suggestions.svelte
@@ -0,0 +1,53 @@
+<script lang="ts">
+	import Bolt from '$lib/components/icons/Bolt.svelte';
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let suggestionPrompts = [];
+	export let className = '';
+
+	let prompts = [];
+
+	$: prompts = (suggestionPrompts ?? [])
+		.reduce((acc, current) => [...acc, ...[current]], [])
+		.sort(() => Math.random() - 0.5);
+</script>
+
+{#if prompts.length > 0}
+	<div class="mb-1 flex gap-1 text-sm font-medium items-center text-gray-400 dark:text-gray-600">
+		<Bolt />
+		{$i18n.t('Suggested')}
+	</div>
+{/if}
+
+<div class=" h-40 max-h-full overflow-auto scrollbar-none {className}">
+	{#each prompts as prompt, promptIdx}
+		<button
+			class="flex flex-col flex-1 shrink-0 w-full justify-between px-3 py-2 rounded-xl bg-transparent hover:bg-black/5 dark:hover:bg-white/5 transition group"
+			on:click={() => {
+				dispatch('select', prompt.content);
+			}}
+		>
+			<div class="flex flex-col text-left">
+				{#if prompt.title && prompt.title[0] !== ''}
+					<div
+						class="  font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
+					>
+						{prompt.title[0]}
+					</div>
+					<div class="text-xs text-gray-500 font-normal line-clamp-1">{prompt.title[1]}</div>
+				{:else}
+					<div
+						class="  font-medium dark:text-gray-300 dark:group-hover:text-gray-200 transition line-clamp-1"
+					>
+						{prompt.content}
+					</div>
+
+					<div class="text-xs text-gray-500 font-normal line-clamp-1">Prompt</div>
+				{/if}
+			</div>
+		</button>
+	{/each}
+</div>
diff --git a/src/lib/components/chat/TagChatModal.svelte b/src/lib/components/chat/TagChatModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f12247dfadd477241f0b933f85c3c3ef9c5bfbba
--- /dev/null
+++ b/src/lib/components/chat/TagChatModal.svelte
@@ -0,0 +1,28 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import Modal from '../common/Modal.svelte';
+
+	import Tags from '../common/Tags.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let tags;
+	export let deleteTag: Function;
+	export let addTag: Function;
+
+	export let show = false;
+</script>
+
+<Modal bind:show size="xs">
+	<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
+		<Tags
+			{tags}
+			on:delete={(e) => {
+				deleteTag(e.detail);
+			}}
+			on:add={(e) => {
+				addTag(e.detail);
+			}}
+		/>
+	</div>
+</Modal>
diff --git a/src/lib/components/chat/Tags.svelte b/src/lib/components/chat/Tags.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cea7b548e1d0dd1a32e42107f479c8789718f4df
--- /dev/null
+++ b/src/lib/components/chat/Tags.svelte
@@ -0,0 +1,82 @@
+<script>
+	import {
+		addTagById,
+		deleteTagById,
+		getAllTags,
+		getChatList,
+		getChatListByTagName,
+		getTagsById,
+		updateChatById
+	} from '$lib/apis/chats';
+	import {
+		tags as _tags,
+		chats,
+		pinnedChats,
+		currentChatPage,
+		scrollPaginationEnabled
+	} from '$lib/stores';
+	import { createEventDispatcher, onMount } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import Tags from '../common/Tags.svelte';
+	import { toast } from 'svelte-sonner';
+
+	export let chatId = '';
+	let tags = [];
+
+	const getTags = async () => {
+		return await getTagsById(localStorage.token, chatId).catch(async (error) => {
+			return [];
+		});
+	};
+
+	const addTag = async (tagName) => {
+		const res = await addTagById(localStorage.token, chatId, tagName).catch(async (error) => {
+			toast.error(error);
+			return null;
+		});
+		if (!res) {
+			return;
+		}
+
+		tags = await getTags();
+		await updateChatById(localStorage.token, chatId, {
+			tags: tags
+		});
+
+		await _tags.set(await getAllTags(localStorage.token));
+		dispatch('add', {
+			name: tagName
+		});
+	};
+
+	const deleteTag = async (tagName) => {
+		const res = await deleteTagById(localStorage.token, chatId, tagName);
+		tags = await getTags();
+		await updateChatById(localStorage.token, chatId, {
+			tags: tags
+		});
+
+		await _tags.set(await getAllTags(localStorage.token));
+		dispatch('delete', {
+			name: tagName
+		});
+	};
+
+	onMount(async () => {
+		if (chatId) {
+			tags = await getTags();
+		}
+	});
+</script>
+
+<Tags
+	{tags}
+	on:delete={(e) => {
+		deleteTag(e.detail);
+	}}
+	on:add={(e) => {
+		addTag(e.detail);
+	}}
+/>
diff --git a/src/lib/components/common/Badge.svelte b/src/lib/components/common/Badge.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..12ad872c5ee96489be251bb2eb1d36163693dc89
--- /dev/null
+++ b/src/lib/components/common/Badge.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let type = 'info';
+	export let content = '';
+
+	const classNames: Record<string, string> = {
+		info: 'bg-blue-500/20 text-blue-700 dark:text-blue-200 ',
+		success: 'bg-green-500/20 text-green-700 dark:text-green-200',
+		warning: 'bg-yellow-500/20 text-yellow-700 dark:text-yellow-200',
+		error: 'bg-red-500/20 text-red-700 dark:text-red-200',
+		muted: 'bg-gray-500/20 text-gray-700 dark:text-gray-200'
+	};
+</script>
+
+<div
+	class=" text-xs font-bold {classNames[type] ??
+		classNames['info']}  w-fit px-2 rounded uppercase line-clamp-1 mr-0.5"
+>
+	{content}
+</div>
diff --git a/src/lib/components/common/Banner.svelte b/src/lib/components/common/Banner.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..378afdf803d1633b33514f2a8c98cead6508d81a
--- /dev/null
+++ b/src/lib/components/common/Banner.svelte
@@ -0,0 +1,128 @@
+<script lang="ts">
+	import type { Banner } from '$lib/types';
+	import { onMount, createEventDispatcher } from 'svelte';
+	import { fade } from 'svelte/transition';
+	import DOMPurify from 'dompurify';
+	import { marked } from 'marked';
+
+	const dispatch = createEventDispatcher();
+
+	export let banner: Banner = {
+		id: '',
+		type: 'info',
+		title: '',
+		content: '',
+		url: '',
+		dismissable: true,
+		timestamp: Math.floor(Date.now() / 1000)
+	};
+
+	export let dismissed = false;
+
+	let mounted = false;
+
+	const classNames: Record<string, string> = {
+		info: 'bg-blue-500/20 text-blue-700 dark:text-blue-200 ',
+		success: 'bg-green-500/20 text-green-700 dark:text-green-200',
+		warning: 'bg-yellow-500/20 text-yellow-700 dark:text-yellow-200',
+		error: 'bg-red-500/20 text-red-700 dark:text-red-200'
+	};
+
+	const dismiss = (id) => {
+		dismissed = true;
+		dispatch('dismiss', id);
+	};
+
+	onMount(() => {
+		mounted = true;
+	});
+</script>
+
+{#if !dismissed}
+	{#if mounted}
+		<div
+			class=" top-0 left-0 right-0 p-2 mx-4 px-3 flex justify-center items-center relative rounded-xl border border-gray-50 dark:border-gray-850 text-gray-800 dark:text-gary-100 bg-white dark:bg-gray-900 backdrop-blur-xl z-30"
+			transition:fade={{ delay: 100, duration: 300 }}
+		>
+			<div class=" flex flex-col md:flex-row md:items-center flex-1 text-sm w-fit gap-1.5">
+				<div class="flex justify-between self-start">
+					<div
+						class=" text-xs font-bold {classNames[banner.type] ??
+							classNames['info']}  w-fit px-2 rounded uppercase line-clamp-1 mr-0.5"
+					>
+						{banner.type}
+					</div>
+
+					{#if banner.url}
+						<div class="flex md:hidden group w-fit md:items-center">
+							<a
+								class="text-gray-700 dark:text-white text-xs font-semibold underline"
+								href="/assets/files/whitepaper.pdf"
+								target="_blank">Learn More</a
+							>
+
+							<div
+								class=" ml-1 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-white"
+							>
+								<!--  -->
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									viewBox="0 0 16 16"
+									fill="currentColor"
+									class="w-4 h-4"
+								>
+									<path
+										fill-rule="evenodd"
+										d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
+										clip-rule="evenodd"
+									/>
+								</svg>
+							</div>
+						</div>
+					{/if}
+				</div>
+
+				<div class="flex-1 text-xs text-gray-700 dark:text-white">
+					{@html marked.parse(DOMPurify.sanitize(banner.content))}
+				</div>
+			</div>
+
+			{#if banner.url}
+				<div class="hidden md:flex group w-fit md:items-center">
+					<a
+						class="text-gray-700 dark:text-white text-xs font-semibold underline"
+						href="/"
+						target="_blank">Learn More</a
+					>
+
+					<div class=" ml-1 text-gray-400 group-hover:text-gray-600 dark:group-hover:text-white">
+						<!--  -->
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="size-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</div>
+			{/if}
+			<div class="flex self-start">
+				{#if banner.dismissible}
+					<button
+						on:click={() => {
+							dismiss(banner.id);
+						}}
+						class="  -mt-1 -mb-2 -translate-y-[1px] ml-1.5 mr-1 text-gray-400 dark:hover:text-white"
+						>&times;</button
+					>
+				{/if}
+			</div>
+		</div>
+	{/if}
+{/if}
diff --git a/src/lib/components/common/Checkbox.svelte b/src/lib/components/common/Checkbox.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..3d43a69509db387136209b670ba6d2eea873546f
--- /dev/null
+++ b/src/lib/components/common/Checkbox.svelte
@@ -0,0 +1,71 @@
+<script lang="ts">
+	import { createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	export let state = 'unchecked';
+	export let indeterminate = false;
+
+	let _state = 'unchecked';
+
+	$: _state = state;
+</script>
+
+<button
+	class=" outline -outline-offset-1 outline-[1.5px] outline-gray-200 dark:outline-gray-600 {state !==
+	'unchecked'
+		? 'bg-black outline-black '
+		: 'hover:outline-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800'} text-white transition-all rounded inline-block w-3.5 h-3.5 relative"
+	on:click={() => {
+		if (_state === 'unchecked') {
+			_state = 'checked';
+			dispatch('change', _state);
+		} else if (_state === 'checked') {
+			_state = 'unchecked';
+			if (!indeterminate) {
+				dispatch('change', _state);
+			}
+		} else if (indeterminate) {
+			_state = 'checked';
+			dispatch('change', _state);
+		}
+	}}
+	type="button"
+>
+	<div class="top-0 left-0 absolute w-full flex justify-center">
+		{#if _state === 'checked'}
+			<svg
+				class="w-3.5 h-3.5"
+				aria-hidden="true"
+				xmlns="http://www.w3.org/2000/svg"
+				fill="none"
+				viewBox="0 0 24 24"
+			>
+				<path
+					stroke="currentColor"
+					stroke-linecap="round"
+					stroke-linejoin="round"
+					stroke-width="3"
+					d="m5 12 4.7 4.5 9.3-9"
+				/>
+			</svg>
+		{:else if indeterminate}
+			<svg
+				class="w-3 h-3.5 text-gray-800 dark:text-white"
+				aria-hidden="true"
+				xmlns="http://www.w3.org/2000/svg"
+				fill="none"
+				viewBox="0 0 24 24"
+			>
+				<path
+					stroke="currentColor"
+					stroke-linecap="round"
+					stroke-linejoin="round"
+					stroke-width="3"
+					d="M5 12h14"
+				/>
+			</svg>
+		{/if}
+	</div>
+
+	<!-- {checked} -->
+</button>
diff --git a/src/lib/components/common/CodeEditor.svelte b/src/lib/components/common/CodeEditor.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b521978f4bcc5e8dbaabbf0bf2031439aafde627
--- /dev/null
+++ b/src/lib/components/common/CodeEditor.svelte
@@ -0,0 +1,183 @@
+<script lang="ts">
+	import { basicSetup, EditorView } from 'codemirror';
+	import { keymap, placeholder } from '@codemirror/view';
+	import { Compartment, EditorState } from '@codemirror/state';
+
+	import { acceptCompletion } from '@codemirror/autocomplete';
+	import { indentWithTab } from '@codemirror/commands';
+
+	import { indentUnit } from '@codemirror/language';
+	import { languages } from '@codemirror/language-data';
+
+	import { oneDark } from '@codemirror/theme-one-dark';
+
+	import { onMount, createEventDispatcher, getContext, tick } from 'svelte';
+
+	import { formatPythonCode } from '$lib/apis/utils';
+	import { toast } from 'svelte-sonner';
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	export let boilerplate = '';
+	export let value = '';
+	let _value = '';
+
+	$: if (value) {
+		updateValue();
+	}
+
+	const updateValue = () => {
+		if (_value !== value) {
+			_value = value;
+			if (codeEditor) {
+				codeEditor.dispatch({
+					changes: [{ from: 0, to: codeEditor.state.doc.length, insert: _value }]
+				});
+			}
+		}
+	};
+
+	export let id = '';
+	export let lang = '';
+
+	let codeEditor;
+
+	let isDarkMode = false;
+	let editorTheme = new Compartment();
+	let editorLanguage = new Compartment();
+
+	const getLang = async () => {
+		const language = languages.find((l) => l.alias.includes(lang));
+		return await language?.load();
+	};
+
+	export const formatPythonCodeHandler = async () => {
+		if (codeEditor) {
+			const res = await formatPythonCode(_value).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (res && res.code) {
+				const formattedCode = res.code;
+				codeEditor.dispatch({
+					changes: [{ from: 0, to: codeEditor.state.doc.length, insert: formattedCode }]
+				});
+
+				_value = formattedCode;
+				dispatch('change', { value: _value });
+				await tick();
+
+				toast.success($i18n.t('Code formatted successfully'));
+				return true;
+			}
+			return false;
+		}
+		return false;
+	};
+
+	let extensions = [
+		basicSetup,
+		keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]),
+		indentUnit.of('    '),
+		placeholder('Enter your code here...'),
+		EditorView.updateListener.of((e) => {
+			if (e.docChanged) {
+				_value = e.state.doc.toString();
+				dispatch('change', { value: _value });
+			}
+		}),
+		editorTheme.of([]),
+		editorLanguage.of([])
+	];
+
+	$: if (lang) {
+		setLanguage();
+	}
+
+	const setLanguage = async () => {
+		const language = await getLang();
+		if (language && codeEditor) {
+			codeEditor.dispatch({
+				effects: editorLanguage.reconfigure(language)
+			});
+		}
+	};
+
+	onMount(() => {
+		console.log(value);
+		if (value === '') {
+			value = boilerplate;
+		}
+
+		_value = value;
+
+		// Check if html class has dark mode
+		isDarkMode = document.documentElement.classList.contains('dark');
+
+		// python code editor, highlight python code
+		codeEditor = new EditorView({
+			state: EditorState.create({
+				doc: _value,
+				extensions: extensions
+			}),
+			parent: document.getElementById(`code-textarea-${id}`)
+		});
+
+		if (isDarkMode) {
+			codeEditor.dispatch({
+				effects: editorTheme.reconfigure(oneDark)
+			});
+		}
+
+		// listen to html class changes this should fire only when dark mode is toggled
+		const observer = new MutationObserver((mutations) => {
+			mutations.forEach((mutation) => {
+				if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
+					const _isDarkMode = document.documentElement.classList.contains('dark');
+
+					if (_isDarkMode !== isDarkMode) {
+						isDarkMode = _isDarkMode;
+						if (_isDarkMode) {
+							codeEditor.dispatch({
+								effects: editorTheme.reconfigure(oneDark)
+							});
+						} else {
+							codeEditor.dispatch({
+								effects: editorTheme.reconfigure()
+							});
+						}
+					}
+				}
+			});
+		});
+
+		observer.observe(document.documentElement, {
+			attributes: true,
+			attributeFilter: ['class']
+		});
+
+		const keydownHandler = async (e) => {
+			if ((e.ctrlKey || e.metaKey) && e.key === 's') {
+				e.preventDefault();
+				dispatch('save');
+			}
+
+			// Format code when Ctrl + Shift + F is pressed
+			if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'f') {
+				e.preventDefault();
+				await formatPythonCodeHandler();
+			}
+		};
+
+		document.addEventListener('keydown', keydownHandler);
+
+		return () => {
+			observer.disconnect();
+			document.removeEventListener('keydown', keydownHandler);
+		};
+	});
+</script>
+
+<div id="code-textarea-{id}" class="h-full w-full" />
diff --git a/src/lib/components/common/Collapsible.svelte b/src/lib/components/common/Collapsible.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a167e3cf260b50a85729cf866b6c6dd64fc6fa5c
--- /dev/null
+++ b/src/lib/components/common/Collapsible.svelte
@@ -0,0 +1,88 @@
+<script lang="ts">
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+	$: dispatch('change', open);
+
+	import { slide } from 'svelte/transition';
+	import { quintOut } from 'svelte/easing';
+
+	import ChevronUp from '../icons/ChevronUp.svelte';
+	import ChevronDown from '../icons/ChevronDown.svelte';
+
+	export let open = false;
+	export let className = '';
+	export let buttonClassName =
+		'w-fit text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition';
+	export let title = null;
+
+	export let grow = false;
+
+	export let disabled = false;
+	export let hide = false;
+</script>
+
+<div class={className}>
+	{#if title !== null}
+		<!-- svelte-ignore a11y-no-static-element-interactions -->
+		<!-- svelte-ignore a11y-click-events-have-key-events -->
+		<div
+			class="{buttonClassName} cursor-pointer"
+			on:pointerup={() => {
+				if (!disabled) {
+					open = !open;
+				}
+			}}
+		>
+			<div class=" w-full font-medium flex items-center justify-between gap-2">
+				<div class="">
+					{title}
+				</div>
+
+				<div>
+					{#if open}
+						<ChevronUp strokeWidth="3.5" className="size-3.5" />
+					{:else}
+						<ChevronDown strokeWidth="3.5" className="size-3.5" />
+					{/if}
+				</div>
+			</div>
+		</div>
+	{:else}
+		<!-- svelte-ignore a11y-no-static-element-interactions -->
+		<!-- svelte-ignore a11y-click-events-have-key-events -->
+		<div
+			class="{buttonClassName} cursor-pointer"
+			on:pointerup={() => {
+				if (!disabled) {
+					open = !open;
+				}
+			}}
+		>
+			<div>
+				<slot />
+
+				{#if grow}
+					{#if open && !hide}
+						<div
+							transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}
+							on:pointerup={(e) => {
+								e.stopPropagation();
+							}}
+						>
+							<slot name="content" />
+						</div>
+					{/if}
+				{/if}
+			</div>
+		</div>
+	{/if}
+
+	{#if !grow}
+		{#if open && !hide}
+			<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
+				<slot name="content" />
+			</div>
+		{/if}
+	{/if}
+</div>
diff --git a/src/lib/components/common/ConfirmDialog.svelte b/src/lib/components/common/ConfirmDialog.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..658174c05230933cedfca288a0d552a08f817bab
--- /dev/null
+++ b/src/lib/components/common/ConfirmDialog.svelte
@@ -0,0 +1,148 @@
+<script lang="ts">
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import { fade } from 'svelte/transition';
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	export let title = '';
+	export let message = '';
+
+	export let cancelLabel = $i18n.t('Cancel');
+	export let confirmLabel = $i18n.t('Confirm');
+
+	export let onConfirm = () => {};
+
+	export let input = false;
+	export let inputPlaceholder = '';
+	export let inputValue = '';
+
+	export let show = false;
+
+	let modalElement = null;
+	let mounted = false;
+
+	const handleKeyDown = (event: KeyboardEvent) => {
+		if (event.key === 'Escape') {
+			console.log('Escape');
+			show = false;
+		}
+
+		if (event.key === 'Enter') {
+			console.log('Enter');
+			confirmHandler();
+		}
+	};
+
+	const confirmHandler = async () => {
+		show = false;
+
+		await onConfirm();
+		dispatch('confirm', inputValue);
+	};
+
+	onMount(() => {
+		mounted = true;
+	});
+
+	$: if (mounted) {
+		if (show) {
+			window.addEventListener('keydown', handleKeyDown);
+			document.body.style.overflow = 'hidden';
+		} else {
+			window.removeEventListener('keydown', handleKeyDown);
+			document.body.style.overflow = 'unset';
+		}
+	}
+</script>
+
+{#if show}
+	<!-- svelte-ignore a11y-click-events-have-key-events -->
+	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<div
+		bind:this={modalElement}
+		class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-[99999] overflow-hidden overscroll-contain"
+		in:fade={{ duration: 10 }}
+		on:mousedown={() => {
+			show = false;
+		}}
+	>
+		<div
+			class=" m-auto rounded-2xl max-w-full w-[32rem] mx-2 bg-gray-50 dark:bg-gray-950 max-h-[100dvh] shadow-3xl"
+			in:flyAndScale
+			on:mousedown={(e) => {
+				e.stopPropagation();
+			}}
+		>
+			<div class="px-[1.75rem] py-6 flex flex-col">
+				<div class=" text-lg font-semibold dark:text-gray-200 mb-2.5">
+					{#if title !== ''}
+						{title}
+					{:else}
+						{$i18n.t('Confirm your action')}
+					{/if}
+				</div>
+
+				<slot>
+					<div class=" text-sm text-gray-500 flex-1">
+						{#if message !== ''}
+							{message}
+						{:else}
+							{$i18n.t('This action cannot be undone. Do you wish to continue?')}
+						{/if}
+
+						{#if input}
+							<textarea
+								bind:value={inputValue}
+								placeholder={inputPlaceholder ? inputPlaceholder : $i18n.t('Enter your message')}
+								class="w-full mt-2 rounded-lg px-4 py-2 text-sm dark:text-gray-300 dark:bg-gray-900 outline-none resize-none"
+								rows="3"
+								required
+							/>
+						{/if}
+					</div>
+				</slot>
+
+				<div class="mt-6 flex justify-between gap-1.5">
+					<button
+						class="bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white font-medium w-full py-2.5 rounded-lg transition"
+						on:click={() => {
+							show = false;
+							dispatch('cancel');
+						}}
+						type="button"
+					>
+						{cancelLabel}
+					</button>
+					<button
+						class="bg-gray-900 hover:bg-gray-850 text-gray-100 dark:bg-gray-100 dark:hover:bg-white dark:text-gray-800 font-medium w-full py-2.5 rounded-lg transition"
+						on:click={() => {
+							confirmHandler();
+						}}
+						type="button"
+					>
+						{confirmLabel}
+					</button>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
+
+<style>
+	.modal-content {
+		animation: scaleUp 0.1s ease-out forwards;
+	}
+
+	@keyframes scaleUp {
+		from {
+			transform: scale(0.985);
+			opacity: 0;
+		}
+		to {
+			transform: scale(1);
+			opacity: 1;
+		}
+	}
+</style>
diff --git a/src/lib/components/common/DragGhost.svelte b/src/lib/components/common/DragGhost.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7169d72f06fd4f1ae121c8b05c0d0012eeddeccc
--- /dev/null
+++ b/src/lib/components/common/DragGhost.svelte
@@ -0,0 +1,30 @@
+<script lang="ts">
+	import { onDestroy, onMount } from 'svelte';
+
+	export let x;
+	export let y;
+
+	let popupElement = null;
+
+	onMount(() => {
+		document.body.appendChild(popupElement);
+		document.body.style.overflow = 'hidden';
+	});
+
+	onDestroy(() => {
+		document.body.removeChild(popupElement);
+		document.body.style.overflow = 'unset';
+	});
+</script>
+
+<!-- svelte-ignore a11y-click-events-have-key-events -->
+<!-- svelte-ignore a11y-no-static-element-interactions -->
+
+<div
+	bind:this={popupElement}
+	class="fixed top-0 left-0 w-screen h-[100dvh] z-50 touch-none pointer-events-none"
+>
+	<div class=" absolute text-white z-[99999]" style="top: {y + 10}px; left: {x + 10}px;">
+		<slot></slot>
+	</div>
+</div>
diff --git a/src/lib/components/common/Drawer.svelte b/src/lib/components/common/Drawer.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f28b624fb2a25dbfe9dfda7333ea3775933cd3b7
--- /dev/null
+++ b/src/lib/components/common/Drawer.svelte
@@ -0,0 +1,91 @@
+<script lang="ts">
+	import { onDestroy, onMount, createEventDispatcher } from 'svelte';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { fade, fly, slide } from 'svelte/transition';
+
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+	export let className = '';
+
+	let modalElement = null;
+	let mounted = false;
+
+	const handleKeyDown = (event: KeyboardEvent) => {
+		if (event.key === 'Escape' && isTopModal()) {
+			console.log('Escape');
+			show = false;
+		}
+	};
+
+	const isTopModal = () => {
+		const modals = document.getElementsByClassName('modal');
+		return modals.length && modals[modals.length - 1] === modalElement;
+	};
+
+	onMount(() => {
+		mounted = true;
+	});
+
+	$: if (show && modalElement) {
+		document.body.appendChild(modalElement);
+		window.addEventListener('keydown', handleKeyDown);
+		document.body.style.overflow = 'hidden';
+	} else if (modalElement) {
+		dispatch('close');
+		window.removeEventListener('keydown', handleKeyDown);
+
+		if (document.body.contains(modalElement)) {
+			document.body.removeChild(modalElement);
+			document.body.style.overflow = 'unset';
+		}
+	}
+
+	onDestroy(() => {
+		show = false;
+		if (modalElement) {
+			if (document.body.contains(modalElement)) {
+				document.body.removeChild(modalElement);
+				document.body.style.overflow = 'unset';
+			}
+		}
+	});
+</script>
+
+<!-- svelte-ignore a11y-click-events-have-key-events -->
+<!-- svelte-ignore a11y-no-static-element-interactions -->
+
+<div
+	bind:this={modalElement}
+	class="modal fixed right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-[9999] overflow-hidden overscroll-contain"
+	in:fly={{ y: 100, duration: 100 }}
+	on:mousedown={() => {
+		show = false;
+	}}
+>
+	<div
+		class=" mt-auto max-w-full w-full bg-gray-50 dark:bg-gray-900 dark:text-gray-100 {className} max-h-[100dvh] overflow-y-auto scrollbar-hidden"
+		on:mousedown={(e) => {
+			e.stopPropagation();
+		}}
+	>
+		<slot />
+	</div>
+</div>
+
+<style>
+	.modal-content {
+		animation: scaleUp 0.1s ease-out forwards;
+	}
+
+	@keyframes scaleUp {
+		from {
+			transform: scale(0.985);
+			opacity: 0;
+		}
+		to {
+			transform: scale(1);
+			opacity: 1;
+		}
+	}
+</style>
diff --git a/src/lib/components/common/Dropdown.svelte b/src/lib/components/common/Dropdown.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b01bceaceb939885ea09f0481191da585e4fc960
--- /dev/null
+++ b/src/lib/components/common/Dropdown.svelte
@@ -0,0 +1,44 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { createEventDispatcher } from 'svelte';
+
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	export let show = false;
+	const dispatch = createEventDispatcher();
+</script>
+
+<DropdownMenu.Root
+	bind:open={show}
+	closeFocus={false}
+	onOpenChange={(state) => {
+		dispatch('change', state);
+	}}
+	typeahead={false}
+>
+	<DropdownMenu.Trigger>
+		<slot />
+	</DropdownMenu.Trigger>
+
+	<slot name="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[130px] rounded-lg px-1 py-1.5 border border-gray-900 z-50 bg-gray-850 text-white"
+			sideOffset={8}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm  font-medium">
+				<div class="flex items-center">Profile</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm  font-medium">
+				<div class="flex items-center">Profile</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item class="flex items-center px-3 py-2 text-sm  font-medium">
+				<div class="flex items-center">Profile</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</slot>
+</DropdownMenu.Root>
diff --git a/src/lib/components/common/FileItem.svelte b/src/lib/components/common/FileItem.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..97e0b91373deffe5de05300c97f741a83cb6c2e2
--- /dev/null
+++ b/src/lib/components/common/FileItem.svelte
@@ -0,0 +1,148 @@
+<script lang="ts">
+	import { createEventDispatcher, getContext } from 'svelte';
+	import { formatFileSize } from '$lib/utils';
+
+	import FileItemModal from './FileItemModal.svelte';
+	import GarbageBin from '../icons/GarbageBin.svelte';
+	import Spinner from './Spinner.svelte';
+	import Tooltip from './Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let className = 'w-60';
+	export let colorClassName = 'bg-white dark:bg-gray-850 border border-gray-50 dark:border-white/5';
+	export let url: string | null = null;
+
+	export let dismissible = false;
+	export let loading = false;
+
+	export let item = null;
+	export let edit = false;
+	export let small = false;
+
+	export let name: string;
+	export let type: string;
+	export let size: number;
+
+	let showModal = false;
+</script>
+
+{#if item}
+	<FileItemModal bind:show={showModal} bind:item {edit} />
+{/if}
+
+<button
+	class="relative group p-1.5 {className} flex items-center gap-1 {colorClassName} {small
+		? 'rounded-xl'
+		: 'rounded-2xl'} text-left"
+	type="button"
+	on:click={async () => {
+		if (item?.file?.data?.content) {
+			showModal = !showModal;
+		} else {
+			if (url) {
+				if (type === 'file') {
+					window.open(`${url}/content`, '_blank').focus();
+				} else {
+					window.open(`${url}`, '_blank').focus();
+				}
+			}
+		}
+
+		dispatch('click');
+	}}
+>
+	{#if !small}
+		<div class="p-3 bg-black/20 dark:bg-white/10 text-white rounded-xl">
+			{#if !loading}
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 24 24"
+					fill="currentColor"
+					class=" size-5"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M5.625 1.5c-1.036 0-1.875.84-1.875 1.875v17.25c0 1.035.84 1.875 1.875 1.875h12.75c1.035 0 1.875-.84 1.875-1.875V12.75A3.75 3.75 0 0 0 16.5 9h-1.875a1.875 1.875 0 0 1-1.875-1.875V5.25A3.75 3.75 0 0 0 9 1.5H5.625ZM7.5 15a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 7.5 15Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H8.25Z"
+						clip-rule="evenodd"
+					/>
+					<path
+						d="M12.971 1.816A5.23 5.23 0 0 1 14.25 5.25v1.875c0 .207.168.375.375.375H16.5a5.23 5.23 0 0 1 3.434 1.279 9.768 9.768 0 0 0-6.963-6.963Z"
+					/>
+				</svg>
+			{:else}
+				<Spinner />
+			{/if}
+		</div>
+	{/if}
+
+	{#if !small}
+		<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
+			<div class=" dark:text-gray-100 text-sm font-medium line-clamp-1 mb-1">
+				{name}
+			</div>
+
+			<div class=" flex justify-between text-gray-500 text-xs line-clamp-1">
+				{#if type === 'file'}
+					{$i18n.t('File')}
+				{:else if type === 'doc'}
+					{$i18n.t('Document')}
+				{:else if type === 'collection'}
+					{$i18n.t('Collection')}
+				{:else}
+					<span class=" capitalize line-clamp-1">{type}</span>
+				{/if}
+				{#if size}
+					<span class="capitalize">{formatFileSize(size)}</span>
+				{/if}
+			</div>
+		</div>
+	{:else}
+		<Tooltip content={name} className="flex flex-col w-full" placement="top-start">
+			<div class="flex flex-col justify-center -space-y-0.5 px-2.5 w-full">
+				<div class=" dark:text-gray-100 text-sm flex justify-between items-center">
+					{#if loading}
+						<div class=" shrink-0 mr-2">
+							<Spinner className="size-4" />
+						</div>
+					{/if}
+					<div class="font-medium line-clamp-1 flex-1">{name}</div>
+					<div class="text-gray-500 text-xs capitalize shrink-0">{formatFileSize(size)}</div>
+				</div>
+			</div>
+		</Tooltip>
+	{/if}
+
+	{#if dismissible}
+		<div class=" absolute -top-1 -right-1">
+			<button
+				class=" bg-gray-400 text-white border border-white rounded-full group-hover:visible invisible transition"
+				type="button"
+				on:click|stopPropagation={() => {
+					dispatch('dismiss');
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+
+			<!-- <button
+				class=" p-1 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-full group-hover:visible invisible transition"
+				type="button"
+				on:click={() => {
+				}}
+			>
+				<GarbageBin />
+			</button> -->
+		</div>
+	{/if}
+</button>
diff --git a/src/lib/components/common/FileItemModal.svelte b/src/lib/components/common/FileItemModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..970ac0821b6ecfb60fffdf263a8ccc5ecacacabc
--- /dev/null
+++ b/src/lib/components/common/FileItemModal.svelte
@@ -0,0 +1,108 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+	import { formatFileSize, getLineCount } from '$lib/utils';
+
+	const i18n = getContext('i18n');
+
+	import Modal from './Modal.svelte';
+	import XMark from '../icons/XMark.svelte';
+	import Info from '../icons/Info.svelte';
+	import Switch from './Switch.svelte';
+	import Tooltip from './Tooltip.svelte';
+
+	export let item;
+	export let show = false;
+
+	export let edit = false;
+
+	let enableFullContent = false;
+
+	onMount(() => {
+		console.log(item);
+
+		if (item?.context === 'full') {
+			enableFullContent = true;
+		}
+	});
+</script>
+
+<Modal bind:show size="lg">
+	<div class="font-primary px-6 py-5 w-full flex flex-col justify-center dark:text-gray-400">
+		<div class=" pb-2">
+			<div class="flex items-start justify-between">
+				<div>
+					<div class=" font-medium text-lg dark:text-gray-100">
+						<a
+							href={item.url ? (item.type === 'file' ? `${item.url}/content` : `${item.url}`) : '#'}
+							target="_blank"
+							class="hover:underline line-clamp-1"
+						>
+							{item?.name ?? 'File'}
+						</a>
+					</div>
+				</div>
+
+				<div>
+					<button
+						on:click={() => {
+							show = false;
+						}}
+					>
+						<XMark />
+					</button>
+				</div>
+			</div>
+
+			<div>
+				<div class="flex flex-col items-center md:flex-row gap-1 justify-between w-full">
+					<div class=" flex flex-wrap text-sm gap-1 text-gray-500">
+						{#if item.size}
+							<div class="capitalize shrink-0">{formatFileSize(item.size)}</div>
+							•
+						{/if}
+
+						{#if item?.file?.data?.content}
+							<div class="capitalize shrink-0">
+								{getLineCount(item?.file?.data?.content ?? '')} extracted lines
+							</div>
+
+							<div class="flex items-center gap-1 shrink-0">
+								<Info />
+
+								Formatting may be inconsistent from source.
+							</div>
+						{/if}
+					</div>
+
+					{#if edit}
+						<div>
+							<Tooltip
+								content={enableFullContent
+									? 'Inject the entire document as context for comprehensive processing, this is recommended for complex queries.'
+									: 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'}
+							>
+								<div class="flex items-center gap-1.5 text-xs">
+									{#if enableFullContent}
+										Using Entire Document
+									{:else}
+										Using Focused Retrieval
+									{/if}
+									<Switch
+										bind:state={enableFullContent}
+										on:change={(e) => {
+											item.context = e.detail ? 'full' : undefined;
+										}}
+									/>
+								</div>
+							</Tooltip>
+						</div>
+					{/if}
+				</div>
+			</div>
+		</div>
+
+		<div class="max-h-96 overflow-scroll scrollbar-hidden text-xs whitespace-pre-wrap">
+			{item?.file?.data?.content ?? 'No content'}
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/common/Folder.svelte b/src/lib/components/common/Folder.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ab15075452007b37a892d75ae1885600b9b7f3ca
--- /dev/null
+++ b/src/lib/components/common/Folder.svelte
@@ -0,0 +1,141 @@
+<script>
+	import { getContext, createEventDispatcher, onMount, onDestroy } from 'svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import ChevronDown from '../icons/ChevronDown.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+	import Collapsible from './Collapsible.svelte';
+
+	export let open = true;
+
+	export let id = '';
+	export let name = '';
+	export let collapsible = true;
+
+	export let className = '';
+
+	let folderElement;
+
+	let draggedOver = false;
+
+	const onDragOver = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		draggedOver = true;
+	};
+
+	const onDrop = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+
+		if (folderElement.contains(e.target)) {
+			console.log('Dropped on the Button');
+
+			if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
+				// Iterate over all items in the DataTransferItemList use functional programming
+				for (const item of Array.from(e.dataTransfer.items)) {
+					// If dropped items aren't files, reject them
+					if (item.kind === 'file') {
+						const file = item.getAsFile();
+						if (file && file.type === 'application/json') {
+							console.log('Dropped file is a JSON file!');
+
+							// Read the JSON file with FileReader
+							const reader = new FileReader();
+							reader.onload = async function (event) {
+								try {
+									const fileContent = JSON.parse(event.target.result);
+									console.log('Parsed JSON Content: ', fileContent);
+									open = true;
+									dispatch('import', fileContent);
+								} catch (error) {
+									console.error('Error parsing JSON file:', error);
+								}
+							};
+
+							// Start reading the file
+							reader.readAsText(file);
+						} else {
+							console.error('Only JSON file types are supported.');
+						}
+					} else {
+						open = true;
+
+						const dataTransfer = e.dataTransfer.getData('text/plain');
+						const data = JSON.parse(dataTransfer);
+
+						console.log(data);
+						dispatch('drop', data);
+					}
+				}
+			}
+
+			draggedOver = false;
+		}
+	};
+
+	const onDragLeave = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+
+		draggedOver = false;
+	};
+
+	onMount(() => {
+		folderElement.addEventListener('dragover', onDragOver);
+		folderElement.addEventListener('drop', onDrop);
+		folderElement.addEventListener('dragleave', onDragLeave);
+	});
+
+	onDestroy(() => {
+		folderElement.addEventListener('dragover', onDragOver);
+		folderElement.removeEventListener('drop', onDrop);
+		folderElement.removeEventListener('dragleave', onDragLeave);
+	});
+</script>
+
+<div bind:this={folderElement} class="relative {className}">
+	{#if draggedOver}
+		<div
+			class="absolute top-0 left-0 w-full h-full rounded-sm bg-[hsla(260,85%,65%,0.1)] bg-opacity-50 dark:bg-opacity-10 z-50 pointer-events-none touch-none"
+		></div>
+	{/if}
+
+	{#if collapsible}
+		<Collapsible
+			bind:open
+			className="w-full "
+			buttonClassName="w-full"
+			on:change={(e) => {
+				dispatch('change', e.detail);
+			}}
+		>
+			<!-- svelte-ignore a11y-no-static-element-interactions -->
+			<div class="w-full">
+				<button
+					class="w-full py-1.5 px-2 rounded-md flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-500 font-medium hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+				>
+					<div class="text-gray-300 dark:text-gray-600">
+						{#if open}
+							<ChevronDown className=" size-3" strokeWidth="2.5" />
+						{:else}
+							<ChevronRight className=" size-3" strokeWidth="2.5" />
+						{/if}
+					</div>
+
+					<div class="translate-y-[0.5px]">
+						{name}
+					</div>
+				</button>
+			</div>
+
+			<div slot="content" class="w-full">
+				<slot></slot>
+			</div>
+		</Collapsible>
+	{:else}
+		<slot></slot>
+	{/if}
+</div>
diff --git a/src/lib/components/common/Image.svelte b/src/lib/components/common/Image.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a5b2844482b48b87e3a277e5b51e88888a078d24
--- /dev/null
+++ b/src/lib/components/common/Image.svelte
@@ -0,0 +1,26 @@
+<script lang="ts">
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import ImagePreview from './ImagePreview.svelte';
+
+	export let src = '';
+	export let alt = '';
+
+	export let className = ' w-full';
+	export let imageClassName = 'rounded-lg';
+
+	let _src = '';
+	$: _src = src.startsWith('/') ? `${WEBUI_BASE_URL}${src}` : src;
+
+	let showImagePreview = false;
+</script>
+
+<button
+	class={className}
+	on:click={() => {
+		showImagePreview = true;
+	}}
+>
+	<img src={_src} {alt} class={imageClassName} draggable="false" data-cy="image" />
+</button>
+
+<ImagePreview bind:show={showImagePreview} src={_src} {alt} />
diff --git a/src/lib/components/common/ImagePreview.svelte b/src/lib/components/common/ImagePreview.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8f20a32d764d39723d22e3d3e05bcd5b50b35edb
--- /dev/null
+++ b/src/lib/components/common/ImagePreview.svelte
@@ -0,0 +1,111 @@
+<script lang="ts">
+	import { onDestroy, onMount } from 'svelte';
+
+	export let show = false;
+	export let src = '';
+	export let alt = '';
+
+	let mounted = false;
+
+	let previewElement = null;
+
+	const downloadImage = (url, filename, prefixName = '') => {
+		fetch(url)
+			.then((response) => response.blob())
+			.then((blob) => {
+				const objectUrl = window.URL.createObjectURL(blob);
+				const link = document.createElement('a');
+				link.href = objectUrl;
+				link.download = `${prefixName}${filename}`;
+				document.body.appendChild(link);
+				link.click();
+				document.body.removeChild(link);
+				window.URL.revokeObjectURL(objectUrl);
+			})
+			.catch((error) => console.error('Error downloading image:', error));
+	};
+
+	const handleKeyDown = (event: KeyboardEvent) => {
+		if (event.key === 'Escape') {
+			console.log('Escape');
+			show = false;
+		}
+	};
+
+	onMount(() => {
+		mounted = true;
+	});
+
+	$: if (show && previewElement) {
+		document.body.appendChild(previewElement);
+		window.addEventListener('keydown', handleKeyDown);
+		document.body.style.overflow = 'hidden';
+	} else if (previewElement) {
+		window.removeEventListener('keydown', handleKeyDown);
+		document.body.removeChild(previewElement);
+		document.body.style.overflow = 'unset';
+	}
+
+	onDestroy(() => {
+		show = false;
+
+		if (previewElement) {
+			document.body.removeChild(previewElement);
+		}
+	});
+</script>
+
+{#if show}
+	<!-- svelte-ignore a11y-click-events-have-key-events -->
+	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<div
+		bind:this={previewElement}
+		class="modal fixed top-0 right-0 left-0 bottom-0 bg-black text-white w-full min-h-screen h-screen flex justify-center z-[9999] overflow-hidden overscroll-contain"
+	>
+		<div class=" absolute left-0 w-full flex justify-between select-none">
+			<div>
+				<button
+					class=" p-5"
+					on:click={() => {
+						show = false;
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="2"
+						stroke="currentColor"
+						class="w-6 h-6"
+					>
+						<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
+					</svg>
+				</button>
+			</div>
+
+			<div>
+				<button
+					class=" p-5"
+					on:click={() => {
+						downloadImage(src, src.substring(src.lastIndexOf('/') + 1), alt);
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="w-6 h-6"
+					>
+						<path
+							d="M10.75 2.75a.75.75 0 0 0-1.5 0v8.614L6.295 8.235a.75.75 0 1 0-1.09 1.03l4.25 4.5a.75.75 0 0 0 1.09 0l4.25-4.5a.75.75 0 0 0-1.09-1.03l-2.955 3.129V2.75Z"
+						/>
+						<path
+							d="M3.5 12.75a.75.75 0 0 0-1.5 0v2.5A2.75 2.75 0 0 0 4.75 18h10.5A2.75 2.75 0 0 0 18 15.25v-2.5a.75.75 0 0 0-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5Z"
+						/>
+					</svg>
+				</button>
+			</div>
+		</div>
+		<img {src} {alt} class=" mx-auto h-full object-scale-down select-none" draggable="false" />
+	</div>
+{/if}
diff --git a/src/lib/components/common/Loader.svelte b/src/lib/components/common/Loader.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ac7ecaf28a5dff995a41107e7166a33bd6d33660
--- /dev/null
+++ b/src/lib/components/common/Loader.svelte
@@ -0,0 +1,46 @@
+<script lang="ts">
+	import { createEventDispatcher, onDestroy, onMount } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	let loaderElement: HTMLElement;
+
+	let observer;
+	let intervalId;
+
+	onMount(() => {
+		observer = new IntersectionObserver(
+			(entries, observer) => {
+				entries.forEach((entry) => {
+					if (entry.isIntersecting) {
+						intervalId = setInterval(() => {
+							dispatch('visible');
+						}, 100);
+						// dispatch('visible');
+						// observer.unobserve(loaderElement); // Stop observing until content is loaded
+					} else {
+						clearInterval(intervalId);
+					}
+				});
+			},
+			{
+				root: null, // viewport
+				rootMargin: '0px',
+				threshold: 0.1 // When 10% of the loader is visible
+			}
+		);
+
+		observer.observe(loaderElement);
+	});
+
+	onDestroy(() => {
+		observer.disconnect();
+
+		if (intervalId) {
+			clearInterval(intervalId);
+		}
+	});
+</script>
+
+<div bind:this={loaderElement}>
+	<slot />
+</div>
diff --git a/src/lib/components/common/Marquee.svelte b/src/lib/components/common/Marquee.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2ab0198cbfda2c12628b44fd6f2aa89d738e38ac
--- /dev/null
+++ b/src/lib/components/common/Marquee.svelte
@@ -0,0 +1,30 @@
+<script lang="ts">
+	import { fade, fly } from 'svelte/transition';
+	import { onMount } from 'svelte';
+
+	let idx = 0;
+
+	export let className = '';
+	export let words = ['lorem', 'ipsum'];
+	export let duration = 4000;
+
+	onMount(() => {
+		setInterval(async () => {
+			if (idx === words.length - 1) {
+				idx = 0;
+			} else {
+				idx = idx + 1;
+			}
+		}, duration);
+	});
+</script>
+
+<div class={className}>
+	<div>
+		{#key idx}
+			<div class=" marquee-item" in:fly={{ y: '30%', duration: 1000 }}>
+				{words.at(idx)}
+			</div>
+		{/key}
+	</div>
+</div>
diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..561254ba305b99a865b8a2a985430f633ee15b56
--- /dev/null
+++ b/src/lib/components/common/Modal.svelte
@@ -0,0 +1,105 @@
+<script lang="ts">
+	import { onDestroy, onMount } from 'svelte';
+	import { fade } from 'svelte/transition';
+
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	export let show = true;
+	export let size = 'md';
+
+	export let containerClassName = 'p-3';
+	export let className = 'bg-gray-50 dark:bg-gray-900 rounded-2xl';
+
+	let modalElement = null;
+	let mounted = false;
+
+	const sizeToWidth = (size) => {
+		if (size === 'full') {
+			return 'w-full';
+		}
+		if (size === 'xs') {
+			return 'w-[16rem]';
+		} else if (size === 'sm') {
+			return 'w-[30rem]';
+		} else if (size === 'md') {
+			return 'w-[42rem]';
+		} else {
+			return 'w-[56rem]';
+		}
+	};
+
+	const handleKeyDown = (event: KeyboardEvent) => {
+		if (event.key === 'Escape' && isTopModal()) {
+			console.log('Escape');
+			show = false;
+		}
+	};
+
+	const isTopModal = () => {
+		const modals = document.getElementsByClassName('modal');
+		return modals.length && modals[modals.length - 1] === modalElement;
+	};
+
+	onMount(() => {
+		mounted = true;
+	});
+
+	$: if (show && modalElement) {
+		document.body.appendChild(modalElement);
+		window.addEventListener('keydown', handleKeyDown);
+		document.body.style.overflow = 'hidden';
+	} else if (modalElement) {
+		window.removeEventListener('keydown', handleKeyDown);
+		document.body.removeChild(modalElement);
+		document.body.style.overflow = 'unset';
+	}
+
+	onDestroy(() => {
+		show = false;
+		if (modalElement) {
+			document.body.removeChild(modalElement);
+		}
+	});
+</script>
+
+{#if show}
+	<!-- svelte-ignore a11y-click-events-have-key-events -->
+	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<div
+		bind:this={modalElement}
+		class="modal fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] {containerClassName} flex justify-center z-[9999] overflow-y-auto overscroll-contain"
+		in:fade={{ duration: 10 }}
+		on:mousedown={() => {
+			show = false;
+		}}
+	>
+		<div
+			class=" m-auto max-w-full {sizeToWidth(size)} {size !== 'full'
+				? 'mx-2'
+				: ''} shadow-3xl min-h-fit scrollbar-hidden {className}"
+			in:flyAndScale
+			on:mousedown={(e) => {
+				e.stopPropagation();
+			}}
+		>
+			<slot />
+		</div>
+	</div>
+{/if}
+
+<style>
+	.modal-content {
+		animation: scaleUp 0.1s ease-out forwards;
+	}
+
+	@keyframes scaleUp {
+		from {
+			transform: scale(0.985);
+			opacity: 0;
+		}
+		to {
+			transform: scale(1);
+			opacity: 1;
+		}
+	}
+</style>
diff --git a/src/lib/components/common/Overlay.svelte b/src/lib/components/common/Overlay.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d38358212ae03a767b4718c74124af0d78b4bffb
--- /dev/null
+++ b/src/lib/components/common/Overlay.svelte
@@ -0,0 +1,33 @@
+<script>
+	import Spinner from './Spinner.svelte';
+
+	export let show = false;
+	export let content = '';
+
+	export let opacity = 1;
+</script>
+
+<div class="relative">
+	{#if show}
+		<div class="absolute w-full h-full flex">
+			<div
+				class="absolute rounded"
+				style="inset: -10px; opacity: {opacity}; backdrop-filter: blur(5px);"
+			/>
+
+			<div class="flex w-full flex-col justify-center">
+				<div class=" py-3">
+					<Spinner className="ml-2" />
+				</div>
+
+				{#if content !== ''}
+					<div class="text-center text-gray-100 text-xs font-medium z-50">
+						{content}
+					</div>
+				{/if}
+			</div>
+		</div>
+	{/if}
+
+	<slot />
+</div>
diff --git a/src/lib/components/common/Pagination.svelte b/src/lib/components/common/Pagination.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e1ee4766dcf8441ccf3a9b8e8fb27f8fdf02a3f2
--- /dev/null
+++ b/src/lib/components/common/Pagination.svelte
@@ -0,0 +1,42 @@
+<script lang="ts">
+	import { Pagination } from 'bits-ui';
+	import { createEventDispatcher } from 'svelte';
+
+	import ChevronLeft from '../icons/ChevronLeft.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+
+	export let page = 0;
+	export let count = 0;
+	export let perPage = 20;
+</script>
+
+<div class="flex justify-center">
+	<Pagination.Root bind:page {count} {perPage} let:pages>
+		<div class="my-2 flex items-center">
+			<Pagination.PrevButton
+				class="mr-[25px] inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-50 dark:hover:bg-gray-850 active:scale-98 disabled:cursor-not-allowed disabled:text-gray-400 dark:disabled:text-gray-700 hover:disabled:bg-transparent dark:hover:disabled:bg-transparent"
+			>
+				<ChevronLeft className="size-4" strokeWidth="2" />
+			</Pagination.PrevButton>
+			<div class="flex items-center gap-2.5">
+				{#each pages as page (page.key)}
+					{#if page.type === 'ellipsis'}
+						<div class="text-sm font-medium text-foreground-alt">...</div>
+					{:else}
+						<Pagination.Page
+							{page}
+							class="inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-50 dark:hover:bg-gray-850 text-sm font-medium hover:bg-dark-10 active:scale-98 disabled:cursor-not-allowed disabled:opacity-50 hover:disabled:bg-transparent data-[selected]:bg-gray-50 data-[selected]:text-gray-700 data-[selected]:hover:bg-gray-100 dark:data-[selected]:bg-gray-850 dark:data-[selected]:text-gray-50 dark:data-[selected]:hover:bg-gray-800 transition"
+						>
+							{page.value}
+						</Pagination.Page>
+					{/if}
+				{/each}
+			</div>
+			<Pagination.NextButton
+				class="ml-[25px]  inline-flex size-8 items-center justify-center rounded-[9px] bg-transparent hover:bg-gray-50 dark:hover:bg-gray-850 active:scale-98 disabled:cursor-not-allowed disabled:text-gray-400 dark:disabled:text-gray-700 hover:disabled:bg-transparent dark:hover:disabled:bg-transparent"
+			>
+				<ChevronRight className="size-4" strokeWidth="2" />
+			</Pagination.NextButton>
+		</div>
+	</Pagination.Root>
+</div>
diff --git a/src/lib/components/common/RichTextInput.svelte b/src/lib/components/common/RichTextInput.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c9c8101c37c60390ac8ec0357df5ba28ab74a03f
--- /dev/null
+++ b/src/lib/components/common/RichTextInput.svelte
@@ -0,0 +1,358 @@
+<script lang="ts">
+	import { marked } from 'marked';
+	import TurndownService from 'turndown';
+	const turndownService = new TurndownService({
+		codeBlockStyle: 'fenced',
+		headingStyle: 'atx'
+	});
+	turndownService.escape = (string) => string;
+
+	import { onMount, onDestroy } from 'svelte';
+	import { createEventDispatcher } from 'svelte';
+	const eventDispatch = createEventDispatcher();
+
+	import { EditorState, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
+	import { Decoration, DecorationSet } from 'prosemirror-view';
+
+	import { Editor } from '@tiptap/core';
+
+	import { AIAutocompletion } from './RichTextInput/AutoCompletion.js';
+
+	import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight';
+	import Placeholder from '@tiptap/extension-placeholder';
+	import Highlight from '@tiptap/extension-highlight';
+	import Typography from '@tiptap/extension-typography';
+	import StarterKit from '@tiptap/starter-kit';
+	import { all, createLowlight } from 'lowlight';
+
+	import { PASTED_TEXT_CHARACTER_LIMIT } from '$lib/constants';
+
+	// create a lowlight instance with all languages loaded
+	const lowlight = createLowlight(all);
+
+	export let className = 'input-prose';
+	export let placeholder = 'Type here...';
+	export let value = '';
+	export let id = '';
+
+	export let preserveBreaks = false;
+	export let generateAutoCompletion: Function = async () => null;
+	export let autocomplete = false;
+	export let messageInput = false;
+	export let shiftEnter = false;
+	export let largeTextAsFile = false;
+
+	let element;
+	let editor;
+
+	const options = {
+		throwOnError: false
+	};
+
+	// Function to find the next template in the document
+	function findNextTemplate(doc, from = 0) {
+		const patterns = [
+			{ start: '[', end: ']' },
+			{ start: '{{', end: '}}' }
+		];
+
+		let result = null;
+
+		doc.nodesBetween(from, doc.content.size, (node, pos) => {
+			if (result) return false; // Stop if we've found a match
+			if (node.isText) {
+				const text = node.text;
+				let index = Math.max(0, from - pos);
+				while (index < text.length) {
+					for (const pattern of patterns) {
+						if (text.startsWith(pattern.start, index)) {
+							const endIndex = text.indexOf(pattern.end, index + pattern.start.length);
+							if (endIndex !== -1) {
+								result = {
+									from: pos + index,
+									to: pos + endIndex + pattern.end.length
+								};
+								return false; // Stop searching
+							}
+						}
+					}
+					index++;
+				}
+			}
+		});
+
+		return result;
+	}
+
+	// Function to select the next template in the document
+	function selectNextTemplate(state, dispatch) {
+		const { doc, selection } = state;
+		const from = selection.to;
+		let template = findNextTemplate(doc, from);
+
+		if (!template) {
+			// If not found, search from the beginning
+			template = findNextTemplate(doc, 0);
+		}
+
+		if (template) {
+			if (dispatch) {
+				const tr = state.tr.setSelection(TextSelection.create(doc, template.from, template.to));
+				dispatch(tr);
+			}
+			return true;
+		}
+		return false;
+	}
+
+	export const setContent = (content) => {
+		editor.commands.setContent(content);
+	};
+
+	const selectTemplate = () => {
+		if (value !== '') {
+			// After updating the state, try to find and select the next template
+			setTimeout(() => {
+				const templateFound = selectNextTemplate(editor.view.state, editor.view.dispatch);
+				if (!templateFound) {
+					// If no template found, set cursor at the end
+					const endPos = editor.view.state.doc.content.size;
+					editor.view.dispatch(
+						editor.view.state.tr.setSelection(TextSelection.create(editor.view.state.doc, endPos))
+					);
+				}
+			}, 0);
+		}
+	};
+
+	onMount(async () => {
+		console.log(value);
+
+		if (preserveBreaks) {
+			turndownService.addRule('preserveBreaks', {
+				filter: 'br', // Target <br> elements
+				replacement: function (content) {
+					return '<br/>';
+				}
+			});
+		}
+
+		async function tryParse(value, attempts = 3, interval = 100) {
+			try {
+				// Try parsing the value
+				return marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
+					breaks: false
+				});
+			} catch (error) {
+				// If no attempts remain, fallback to plain text
+				if (attempts <= 1) {
+					return value;
+				}
+				// Wait for the interval, then retry
+				await new Promise((resolve) => setTimeout(resolve, interval));
+				return tryParse(value, attempts - 1, interval); // Recursive call
+			}
+		}
+
+		// Usage example
+		let content = await tryParse(value);
+
+		editor = new Editor({
+			element: element,
+			extensions: [
+				StarterKit,
+				CodeBlockLowlight.configure({
+					lowlight
+				}),
+				Highlight,
+				Typography,
+				Placeholder.configure({ placeholder }),
+				...(autocomplete
+					? [
+							AIAutocompletion.configure({
+								generateCompletion: async (text) => {
+									if (text.trim().length === 0) {
+										return null;
+									}
+
+									const suggestion = await generateAutoCompletion(text).catch(() => null);
+									if (!suggestion || suggestion.trim().length === 0) {
+										return null;
+									}
+
+									return suggestion;
+								}
+							})
+						]
+					: [])
+			],
+			content: content,
+			autofocus: messageInput ? true : false,
+			onTransaction: () => {
+				// force re-render so `editor.isActive` works as expected
+				editor = editor;
+				const newValue = turndownService
+					.turndown(
+						(preserveBreaks
+							? editor.getHTML().replace(/<p><\/p>/g, '<br/>')
+							: editor.getHTML()
+						).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
+					)
+					.replace(/\u00a0/g, ' ');
+
+				if (value !== newValue) {
+					value = newValue;
+
+					// check if the node is paragraph as well
+					if (editor.isActive('paragraph')) {
+						if (value === '') {
+							editor.commands.clearContent();
+						}
+					}
+				}
+			},
+			editorProps: {
+				attributes: { id },
+				handleDOMEvents: {
+					focus: (view, event) => {
+						eventDispatch('focus', { event });
+						return false;
+					},
+					keyup: (view, event) => {
+						eventDispatch('keyup', { event });
+						return false;
+					},
+					keydown: (view, event) => {
+						if (messageInput) {
+							// Handle Tab Key
+							if (event.key === 'Tab') {
+								const handled = selectNextTemplate(view.state, view.dispatch);
+								if (handled) {
+									event.preventDefault();
+									return true;
+								}
+							}
+
+							if (event.key === 'Enter') {
+								// Check if the current selection is inside a structured block (like codeBlock or list)
+								const { state } = view;
+								const { $head } = state.selection;
+
+								// Recursive function to check ancestors for specific node types
+								function isInside(nodeTypes: string[]): boolean {
+									let currentNode = $head;
+									while (currentNode) {
+										if (nodeTypes.includes(currentNode.parent.type.name)) {
+											return true;
+										}
+										if (!currentNode.depth) break; // Stop if we reach the top
+										currentNode = state.doc.resolve(currentNode.before()); // Move to the parent node
+									}
+									return false;
+								}
+
+								const isInCodeBlock = isInside(['codeBlock']);
+								const isInList = isInside(['listItem', 'bulletList', 'orderedList']);
+								const isInHeading = isInside(['heading']);
+
+								if (isInCodeBlock || isInList || isInHeading) {
+									// Let ProseMirror handle the normal Enter behavior
+									return false;
+								}
+							}
+
+							// Handle shift + Enter for a line break
+							if (shiftEnter) {
+								if (event.key === 'Enter' && event.shiftKey && !event.ctrlKey && !event.metaKey) {
+									editor.commands.setHardBreak(); // Insert a hard break
+									view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor
+									event.preventDefault();
+									return true;
+								}
+							}
+						}
+						eventDispatch('keydown', { event });
+						return false;
+					},
+					paste: (view, event) => {
+						if (event.clipboardData) {
+							// Extract plain text from clipboard and paste it without formatting
+							const plainText = event.clipboardData.getData('text/plain');
+							if (plainText) {
+								if (largeTextAsFile) {
+									if (plainText.length > PASTED_TEXT_CHARACTER_LIMIT) {
+										// Dispatch paste event to parent component
+										eventDispatch('paste', { event });
+										event.preventDefault();
+										return true;
+									}
+								}
+								return false;
+							}
+
+							// Check if the pasted content contains image files
+							const hasImageFile = Array.from(event.clipboardData.files).some((file) =>
+								file.type.startsWith('image/')
+							);
+
+							// Check for image in dataTransfer items (for cases where files are not available)
+							const hasImageItem = Array.from(event.clipboardData.items).some((item) =>
+								item.type.startsWith('image/')
+							);
+							if (hasImageFile) {
+								// If there's an image, dispatch the event to the parent
+								eventDispatch('paste', { event });
+								event.preventDefault();
+								return true;
+							}
+
+							if (hasImageItem) {
+								// If there's an image item, dispatch the event to the parent
+								eventDispatch('paste', { event });
+								event.preventDefault();
+								return true;
+							}
+						}
+
+						// For all other cases (text, formatted text, etc.), let ProseMirror handle it
+						view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor after pasting
+						return false;
+					}
+				}
+			}
+		});
+
+		if (messageInput) {
+			selectTemplate();
+		}
+	});
+
+	onDestroy(() => {
+		if (editor) {
+			editor.destroy();
+		}
+	});
+
+	// Update the editor content if the external `value` changes
+	$: if (
+		editor &&
+		value !==
+			turndownService
+				.turndown(
+					(preserveBreaks
+						? editor.getHTML().replace(/<p><\/p>/g, '<br/>')
+						: editor.getHTML()
+					).replace(/ {2,}/g, (m) => m.replace(/ /g, '\u00a0'))
+				)
+				.replace(/\u00a0/g, ' ')
+	) {
+		editor.commands.setContent(
+			marked.parse(value.replaceAll(`\n<br/>`, `<br/>`), {
+				breaks: false
+			})
+		); // Update editor content
+		selectTemplate();
+	}
+</script>
+
+<div bind:this={element} class="relative w-full min-w-full h-full min-h-fit {className}" />
diff --git a/src/lib/components/common/RichTextInput/AutoCompletion.js b/src/lib/components/common/RichTextInput/AutoCompletion.js
new file mode 100644
index 0000000000000000000000000000000000000000..733ac4975d94ad20a87ac563f851e3f6f15d6a5f
--- /dev/null
+++ b/src/lib/components/common/RichTextInput/AutoCompletion.js
@@ -0,0 +1,278 @@
+/*
+Here we initialize the plugin with keyword mapping.
+Intended to handle user interactions seamlessly.
+
+Observe the keydown events for proactive suggestions.
+Provide a mechanism for accepting AI suggestions.
+Evaluate each input change with debounce logic.
+Next, we implement touch and mouse interactions.
+
+Anchor the user experience to intuitive behavior.
+Intelligently reset suggestions on new input.
+*/
+
+import { Extension } from '@tiptap/core';
+import { Plugin, PluginKey } from 'prosemirror-state';
+
+export const AIAutocompletion = Extension.create({
+	name: 'aiAutocompletion',
+
+	addOptions() {
+		return {
+			generateCompletion: () => Promise.resolve(''),
+			debounceTime: 1000
+		};
+	},
+
+	addGlobalAttributes() {
+		return [
+			{
+				types: ['paragraph'],
+				attributes: {
+					class: {
+						default: null,
+						parseHTML: (element) => element.getAttribute('class'),
+						renderHTML: (attributes) => {
+							if (!attributes.class) return {};
+							return { class: attributes.class };
+						}
+					},
+					'data-prompt': {
+						default: null,
+						parseHTML: (element) => element.getAttribute('data-prompt'),
+						renderHTML: (attributes) => {
+							if (!attributes['data-prompt']) return {};
+							return { 'data-prompt': attributes['data-prompt'] };
+						}
+					},
+					'data-suggestion': {
+						default: null,
+						parseHTML: (element) => element.getAttribute('data-suggestion'),
+						renderHTML: (attributes) => {
+							if (!attributes['data-suggestion']) return {};
+							return { 'data-suggestion': attributes['data-suggestion'] };
+						}
+					}
+				}
+			}
+		];
+	},
+
+	addProseMirrorPlugins() {
+		let debounceTimer = null;
+		let loading = false;
+
+		let touchStartX = 0;
+		let touchStartY = 0;
+
+		return [
+			new Plugin({
+				key: new PluginKey('aiAutocompletion'),
+				props: {
+					handleKeyDown: (view, event) => {
+						const { state, dispatch } = view;
+						const { selection } = state;
+						const { $head } = selection;
+
+						if ($head.parent.type.name !== 'paragraph') return false;
+
+						const node = $head.parent;
+
+						if (event.key === 'Tab') {
+							// if (!node.attrs['data-suggestion']) {
+							//   // Generate completion
+							//   if (loading) return true
+							//   loading = true
+							//   const prompt = node.textContent
+							//   this.options.generateCompletion(prompt).then(suggestion => {
+							//     if (suggestion && suggestion.trim() !== '') {
+							//       dispatch(state.tr.setNodeMarkup($head.before(), null, {
+							//         ...node.attrs,
+							//         class: 'ai-autocompletion',
+							//         'data-prompt': prompt,
+							//         'data-suggestion': suggestion,
+							//       }))
+							//     }
+							//     // If suggestion is empty or null, do nothing
+							//   }).finally(() => {
+							//     loading = false
+							//   })
+							// }
+
+							if (node.attrs['data-suggestion']) {
+								// Accept suggestion
+								const suggestion = node.attrs['data-suggestion'];
+								dispatch(
+									state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
+										...node.attrs,
+										class: null,
+										'data-prompt': null,
+										'data-suggestion': null
+									})
+								);
+								return true;
+							}
+						} else {
+							if (node.attrs['data-suggestion']) {
+								// Reset suggestion on any other key press
+								dispatch(
+									state.tr.setNodeMarkup($head.before(), null, {
+										...node.attrs,
+										class: null,
+										'data-prompt': null,
+										'data-suggestion': null
+									})
+								);
+							}
+
+							// Start debounce logic for AI generation only if the cursor is at the end of the paragraph
+							if (selection.empty && $head.pos === $head.end()) {
+								// Set up debounce for AI generation
+								if (this.options.debounceTime !== null) {
+									clearTimeout(debounceTimer);
+
+									// Capture current position
+									const currentPos = $head.before();
+
+									debounceTimer = setTimeout(() => {
+										const newState = view.state;
+										const newSelection = newState.selection;
+										const newNode = newState.doc.nodeAt(currentPos);
+
+										// Check if the node still exists and is still a paragraph
+										if (
+											newNode &&
+											newNode.type.name === 'paragraph' &&
+											newSelection.$head.pos === newSelection.$head.end() &&
+											newSelection.$head.pos === currentPos + newNode.nodeSize - 1
+										) {
+											const prompt = newNode.textContent;
+
+											if (prompt.trim() !== '') {
+												if (loading) return true;
+												loading = true;
+												this.options
+													.generateCompletion(prompt)
+													.then((suggestion) => {
+														if (suggestion && suggestion.trim() !== '') {
+															if (
+																view.state.selection.$head.pos === view.state.selection.$head.end()
+															) {
+																view.dispatch(
+																	newState.tr.setNodeMarkup(currentPos, null, {
+																		...newNode.attrs,
+																		class: 'ai-autocompletion',
+																		'data-prompt': prompt,
+																		'data-suggestion': suggestion
+																	})
+																);
+															}
+														}
+													})
+													.finally(() => {
+														loading = false;
+													});
+											}
+										}
+									}, this.options.debounceTime);
+								}
+							}
+						}
+						return false;
+					},
+					handleDOMEvents: {
+						touchstart: (view, event) => {
+							touchStartX = event.touches[0].clientX;
+							touchStartY = event.touches[0].clientY;
+							return false;
+						},
+						touchend: (view, event) => {
+							const touchEndX = event.changedTouches[0].clientX;
+							const touchEndY = event.changedTouches[0].clientY;
+
+							const deltaX = touchEndX - touchStartX;
+							const deltaY = touchEndY - touchStartY;
+
+							// Check if the swipe was primarily horizontal and to the right
+							if (Math.abs(deltaX) > Math.abs(deltaY) && deltaX > 50) {
+								const { state, dispatch } = view;
+								const { selection } = state;
+								const { $head } = selection;
+								const node = $head.parent;
+
+								if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
+									const suggestion = node.attrs['data-suggestion'];
+									dispatch(
+										state.tr.insertText(suggestion, $head.pos).setNodeMarkup($head.before(), null, {
+											...node.attrs,
+											class: null,
+											'data-prompt': null,
+											'data-suggestion': null
+										})
+									);
+									return true;
+								}
+							}
+							return false;
+						},
+						// Add mousedown behavior
+						// mouseup: (view, event) => {
+						// 	const { state, dispatch } = view;
+						// 	const { selection } = state;
+						// 	const { $head } = selection;
+						// 	const node = $head.parent;
+
+						// 	// Reset debounce timer on mouse click
+						// 	clearTimeout(debounceTimer);
+
+						// 	// If a suggestion exists and the cursor moves, remove the suggestion
+						// 	if (
+						// 		node.type.name === 'paragraph' &&
+						// 		node.attrs['data-suggestion'] &&
+						// 		view.state.selection.$head.pos !== view.state.selection.$head.end()
+						// 	) {
+						// 		dispatch(
+						// 			state.tr.setNodeMarkup($head.before(), null, {
+						// 				...node.attrs,
+						// 				class: null,
+						// 				'data-prompt': null,
+						// 				'data-suggestion': null
+						// 			})
+						// 		);
+						// 	}
+
+						// 	return false;
+						// }
+						mouseup: (view, event) => {
+							const { state, dispatch } = view;
+
+							// Reset debounce timer on mouse click
+							clearTimeout(debounceTimer);
+
+							// Iterate over all nodes in the document
+							const tr = state.tr;
+							state.doc.descendants((node, pos) => {
+								if (node.type.name === 'paragraph' && node.attrs['data-suggestion']) {
+									// Remove suggestion from this paragraph
+									tr.setNodeMarkup(pos, null, {
+										...node.attrs,
+										class: null,
+										'data-prompt': null,
+										'data-suggestion': null
+									});
+								}
+							});
+
+							// Apply the transaction if any changes were made
+							if (tr.docChanged) {
+								dispatch(tr);
+							}
+
+							return false;
+						}
+					}
+				}
+			})
+		];
+	}
+});
diff --git a/src/lib/components/common/SVGPanZoom.svelte b/src/lib/components/common/SVGPanZoom.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e576ffb06c39d1289f504375049b39340a203906
--- /dev/null
+++ b/src/lib/components/common/SVGPanZoom.svelte
@@ -0,0 +1,53 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import panzoom from 'panzoom';
+
+	import DOMPurify from 'dompurify';
+	import DocumentDuplicate from '../icons/DocumentDuplicate.svelte';
+	import { copyToClipboard } from '$lib/utils';
+	import { toast } from 'svelte-sonner';
+	import Tooltip from './Tooltip.svelte';
+	import Clipboard from '../icons/Clipboard.svelte';
+
+	export let className = '';
+	export let svg = '';
+	export let content = '';
+
+	let instance;
+
+	let sceneParentElement: HTMLElement;
+	let sceneElement: HTMLElement;
+
+	$: if (sceneElement) {
+		instance = panzoom(sceneElement, {
+			bounds: true,
+			boundsPadding: 0.1,
+
+			zoomSpeed: 0.065
+		});
+	}
+</script>
+
+<div bind:this={sceneParentElement} class="relative {className}">
+	<div bind:this={sceneElement} class="flex h-full max-h-full justify-center items-center">
+		{@html svg}
+	</div>
+
+	{#if content}
+		<div class=" absolute top-1 right-1">
+			<Tooltip content={$i18n.t('Copy to clipboard')}>
+				<button
+					class="p-1.5 rounded-lg border border-gray-100 dark:border-none dark:bg-gray-850 hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+					on:click={() => {
+						copyToClipboard(content);
+						toast.success($i18n.t('Copied to clipboard'));
+					}}
+				>
+					<Clipboard className=" size-4" strokeWidth="1.5" />
+				</button>
+			</Tooltip>
+		</div>
+	{/if}
+</div>
diff --git a/src/lib/components/common/Selector.svelte b/src/lib/components/common/Selector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a449f5ca80d5d082e8a122e5c2d05b4d4571bf05
--- /dev/null
+++ b/src/lib/components/common/Selector.svelte
@@ -0,0 +1,95 @@
+<script lang="ts">
+	import { Select } from 'bits-ui';
+
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	import { createEventDispatcher } from 'svelte';
+	import ChevronDown from '../icons/ChevronDown.svelte';
+	import Check from '../icons/Check.svelte';
+	import Search from '../icons/Search.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	export let value = '';
+	export let placeholder = 'Select a model';
+	export let searchEnabled = true;
+	export let searchPlaceholder = 'Search a model';
+
+	export let items = [
+		{ value: 'mango', label: 'Mango' },
+		{ value: 'watermelon', label: 'Watermelon' },
+		{ value: 'apple', label: 'Apple' },
+		{ value: 'pineapple', label: 'Pineapple' },
+		{ value: 'orange', label: 'Orange' }
+	];
+
+	let searchValue = '';
+
+	$: filteredItems = searchValue
+		? items.filter((item) => item.value.toLowerCase().includes(searchValue.toLowerCase()))
+		: items;
+</script>
+
+<Select.Root
+	{items}
+	onOpenChange={() => {
+		searchValue = '';
+	}}
+	selected={items.find((item) => item.value === value)}
+	onSelectedChange={(selectedItem) => {
+		value = selectedItem.value;
+	}}
+>
+	<Select.Trigger class="relative w-full" aria-label={placeholder}>
+		<Select.Value
+			class="inline-flex h-input px-0.5 w-full outline-none bg-transparent truncate text-lg font-semibold placeholder-gray-400  focus:outline-none"
+			{placeholder}
+		/>
+		<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
+	</Select.Trigger>
+	<Select.Content
+		class="w-full rounded-lg  bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/40  outline-none"
+		transition={flyAndScale}
+		sideOffset={4}
+	>
+		<slot>
+			{#if searchEnabled}
+				<div class="flex items-center gap-2.5 px-5 mt-3.5 mb-3">
+					<Search className="size-4" strokeWidth="2.5" />
+
+					<input
+						bind:value={searchValue}
+						class="w-full text-sm bg-transparent outline-none"
+						placeholder={searchPlaceholder}
+					/>
+				</div>
+
+				<hr class="border-gray-100 dark:border-gray-800" />
+			{/if}
+
+			<div class="px-3 my-2 max-h-80 overflow-y-auto">
+				{#each filteredItems as item}
+					<Select.Item
+						class="flex w-full font-medium line-clamp-1 select-none items-center rounded-button py-2 pl-3 pr-1.5 text-sm  text-gray-700 dark:text-gray-100  outline-none transition-all duration-75 hover:bg-gray-100 dark:hover:bg-gray-850 rounded-lg cursor-pointer data-[highlighted]:bg-muted"
+						value={item.value}
+						label={item.label}
+					>
+						{item.label}
+
+						{#if value === item.value}
+							<div class="ml-auto">
+								<Check />
+							</div>
+						{/if}
+					</Select.Item>
+				{:else}
+					<div>
+						<div class="block px-5 py-2 text-sm text-gray-700 dark:text-gray-100">
+							No results found
+						</div>
+					</div>
+				{/each}
+			</div>
+		</slot>
+	</Select.Content>
+</Select.Root>
diff --git a/src/lib/components/common/SensitiveInput.svelte b/src/lib/components/common/SensitiveInput.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cb6484a0ba306fb14617b92330c5eeac3ab6777f
--- /dev/null
+++ b/src/lib/components/common/SensitiveInput.svelte
@@ -0,0 +1,63 @@
+<script lang="ts">
+	export let value: string = '';
+	export let placeholder = '';
+	export let required = true;
+	export let readOnly = false;
+	export let outerClassName = 'flex flex-1 bg-transparent';
+	export let inputClassName =
+		'w-full text-sm py-0.5 placeholder:text-gray-300 dark:placeholder:text-gray-700 bg-transparent outline-none';
+	export let showButtonClassName = 'pl-1.5  transition bg-transparent';
+
+	let show = false;
+</script>
+
+<div class={outerClassName}>
+	<input
+		class={`${inputClassName} ${show ? '' : 'password'}`}
+		{placeholder}
+		bind:value
+		required={required && !readOnly}
+		disabled={readOnly}
+		autocomplete="off"
+		type="text"
+	/>
+	<button
+		class={showButtonClassName}
+		on:click={(e) => {
+			e.preventDefault();
+			show = !show;
+		}}
+	>
+		{#if show}
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 16 16"
+				fill="currentColor"
+				class="size-4"
+			>
+				<path
+					fill-rule="evenodd"
+					d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l10.5 10.5a.75.75 0 1 0 1.06-1.06l-1.322-1.323a7.012 7.012 0 0 0 2.16-3.11.87.87 0 0 0 0-.567A7.003 7.003 0 0 0 4.82 3.76l-1.54-1.54Zm3.196 3.195 1.135 1.136A1.502 1.502 0 0 1 9.45 8.389l1.136 1.135a3 3 0 0 0-4.109-4.109Z"
+					clip-rule="evenodd"
+				/>
+				<path
+					d="m7.812 10.994 1.816 1.816A7.003 7.003 0 0 1 1.38 8.28a.87.87 0 0 1 0-.566 6.985 6.985 0 0 1 1.113-2.039l2.513 2.513a3 3 0 0 0 2.806 2.806Z"
+				/>
+			</svg>
+		{:else}
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 16 16"
+				fill="currentColor"
+				class="size-4"
+			>
+				<path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z" />
+				<path
+					fill-rule="evenodd"
+					d="M1.38 8.28a.87.87 0 0 1 0-.566 7.003 7.003 0 0 1 13.238.006.87.87 0 0 1 0 .566A7.003 7.003 0 0 1 1.379 8.28ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
+					clip-rule="evenodd"
+				/>
+			</svg>
+		{/if}
+	</button>
+</div>
diff --git a/src/lib/components/common/Sidebar.svelte b/src/lib/components/common/Sidebar.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7e32a8f2400e7292496549558bdc167ee51aeff8
--- /dev/null
+++ b/src/lib/components/common/Sidebar.svelte
@@ -0,0 +1,31 @@
+<script lang="ts">
+	import { quadInOut, quintIn } from 'svelte/easing';
+	import { fade, slide } from 'svelte/transition';
+
+	export let show = false;
+	export let side = 'right';
+	export let width = '200px';
+
+	export let className = '';
+	export let duration = 100;
+</script>
+
+{#if show}
+	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<div
+		class="absolute z-20 top-0 right-0 left-0 bottom-0 bg-white/20 dark:bg-black/5 w-full min-h-full h-full flex justify-center overflow-hidden overscroll-contain"
+		on:mousedown={() => {
+			show = false;
+		}}
+		transition:fade={{ duration: duration }}
+	/>
+
+	<div
+		class="absolute z-30 shadow-xl {side === 'right' ? 'right-0' : 'left-0'} top-0 bottom-0"
+		transition:slide={{ duration: duration, easing: quadInOut, axis: side === 'right' ? 'x' : 'y' }}
+	>
+		<div class="{className} h-full" style="width: {show ? width : '0px'}">
+			<slot />
+		</div>
+	</div>
+{/if}
diff --git a/src/lib/components/common/SlideShow.svelte b/src/lib/components/common/SlideShow.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8d224343c97a21096aa2f482fdd0463709a0f2d0
--- /dev/null
+++ b/src/lib/components/common/SlideShow.svelte
@@ -0,0 +1,39 @@
+<script lang="ts">
+	import { onMount } from 'svelte';
+
+	export let imageUrls = [
+		'/assets/images/adam.jpg',
+		'/assets/images/galaxy.jpg',
+		'/assets/images/earth.jpg',
+		'/assets/images/space.jpg'
+	];
+	export let duration = 5000;
+	let selectedImageIdx = 0;
+
+	onMount(() => {
+		setInterval(() => {
+			selectedImageIdx = (selectedImageIdx + 1) % (imageUrls.length - 1);
+		}, duration);
+	});
+</script>
+
+{#each imageUrls as imageUrl, idx (idx)}
+	<div
+		class="image w-full h-full absolute top-0 left-0 bg-cover bg-center transition-opacity duration-1000"
+		style="opacity: {selectedImageIdx === idx ? 1 : 0}; background-image: url('{imageUrl}')"
+	></div>
+{/each}
+
+<style>
+	.image {
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		background-size: cover;
+		background-position: center; /* Center the background images */
+		transition: opacity 1s ease-in-out; /* Smooth fade effect */
+		opacity: 0; /* Make images initially not visible */
+	}
+</style>
diff --git a/src/lib/components/common/Spinner.svelte b/src/lib/components/common/Spinner.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a22f56dcb2c0d9c4451f42876902777db0b128ad
--- /dev/null
+++ b/src/lib/components/common/Spinner.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+	export let className: string = 'size-5';
+</script>
+
+<div class="flex justify-center text-center">
+	<svg class={className} viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
+		><style>
+			.spinner_ajPY {
+				transform-origin: center;
+				animation: spinner_AtaB 0.75s infinite linear;
+			}
+			@keyframes spinner_AtaB {
+				100% {
+					transform: rotate(360deg);
+				}
+			}
+		</style><path
+			d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+			opacity=".25"
+		/><path
+			d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+			class="spinner_ajPY"
+		/></svg
+	>
+</div>
diff --git a/src/lib/components/common/Switch.svelte b/src/lib/components/common/Switch.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d19b160f4f1df95ad11ed8822aa5f47df89631be
--- /dev/null
+++ b/src/lib/components/common/Switch.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+	import { createEventDispatcher, tick } from 'svelte';
+	import { Switch } from 'bits-ui';
+	export let state = true;
+
+	const dispatch = createEventDispatcher();
+
+	$: dispatch('change', state);
+</script>
+
+<Switch.Root
+	bind:checked={state}
+	class="flex h-5 min-h-5 w-9 shrink-0 cursor-pointer items-center rounded-full px-[3px] mx-[1px] transition  {state
+		? ' bg-emerald-600'
+		: 'bg-gray-200 dark:bg-transparent'} outline outline-1 outline-gray-100 dark:outline-gray-800"
+>
+	<Switch.Thumb
+		class="pointer-events-none block size-4 shrink-0 rounded-full bg-white transition-transform data-[state=checked]:translate-x-3.5 data-[state=unchecked]:translate-x-0 data-[state=unchecked]:shadow-mini "
+	/>
+</Switch.Root>
diff --git a/src/lib/components/common/Tags.svelte b/src/lib/components/common/Tags.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0f19eed9d82d0db8a64a8d7f776896521c3a2dc9
--- /dev/null
+++ b/src/lib/components/common/Tags.svelte
@@ -0,0 +1,26 @@
+<script lang="ts">
+	import TagInput from './Tags/TagInput.svelte';
+	import TagList from './Tags/TagList.svelte';
+	import { getContext, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let tags = [];
+</script>
+
+<div class="flex flex-row flex-wrap gap-1 line-clamp-1">
+	<TagList
+		{tags}
+		on:delete={(e) => {
+			dispatch('delete', e.detail);
+		}}
+	/>
+
+	<TagInput
+		label={tags.length == 0 ? $i18n.t('Add Tags') : ''}
+		on:add={(e) => {
+			dispatch('add', e.detail);
+		}}
+	/>
+</div>
diff --git a/src/lib/components/common/Tags/TagInput.svelte b/src/lib/components/common/Tags/TagInput.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d854a9c305c86023a884cac3c2e3192b27784ab2
--- /dev/null
+++ b/src/lib/components/common/Tags/TagInput.svelte
@@ -0,0 +1,88 @@
+<script lang="ts">
+	import { createEventDispatcher, getContext } from 'svelte';
+	import { tags } from '$lib/stores';
+	import { toast } from 'svelte-sonner';
+	const dispatch = createEventDispatcher();
+
+	const i18n = getContext('i18n');
+
+	export let label = '';
+	let showTagInput = false;
+	let tagName = '';
+
+	const addTagHandler = async () => {
+		tagName = tagName.trim();
+		if (tagName !== '') {
+			dispatch('add', tagName);
+			tagName = '';
+			showTagInput = false;
+		} else {
+			toast.error($i18n.t(`Invalid Tag`));
+		}
+	};
+</script>
+
+<div class="px-0.5 flex {showTagInput ? 'flex-row-reverse' : ''}">
+	{#if showTagInput}
+		<div class="flex items-center">
+			<input
+				bind:value={tagName}
+				class=" px-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-none line-clamp-1 w-[6.5rem]"
+				placeholder={$i18n.t('Add a tag')}
+				list="tagOptions"
+				on:keydown={(event) => {
+					if (event.key === 'Enter') {
+						addTagHandler();
+					}
+				}}
+			/>
+			<datalist id="tagOptions">
+				{#each $tags as tag}
+					<option value={tag.name} />
+				{/each}
+			</datalist>
+
+			<button type="button" aria-label={$i18n.t('Save Tag')} on:click={addTagHandler}>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 16 16"
+					fill="currentColor"
+					stroke-width="2"
+					class="w-3 h-3"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</button>
+		</div>
+	{/if}
+
+	<button
+		class=" cursor-pointer self-center p-0.5 flex h-fit items-center dark:hover:bg-gray-700 rounded-full transition border dark:border-gray-600 border-dashed"
+		type="button"
+		aria-label={$i18n.t('Add Tag')}
+		on:click={() => {
+			showTagInput = !showTagInput;
+		}}
+	>
+		<div class=" m-auto self-center">
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 16 16"
+				fill="currentColor"
+				class="w-3 h-3 {showTagInput ? 'rotate-45' : ''} transition-all transform"
+			>
+				<path
+					d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
+				/>
+			</svg>
+		</div>
+	</button>
+
+	{#if label && !showTagInput}
+		<span class="text-xs pl-2 self-center">{label}</span>
+	{/if}
+</div>
diff --git a/src/lib/components/common/Tags/TagList.svelte b/src/lib/components/common/Tags/TagList.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9751da929c6ba35407a4dc0442ba42b4613ce0d5
--- /dev/null
+++ b/src/lib/components/common/Tags/TagList.svelte
@@ -0,0 +1,32 @@
+<script lang="ts">
+	import { createEventDispatcher } from 'svelte';
+	import Tooltip from '../Tooltip.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
+	import Badge from '../Badge.svelte';
+	const dispatch = createEventDispatcher();
+
+	export let tags = [];
+</script>
+
+{#each tags as tag}
+	<Tooltip content={tag.name}>
+		<div
+			class="relative group/tags px-1.5 py-[0.2px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-full bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer"
+		>
+			<div class=" text-[0.7rem] font-medium self-center line-clamp-1 w-fit">
+				{tag.name}
+			</div>
+			<div class="absolute invisible right-0.5 group-hover/tags:visible transition">
+				<button
+					class="rounded-full border bg-white dark:bg-gray-700 h-full flex self-center cursor-pointer"
+					on:click={() => {
+						dispatch('delete', tag.name);
+					}}
+					type="button"
+				>
+					<XMark className="size-3" strokeWidth="2.5" />
+				</button>
+			</div>
+		</div>
+	</Tooltip>
+{/each}
diff --git a/src/lib/components/common/Textarea.svelte b/src/lib/components/common/Textarea.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..893f579b3f4e57f53bb881ee1dd3d6bb05af5a41
--- /dev/null
+++ b/src/lib/components/common/Textarea.svelte
@@ -0,0 +1,67 @@
+<script lang="ts">
+	import { onMount, tick } from 'svelte';
+
+	export let value = '';
+	export let placeholder = '';
+	export let className =
+		'w-full rounded-lg px-3 py-2 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none resize-none h-full';
+
+	let textareaElement;
+
+	$: if (textareaElement) {
+		if (textareaElement.innerText !== value && value !== '') {
+			textareaElement.innerText = value ?? '';
+		}
+	}
+
+	// Adjust height on mount and after setting the element.
+	onMount(async () => {
+		await tick();
+	});
+
+	// Handle paste event to ensure only plaintext is pasted
+	function handlePaste(event: ClipboardEvent) {
+		event.preventDefault(); // Prevent the default paste action
+		const clipboardData = event.clipboardData?.getData('text/plain'); // Get plaintext from clipboard
+
+		// Insert plaintext into the textarea
+		document.execCommand('insertText', false, clipboardData);
+	}
+</script>
+
+<div
+	contenteditable="true"
+	bind:this={textareaElement}
+	class="{className} whitespace-pre-wrap relative {value
+		? !value.trim()
+			? 'placeholder'
+			: ''
+		: 'placeholder'}"
+	style="field-sizing: content; -moz-user-select: text !important;"
+	on:input={() => {
+		const text = textareaElement.innerText;
+		if (text === '\n') {
+			value = '';
+			return;
+		}
+
+		value = text;
+	}}
+	on:paste={handlePaste}
+	data-placeholder={placeholder}
+/>
+
+<style>
+	.placeholder::before {
+		/* abolute */
+		position: absolute;
+		content: attr(data-placeholder);
+		color: #adb5bd;
+		overflow: hidden;
+		display: -webkit-box;
+		-webkit-box-orient: vertical;
+		-webkit-line-clamp: 1;
+		pointer-events: none;
+		touch-action: none;
+	}
+</style>
diff --git a/src/lib/components/common/Tooltip.svelte b/src/lib/components/common/Tooltip.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..23042752559cda22157605cff00519200aa822c5
--- /dev/null
+++ b/src/lib/components/common/Tooltip.svelte
@@ -0,0 +1,51 @@
+<script lang="ts">
+	import DOMPurify from 'dompurify';
+
+	import { onDestroy } from 'svelte';
+	import { marked } from 'marked';
+
+	import tippy from 'tippy.js';
+	import { roundArrow } from 'tippy.js';
+
+	export let placement = 'top';
+	export let content = `I'm a tooltip!`;
+	export let touch = true;
+	export let className = 'flex';
+	export let theme = '';
+	export let allowHTML = true;
+	export let tippyOptions = {};
+
+	let tooltipElement;
+	let tooltipInstance;
+
+	$: if (tooltipElement && content) {
+		if (tooltipInstance) {
+			tooltipInstance.setContent(DOMPurify.sanitize(content));
+		} else {
+			tooltipInstance = tippy(tooltipElement, {
+				content: DOMPurify.sanitize(content),
+				placement: placement,
+				allowHTML: allowHTML,
+				touch: touch,
+				...(theme !== '' ? { theme } : { theme: 'dark' }),
+				arrow: false,
+				offset: [0, 4],
+				...tippyOptions
+			});
+		}
+	} else if (tooltipInstance && content === '') {
+		if (tooltipInstance) {
+			tooltipInstance.destroy();
+		}
+	}
+
+	onDestroy(() => {
+		if (tooltipInstance) {
+			tooltipInstance.destroy();
+		}
+	});
+</script>
+
+<div bind:this={tooltipElement} aria-label={DOMPurify.sanitize(content)} class={className}>
+	<slot />
+</div>
diff --git a/src/lib/components/common/Valves.svelte b/src/lib/components/common/Valves.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e88253f3f56038c074fbedf3ab5bcdfc60d51fee
--- /dev/null
+++ b/src/lib/components/common/Valves.svelte
@@ -0,0 +1,109 @@
+<script>
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import Switch from './Switch.svelte';
+
+	export let valvesSpec = null;
+	export let valves = {};
+</script>
+
+{#if valvesSpec && Object.keys(valvesSpec?.properties ?? {}).length}
+	{#each Object.keys(valvesSpec.properties) as property, idx}
+		<div class=" py-0.5 w-full justify-between">
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-xs font-medium">
+					{valvesSpec.properties[property].title}
+
+					{#if (valvesSpec?.required ?? []).includes(property)}
+						<span class=" text-gray-500">*required</span>
+					{/if}
+				</div>
+
+				<button
+					class="p-1 px-3 text-xs flex rounded transition"
+					type="button"
+					on:click={() => {
+						valves[property] =
+							(valves[property] ?? null) === null
+								? (valvesSpec.properties[property]?.default ?? '')
+								: null;
+
+						dispatch('change');
+					}}
+				>
+					{#if (valves[property] ?? null) === null}
+						<span class="ml-2 self-center">
+							{#if (valvesSpec?.required ?? []).includes(property)}
+								{$i18n.t('None')}
+							{:else}
+								{$i18n.t('Default')}
+							{/if}
+						</span>
+					{:else}
+						<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
+					{/if}
+				</button>
+			</div>
+
+			{#if (valves[property] ?? null) !== null}
+				<!-- {valves[property]} -->
+				<div class="flex mt-0.5 mb-1.5 space-x-2">
+					<div class=" flex-1">
+						{#if valvesSpec.properties[property]?.enum ?? null}
+							<select
+								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none border border-gray-100 dark:border-gray-800"
+								bind:value={valves[property]}
+								on:change={() => {
+									dispatch('change');
+								}}
+							>
+								{#each valvesSpec.properties[property].enum as option}
+									<option value={option} selected={option === valves[property]}>
+										{option}
+									</option>
+								{/each}
+							</select>
+						{:else if (valvesSpec.properties[property]?.type ?? null) === 'boolean'}
+							<div class="flex justify-between items-center">
+								<div class="text-xs text-gray-500">
+									{valves[property] ? 'Enabled' : 'Disabled'}
+								</div>
+
+								<div class=" pr-2">
+									<Switch
+										bind:state={valves[property]}
+										on:change={() => {
+											dispatch('change');
+										}}
+									/>
+								</div>
+							</div>
+						{:else}
+							<input
+								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none border border-gray-100 dark:border-gray-800"
+								type="text"
+								placeholder={valvesSpec.properties[property].title}
+								bind:value={valves[property]}
+								autocomplete="off"
+								required
+								on:change={() => {
+									dispatch('change');
+								}}
+							/>
+						{/if}
+					</div>
+				</div>
+			{/if}
+
+			{#if (valvesSpec.properties[property]?.description ?? null) !== null}
+				<div class="text-xs text-gray-500">
+					{valvesSpec.properties[property].description}
+				</div>
+			{/if}
+		</div>
+	{/each}
+{:else}
+	<div class="text-xs">No valves</div>
+{/if}
diff --git a/src/lib/components/icons/AdjustmentsHorizontal.svelte b/src/lib/components/icons/AdjustmentsHorizontal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4cc509e8460877f15613d0e45575ba1a9425b7ad
--- /dev/null
+++ b/src/lib/components/icons/AdjustmentsHorizontal.svelte
@@ -0,0 +1,17 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	viewBox="0 0 24 24"
+	stroke="currentColor"
+	fill="currentColor"
+	class={className}
+	stroke-width={strokeWidth}
+>
+	<path
+		d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArchiveBox.svelte b/src/lib/components/icons/ArchiveBox.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d4f6e201abf2c226a8b820ab21a5c65c204087b4
--- /dev/null
+++ b/src/lib/components/icons/ArchiveBox.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-3.5';
+	export let strokeWidth = '2.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m20.25 7.5-.625 10.632a2.25 2.25 0 0 1-2.247 2.118H6.622a2.25 2.25 0 0 1-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArrowDownTray.svelte b/src/lib/components/icons/ArrowDownTray.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..55620e9feaf4b1d92cd8373a1167ecc9ddfef289
--- /dev/null
+++ b/src/lib/components/icons/ArrowDownTray.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArrowLeft.svelte b/src/lib/components/icons/ArrowLeft.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..166aee7f6c92b59867b4beac7937f0a39596934c
--- /dev/null
+++ b/src/lib/components/icons/ArrowLeft.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18" />
+</svg>
diff --git a/src/lib/components/icons/ArrowPath.svelte b/src/lib/components/icons/ArrowPath.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5b31652d6d2028c0e80e7502ba23073dda1f4cc5
--- /dev/null
+++ b/src/lib/components/icons/ArrowPath.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArrowRight.svelte b/src/lib/components/icons/ArrowRight.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a3ac1af4b47e5b53a9e5d85203ba629356f7a8da
--- /dev/null
+++ b/src/lib/components/icons/ArrowRight.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
+</svg>
diff --git a/src/lib/components/icons/ArrowRightCircle.svelte b/src/lib/components/icons/ArrowRightCircle.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f899453f171a5f7ce8894258fd950b9c2f550081
--- /dev/null
+++ b/src/lib/components/icons/ArrowRightCircle.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArrowUpCircle.svelte b/src/lib/components/icons/ArrowUpCircle.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..caa5f219b880dc8e9af89f62b30a3e1b98f48f68
--- /dev/null
+++ b/src/lib/components/icons/ArrowUpCircle.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m15 11.25-3-3m0 0-3 3m3-3v7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ArrowsPointingOut.svelte b/src/lib/components/icons/ArrowsPointingOut.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..efc7f98b6b96eb3ab8d8eac4bd49e4b8ab0a813d
--- /dev/null
+++ b/src/lib/components/icons/ArrowsPointingOut.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"
+	/>
+</svg>
diff --git a/src/lib/components/icons/BarsArrowUp.svelte b/src/lib/components/icons/BarsArrowUp.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d34dbde676aac1e5a915b66ac1fa3bdfc693ec09
--- /dev/null
+++ b/src/lib/components/icons/BarsArrowUp.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Bolt.svelte b/src/lib/components/icons/Bolt.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..681ef53c9562d600163a16923500b55ed2f3fddc
--- /dev/null
+++ b/src/lib/components/icons/Bolt.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-3';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/BookOpen.svelte b/src/lib/components/icons/BookOpen.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5a77433d5c0fcd2c2049c95d7f18d3bfe5a40140
--- /dev/null
+++ b/src/lib/components/icons/BookOpen.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 6.042A8.967 8.967 0 0 0 6 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 0 1 6 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 0 1 6-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0 0 18 18a8.967 8.967 0 0 0-6 2.292m0-14.25v14.25"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Bookmark.svelte b/src/lib/components/icons/Bookmark.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ea8028457d0f4fdf08398513e192d69aa97f7c5f
--- /dev/null
+++ b/src/lib/components/icons/Bookmark.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M17.593 3.322c1.1.128 1.907 1.077 1.907 2.185V21L12 17.25 4.5 21V5.507c0-1.108.806-2.057 1.907-2.185a48.507 48.507 0 0 1 11.186 0Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/BookmarkSlash.svelte b/src/lib/components/icons/BookmarkSlash.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6b80ea3cab1b437c51834d5bb12464bdfc55169a
--- /dev/null
+++ b/src/lib/components/icons/BookmarkSlash.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m3 3 1.664 1.664M21 21l-1.5-1.5m-5.485-1.242L12 17.25 4.5 21V8.742m.164-4.078a2.15 2.15 0 0 1 1.743-1.342 48.507 48.507 0 0 1 11.186 0c1.1.128 1.907 1.077 1.907 2.185V19.5M4.664 4.664 19.5 19.5"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ChartBar.svelte b/src/lib/components/icons/ChartBar.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6f2d35821a4050fd6159c07ae77601f923ada073
--- /dev/null
+++ b/src/lib/components/icons/ChartBar.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		d="M18.375 2.25c-1.035 0-1.875.84-1.875 1.875v15.75c0 1.035.84 1.875 1.875 1.875h.75c1.035 0 1.875-.84 1.875-1.875V4.125c0-1.036-.84-1.875-1.875-1.875h-.75ZM9.75 8.625c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-.75a1.875 1.875 0 0 1-1.875-1.875V8.625ZM3 13.125c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v6.75c0 1.035-.84 1.875-1.875 1.875h-.75A1.875 1.875 0 0 1 3 19.875v-6.75Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ChatBubble.svelte b/src/lib/components/icons/ChatBubble.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7ece2c4c7a658fd9d161da7a1f99b6f382dcfb63
--- /dev/null
+++ b/src/lib/components/icons/ChatBubble.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 0 1-2.555-.337A5.972 5.972 0 0 1 5.41 20.97a5.969 5.969 0 0 1-.474-.065 4.48 4.48 0 0 0 .978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ChatBubbleOval.svelte b/src/lib/components/icons/ChatBubbleOval.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4edbbb61404680117c00834c6f26ab3bb344e92a
--- /dev/null
+++ b/src/lib/components/icons/ChatBubbleOval.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 0 1-.923 1.785A5.969 5.969 0 0 0 6 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/ChatBubbles.svelte b/src/lib/components/icons/ChatBubbles.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1651746fd6fbedd745cdbeb7f078872ff3f1e036
--- /dev/null
+++ b/src/lib/components/icons/ChatBubbles.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Check.svelte b/src/lib/components/icons/Check.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..37eb9d7139f53afd199c2e011ae6481b6286250a
--- /dev/null
+++ b/src/lib/components/icons/Check.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
+</svg>
diff --git a/src/lib/components/icons/ChevronDown.svelte b/src/lib/components/icons/ChevronDown.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..16686ea3a3be64ce629c8d020764f14f8b4184ae
--- /dev/null
+++ b/src/lib/components/icons/ChevronDown.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
+</svg>
diff --git a/src/lib/components/icons/ChevronLeft.svelte b/src/lib/components/icons/ChevronLeft.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..78ee64d2422a4729cbaa28fd7d86696781f848ab
--- /dev/null
+++ b/src/lib/components/icons/ChevronLeft.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
+</svg>
diff --git a/src/lib/components/icons/ChevronRight.svelte b/src/lib/components/icons/ChevronRight.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7daf4a14a5297f72d9aa1fcddedb6d17eb556456
--- /dev/null
+++ b/src/lib/components/icons/ChevronRight.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
+</svg>
diff --git a/src/lib/components/icons/ChevronUp.svelte b/src/lib/components/icons/ChevronUp.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6fe7ca1ccd0473c8dbd1d32bdc4ffb8abeb6d0b6
--- /dev/null
+++ b/src/lib/components/icons/ChevronUp.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
+</svg>
diff --git a/src/lib/components/icons/ChevronUpDown.svelte b/src/lib/components/icons/ChevronUpDown.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7f23435a2d0ca45e0295994428e467722cc79a93
--- /dev/null
+++ b/src/lib/components/icons/ChevronUpDown.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Clipboard.svelte b/src/lib/components/icons/Clipboard.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b1a793a76e2f5ef95409fb920eb2a648648d9e48
--- /dev/null
+++ b/src/lib/components/icons/Clipboard.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184"
+	/>
+</svg>
diff --git a/src/lib/components/icons/CloudArrowUp.svelte b/src/lib/components/icons/CloudArrowUp.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bb54d861fa2cc2824cb2dfedf1fc43c8d6ec76a2
--- /dev/null
+++ b/src/lib/components/icons/CloudArrowUp.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 16.5V9.75m0 0 3 3m-3-3-3 3M6.75 19.5a4.5 4.5 0 0 1-1.41-8.775 5.25 5.25 0 0 1 10.233-2.33 3 3 0 0 1 3.758 3.848A3.752 3.752 0 0 1 18 19.5H6.75Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Cog6.svelte b/src/lib/components/icons/Cog6.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..286b62624aecc15eb2588fd2764dca1d76ded88f
--- /dev/null
+++ b/src/lib/components/icons/Cog6.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+	/>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
+</svg>
diff --git a/src/lib/components/icons/Cube.svelte b/src/lib/components/icons/Cube.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..4015b2d7cc93a1ecf1f673d11341627aa79b0dea
--- /dev/null
+++ b/src/lib/components/icons/Cube.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Document.svelte b/src/lib/components/icons/Document.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9ae719725bee33303f9f9b8310c0cc04e365c0e5
--- /dev/null
+++ b/src/lib/components/icons/Document.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/DocumentArrowDown.svelte b/src/lib/components/icons/DocumentArrowDown.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c6d25da34ef71604e90bc937d2bc83fa0e5e60c8
--- /dev/null
+++ b/src/lib/components/icons/DocumentArrowDown.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m.75 12 3 3m0 0 3-3m-3 3v-6m-1.5-9H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/DocumentArrowUpSolid.svelte b/src/lib/components/icons/DocumentArrowUpSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2690f55370964ddc86ee805145e29d470b25d8d8
--- /dev/null
+++ b/src/lib/components/icons/DocumentArrowUpSolid.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6.905 9.97a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72V18a.75.75 0 0 0 1.5 0v-4.19l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z"
+		clip-rule="evenodd"
+	/>
+	<path
+		d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/DocumentChartBar.svelte b/src/lib/components/icons/DocumentChartBar.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bc811bed1abcaae386c5746c61e93559a4d7cd3d
--- /dev/null
+++ b/src/lib/components/icons/DocumentChartBar.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875ZM9.75 17.25a.75.75 0 0 0-1.5 0V18a.75.75 0 0 0 1.5 0v-.75Zm2.25-3a.75.75 0 0 1 .75.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 .75-.75Zm3.75-1.5a.75.75 0 0 0-1.5 0V18a.75.75 0 0 0 1.5 0v-5.25Z"
+		clip-rule="evenodd"
+	/>
+	<path
+		d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/DocumentDuplicate.svelte b/src/lib/components/icons/DocumentDuplicate.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a208fefc8ac1ecc3acedf9d6a0aa1013ea6bab18
--- /dev/null
+++ b/src/lib/components/icons/DocumentDuplicate.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Download.svelte b/src/lib/components/icons/Download.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..55620e9feaf4b1d92cd8373a1167ecc9ddfef289
--- /dev/null
+++ b/src/lib/components/icons/Download.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"
+	/>
+</svg>
diff --git a/src/lib/components/icons/EllipsisHorizontal.svelte b/src/lib/components/icons/EllipsisHorizontal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6a7d532e38cc572fadb6d43cf7f64527f1437c1f
--- /dev/null
+++ b/src/lib/components/icons/EllipsisHorizontal.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/EllipsisVertical.svelte b/src/lib/components/icons/EllipsisVertical.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7ccbea2947395ce6cc584917c886bc44169af935
--- /dev/null
+++ b/src/lib/components/icons/EllipsisVertical.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 6.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 12.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5ZM12 18.75a.75.75 0 1 1 0-1.5.75.75 0 0 1 0 1.5Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/EyeSlash.svelte b/src/lib/components/icons/EyeSlash.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ec53cfb09e48f612dbbef1b33a1929de5c139fb7
--- /dev/null
+++ b/src/lib/components/icons/EyeSlash.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"
+	/>
+</svg>
diff --git a/src/lib/components/icons/FloppyDisk.svelte b/src/lib/components/icons/FloppyDisk.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bcb481e826541f74ddba12ad333c4dc497ef6312
--- /dev/null
+++ b/src/lib/components/icons/FloppyDisk.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	stroke="currentColor"
+	class={className}
+	aria-hidden="true"
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+>
+	<path
+		stroke="currentColor"
+		stroke-linecap="round"
+		stroke-width={strokeWidth}
+		d="M11 16h2m6.707-9.293-2.414-2.414A1 1 0 0 0 16.586 4H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V7.414a1 1 0 0 0-.293-.707ZM16 20v-6a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v6h8ZM9 4h6v3a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1V4Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/FolderOpen.svelte b/src/lib/components/icons/FolderOpen.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f6b3c64b30149eaf8b75e3f8c75b4727d4ceb60e
--- /dev/null
+++ b/src/lib/components/icons/FolderOpen.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 0 0-1.883 2.542l.857 6a2.25 2.25 0 0 0 2.227 1.932H19.05a2.25 2.25 0 0 0 2.227-1.932l.857-6a2.25 2.25 0 0 0-1.883-2.542m-16.5 0V6A2.25 2.25 0 0 1 6 3.75h3.879a1.5 1.5 0 0 1 1.06.44l2.122 2.12a1.5 1.5 0 0 0 1.06.44H18A2.25 2.25 0 0 1 20.25 9v.776"
+	/>
+</svg>
diff --git a/src/lib/components/icons/GarbageBin.svelte b/src/lib/components/icons/GarbageBin.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..31530fc7210733a9fb8f8cc0064c5854a4859ec6
--- /dev/null
+++ b/src/lib/components/icons/GarbageBin.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
+	/>
+</svg>
diff --git a/src/lib/components/icons/GlobeAlt.svelte b/src/lib/components/icons/GlobeAlt.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d2f86f43801f5baaa7a6a83cbdc386a06c0305a4
--- /dev/null
+++ b/src/lib/components/icons/GlobeAlt.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418"
+	/>
+</svg>
diff --git a/src/lib/components/icons/GlobeAltSolid.svelte b/src/lib/components/icons/GlobeAltSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7054d311f82bb8a8ed946abc4e81e0b258ddbdf8
--- /dev/null
+++ b/src/lib/components/icons/GlobeAltSolid.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Headphone.svelte b/src/lib/components/icons/Headphone.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..10902a7cced4fb8dfae3cb2e3eecd802529c0b30
--- /dev/null
+++ b/src/lib/components/icons/Headphone.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '0';
+</script>
+
+<svg
+	aria-hidden="true"
+	xmlns="http://www.w3.org/2000/svg"
+	fill="currentColor"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		fill-rule="evenodd"
+		d="M12 5a7 7 0 0 0-7 7v1.17c.313-.11.65-.17 1-.17h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H6a3 3 0 0 1-3-3v-6a9 9 0 0 1 18 0v6a3 3 0 0 1-3 3h-2a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h2c.35 0 .687.06 1 .17V12a7 7 0 0 0-7-7Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Heart.svelte b/src/lib/components/icons/Heart.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ba042ff6552108af5ec0e3aa75cdfb7a868c631c
--- /dev/null
+++ b/src/lib/components/icons/Heart.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Info.svelte b/src/lib/components/icons/Info.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2849ac532b4ea366399b4583e3f84c57c461a094
--- /dev/null
+++ b/src/lib/components/icons/Info.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Keyboard.svelte b/src/lib/components/icons/Keyboard.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..baf633c0d6c4b998557335472015f3df8ddf2027
--- /dev/null
+++ b/src/lib/components/icons/Keyboard.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	aria-hidden="true"
+	xmlns="http://www.w3.org/2000/svg"
+	fill="currentColor"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	class={className}
+>
+	<path
+		fill-rule="evenodd"
+		d="M2 7a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7Zm5.01 1H5v2.01h2.01V8Zm3 0H8v2.01h2.01V8Zm3 0H11v2.01h2.01V8Zm3 0H14v2.01h2.01V8Zm3 0H17v2.01h2.01V8Zm-12 3H5v2.01h2.01V11Zm3 0H8v2.01h2.01V11Zm3 0H11v2.01h2.01V11Zm3 0H14v2.01h2.01V11Zm3 0H17v2.01h2.01V11Zm-12 3H5v2.01h2.01V14ZM8 14l-.001 2 8.011.01V14H8Zm11.01 0H17v2.01h2.01V14Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Lifebuoy.svelte b/src/lib/components/icons/Lifebuoy.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d8c49b0ae3733e58eac0774bdb1d841097bbc8e0
--- /dev/null
+++ b/src/lib/components/icons/Lifebuoy.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M16.712 4.33a9.027 9.027 0 0 1 1.652 1.306c.51.51.944 1.064 1.306 1.652M16.712 4.33l-3.448 4.138m3.448-4.138a9.014 9.014 0 0 0-9.424 0M19.67 7.288l-4.138 3.448m4.138-3.448a9.014 9.014 0 0 1 0 9.424m-4.138-5.976a3.736 3.736 0 0 0-.88-1.388 3.737 3.737 0 0 0-1.388-.88m2.268 2.268a3.765 3.765 0 0 1 0 2.528m-2.268-4.796a3.765 3.765 0 0 0-2.528 0m4.796 4.796c-.181.506-.475.982-.88 1.388a3.736 3.736 0 0 1-1.388.88m2.268-2.268 4.138 3.448m0 0a9.027 9.027 0 0 1-1.306 1.652c-.51.51-1.064.944-1.652 1.306m0 0-3.448-4.138m3.448 4.138a9.014 9.014 0 0 1-9.424 0m5.976-4.138a3.765 3.765 0 0 1-2.528 0m0 0a3.736 3.736 0 0 1-1.388-.88 3.737 3.737 0 0 1-.88-1.388m2.268 2.268L7.288 19.67m0 0a9.024 9.024 0 0 1-1.652-1.306 9.027 9.027 0 0 1-1.306-1.652m0 0 4.138-3.448M4.33 16.712a9.014 9.014 0 0 1 0-9.424m4.138 5.976a3.765 3.765 0 0 1 0-2.528m0 0c.181-.506.475-.982.88-1.388a3.736 3.736 0 0 1 1.388-.88m-2.268 2.268L4.33 7.288m6.406 1.18L7.288 4.33m0 0a9.024 9.024 0 0 0-1.652 1.306A9.025 9.025 0 0 0 4.33 7.288"
+	/>
+</svg>
diff --git a/src/lib/components/icons/LightBlub.svelte b/src/lib/components/icons/LightBlub.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0778e6dad3578189c1820c25aae7331776281e2c
--- /dev/null
+++ b/src/lib/components/icons/LightBlub.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Link.svelte b/src/lib/components/icons/Link.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9f1a7231105c131d01f76aaa115546af7618401a
--- /dev/null
+++ b/src/lib/components/icons/Link.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M8.914 6.025a.75.75 0 0 1 1.06 0 3.5 3.5 0 0 1 0 4.95l-2 2a3.5 3.5 0 0 1-5.396-4.402.75.75 0 0 1 1.251.827 2 2 0 0 0 3.085 2.514l2-2a2 2 0 0 0 0-2.828.75.75 0 0 1 0-1.06Z"
+		clip-rule="evenodd"
+	/>
+	<path
+		fill-rule="evenodd"
+		d="M7.086 9.975a.75.75 0 0 1-1.06 0 3.5 3.5 0 0 1 0-4.95l2-2a3.5 3.5 0 0 1 5.396 4.402.75.75 0 0 1-1.251-.827 2 2 0 0 0-3.085-2.514l-2 2a2 2 0 0 0 0 2.828.75.75 0 0 1 0 1.06Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/LockClosed.svelte b/src/lib/components/icons/LockClosed.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c41171f4e98a9aae60cf49cd9604bdf47017217b
--- /dev/null
+++ b/src/lib/components/icons/LockClosed.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M16.5 10.5V6.75a4.5 4.5 0 1 0-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 0 0 2.25-2.25v-6.75a2.25 2.25 0 0 0-2.25-2.25H6.75a2.25 2.25 0 0 0-2.25 2.25v6.75a2.25 2.25 0 0 0 2.25 2.25Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/MagnifyingGlass.svelte b/src/lib/components/icons/MagnifyingGlass.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a61186b5d0f40ce156632a8c3172e5a79a72816f
--- /dev/null
+++ b/src/lib/components/icons/MagnifyingGlass.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Map.svelte b/src/lib/components/icons/Map.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2ae0ce6a820da2f8a75b1d04e761c6448d8c235e
--- /dev/null
+++ b/src/lib/components/icons/Map.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M9 6.75V15m6-6v8.25m.503 3.498 4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 0 0-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/MenuLines.svelte b/src/lib/components/icons/MenuLines.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c2b27c45b72ca615c5d8608986aead7983fcd7e9
--- /dev/null
+++ b/src/lib/components/icons/MenuLines.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-5';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Merge.svelte b/src/lib/components/icons/Merge.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..998303677050fdc7c7034133136ec386148a2810
--- /dev/null
+++ b/src/lib/components/icons/Merge.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M8 8v8m0-8a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm0 8a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm6-2a2 2 0 1 1 4 0 2 2 0 0 1-4 0Zm0 0h-1a5 5 0 0 1-5-5v-.5"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Mic.svelte b/src/lib/components/icons/Mic.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7967b6da24471affff7ddebe1403d78cfa26bf20
--- /dev/null
+++ b/src/lib/components/icons/Mic.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class={className}>
+	<path d="M7 4a3 3 0 0 1 6 0v6a3 3 0 1 1-6 0V4Z" />
+	<path
+		d="M5.5 9.643a.75.75 0 0 0-1.5 0V10c0 3.06 2.29 5.585 5.25 5.954V17.5h-1.5a.75.75 0 0 0 0 1.5h4.5a.75.75 0 0 0 0-1.5h-1.5v-1.546A6.001 6.001 0 0 0 16 10v-.357a.75.75 0 0 0-1.5 0V10a4.5 4.5 0 0 1-9 0v-.357Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Minus.svelte b/src/lib/components/icons/Minus.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9b2b7f4ad98c61ed1b057c52c9ca4525fef5bfd6
--- /dev/null
+++ b/src/lib/components/icons/Minus.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14" />
+</svg>
diff --git a/src/lib/components/icons/Pencil.svelte b/src/lib/components/icons/Pencil.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..42b60916c4bb4aaf2d71c9684e764dd85fb54e51
--- /dev/null
+++ b/src/lib/components/icons/Pencil.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+	/>
+</svg>
diff --git a/src/lib/components/icons/PencilSolid.svelte b/src/lib/components/icons/PencilSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f986eeb1c2b6341d7e76edcb95a66128b4fea286
--- /dev/null
+++ b/src/lib/components/icons/PencilSolid.svelte
@@ -0,0 +1,10 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		d="M21.731 2.269a2.625 2.625 0 0 0-3.712 0l-1.157 1.157 3.712 3.712 1.157-1.157a2.625 2.625 0 0 0 0-3.712ZM19.513 8.199l-3.712-3.712-12.15 12.15a5.25 5.25 0 0 0-1.32 2.214l-.8 2.685a.75.75 0 0 0 .933.933l2.685-.8a5.25 5.25 0 0 0 2.214-1.32L19.513 8.2Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/PencilSquare.svelte b/src/lib/components/icons/PencilSquare.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d1e02b1ad720d944256f3c08136c4b3908b9d8bf
--- /dev/null
+++ b/src/lib/components/icons/PencilSquare.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'size-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Plus.svelte b/src/lib/components/icons/Plus.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e5f3ba77004695d0868dd855e31eadff718cf3c4
--- /dev/null
+++ b/src/lib/components/icons/Plus.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
+</svg>
diff --git a/src/lib/components/icons/QuestionMarkCircle.svelte b/src/lib/components/icons/QuestionMarkCircle.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..79c2e7d84c2e7a2f1271cfc0dcc644cbdf24127b
--- /dev/null
+++ b/src/lib/components/icons/QuestionMarkCircle.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Search.svelte b/src/lib/components/icons/Search.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c2dc55845d54f1ad900ea75f3be5674b264d1955
--- /dev/null
+++ b/src/lib/components/icons/Search.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Share.svelte b/src/lib/components/icons/Share.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f098995c68da47b9261d492033b60780a033c587
--- /dev/null
+++ b/src/lib/components/icons/Share.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M15.75 4.5a3 3 0 1 1 .825 2.066l-8.421 4.679a3.002 3.002 0 0 1 0 1.51l8.421 4.679a3 3 0 1 1-.729 1.31l-8.421-4.678a3 3 0 1 1 0-4.132l8.421-4.679a3 3 0 0 1-.096-.755Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Sparkles.svelte b/src/lib/components/icons/Sparkles.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0f9034d261fa9f424a2e5b74d0366e0eeb539361
--- /dev/null
+++ b/src/lib/components/icons/Sparkles.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/SparklesSolid.svelte b/src/lib/components/icons/SparklesSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d69e0c8b69a134186fca479b1641ad5fc6045969
--- /dev/null
+++ b/src/lib/components/icons/SparklesSolid.svelte
@@ -0,0 +1,12 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M9 4.5a.75.75 0 0 1 .721.544l.813 2.846a3.75 3.75 0 0 0 2.576 2.576l2.846.813a.75.75 0 0 1 0 1.442l-2.846.813a3.75 3.75 0 0 0-2.576 2.576l-.813 2.846a.75.75 0 0 1-1.442 0l-.813-2.846a3.75 3.75 0 0 0-2.576-2.576l-2.846-.813a.75.75 0 0 1 0-1.442l2.846-.813A3.75 3.75 0 0 0 7.466 7.89l.813-2.846A.75.75 0 0 1 9 4.5ZM18 1.5a.75.75 0 0 1 .728.568l.258 1.036c.236.94.97 1.674 1.91 1.91l1.036.258a.75.75 0 0 1 0 1.456l-1.036.258c-.94.236-1.674.97-1.91 1.91l-.258 1.036a.75.75 0 0 1-1.456 0l-.258-1.036a2.625 2.625 0 0 0-1.91-1.91l-1.036-.258a.75.75 0 0 1 0-1.456l1.036-.258a2.625 2.625 0 0 0 1.91-1.91l.258-1.036A.75.75 0 0 1 18 1.5ZM16.5 15a.75.75 0 0 1 .712.513l.394 1.183c.15.447.5.799.948.948l1.183.395a.75.75 0 0 1 0 1.422l-1.183.395c-.447.15-.799.5-.948.948l-.395 1.183a.75.75 0 0 1-1.422 0l-.395-1.183a1.5 1.5 0 0 0-.948-.948l-1.183-.395a.75.75 0 0 1 0-1.422l1.183-.395c.447-.15.799-.5.948-.948l.395-1.183A.75.75 0 0 1 16.5 15Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Star.svelte b/src/lib/components/icons/Star.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..45faf808b679a4f2c9485eeeeab250de8b1ba2b7
--- /dev/null
+++ b/src/lib/components/icons/Star.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/User.svelte b/src/lib/components/icons/User.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..71a87a2f52b1b194d9d72779a227464eb155f0d5
--- /dev/null
+++ b/src/lib/components/icons/User.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/UserCircleSolid.svelte b/src/lib/components/icons/UserCircleSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9b34c01642106243abcaaa1c9dbb35f55a078fb5
--- /dev/null
+++ b/src/lib/components/icons/UserCircleSolid.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0Zm-5-2a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 9c-1.825 0-3.422.977-4.295 2.437A5.49 5.49 0 0 0 8 13.5a5.49 5.49 0 0 0 4.294-2.063A4.997 4.997 0 0 0 8 9Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/UserPlusSolid.svelte b/src/lib/components/icons/UserPlusSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2805ed5624316f2ca6adab8f795ff35ce0ed2734
--- /dev/null
+++ b/src/lib/components/icons/UserPlusSolid.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class={className}>
+	<path
+		d="M10 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM1.615 16.428a1.224 1.224 0 0 1-.569-1.175 6.002 6.002 0 0 1 11.908 0c.058.467-.172.92-.57 1.174A9.953 9.953 0 0 1 7 18a9.953 9.953 0 0 1-5.385-1.572ZM16.25 5.75a.75.75 0 0 0-1.5 0v2h-2a.75.75 0 0 0 0 1.5h2v2a.75.75 0 0 0 1.5 0v-2h2a.75.75 0 0 0 0-1.5h-2v-2Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/UsersSolid.svelte b/src/lib/components/icons/UsersSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..db939ba71d06ad6d65244bb12c518c83f21b6642
--- /dev/null
+++ b/src/lib/components/icons/UsersSolid.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		d="M4.5 6.375a4.125 4.125 0 1 1 8.25 0 4.125 4.125 0 0 1-8.25 0ZM14.25 8.625a3.375 3.375 0 1 1 6.75 0 3.375 3.375 0 0 1-6.75 0ZM1.5 19.125a7.125 7.125 0 0 1 14.25 0v.003l-.001.119a.75.75 0 0 1-.363.63 13.067 13.067 0 0 1-6.761 1.873c-2.472 0-4.786-.684-6.76-1.873a.75.75 0 0 1-.364-.63l-.001-.122ZM17.25 19.128l-.001.144a2.25 2.25 0 0 1-.233.96 10.088 10.088 0 0 0 5.06-1.01.75.75 0 0 0 .42-.643 4.875 4.875 0 0 0-6.957-4.611 8.586 8.586 0 0 1 1.71 5.157v.003Z"
+	/>
+</svg>
diff --git a/src/lib/components/icons/Wrench.svelte b/src/lib/components/icons/Wrench.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d18785a602903ada2d3e5fe4c30ac1c066538488
--- /dev/null
+++ b/src/lib/components/icons/Wrench.svelte
@@ -0,0 +1,20 @@
+<script lang="ts">
+	export let className = 'w-4 h-4';
+	export let strokeWidth = '1.5';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path
+		stroke-linecap="round"
+		stroke-linejoin="round"
+		d="M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z"
+	/>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M4.867 19.125h.008v.008h-.008v-.008Z" />
+</svg>
diff --git a/src/lib/components/icons/WrenchSolid.svelte b/src/lib/components/icons/WrenchSolid.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..66a2113018376da328c13f146ebbadf8c5511794
--- /dev/null
+++ b/src/lib/components/icons/WrenchSolid.svelte
@@ -0,0 +1,11 @@
+<script lang="ts">
+	export let className = 'size-4';
+</script>
+
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
+	<path
+		fill-rule="evenodd"
+		d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z"
+		clip-rule="evenodd"
+	/>
+</svg>
diff --git a/src/lib/components/icons/XMark.svelte b/src/lib/components/icons/XMark.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b75be506ca797fdbb687b27be4118dadaeef4d4e
--- /dev/null
+++ b/src/lib/components/icons/XMark.svelte
@@ -0,0 +1,15 @@
+<script lang="ts">
+	export let className = 'size-3.5';
+	export let strokeWidth = '2';
+</script>
+
+<svg
+	xmlns="http://www.w3.org/2000/svg"
+	fill="none"
+	viewBox="0 0 24 24"
+	stroke-width={strokeWidth}
+	stroke="currentColor"
+	class={className}
+>
+	<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
+</svg>
diff --git a/src/lib/components/layout/Help.svelte b/src/lib/components/layout/Help.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9e0a18582e54ff1b2e32a0d30b9d730f12ee3272
--- /dev/null
+++ b/src/lib/components/layout/Help.svelte
@@ -0,0 +1,40 @@
+<script lang="ts">
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import ShortcutsModal from '../chat/ShortcutsModal.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import HelpMenu from './Help/HelpMenu.svelte';
+
+	let showShortcuts = false;
+</script>
+
+<div class=" hidden lg:flex fixed bottom-0 right-0 px-2 py-2 z-20">
+	<button
+		id="show-shortcuts-button"
+		class="hidden"
+		on:click={() => {
+			showShortcuts = !showShortcuts;
+		}}
+	/>
+
+	<HelpMenu
+		showDocsHandler={() => {
+			showShortcuts = !showShortcuts;
+		}}
+		showShortcutsHandler={() => {
+			showShortcuts = !showShortcuts;
+		}}
+	>
+		<Tooltip content={$i18n.t('Help')} placement="left">
+			<button
+				class="text-gray-600 dark:text-gray-300 bg-gray-300/20 size-5 flex items-center justify-center text-[0.7rem] rounded-full"
+			>
+				?
+			</button>
+		</Tooltip>
+	</HelpMenu>
+</div>
+
+<ShortcutsModal bind:show={showShortcuts} />
diff --git a/src/lib/components/layout/Help/HelpMenu.svelte b/src/lib/components/layout/Help/HelpMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7371f629c86e9079288f2a68610aec124198cfba
--- /dev/null
+++ b/src/lib/components/layout/Help/HelpMenu.svelte
@@ -0,0 +1,60 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { getContext } from 'svelte';
+
+	import { showSettings } from '$lib/stores';
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import QuestionMarkCircle from '$lib/components/icons/QuestionMarkCircle.svelte';
+	import Lifebuoy from '$lib/components/icons/Lifebuoy.svelte';
+	import Keyboard from '$lib/components/icons/Keyboard.svelte';
+	const i18n = getContext('i18n');
+
+	export let showDocsHandler: Function;
+	export let showShortcutsHandler: Function;
+
+	export let onClose: Function = () => {};
+</script>
+
+<Dropdown
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<slot />
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[200px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={4}
+			side="top"
+			align="end"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				id="chat-share-button"
+				on:click={() => {
+					window.open('https://docs.openwebui.com', '_blank');
+				}}
+			>
+				<QuestionMarkCircle className="size-5" />
+				<div class="flex items-center">{$i18n.t('Documentation')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				id="chat-share-button"
+				on:click={() => {
+					showShortcutsHandler();
+				}}
+			>
+				<Keyboard className="size-5" />
+				<div class="flex items-center">{$i18n.t('Keyboard shortcuts')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/layout/Navbar.svelte b/src/lib/components/layout/Navbar.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b5cbb02a20a54725fe97bbfcb0e6e445c22f49a0
--- /dev/null
+++ b/src/lib/components/layout/Navbar.svelte
@@ -0,0 +1,195 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	import {
+		WEBUI_NAME,
+		chatId,
+		mobile,
+		settings,
+		showArchivedChats,
+		showControls,
+		showSidebar,
+		temporaryChatEnabled,
+		user
+	} from '$lib/stores';
+
+	import { slide } from 'svelte/transition';
+	import ShareChatModal from '../chat/ShareChatModal.svelte';
+	import ModelSelector from '../chat/ModelSelector.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import Menu from './Navbar/Menu.svelte';
+	import { page } from '$app/stores';
+	import UserMenu from './Sidebar/UserMenu.svelte';
+	import MenuLines from '../icons/MenuLines.svelte';
+	import AdjustmentsHorizontal from '../icons/AdjustmentsHorizontal.svelte';
+	import Map from '../icons/Map.svelte';
+	import { stringify } from 'postcss';
+	import PencilSquare from '../icons/PencilSquare.svelte';
+	import Plus from '../icons/Plus.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let initNewChat: Function;
+	export let title: string = $WEBUI_NAME;
+	export let shareEnabled: boolean = false;
+
+	export let chat;
+	export let selectedModels;
+	export let showModelSelector = true;
+
+	let showShareChatModal = false;
+	let showDownloadChatModal = false;
+</script>
+
+<ShareChatModal bind:show={showShareChatModal} chatId={$chatId} />
+
+<div class="sticky top-0 z-30 w-full px-1.5 py-1.5 -mb-8 flex items-center">
+	<div
+		class=" bg-gradient-to-b via-50% from-white via-white to-transparent dark:from-gray-900 dark:via-gray-900 dark:to-transparent pointer-events-none absolute inset-0 -bottom-7 z-[-1] blur"
+	></div>
+
+	<div class=" flex max-w-full w-full mx-auto px-1 pt-0.5 bg-transparent">
+		<div class="flex items-center w-full max-w-full">
+			<div
+				class="{$showSidebar
+					? 'md:hidden'
+					: ''} mr-1 self-start flex flex-none items-center text-gray-600 dark:text-gray-400"
+			>
+				<button
+					id="sidebar-toggle-button"
+					class="cursor-pointer px-2 py-2 flex rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+					on:click={() => {
+						showSidebar.set(!$showSidebar);
+					}}
+					aria-label="Toggle Sidebar"
+				>
+					<div class=" m-auto self-center">
+						<MenuLines />
+					</div>
+				</button>
+			</div>
+
+			<div
+				class="flex-1 overflow-hidden max-w-full py-0.5
+			{$showSidebar ? 'ml-1' : ''}
+			"
+			>
+				{#if showModelSelector}
+					<ModelSelector bind:selectedModels showSetDefault={!shareEnabled} />
+				{/if}
+			</div>
+
+			<div class="self-start flex flex-none items-center text-gray-600 dark:text-gray-400">
+				<!-- <div class="md:hidden flex self-center w-[1px] h-5 mx-2 bg-gray-300 dark:bg-stone-700" /> -->
+				{#if shareEnabled && chat && (chat.id || $temporaryChatEnabled)}
+					<Menu
+						{chat}
+						{shareEnabled}
+						shareHandler={() => {
+							showShareChatModal = !showShareChatModal;
+						}}
+						downloadHandler={() => {
+							showDownloadChatModal = !showDownloadChatModal;
+						}}
+					>
+						<button
+							class="flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							id="chat-context-menu-button"
+						>
+							<div class=" m-auto self-center">
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="1.5"
+									stroke="currentColor"
+									class="size-5"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
+									/>
+								</svg>
+							</div>
+						</button>
+					</Menu>
+				{:else if $mobile}
+					<Tooltip content={$i18n.t('Controls')}>
+						<button
+							class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							on:click={async () => {
+								await showControls.set(!$showControls);
+							}}
+							aria-label="Controls"
+						>
+							<div class=" m-auto self-center">
+								<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
+							</div>
+						</button>
+					</Tooltip>
+				{/if}
+
+				{#if !$mobile}
+					<Tooltip content={$i18n.t('Controls')}>
+						<button
+							class=" flex cursor-pointer px-2 py-2 rounded-xl hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							on:click={async () => {
+								await showControls.set(!$showControls);
+							}}
+							aria-label="Controls"
+						>
+							<div class=" m-auto self-center">
+								<AdjustmentsHorizontal className=" size-5" strokeWidth="0.5" />
+							</div>
+						</button>
+					</Tooltip>
+				{/if}
+
+				<Tooltip content={$i18n.t('New Chat')}>
+					<button
+						id="new-chat-button"
+						class=" flex {$showSidebar
+							? 'md:hidden'
+							: ''} cursor-pointer px-2 py-2 rounded-xl text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+						on:click={() => {
+							initNewChat();
+						}}
+						aria-label="New Chat"
+					>
+						<div class=" m-auto self-center">
+							<PencilSquare className=" size-5" strokeWidth="2" />
+						</div>
+					</button>
+				</Tooltip>
+
+				{#if $user !== undefined}
+					<UserMenu
+						className="max-w-[200px]"
+						role={$user.role}
+						on:show={(e) => {
+							if (e.detail === 'archived-chat') {
+								showArchivedChats.set(true);
+							}
+						}}
+					>
+						<button
+							class="select-none flex rounded-xl p-1.5 w-full hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+							aria-label="User Menu"
+						>
+							<div class=" self-center">
+								<img
+									src={$user.profile_image_url}
+									class="size-6 object-cover rounded-full"
+									alt="User profile"
+									draggable="false"
+								/>
+							</div>
+						</button>
+					</UserMenu>
+				{/if}
+			</div>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/layout/Navbar/Menu.svelte b/src/lib/components/layout/Navbar/Menu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..67ac9dcb2b0a4a906534c4cb68dc46711f545f90
--- /dev/null
+++ b/src/lib/components/layout/Navbar/Menu.svelte
@@ -0,0 +1,285 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { DropdownMenu } from 'bits-ui';
+	import { getContext } from 'svelte';
+
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { downloadChatAsPDF } from '$lib/apis/utils';
+	import { copyToClipboard, createMessagesList } from '$lib/utils';
+
+	import {
+		showOverview,
+		showControls,
+		showArtifacts,
+		mobile,
+		temporaryChatEnabled
+	} from '$lib/stores';
+	import { flyAndScale } from '$lib/utils/transitions';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Map from '$lib/components/icons/Map.svelte';
+	import Clipboard from '$lib/components/icons/Clipboard.svelte';
+	import AdjustmentsHorizontal from '$lib/components/icons/AdjustmentsHorizontal.svelte';
+	import Cube from '$lib/components/icons/Cube.svelte';
+	import { getChatById } from '$lib/apis/chats';
+
+	const i18n = getContext('i18n');
+
+	export let shareEnabled: boolean = false;
+	export let shareHandler: Function;
+	export let downloadHandler: Function;
+
+	// export let tagHandler: Function;
+
+	export let chat;
+	export let onClose: Function = () => {};
+
+	const getChatAsText = async () => {
+		const history = chat.chat.history;
+		const messages = createMessagesList(history, history.currentId);
+		const chatText = messages.reduce((a, message, i, arr) => {
+			return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
+		}, '');
+
+		return chatText.trim();
+	};
+
+	const downloadTxt = async () => {
+		const chatText = await getChatAsText();
+
+		let blob = new Blob([chatText], {
+			type: 'text/plain'
+		});
+
+		saveAs(blob, `chat-${chat.chat.title}.txt`);
+	};
+
+	const downloadPdf = async () => {
+		const history = chat.chat.history;
+		const messages = createMessagesList(history, history.currentId);
+		const blob = await downloadChatAsPDF(chat.chat.title, messages);
+
+		// Create a URL for the blob
+		const url = window.URL.createObjectURL(blob);
+
+		// Create a link element to trigger the download
+		const a = document.createElement('a');
+		a.href = url;
+		a.download = `chat-${chat.chat.title}.pdf`;
+
+		// Append the link to the body and click it programmatically
+		document.body.appendChild(a);
+		a.click();
+
+		// Remove the link from the body
+		document.body.removeChild(a);
+
+		// Revoke the URL to release memory
+		window.URL.revokeObjectURL(url);
+	};
+
+	const downloadJSONExport = async () => {
+		if (chat.id) {
+			chat = await getChatById(localStorage.token, chat.id);
+		}
+		let blob = new Blob([JSON.stringify([chat])], {
+			type: 'application/json'
+		});
+		saveAs(blob, `chat-export-${Date.now()}.json`);
+	};
+</script>
+
+<Dropdown
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<slot />
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[200px] rounded-xl px-1 py-1.5  z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={8}
+			side="bottom"
+			align="end"
+			transition={flyAndScale}
+		>
+			<!-- <DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer dark:hover:bg-gray-800 rounded-md"
+				on:click={async () => {
+					await showSettings.set(!$showSettings);
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					fill="none"
+					viewBox="0 0 24 24"
+					stroke-width="1.5"
+					stroke="currentColor"
+					class="size-4"
+				>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+					/>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
+					/>
+				</svg>
+				<div class="flex items-center">{$i18n.t('Settings')}</div>
+			</DropdownMenu.Item> -->
+
+			{#if $mobile}
+				<DropdownMenu.Item
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+					id="chat-controls-button"
+					on:click={async () => {
+						await showControls.set(true);
+						await showOverview.set(false);
+						await showArtifacts.set(false);
+					}}
+				>
+					<AdjustmentsHorizontal className=" size-4" strokeWidth="0.5" />
+					<div class="flex items-center">{$i18n.t('Controls')}</div>
+				</DropdownMenu.Item>
+			{/if}
+
+			{#if !$temporaryChatEnabled}
+				<DropdownMenu.Item
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+					id="chat-share-button"
+					on:click={() => {
+						shareHandler();
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 24 24"
+						fill="currentColor"
+						class="size-4"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M15.75 4.5a3 3 0 1 1 .825 2.066l-8.421 4.679a3.002 3.002 0 0 1 0 1.51l8.421 4.679a3 3 0 1 1-.729 1.31l-8.421-4.678a3 3 0 1 1 0-4.132l8.421-4.679a3 3 0 0 1-.096-.755Z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+					<div class="flex items-center">{$i18n.t('Share')}</div>
+				</DropdownMenu.Item>
+			{/if}
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				id="chat-overview-button"
+				on:click={async () => {
+					await showControls.set(true);
+					await showOverview.set(true);
+					await showArtifacts.set(false);
+				}}
+			>
+				<Map className=" size-4" strokeWidth="1.5" />
+				<div class="flex items-center">{$i18n.t('Overview')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				id="chat-overview-button"
+				on:click={async () => {
+					await showControls.set(true);
+					await showArtifacts.set(true);
+					await showOverview.set(false);
+				}}
+			>
+				<Cube className=" size-4" strokeWidth="1.5" />
+				<div class="flex items-center">{$i18n.t('Artifacts')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Sub>
+				<DropdownMenu.SubTrigger
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="1.5"
+						stroke="currentColor"
+						class="size-4"
+					>
+						<path
+							stroke-linecap="round"
+							stroke-linejoin="round"
+							d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3"
+						/>
+					</svg>
+
+					<div class="flex items-center">{$i18n.t('Download')}</div>
+				</DropdownMenu.SubTrigger>
+				<DropdownMenu.SubContent
+					class="w-full rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+					transition={flyAndScale}
+					sideOffset={8}
+				>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadJSONExport();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('Export chat (.json)')}</div>
+					</DropdownMenu.Item>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadTxt();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('Plain text (.txt)')}</div>
+					</DropdownMenu.Item>
+
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadPdf();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('PDF document (.pdf)')}</div>
+					</DropdownMenu.Item>
+				</DropdownMenu.SubContent>
+			</DropdownMenu.Sub>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				id="chat-copy-button"
+				on:click={async () => {
+					const res = await copyToClipboard(await getChatAsText()).catch((e) => {
+						console.error(e);
+					});
+
+					if (res) {
+						toast.success($i18n.t('Copied to clipboard'));
+					}
+				}}
+			>
+				<Clipboard className=" size-4" strokeWidth="1.5" />
+				<div class="flex items-center">{$i18n.t('Copy')}</div>
+			</DropdownMenu.Item>
+
+			{#if !$temporaryChatEnabled}
+				<hr class="border-gray-50 dark:border-gray-850 my-0.5" />
+
+				<div class="flex p-1">
+					<Tags chatId={chat.id} />
+				</div>
+			{/if}
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/layout/Overlay/AccountPending.svelte b/src/lib/components/layout/Overlay/AccountPending.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c272963e9b8738ba50645a2024046845590f9913
--- /dev/null
+++ b/src/lib/components/layout/Overlay/AccountPending.svelte
@@ -0,0 +1,62 @@
+<script lang="ts">
+	import { getAdminDetails } from '$lib/apis/auths';
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	let adminDetails = null;
+
+	onMount(async () => {
+		adminDetails = await getAdminDetails(localStorage.token).catch((err) => {
+			console.error(err);
+			return null;
+		});
+	});
+</script>
+
+<div class="fixed w-full h-full flex z-[999]">
+	<div
+		class="absolute w-full h-full backdrop-blur-lg bg-white/10 dark:bg-gray-900/50 flex justify-center"
+	>
+		<div class="m-auto pb-10 flex flex-col justify-center">
+			<div class="max-w-md">
+				<div class="text-center dark:text-white text-2xl font-medium z-50">
+					{$i18n.t('Account Activation Pending')}<br />
+					{$i18n.t('Contact Admin for WebUI Access')}
+				</div>
+
+				<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
+					{$i18n.t('Your account status is currently pending activation.')}<br />
+					{$i18n.t(
+						'To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.'
+					)}
+				</div>
+
+				{#if adminDetails}
+					<div class="mt-4 text-sm font-medium text-center">
+						<div>{$i18n.t('Admin')}: {adminDetails.name} ({adminDetails.email})</div>
+					</div>
+				{/if}
+
+				<div class=" mt-6 mx-auto relative group w-fit">
+					<button
+						class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 text-gray-700 transition font-medium text-sm"
+						on:click={async () => {
+							location.href = '/';
+						}}
+					>
+						{$i18n.t('Check Again')}
+					</button>
+
+					<button
+						class="text-xs text-center w-full mt-2 text-gray-400 underline"
+						on:click={async () => {
+							localStorage.removeItem('token');
+							location.href = '/auth';
+						}}>{$i18n.t('Sign Out')}</button
+					>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..392a9e7322a4a077864d84a4ba3b1b7fc1f7c719
--- /dev/null
+++ b/src/lib/components/layout/Sidebar.svelte
@@ -0,0 +1,815 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { v4 as uuidv4 } from 'uuid';
+
+	import { goto } from '$app/navigation';
+	import {
+		user,
+		chats,
+		settings,
+		showSettings,
+		chatId,
+		tags,
+		showSidebar,
+		mobile,
+		showArchivedChats,
+		pinnedChats,
+		scrollPaginationEnabled,
+		currentChatPage,
+		temporaryChatEnabled
+	} from '$lib/stores';
+	import { onMount, getContext, tick, onDestroy } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import {
+		deleteChatById,
+		getChatList,
+		getAllTags,
+		getChatListBySearchText,
+		createNewChat,
+		getPinnedChatList,
+		toggleChatPinnedStatusById,
+		getChatPinnedStatusById,
+		getChatById,
+		updateChatFolderIdById,
+		importChat
+	} from '$lib/apis/chats';
+	import { createNewFolder, getFolders, updateFolderParentIdById } from '$lib/apis/folders';
+	import { WEBUI_BASE_URL } from '$lib/constants';
+
+	import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
+	import UserMenu from './Sidebar/UserMenu.svelte';
+	import ChatItem from './Sidebar/ChatItem.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import Loader from '../common/Loader.svelte';
+	import AddFilesPlaceholder from '../AddFilesPlaceholder.svelte';
+	import SearchInput from './Sidebar/SearchInput.svelte';
+	import Folder from '../common/Folder.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import Folders from './Sidebar/Folders.svelte';
+
+	const BREAKPOINT = 768;
+
+	let navElement;
+	let search = '';
+
+	let shiftKey = false;
+
+	let selectedChatId = null;
+	let showDropdown = false;
+	let showPinnedChat = true;
+
+	// Pagination variables
+	let chatListLoading = false;
+	let allChatsLoaded = false;
+
+	let folders = {};
+
+	const initFolders = async () => {
+		const folderList = await getFolders(localStorage.token).catch((error) => {
+			toast.error(error);
+			return [];
+		});
+
+		folders = {};
+
+		// First pass: Initialize all folder entries
+		for (const folder of folderList) {
+			// Ensure folder is added to folders with its data
+			folders[folder.id] = { ...(folders[folder.id] || {}), ...folder };
+		}
+
+		// Second pass: Tie child folders to their parents
+		for (const folder of folderList) {
+			if (folder.parent_id) {
+				// Ensure the parent folder is initialized if it doesn't exist
+				if (!folders[folder.parent_id]) {
+					folders[folder.parent_id] = {}; // Create a placeholder if not already present
+				}
+
+				// Initialize childrenIds array if it doesn't exist and add the current folder id
+				folders[folder.parent_id].childrenIds = folders[folder.parent_id].childrenIds
+					? [...folders[folder.parent_id].childrenIds, folder.id]
+					: [folder.id];
+
+				// Sort the children by updated_at field
+				folders[folder.parent_id].childrenIds.sort((a, b) => {
+					return folders[b].updated_at - folders[a].updated_at;
+				});
+			}
+		}
+	};
+
+	const createFolder = async (name = 'Untitled') => {
+		if (name === '') {
+			toast.error($i18n.t('Folder name cannot be empty.'));
+			return;
+		}
+
+		const rootFolders = Object.values(folders).filter((folder) => folder.parent_id === null);
+		if (rootFolders.find((folder) => folder.name.toLowerCase() === name.toLowerCase())) {
+			// If a folder with the same name already exists, append a number to the name
+			let i = 1;
+			while (
+				rootFolders.find((folder) => folder.name.toLowerCase() === `${name} ${i}`.toLowerCase())
+			) {
+				i++;
+			}
+
+			name = `${name} ${i}`;
+		}
+
+		// Add a dummy folder to the list to show the user that the folder is being created
+		const tempId = uuidv4();
+		folders = {
+			...folders,
+			tempId: {
+				id: tempId,
+				name: name,
+				created_at: Date.now(),
+				updated_at: Date.now()
+			}
+		};
+
+		const res = await createNewFolder(localStorage.token, name).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			await initFolders();
+		}
+	};
+
+	const initChatList = async () => {
+		// Reset pagination variables
+		tags.set(await getAllTags(localStorage.token));
+		pinnedChats.set(await getPinnedChatList(localStorage.token));
+		initFolders();
+
+		currentChatPage.set(1);
+		allChatsLoaded = false;
+
+		if (search) {
+			await chats.set(await getChatListBySearchText(localStorage.token, search, $currentChatPage));
+		} else {
+			await chats.set(await getChatList(localStorage.token, $currentChatPage));
+		}
+
+		// Enable pagination
+		scrollPaginationEnabled.set(true);
+	};
+
+	const loadMoreChats = async () => {
+		chatListLoading = true;
+
+		currentChatPage.set($currentChatPage + 1);
+
+		let newChatList = [];
+
+		if (search) {
+			newChatList = await getChatListBySearchText(localStorage.token, search, $currentChatPage);
+		} else {
+			newChatList = await getChatList(localStorage.token, $currentChatPage);
+		}
+
+		// once the bottom of the list has been reached (no results) there is no need to continue querying
+		allChatsLoaded = newChatList.length === 0;
+		await chats.set([...($chats ? $chats : []), ...newChatList]);
+
+		chatListLoading = false;
+	};
+
+	let searchDebounceTimeout;
+
+	const searchDebounceHandler = async () => {
+		console.log('search', search);
+		chats.set(null);
+
+		if (searchDebounceTimeout) {
+			clearTimeout(searchDebounceTimeout);
+		}
+
+		if (search === '') {
+			await initChatList();
+			return;
+		} else {
+			searchDebounceTimeout = setTimeout(async () => {
+				allChatsLoaded = false;
+				currentChatPage.set(1);
+				await chats.set(await getChatListBySearchText(localStorage.token, search));
+
+				if ($chats.length === 0) {
+					tags.set(await getAllTags(localStorage.token));
+				}
+			}, 1000);
+		}
+	};
+
+	const importChatHandler = async (items, pinned = false, folderId = null) => {
+		console.log('importChatHandler', items, pinned, folderId);
+		for (const item of items) {
+			console.log(item);
+			if (item.chat) {
+				await importChat(localStorage.token, item.chat, item?.meta ?? {}, pinned, folderId);
+			}
+		}
+
+		initChatList();
+	};
+
+	const inputFilesHandler = async (files) => {
+		console.log(files);
+
+		for (const file of files) {
+			const reader = new FileReader();
+			reader.onload = async (e) => {
+				const content = e.target.result;
+
+				try {
+					const chatItems = JSON.parse(content);
+					importChatHandler(chatItems);
+				} catch {
+					toast.error($i18n.t(`Invalid file format.`));
+				}
+			};
+
+			reader.readAsText(file);
+		}
+	};
+
+	const tagEventHandler = async (type, tagName, chatId) => {
+		console.log(type, tagName, chatId);
+		if (type === 'delete') {
+			initChatList();
+		} else if (type === 'add') {
+			initChatList();
+		}
+	};
+
+	let draggedOver = false;
+
+	const onDragOver = (e) => {
+		e.preventDefault();
+
+		// Check if a file is being draggedOver.
+		if (e.dataTransfer?.types?.includes('Files')) {
+			draggedOver = true;
+		} else {
+			draggedOver = false;
+		}
+	};
+
+	const onDragLeave = () => {
+		draggedOver = false;
+	};
+
+	const onDrop = async (e) => {
+		e.preventDefault();
+		console.log(e); // Log the drop event
+
+		// Perform file drop check and handle it accordingly
+		if (e.dataTransfer?.files) {
+			const inputFiles = Array.from(e.dataTransfer?.files);
+
+			if (inputFiles && inputFiles.length > 0) {
+				console.log(inputFiles); // Log the dropped files
+				inputFilesHandler(inputFiles); // Handle the dropped files
+			}
+		}
+
+		draggedOver = false; // Reset draggedOver status after drop
+	};
+
+	let touchstart;
+	let touchend;
+
+	function checkDirection() {
+		const screenWidth = window.innerWidth;
+		const swipeDistance = Math.abs(touchend.screenX - touchstart.screenX);
+		if (touchstart.clientX < 40 && swipeDistance >= screenWidth / 8) {
+			if (touchend.screenX < touchstart.screenX) {
+				showSidebar.set(false);
+			}
+			if (touchend.screenX > touchstart.screenX) {
+				showSidebar.set(true);
+			}
+		}
+	}
+
+	const onTouchStart = (e) => {
+		touchstart = e.changedTouches[0];
+		console.log(touchstart.clientX);
+	};
+
+	const onTouchEnd = (e) => {
+		touchend = e.changedTouches[0];
+		checkDirection();
+	};
+
+	const onKeyDown = (e) => {
+		if (e.key === 'Shift') {
+			shiftKey = true;
+		}
+	};
+
+	const onKeyUp = (e) => {
+		if (e.key === 'Shift') {
+			shiftKey = false;
+		}
+	};
+
+	const onFocus = () => {};
+
+	const onBlur = () => {
+		shiftKey = false;
+		selectedChatId = null;
+	};
+
+	onMount(async () => {
+		showPinnedChat = localStorage?.showPinnedChat ? localStorage.showPinnedChat === 'true' : true;
+
+		mobile.subscribe((e) => {
+			if ($showSidebar && e) {
+				showSidebar.set(false);
+			}
+
+			if (!$showSidebar && !e) {
+				showSidebar.set(true);
+			}
+		});
+
+		showSidebar.set(!$mobile ? localStorage.sidebar === 'true' : false);
+		showSidebar.subscribe((value) => {
+			localStorage.sidebar = value;
+		});
+
+		await initChatList();
+
+		window.addEventListener('keydown', onKeyDown);
+		window.addEventListener('keyup', onKeyUp);
+
+		window.addEventListener('touchstart', onTouchStart);
+		window.addEventListener('touchend', onTouchEnd);
+
+		window.addEventListener('focus', onFocus);
+		window.addEventListener('blur', onBlur);
+
+		const dropZone = document.getElementById('sidebar');
+
+		dropZone?.addEventListener('dragover', onDragOver);
+		dropZone?.addEventListener('drop', onDrop);
+		dropZone?.addEventListener('dragleave', onDragLeave);
+	});
+
+	onDestroy(() => {
+		window.removeEventListener('keydown', onKeyDown);
+		window.removeEventListener('keyup', onKeyUp);
+
+		window.removeEventListener('touchstart', onTouchStart);
+		window.removeEventListener('touchend', onTouchEnd);
+
+		window.removeEventListener('focus', onFocus);
+		window.removeEventListener('blur', onBlur);
+
+		const dropZone = document.getElementById('sidebar');
+
+		dropZone?.removeEventListener('dragover', onDragOver);
+		dropZone?.removeEventListener('drop', onDrop);
+		dropZone?.removeEventListener('dragleave', onDragLeave);
+	});
+</script>
+
+<ArchivedChatsModal
+	bind:show={$showArchivedChats}
+	on:change={async () => {
+		await initChatList();
+	}}
+/>
+
+<!-- svelte-ignore a11y-no-static-element-interactions -->
+
+{#if $showSidebar}
+	<div
+		class=" fixed md:hidden z-40 top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center overflow-hidden overscroll-contain"
+		on:mousedown={() => {
+			showSidebar.set(!$showSidebar);
+		}}
+	/>
+{/if}
+
+<div
+	bind:this={navElement}
+	id="sidebar"
+	class="h-screen max-h-[100dvh] min-h-screen select-none {$showSidebar
+		? 'md:relative w-[260px] max-w-[260px]'
+		: '-translate-x-[260px] w-[0px]'} bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200 text-sm transition fixed z-50 top-0 left-0 overflow-x-hidden
+        "
+	data-state={$showSidebar}
+>
+	<div
+		class="py-2 my-auto flex flex-col justify-between h-screen max-h-[100dvh] w-[260px] overflow-x-hidden z-50 {$showSidebar
+			? ''
+			: 'invisible'}"
+	>
+		<div class="px-1.5 flex justify-between space-x-1 text-gray-600 dark:text-gray-400">
+			<a
+				id="sidebar-new-chat-button"
+				class="flex flex-1 rounded-lg px-2 py-1 h-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+				href="/"
+				draggable="false"
+				on:click={async () => {
+					selectedChatId = null;
+					await goto('/');
+					const newChatButton = document.getElementById('new-chat-button');
+					setTimeout(() => {
+						newChatButton?.click();
+						if ($mobile) {
+							showSidebar.set(false);
+						}
+					}, 0);
+				}}
+			>
+				<div class="self-center mx-1.5">
+					<img
+						crossorigin="anonymous"
+						src="{WEBUI_BASE_URL}/static/favicon.png"
+						class=" size-5 -translate-x-1.5 rounded-full"
+						alt="logo"
+					/>
+				</div>
+				<div class=" self-center font-medium text-sm text-gray-850 dark:text-white font-primary">
+					{$i18n.t('New Chat')}
+				</div>
+			</a>
+
+			<button
+				class=" cursor-pointer p-[7px] flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+				on:click={() => {
+					showSidebar.set(!$showSidebar);
+				}}
+			>
+				<div class=" m-auto self-center">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="2"
+						stroke="currentColor"
+						class="size-5"
+					>
+						<path
+							stroke-linecap="round"
+							stroke-linejoin="round"
+							d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25H12"
+						/>
+					</svg>
+				</div>
+			</button>
+		</div>
+
+		{#if $user?.role === 'admin' || $user?.permissions?.workspace?.models || $user?.permissions?.workspace?.knowledge || $user?.permissions?.workspace?.prompts || $user?.permissions?.workspace?.tools}
+			<div class="px-1.5 flex justify-center text-gray-800 dark:text-gray-200">
+				<a
+					class="flex-grow flex space-x-3 rounded-lg px-2 py-[7px] hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+					href="/workspace"
+					on:click={() => {
+						selectedChatId = null;
+						chatId.set('');
+
+						if ($mobile) {
+							showSidebar.set(false);
+						}
+					}}
+					draggable="false"
+				>
+					<div class="self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="2"
+							stroke="currentColor"
+							class="size-[1.1rem]"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z"
+							/>
+						</svg>
+					</div>
+
+					<div class="flex self-center">
+						<div class=" self-center font-medium text-sm font-primary">{$i18n.t('Workspace')}</div>
+					</div>
+				</a>
+			</div>
+		{/if}
+
+		<div class="relative {$temporaryChatEnabled ? 'opacity-20' : ''}">
+			{#if $temporaryChatEnabled}
+				<div class="absolute z-40 w-full h-full flex justify-center"></div>
+			{/if}
+
+			<SearchInput
+				bind:value={search}
+				on:input={searchDebounceHandler}
+				placeholder={$i18n.t('Search')}
+			/>
+
+			<div class="absolute z-40 right-3.5 top-1">
+				<Tooltip content={$i18n.t('New folder')}>
+					<button
+						class="p-1 rounded-lg bg-gray-50 hover:bg-gray-100 dark:bg-gray-950 dark:hover:bg-gray-900 transition"
+						on:click={() => {
+							createFolder();
+						}}
+					>
+						<Plus />
+					</button>
+				</Tooltip>
+			</div>
+		</div>
+
+		<div
+			class="relative flex flex-col flex-1 overflow-y-auto {$temporaryChatEnabled
+				? 'opacity-20'
+				: ''}"
+		>
+			{#if $temporaryChatEnabled}
+				<div class="absolute z-40 w-full h-full flex justify-center"></div>
+			{/if}
+
+			{#if !search && $pinnedChats.length > 0}
+				<div class="flex flex-col space-y-1 rounded-xl">
+					<Folder
+						className="px-2"
+						bind:open={showPinnedChat}
+						on:change={(e) => {
+							localStorage.setItem('showPinnedChat', e.detail);
+							console.log(e.detail);
+						}}
+						on:import={(e) => {
+							importChatHandler(e.detail, true);
+						}}
+						on:drop={async (e) => {
+							const { type, id, item } = e.detail;
+
+							if (type === 'chat') {
+								let chat = await getChatById(localStorage.token, id).catch((error) => {
+									return null;
+								});
+								if (!chat && item) {
+									chat = await importChat(localStorage.token, item.chat, item?.meta ?? {});
+								}
+
+								if (chat) {
+									console.log(chat);
+									if (chat.folder_id) {
+										const res = await updateChatFolderIdById(
+											localStorage.token,
+											chat.id,
+											null
+										).catch((error) => {
+											toast.error(error);
+											return null;
+										});
+									}
+
+									if (!chat.pinned) {
+										const res = await toggleChatPinnedStatusById(localStorage.token, chat.id);
+									}
+
+									initChatList();
+								}
+							}
+						}}
+						name={$i18n.t('Pinned')}
+					>
+						<div
+							class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900"
+						>
+							{#each $pinnedChats as chat, idx}
+								<ChatItem
+									className=""
+									id={chat.id}
+									title={chat.title}
+									{shiftKey}
+									selected={selectedChatId === chat.id}
+									on:select={() => {
+										selectedChatId = chat.id;
+									}}
+									on:unselect={() => {
+										selectedChatId = null;
+									}}
+									on:change={async () => {
+										initChatList();
+									}}
+									on:tag={(e) => {
+										const { type, name } = e.detail;
+										tagEventHandler(type, name, chat.id);
+									}}
+								/>
+							{/each}
+						</div>
+					</Folder>
+				</div>
+			{/if}
+
+			<div class=" flex-1 flex flex-col overflow-y-auto scrollbar-hidden">
+				{#if !search && folders}
+					<Folders
+						{folders}
+						on:import={(e) => {
+							const { folderId, items } = e.detail;
+							importChatHandler(items, false, folderId);
+						}}
+						on:update={async (e) => {
+							initChatList();
+						}}
+						on:change={async () => {
+							initChatList();
+						}}
+					/>
+				{/if}
+
+				<Folder
+					collapsible={!search}
+					className="px-2 mt-0.5"
+					name={$i18n.t('All chats')}
+					on:import={(e) => {
+						importChatHandler(e.detail);
+					}}
+					on:drop={async (e) => {
+						const { type, id, item } = e.detail;
+
+						if (type === 'chat') {
+							let chat = await getChatById(localStorage.token, id).catch((error) => {
+								return null;
+							});
+							if (!chat && item) {
+								chat = await importChat(localStorage.token, item.chat, item?.meta ?? {});
+							}
+
+							if (chat) {
+								console.log(chat);
+								if (chat.folder_id) {
+									const res = await updateChatFolderIdById(localStorage.token, chat.id, null).catch(
+										(error) => {
+											toast.error(error);
+											return null;
+										}
+									);
+								}
+
+								if (chat.pinned) {
+									const res = await toggleChatPinnedStatusById(localStorage.token, chat, id);
+								}
+
+								initChatList();
+							}
+						} else if (type === 'folder') {
+							if (folders[id].parent_id === null) {
+								return;
+							}
+
+							const res = await updateFolderParentIdById(localStorage.token, id, null).catch(
+								(error) => {
+									toast.error(error);
+									return null;
+								}
+							);
+
+							if (res) {
+								await initFolders();
+							}
+						}
+					}}
+				>
+					<div class="pt-1.5">
+						{#if $chats}
+							{#each $chats as chat, idx}
+								{#if idx === 0 || (idx > 0 && chat.time_range !== $chats[idx - 1].time_range)}
+									<div
+										class="w-full pl-2.5 text-xs text-gray-500 dark:text-gray-500 font-medium {idx ===
+										0
+											? ''
+											: 'pt-5'} pb-1.5"
+									>
+										{$i18n.t(chat.time_range)}
+										<!-- localisation keys for time_range to be recognized from the i18next parser (so they don't get automatically removed):
+							{$i18n.t('Today')}
+							{$i18n.t('Yesterday')}
+							{$i18n.t('Previous 7 days')}
+							{$i18n.t('Previous 30 days')}
+							{$i18n.t('January')}
+							{$i18n.t('February')}
+							{$i18n.t('March')}
+							{$i18n.t('April')}
+							{$i18n.t('May')}
+							{$i18n.t('June')}
+							{$i18n.t('July')}
+							{$i18n.t('August')}
+							{$i18n.t('September')}
+							{$i18n.t('October')}
+							{$i18n.t('November')}
+							{$i18n.t('December')}
+							-->
+									</div>
+								{/if}
+
+								<ChatItem
+									className=""
+									id={chat.id}
+									title={chat.title}
+									{shiftKey}
+									selected={selectedChatId === chat.id}
+									on:select={() => {
+										selectedChatId = chat.id;
+									}}
+									on:unselect={() => {
+										selectedChatId = null;
+									}}
+									on:change={async () => {
+										initChatList();
+									}}
+									on:tag={(e) => {
+										const { type, name } = e.detail;
+										tagEventHandler(type, name, chat.id);
+									}}
+								/>
+							{/each}
+
+							{#if $scrollPaginationEnabled && !allChatsLoaded}
+								<Loader
+									on:visible={(e) => {
+										if (!chatListLoading) {
+											loadMoreChats();
+										}
+									}}
+								>
+									<div
+										class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2"
+									>
+										<Spinner className=" size-4" />
+										<div class=" ">Loading...</div>
+									</div>
+								</Loader>
+							{/if}
+						{:else}
+							<div class="w-full flex justify-center py-1 text-xs animate-pulse items-center gap-2">
+								<Spinner className=" size-4" />
+								<div class=" ">Loading...</div>
+							</div>
+						{/if}
+					</div>
+				</Folder>
+			</div>
+		</div>
+
+		<div class="px-2">
+			<div class="flex flex-col font-primary">
+				{#if $user !== undefined}
+					<UserMenu
+						role={$user.role}
+						on:show={(e) => {
+							if (e.detail === 'archived-chat') {
+								showArchivedChats.set(true);
+							}
+						}}
+					>
+						<button
+							class=" flex items-center rounded-xl py-2.5 px-2.5 w-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+							on:click={() => {
+								showDropdown = !showDropdown;
+							}}
+						>
+							<div class=" self-center mr-3">
+								<img
+									src={$user.profile_image_url}
+									class=" max-w-[30px] object-cover rounded-full"
+									alt="User profile"
+								/>
+							</div>
+							<div class=" self-center font-medium">{$user.name}</div>
+						</button>
+					</UserMenu>
+				{/if}
+			</div>
+		</div>
+	</div>
+</div>
+
+<style>
+	.scrollbar-hidden:active::-webkit-scrollbar-thumb,
+	.scrollbar-hidden:focus::-webkit-scrollbar-thumb,
+	.scrollbar-hidden:hover::-webkit-scrollbar-thumb {
+		visibility: visible;
+	}
+	.scrollbar-hidden::-webkit-scrollbar-thumb {
+		visibility: hidden;
+	}
+</style>
diff --git a/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte b/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..260c99cf808a86e7cd46d43598722eddfe3bb73d
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/ArchivedChatsModal.svelte
@@ -0,0 +1,252 @@
+<script lang="ts">
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+
+	import {
+		archiveChatById,
+		deleteChatById,
+		getAllArchivedChats,
+		getArchivedChatList
+	} from '$lib/apis/chats';
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import UnarchiveAllConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	const i18n = getContext('i18n');
+
+	export let show = false;
+
+	let chats = [];
+
+	let searchValue = '';
+	let showUnarchiveAllConfirmDialog = false;
+
+	const unarchiveChatHandler = async (chatId) => {
+		const res = await archiveChatById(localStorage.token, chatId).catch((error) => {
+			toast.error(error);
+		});
+
+		chats = await getArchivedChatList(localStorage.token);
+		dispatch('change');
+	};
+
+	const deleteChatHandler = async (chatId) => {
+		const res = await deleteChatById(localStorage.token, chatId).catch((error) => {
+			toast.error(error);
+		});
+
+		chats = await getArchivedChatList(localStorage.token);
+	};
+
+	const exportChatsHandler = async () => {
+		const chats = await getAllArchivedChats(localStorage.token);
+		let blob = new Blob([JSON.stringify(chats)], {
+			type: 'application/json'
+		});
+		saveAs(blob, `${$i18n.t('archived-chat-export')}-${Date.now()}.json`);
+	};
+
+	const unarchiveAllHandler = async () => {
+		for (const chat of chats) {
+			await archiveChatById(localStorage.token, chat.id);
+		}
+		chats = await getArchivedChatList(localStorage.token);
+	};
+
+	$: if (show) {
+		(async () => {
+			chats = await getArchivedChatList(localStorage.token);
+		})();
+	}
+</script>
+
+<UnarchiveAllConfirmDialog
+	bind:show={showUnarchiveAllConfirmDialog}
+	message={$i18n.t('Are you sure you want to unarchive all archived chats?')}
+	confirmLabel={$i18n.t('Unarchive All')}
+	on:confirm={() => {
+		unarchiveAllHandler();
+	}}
+/>
+
+<Modal size="lg" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Archived Chats')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col w-full px-5 pb-4 dark:text-gray-200">
+			<div class=" flex w-full mt-2 space-x-2">
+				<div class="flex flex-1">
+					<div class=" self-center ml-1 mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<input
+						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+						bind:value={searchValue}
+						placeholder={$i18n.t('Search Chats')}
+					/>
+				</div>
+			</div>
+			<hr class=" dark:border-gray-850 my-2" />
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				{#if chats.length > 0}
+					<div class="w-full">
+						<div class="text-left text-sm w-full mb-3 max-h-[22rem] overflow-y-scroll">
+							<div class="relative overflow-x-auto">
+								<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
+									<thead
+										class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-800"
+									>
+										<tr>
+											<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
+											<th scope="col" class="px-3 py-2 hidden md:flex">
+												{$i18n.t('Created At')}
+											</th>
+											<th scope="col" class="px-3 py-2 text-right" />
+										</tr>
+									</thead>
+									<tbody>
+										{#each chats.filter((c) => searchValue === '' || c.title
+													.toLowerCase()
+													.includes(searchValue.toLowerCase())) as chat, idx}
+											<tr
+												class="bg-transparent {idx !== chats.length - 1 &&
+													'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
+											>
+												<td class="px-3 py-1 w-2/3">
+													<a href="/c/{chat.id}" target="_blank">
+														<div class=" underline line-clamp-1">
+															{chat.title}
+														</div>
+													</a>
+												</td>
+
+												<td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
+													<div class="my-auto">
+														{dayjs(chat.created_at * 1000).format($i18n.t('MMMM DD, YYYY HH:mm'))}
+													</div>
+												</td>
+
+												<td class="px-3 py-1 text-right">
+													<div class="flex justify-end w-full">
+														<Tooltip content={$i18n.t('Unarchive Chat')}>
+															<button
+																class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+																on:click={async () => {
+																	unarchiveChatHandler(chat.id);
+																}}
+															>
+																<svg
+																	xmlns="http://www.w3.org/2000/svg"
+																	fill="none"
+																	viewBox="0 0 24 24"
+																	stroke-width="1.5"
+																	stroke="currentColor"
+																	class="size-4"
+																>
+																	<path
+																		stroke-linecap="round"
+																		stroke-linejoin="round"
+																		d="M9 8.25H7.5a2.25 2.25 0 0 0-2.25 2.25v9a2.25 2.25 0 0 0 2.25 2.25h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25H15m0-3-3-3m0 0-3 3m3-3V15"
+																	/>
+																</svg>
+															</button>
+														</Tooltip>
+
+														<Tooltip content={$i18n.t('Delete Chat')}>
+															<button
+																class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+																on:click={async () => {
+																	deleteChatHandler(chat.id);
+																}}
+															>
+																<svg
+																	xmlns="http://www.w3.org/2000/svg"
+																	fill="none"
+																	viewBox="0 0 24 24"
+																	stroke-width="1.5"
+																	stroke="currentColor"
+																	class="w-4 h-4"
+																>
+																	<path
+																		stroke-linecap="round"
+																		stroke-linejoin="round"
+																		d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
+																	/>
+																</svg>
+															</button>
+														</Tooltip>
+													</div>
+												</td>
+											</tr>
+										{/each}
+									</tbody>
+								</table>
+							</div>
+						</div>
+
+						<div class="flex flex-wrap text-sm font-medium gap-1.5 mt-2 m-1 justify-end w-full">
+							<button
+								class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
+								on:click={() => {
+									showUnarchiveAllConfirmDialog = true;
+								}}
+							>
+								{$i18n.t('Unarchive All Archived Chats')}
+							</button>
+
+							<button
+								class="px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
+								on:click={() => {
+									exportChatsHandler();
+								}}
+							>
+								{$i18n.t('Export All Archived Chats')}
+							</button>
+						</div>
+					</div>
+				{:else}
+					<div class="text-left text-sm w-full mb-8">
+						{$i18n.t('You have no archived conversations.')}
+					</div>
+				{/if}
+			</div>
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/layout/Sidebar/ChatItem.svelte b/src/lib/components/layout/Sidebar/ChatItem.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..110233bf88644914a62d0327bd5301516d315d52
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/ChatItem.svelte
@@ -0,0 +1,426 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { goto, invalidate, invalidateAll } from '$app/navigation';
+	import { onMount, getContext, createEventDispatcher, tick, onDestroy } from 'svelte';
+	const i18n = getContext('i18n');
+
+	const dispatch = createEventDispatcher();
+
+	import {
+		archiveChatById,
+		cloneChatById,
+		deleteChatById,
+		getAllTags,
+		getChatById,
+		getChatList,
+		getChatListByTagName,
+		getPinnedChatList,
+		updateChatById
+	} from '$lib/apis/chats';
+	import {
+		chatId,
+		chatTitle as _chatTitle,
+		chats,
+		mobile,
+		pinnedChats,
+		showSidebar,
+		currentChatPage,
+		tags
+	} from '$lib/stores';
+
+	import ChatMenu from './ChatMenu.svelte';
+	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import ShareChatModal from '$lib/components/chat/ShareChatModal.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DragGhost from '$lib/components/common/DragGhost.svelte';
+	import Check from '$lib/components/icons/Check.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
+	import Document from '$lib/components/icons/Document.svelte';
+
+	export let className = '';
+
+	export let id;
+	export let title;
+
+	export let selected = false;
+	export let shiftKey = false;
+
+	let chat = null;
+
+	let mouseOver = false;
+	let draggable = false;
+	$: if (mouseOver) {
+		loadChat();
+	}
+
+	const loadChat = async () => {
+		if (!chat) {
+			draggable = false;
+			chat = await getChatById(localStorage.token, id);
+			draggable = true;
+		}
+	};
+
+	let showShareChatModal = false;
+	let confirmEdit = false;
+
+	let chatTitle = title;
+
+	const editChatTitle = async (id, title) => {
+		if (title === '') {
+			toast.error($i18n.t('Title cannot be an empty string.'));
+		} else {
+			await updateChatById(localStorage.token, id, {
+				title: title
+			});
+
+			if (id === $chatId) {
+				_chatTitle.set(title);
+			}
+
+			currentChatPage.set(1);
+			await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			await pinnedChats.set(await getPinnedChatList(localStorage.token));
+		}
+	};
+
+	const cloneChatHandler = async (id) => {
+		const res = await cloneChatById(localStorage.token, id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			goto(`/c/${res.id}`);
+
+			currentChatPage.set(1);
+			await chats.set(await getChatList(localStorage.token, $currentChatPage));
+			await pinnedChats.set(await getPinnedChatList(localStorage.token));
+		}
+	};
+
+	const deleteChatHandler = async (id) => {
+		const res = await deleteChatById(localStorage.token, id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			tags.set(await getAllTags(localStorage.token));
+			if ($chatId === id) {
+				await chatId.set('');
+				await tick();
+				goto('/');
+			}
+
+			dispatch('change');
+		}
+	};
+
+	const archiveChatHandler = async (id) => {
+		await archiveChatById(localStorage.token, id);
+		dispatch('change');
+	};
+
+	const focusEdit = async (node: HTMLInputElement) => {
+		node.focus();
+	};
+
+	let itemElement;
+
+	let dragged = false;
+	let x = 0;
+	let y = 0;
+
+	const dragImage = new Image();
+	dragImage.src =
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
+
+	const onDragStart = (event) => {
+		event.stopPropagation();
+
+		event.dataTransfer.setDragImage(dragImage, 0, 0);
+
+		// Set the data to be transferred
+		event.dataTransfer.setData(
+			'text/plain',
+			JSON.stringify({
+				type: 'chat',
+				id: id,
+				item: chat
+			})
+		);
+
+		dragged = true;
+		itemElement.style.opacity = '0.5'; // Optional: Visual cue to show it's being dragged
+	};
+
+	const onDrag = (event) => {
+		event.stopPropagation();
+
+		x = event.clientX;
+		y = event.clientY;
+	};
+
+	const onDragEnd = (event) => {
+		event.stopPropagation();
+
+		itemElement.style.opacity = '1'; // Reset visual cue after drag
+		dragged = false;
+	};
+
+	onMount(() => {
+		if (itemElement) {
+			// Event listener for when dragging starts
+			itemElement.addEventListener('dragstart', onDragStart);
+			// Event listener for when dragging occurs (optional)
+			itemElement.addEventListener('drag', onDrag);
+			// Event listener for when dragging ends
+			itemElement.addEventListener('dragend', onDragEnd);
+		}
+	});
+
+	onDestroy(() => {
+		if (itemElement) {
+			itemElement.removeEventListener('dragstart', onDragStart);
+			itemElement.removeEventListener('drag', onDrag);
+			itemElement.removeEventListener('dragend', onDragEnd);
+		}
+	});
+
+	let showDeleteConfirm = false;
+</script>
+
+<ShareChatModal bind:show={showShareChatModal} chatId={id} />
+
+<DeleteConfirmDialog
+	bind:show={showDeleteConfirm}
+	title={$i18n.t('Delete chat?')}
+	on:confirm={() => {
+		deleteChatHandler(id);
+	}}
+>
+	<div class=" text-sm text-gray-500 flex-1 line-clamp-3">
+		{$i18n.t('This will delete')} <span class="  font-semibold">{title}</span>.
+	</div>
+</DeleteConfirmDialog>
+
+{#if dragged && x && y}
+	<DragGhost {x} {y}>
+		<div class=" bg-black/80 backdrop-blur-2xl px-2 py-1 rounded-lg w-fit max-w-40">
+			<div class="flex items-center gap-1">
+				<Document className=" size-[18px]" strokeWidth="2" />
+				<div class=" text-xs text-white line-clamp-1">
+					{title}
+				</div>
+			</div>
+		</div>
+	</DragGhost>
+{/if}
+
+<div bind:this={itemElement} class=" w-full {className} relative group" {draggable}>
+	{#if confirmEdit}
+		<div
+			class=" w-full flex justify-between rounded-lg px-[11px] py-[6px] {id === $chatId ||
+			confirmEdit
+				? 'bg-gray-200 dark:bg-gray-900'
+				: selected
+					? 'bg-gray-100 dark:bg-gray-950'
+					: 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'}  whitespace-nowrap text-ellipsis"
+		>
+			<input
+				use:focusEdit
+				bind:value={chatTitle}
+				class=" bg-transparent w-full outline-none mr-10"
+			/>
+		</div>
+	{:else}
+		<a
+			class=" w-full flex justify-between rounded-lg px-[11px] py-[6px] {id === $chatId ||
+			confirmEdit
+				? 'bg-gray-200 dark:bg-gray-900'
+				: selected
+					? 'bg-gray-100 dark:bg-gray-950'
+					: ' group-hover:bg-gray-100 dark:group-hover:bg-gray-950'}  whitespace-nowrap text-ellipsis"
+			href="/c/{id}"
+			on:click={() => {
+				dispatch('select');
+
+				if ($mobile) {
+					showSidebar.set(false);
+				}
+			}}
+			on:dblclick={() => {
+				chatTitle = title;
+				confirmEdit = true;
+			}}
+			on:mouseenter={(e) => {
+				mouseOver = true;
+			}}
+			on:mouseleave={(e) => {
+				mouseOver = false;
+			}}
+			on:focus={(e) => {}}
+			draggable="false"
+		>
+			<div class=" flex self-center flex-1 w-full">
+				<div class=" text-left self-center overflow-hidden w-full h-[20px]">
+					{title}
+				</div>
+			</div>
+		</a>
+	{/if}
+
+	<!-- svelte-ignore a11y-no-static-element-interactions -->
+	<div
+		class="
+        {id === $chatId || confirmEdit
+			? 'from-gray-200 dark:from-gray-900'
+			: selected
+				? 'from-gray-100 dark:from-gray-950'
+				: 'invisible group-hover:visible from-gray-100 dark:from-gray-950'}
+            absolute {className === 'pr-2'
+			? 'right-[8px]'
+			: 'right-0'}  top-[4px] py-1 pr-0.5 mr-1.5 pl-5 bg-gradient-to-l from-80%
+
+              to-transparent"
+		on:mouseenter={(e) => {
+			mouseOver = true;
+		}}
+		on:mouseleave={(e) => {
+			mouseOver = false;
+		}}
+	>
+		{#if confirmEdit}
+			<div
+				class="flex self-center items-center space-x-1.5 z-10 translate-y-[0.5px] -translate-x-[0.5px]"
+			>
+				<Tooltip content={$i18n.t('Confirm')}>
+					<button
+						class=" self-center dark:hover:text-white transition"
+						on:click={() => {
+							editChatTitle(id, chatTitle);
+							confirmEdit = false;
+							chatTitle = '';
+						}}
+					>
+						<Check className=" size-3.5" strokeWidth="2.5" />
+					</button>
+				</Tooltip>
+
+				<Tooltip content={$i18n.t('Cancel')}>
+					<button
+						class=" self-center dark:hover:text-white transition"
+						on:click={() => {
+							confirmEdit = false;
+							chatTitle = '';
+						}}
+					>
+						<XMark strokeWidth="2.5" />
+					</button>
+				</Tooltip>
+			</div>
+		{:else if shiftKey && mouseOver}
+			<div class=" flex items-center self-center space-x-1.5">
+				<Tooltip content={$i18n.t('Archive')} className="flex items-center">
+					<button
+						class=" self-center dark:hover:text-white transition"
+						on:click={() => {
+							archiveChatHandler(id);
+						}}
+						type="button"
+					>
+						<ArchiveBox className="size-4  translate-y-[0.5px]" strokeWidth="2" />
+					</button>
+				</Tooltip>
+
+				<Tooltip content={$i18n.t('Delete')}>
+					<button
+						class=" self-center dark:hover:text-white transition"
+						on:click={() => {
+							deleteChatHandler(id);
+						}}
+						type="button"
+					>
+						<GarbageBin strokeWidth="2" />
+					</button>
+				</Tooltip>
+			</div>
+		{:else}
+			<div class="flex self-center space-x-1 z-10">
+				<ChatMenu
+					chatId={id}
+					cloneChatHandler={() => {
+						cloneChatHandler(id);
+					}}
+					shareHandler={() => {
+						showShareChatModal = true;
+					}}
+					archiveChatHandler={() => {
+						archiveChatHandler(id);
+					}}
+					renameHandler={() => {
+						chatTitle = title;
+
+						confirmEdit = true;
+					}}
+					deleteHandler={() => {
+						showDeleteConfirm = true;
+					}}
+					onClose={() => {
+						dispatch('unselect');
+					}}
+					on:change={async () => {
+						dispatch('change');
+					}}
+					on:tag={(e) => {
+						dispatch('tag', e.detail);
+					}}
+				>
+					<button
+						aria-label="Chat Menu"
+						class=" self-center dark:hover:text-white transition"
+						on:click={() => {
+							dispatch('select');
+						}}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
+							/>
+						</svg>
+					</button>
+				</ChatMenu>
+
+				{#if id === $chatId}
+					<!-- Shortcut support using "delete-chat-button" id -->
+					<button
+						id="delete-chat-button"
+						class="hidden"
+						on:click={() => {
+							showDeleteConfirm = true;
+						}}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								d="M2 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM6.5 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0ZM12.5 6.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"
+							/>
+						</svg>
+					</button>
+				{/if}
+			</div>
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/layout/Sidebar/ChatMenu.svelte b/src/lib/components/layout/Sidebar/ChatMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..391e60fb74e67e723bca1903fc5443f6cdfe839b
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/ChatMenu.svelte
@@ -0,0 +1,277 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	const dispatch = createEventDispatcher();
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import Bookmark from '$lib/components/icons/Bookmark.svelte';
+	import BookmarkSlash from '$lib/components/icons/BookmarkSlash.svelte';
+	import {
+		getChatById,
+		getChatPinnedStatusById,
+		toggleChatPinnedStatusById
+	} from '$lib/apis/chats';
+	import { chats } from '$lib/stores';
+	import { createMessagesList } from '$lib/utils';
+	import { downloadChatAsPDF } from '$lib/apis/utils';
+	import Download from '$lib/components/icons/Download.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let shareHandler: Function;
+	export let cloneChatHandler: Function;
+	export let archiveChatHandler: Function;
+	export let renameHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	export let chatId = '';
+
+	let show = false;
+	let pinned = false;
+
+	const pinHandler = async () => {
+		await toggleChatPinnedStatusById(localStorage.token, chatId);
+		dispatch('change');
+	};
+
+	const checkPinned = async () => {
+		pinned = await getChatPinnedStatusById(localStorage.token, chatId);
+	};
+
+	const getChatAsText = async (chat) => {
+		const history = chat.chat.history;
+		const messages = createMessagesList(history, history.currentId);
+		const chatText = messages.reduce((a, message, i, arr) => {
+			return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
+		}, '');
+
+		return chatText.trim();
+	};
+
+	const downloadTxt = async () => {
+		const chat = await getChatById(localStorage.token, chatId);
+		if (!chat) {
+			return;
+		}
+
+		const chatText = await getChatAsText(chat);
+		let blob = new Blob([chatText], {
+			type: 'text/plain'
+		});
+
+		saveAs(blob, `chat-${chat.chat.title}.txt`);
+	};
+
+	const downloadPdf = async () => {
+		const chat = await getChatById(localStorage.token, chatId);
+		if (!chat) {
+			return;
+		}
+
+		const history = chat.chat.history;
+		const messages = createMessagesList(history, history.currentId);
+		const blob = await downloadChatAsPDF(chat.chat.title, messages);
+
+		// Create a URL for the blob
+		const url = window.URL.createObjectURL(blob);
+
+		// Create a link element to trigger the download
+		const a = document.createElement('a');
+		a.href = url;
+		a.download = `chat-${chat.chat.title}.pdf`;
+
+		// Append the link to the body and click it programmatically
+		document.body.appendChild(a);
+		a.click();
+
+		// Remove the link from the body
+		document.body.removeChild(a);
+
+		// Revoke the URL to release memory
+		window.URL.revokeObjectURL(url);
+	};
+
+	const downloadJSONExport = async () => {
+		const chat = await getChatById(localStorage.token, chatId);
+
+		if (chat) {
+			let blob = new Blob([JSON.stringify([chat])], {
+				type: 'application/json'
+			});
+			saveAs(blob, `chat-export-${Date.now()}.json`);
+		}
+	};
+
+	$: if (show) {
+		checkPinned();
+	}
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[200px] rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					pinHandler();
+				}}
+			>
+				{#if pinned}
+					<BookmarkSlash strokeWidth="2" />
+					<div class="flex items-center">{$i18n.t('Unpin')}</div>
+				{:else}
+					<Bookmark strokeWidth="2" />
+					<div class="flex items-center">{$i18n.t('Pin')}</div>
+				{/if}
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					renameHandler();
+				}}
+			>
+				<Pencil strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Rename')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					cloneChatHandler();
+				}}
+			>
+				<DocumentDuplicate strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					archiveChatHandler();
+				}}
+			>
+				<ArchiveBox strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Archive')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Sub>
+				<DropdownMenu.SubTrigger
+					class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				>
+					<Download strokeWidth="2" />
+
+					<div class="flex items-center">{$i18n.t('Download')}</div>
+				</DropdownMenu.SubTrigger>
+				<DropdownMenu.SubContent
+					class="w-full rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+					transition={flyAndScale}
+					sideOffset={8}
+				>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadJSONExport();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('Export chat (.json)')}</div>
+					</DropdownMenu.Item>
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadTxt();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('Plain text (.txt)')}</div>
+					</DropdownMenu.Item>
+
+					<DropdownMenu.Item
+						class="flex gap-2 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+						on:click={() => {
+							downloadPdf();
+						}}
+					>
+						<div class="flex items-center line-clamp-1">{$i18n.t('PDF document (.pdf)')}</div>
+					</DropdownMenu.Item>
+				</DropdownMenu.SubContent>
+			</DropdownMenu.Sub>
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-50 dark:border-gray-850 my-0.5" />
+
+			<div class="flex p-1">
+				<Tags
+					{chatId}
+					on:add={(e) => {
+						dispatch('tag', {
+							type: 'add',
+							name: e.detail.name
+						});
+
+						show = false;
+					}}
+					on:delete={(e) => {
+						dispatch('tag', {
+							type: 'delete',
+							name: e.detail.name
+						});
+
+						show = false;
+					}}
+					on:close={() => {
+						show = false;
+						onClose();
+					}}
+				/>
+			</div>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/layout/Sidebar/Folders.svelte b/src/lib/components/layout/Sidebar/Folders.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..fb0c955c572d7ce594a3d2f78911199615c74e81
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/Folders.svelte
@@ -0,0 +1,35 @@
+<script lang="ts">
+	import { createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+	import RecursiveFolder from './RecursiveFolder.svelte';
+	export let folders = {};
+
+	let folderList = [];
+	// Get the list of folders that have no parent, sorted by name alphabetically
+	$: folderList = Object.keys(folders)
+		.filter((key) => folders[key].parent_id === null)
+		.sort((a, b) =>
+			folders[a].name.localeCompare(folders[b].name, undefined, {
+				numeric: true,
+				sensitivity: 'base'
+			})
+		);
+</script>
+
+{#each folderList as folderId (folderId)}
+	<RecursiveFolder
+		className="px-2"
+		{folders}
+		{folderId}
+		on:import={(e) => {
+			dispatch('import', e.detail);
+		}}
+		on:update={(e) => {
+			dispatch('update', e.detail);
+		}}
+		on:change={(e) => {
+			dispatch('change', e.detail);
+		}}
+	/>
+{/each}
diff --git a/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte b/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e485a95fd479dd287c46a4b438a93f7ff4176ae7
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/Folders/FolderMenu.svelte
@@ -0,0 +1,70 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Download from '$lib/components/icons/Download.svelte';
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			dispatch('close');
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-lg px-1 py-1.5  z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('rename');
+				}}
+			>
+				<Pencil strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Rename')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('export');
+				}}
+			>
+				<Download strokeWidth="2" />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-1.5 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('delete');
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/layout/Sidebar/RecursiveFolder.svelte b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..63a31af3cf5db51c1f37afd66b8a7cbd85871798
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/RecursiveFolder.svelte
@@ -0,0 +1,495 @@
+<script>
+	import { getContext, createEventDispatcher, onMount, onDestroy, tick } from 'svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import DOMPurify from 'dompurify';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import ChevronDown from '../../icons/ChevronDown.svelte';
+	import ChevronRight from '../../icons/ChevronRight.svelte';
+	import Collapsible from '../../common/Collapsible.svelte';
+	import DragGhost from '$lib/components/common/DragGhost.svelte';
+
+	import FolderOpen from '$lib/components/icons/FolderOpen.svelte';
+	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+	import {
+		deleteFolderById,
+		updateFolderIsExpandedById,
+		updateFolderNameById,
+		updateFolderParentIdById
+	} from '$lib/apis/folders';
+	import { toast } from 'svelte-sonner';
+	import {
+		getChatById,
+		getChatsByFolderId,
+		importChat,
+		updateChatFolderIdById
+	} from '$lib/apis/chats';
+	import ChatItem from './ChatItem.svelte';
+	import FolderMenu from './Folders/FolderMenu.svelte';
+	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+
+	export let open = false;
+
+	export let folders;
+	export let folderId;
+
+	export let className = '';
+
+	export let parentDragged = false;
+
+	let folderElement;
+
+	let edit = false;
+
+	let draggedOver = false;
+	let dragged = false;
+
+	let name = '';
+
+	const onDragOver = (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		if (dragged || parentDragged) {
+			return;
+		}
+		draggedOver = true;
+	};
+
+	const onDrop = async (e) => {
+		e.preventDefault();
+		e.stopPropagation();
+		if (dragged || parentDragged) {
+			return;
+		}
+
+		if (folderElement.contains(e.target)) {
+			console.log('Dropped on the Button');
+
+			if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
+				// Iterate over all items in the DataTransferItemList use functional programming
+				for (const item of Array.from(e.dataTransfer.items)) {
+					// If dropped items aren't files, reject them
+					if (item.kind === 'file') {
+						const file = item.getAsFile();
+						if (file && file.type === 'application/json') {
+							console.log('Dropped file is a JSON file!');
+
+							// Read the JSON file with FileReader
+							const reader = new FileReader();
+							reader.onload = async function (event) {
+								try {
+									const fileContent = JSON.parse(event.target.result);
+									open = true;
+									dispatch('import', {
+										folderId: folderId,
+										items: fileContent
+									});
+								} catch (error) {
+									console.error('Error parsing JSON file:', error);
+								}
+							};
+
+							// Start reading the file
+							reader.readAsText(file);
+						} else {
+							console.error('Only JSON file types are supported.');
+						}
+
+						console.log(file);
+					} else {
+						// Handle the drag-and-drop data for folders or chats (same as before)
+						const dataTransfer = e.dataTransfer.getData('text/plain');
+						const data = JSON.parse(dataTransfer);
+						console.log(data);
+
+						const { type, id, item } = data;
+
+						if (type === 'folder') {
+							open = true;
+							if (id === folderId) {
+								return;
+							}
+							// Move the folder
+							const res = await updateFolderParentIdById(localStorage.token, id, folderId).catch(
+								(error) => {
+									toast.error(error);
+									return null;
+								}
+							);
+
+							if (res) {
+								dispatch('update');
+							}
+						} else if (type === 'chat') {
+							open = true;
+
+							let chat = await getChatById(localStorage.token, id).catch((error) => {
+								return null;
+							});
+							if (!chat && item) {
+								chat = await importChat(localStorage.token, item.chat, item?.meta ?? {});
+							}
+
+							// Move the chat
+							const res = await updateChatFolderIdById(localStorage.token, chat.id, folderId).catch(
+								(error) => {
+									toast.error(error);
+									return null;
+								}
+							);
+
+							if (res) {
+								dispatch('update');
+							}
+						}
+					}
+				}
+			}
+
+			draggedOver = false;
+		}
+	};
+
+	const onDragLeave = (e) => {
+		e.preventDefault();
+		if (dragged || parentDragged) {
+			return;
+		}
+
+		draggedOver = false;
+	};
+
+	const dragImage = new Image();
+	dragImage.src =
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
+
+	let x;
+	let y;
+
+	const onDragStart = (event) => {
+		event.stopPropagation();
+		event.dataTransfer.setDragImage(dragImage, 0, 0);
+
+		// Set the data to be transferred
+		event.dataTransfer.setData(
+			'text/plain',
+			JSON.stringify({
+				type: 'folder',
+				id: folderId
+			})
+		);
+
+		dragged = true;
+		folderElement.style.opacity = '0.5'; // Optional: Visual cue to show it's being dragged
+	};
+
+	const onDrag = (event) => {
+		event.stopPropagation();
+
+		x = event.clientX;
+		y = event.clientY;
+	};
+
+	const onDragEnd = (event) => {
+		event.stopPropagation();
+
+		folderElement.style.opacity = '1'; // Reset visual cue after drag
+		dragged = false;
+	};
+
+	onMount(() => {
+		open = folders[folderId].is_expanded;
+		if (folderElement) {
+			folderElement.addEventListener('dragover', onDragOver);
+			folderElement.addEventListener('drop', onDrop);
+			folderElement.addEventListener('dragleave', onDragLeave);
+
+			// Event listener for when dragging starts
+			folderElement.addEventListener('dragstart', onDragStart);
+			// Event listener for when dragging occurs (optional)
+			folderElement.addEventListener('drag', onDrag);
+			// Event listener for when dragging ends
+			folderElement.addEventListener('dragend', onDragEnd);
+		}
+	});
+
+	onDestroy(() => {
+		if (folderElement) {
+			folderElement.addEventListener('dragover', onDragOver);
+			folderElement.removeEventListener('drop', onDrop);
+			folderElement.removeEventListener('dragleave', onDragLeave);
+
+			folderElement.removeEventListener('dragstart', onDragStart);
+			folderElement.removeEventListener('drag', onDrag);
+			folderElement.removeEventListener('dragend', onDragEnd);
+		}
+	});
+
+	let showDeleteConfirm = false;
+
+	const deleteHandler = async () => {
+		const res = await deleteFolderById(localStorage.token, folderId).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Folder deleted successfully'));
+			dispatch('update');
+		}
+	};
+
+	const nameUpdateHandler = async () => {
+		if (name === '') {
+			toast.error($i18n.t('Folder name cannot be empty'));
+			return;
+		}
+
+		if (name === folders[folderId].name) {
+			edit = false;
+			return;
+		}
+
+		const currentName = folders[folderId].name;
+
+		name = name.trim();
+		folders[folderId].name = name;
+
+		const res = await updateFolderNameById(localStorage.token, folderId, name).catch((error) => {
+			toast.error(error);
+
+			folders[folderId].name = currentName;
+			return null;
+		});
+
+		if (res) {
+			folders[folderId].name = name;
+			toast.success($i18n.t('Folder name updated successfully'));
+			dispatch('update');
+		}
+	};
+
+	const isExpandedUpdateHandler = async () => {
+		const res = await updateFolderIsExpandedById(localStorage.token, folderId, open).catch(
+			(error) => {
+				toast.error(error);
+				return null;
+			}
+		);
+	};
+
+	let isExpandedUpdateTimeout;
+
+	const isExpandedUpdateDebounceHandler = (open) => {
+		clearTimeout(isExpandedUpdateTimeout);
+		isExpandedUpdateTimeout = setTimeout(() => {
+			isExpandedUpdateHandler();
+		}, 500);
+	};
+
+	$: isExpandedUpdateDebounceHandler(open);
+
+	const editHandler = async () => {
+		console.log('Edit');
+		await tick();
+		name = folders[folderId].name;
+		edit = true;
+
+		await tick();
+
+		// focus on the input
+		setTimeout(() => {
+			const input = document.getElementById(`folder-${folderId}-input`);
+			input.focus();
+		}, 100);
+	};
+
+	const exportHandler = async () => {
+		const chats = await getChatsByFolderId(localStorage.token, folderId).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+		if (!chats) {
+			return;
+		}
+
+		const blob = new Blob([JSON.stringify(chats)], {
+			type: 'application/json'
+		});
+
+		saveAs(blob, `folder-${folders[folderId].name}-export-${Date.now()}.json`);
+	};
+</script>
+
+<DeleteConfirmDialog
+	bind:show={showDeleteConfirm}
+	title={$i18n.t('Delete folder?')}
+	on:confirm={() => {
+		deleteHandler();
+	}}
+>
+	<div class=" text-sm text-gray-700 dark:text-gray-300 flex-1 line-clamp-3">
+		{@html DOMPurify.sanitize(
+			$i18n.t('This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.', {
+				NAME: folders[folderId].name
+			})
+		)}
+	</div>
+</DeleteConfirmDialog>
+
+{#if dragged && x && y}
+	<DragGhost {x} {y}>
+		<div class=" bg-black/80 backdrop-blur-2xl px-2 py-1 rounded-lg w-fit max-w-40">
+			<div class="flex items-center gap-1">
+				<FolderOpen className="size-3.5" strokeWidth="2" />
+				<div class=" text-xs text-white line-clamp-1">
+					{folders[folderId].name}
+				</div>
+			</div>
+		</div>
+	</DragGhost>
+{/if}
+
+<div bind:this={folderElement} class="relative {className}" draggable="true">
+	{#if draggedOver}
+		<div
+			class="absolute top-0 left-0 w-full h-full rounded-sm bg-[hsla(260,85%,65%,0.1)] bg-opacity-50 dark:bg-opacity-10 z-50 pointer-events-none touch-none"
+		></div>
+	{/if}
+
+	<Collapsible
+		bind:open
+		className="w-full"
+		buttonClassName="w-full"
+		hide={(folders[folderId]?.childrenIds ?? []).length === 0 &&
+			(folders[folderId].items?.chats ?? []).length === 0}
+		on:change={(e) => {
+			dispatch('open', e.detail);
+		}}
+	>
+		<!-- svelte-ignore a11y-no-static-element-interactions -->
+		<div class="w-full group">
+			<button
+				id="folder-{folderId}-button"
+				class="relative w-full py-1.5 px-2 rounded-md flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-500 font-medium hover:bg-gray-100 dark:hover:bg-gray-900 transition"
+				on:dblclick={() => {
+					editHandler();
+				}}
+			>
+				<div class="text-gray-300 dark:text-gray-600">
+					{#if open}
+						<ChevronDown className=" size-3" strokeWidth="2.5" />
+					{:else}
+						<ChevronRight className=" size-3" strokeWidth="2.5" />
+					{/if}
+				</div>
+
+				<div class="translate-y-[0.5px] flex-1 justify-start text-start line-clamp-1">
+					{#if edit}
+						<input
+							id="folder-{folderId}-input"
+							type="text"
+							bind:value={name}
+							on:blur={() => {
+								nameUpdateHandler();
+								edit = false;
+							}}
+							on:click={(e) => {
+								// Prevent accidental collapse toggling when clicking inside input
+								e.stopPropagation();
+							}}
+							on:mousedown={(e) => {
+								// Prevent accidental collapse toggling when clicking inside input
+								e.stopPropagation();
+							}}
+							on:keydown={(e) => {
+								if (e.key === 'Enter') {
+									nameUpdateHandler();
+									edit = false;
+								}
+							}}
+							class="w-full h-full bg-transparent text-gray-500 dark:text-gray-500 outline-none"
+						/>
+					{:else}
+						{folders[folderId].name}
+					{/if}
+				</div>
+
+				<button
+					class="absolute z-10 right-2 invisible group-hover:visible self-center flex items-center dark:text-gray-300"
+					on:pointerup={(e) => {
+						e.stopPropagation();
+					}}
+				>
+					<FolderMenu
+						on:rename={() => {
+							editHandler();
+						}}
+						on:delete={() => {
+							showDeleteConfirm = true;
+						}}
+						on:export={() => {
+							exportHandler();
+						}}
+					>
+						<button class="p-0.5 dark:hover:bg-gray-850 rounded-lg touch-auto" on:click={(e) => {}}>
+							<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
+						</button>
+					</FolderMenu>
+				</button>
+			</button>
+		</div>
+
+		<div slot="content" class="w-full">
+			{#if (folders[folderId]?.childrenIds ?? []).length > 0 || (folders[folderId].items?.chats ?? []).length > 0}
+				<div
+					class="ml-3 pl-1 mt-[1px] flex flex-col overflow-y-auto scrollbar-hidden border-s border-gray-100 dark:border-gray-900"
+				>
+					{#if folders[folderId]?.childrenIds}
+						{@const children = folders[folderId]?.childrenIds
+							.map((id) => folders[id])
+							.sort((a, b) =>
+								a.name.localeCompare(b.name, undefined, {
+									numeric: true,
+									sensitivity: 'base'
+								})
+							)}
+
+						{#each children as childFolder (`${folderId}-${childFolder.id}`)}
+							<svelte:self
+								{folders}
+								folderId={childFolder.id}
+								parentDragged={dragged}
+								on:import={(e) => {
+									dispatch('import', e.detail);
+								}}
+								on:update={(e) => {
+									dispatch('update', e.detail);
+								}}
+								on:change={(e) => {
+									dispatch('change', e.detail);
+								}}
+							/>
+						{/each}
+					{/if}
+
+					{#if folders[folderId].items?.chats}
+						{#each folders[folderId].items.chats as chat (chat.id)}
+							<ChatItem
+								id={chat.id}
+								title={chat.title}
+								on:change={(e) => {
+									dispatch('change', e.detail);
+								}}
+							/>
+						{/each}
+					{/if}
+				</div>
+			{/if}
+		</div>
+	</Collapsible>
+</div>
diff --git a/src/lib/components/layout/Sidebar/SearchInput.svelte b/src/lib/components/layout/Sidebar/SearchInput.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5aab853179afe78b2c53faa63fb9f72bdea325cc
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/SearchInput.svelte
@@ -0,0 +1,228 @@
+<script lang="ts">
+	import { getAllTags } from '$lib/apis/chats';
+	import { tags } from '$lib/stores';
+	import { getContext, createEventDispatcher, onMount, onDestroy, tick } from 'svelte';
+	import { fade } from 'svelte/transition';
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	export let placeholder = '';
+	export let value = '';
+
+	let selectedIdx = 0;
+
+	let lastWord = '';
+	$: lastWord = value ? value.split(' ').at(-1) : value;
+
+	let options = [
+		{
+			name: 'tag:',
+			description: $i18n.t('search for tags')
+		}
+	];
+	let focused = false;
+	let loading = false;
+
+	let filteredOptions = options;
+	$: filteredOptions = options.filter((option) => {
+		return option.name.startsWith(lastWord);
+	});
+
+	let filteredTags = [];
+	$: filteredTags = lastWord.startsWith('tag:')
+		? [
+				...$tags,
+				{
+					id: 'none',
+					name: $i18n.t('Untagged')
+				}
+			].filter((tag) => {
+				const tagName = lastWord.slice(4);
+				if (tagName) {
+					const tagId = tagName.replace(' ', '_').toLowerCase();
+
+					if (tag.id !== tagId) {
+						return tag.id.startsWith(tagId);
+					} else {
+						return false;
+					}
+				} else {
+					return true;
+				}
+			})
+		: [];
+
+	const initTags = async () => {
+		loading = true;
+		await tags.set(await getAllTags(localStorage.token));
+		loading = false;
+	};
+
+	const documentClickHandler = (e) => {
+		const searchContainer = document.getElementById('search-container');
+		const chatSearch = document.getElementById('chat-search');
+
+		if (!searchContainer.contains(e.target) && !chatSearch.contains(e.target)) {
+			if (e.target.id.startsWith('search-tag-') || e.target.id.startsWith('search-option-')) {
+				return;
+			}
+			focused = false;
+		}
+	};
+
+	onMount(() => {
+		document.addEventListener('click', documentClickHandler);
+	});
+
+	onDestroy(() => {
+		document.removeEventListener('click', documentClickHandler);
+	});
+</script>
+
+<div class="px-1 mb-1 flex justify-center space-x-2 relative z-10" id="search-container">
+	<div class="flex w-full rounded-xl" id="chat-search">
+		<div class="self-center pl-3 py-2 rounded-l-xl bg-transparent">
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 20 20"
+				fill="currentColor"
+				class="w-4 h-4"
+			>
+				<path
+					fill-rule="evenodd"
+					d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+					clip-rule="evenodd"
+				/>
+			</svg>
+		</div>
+
+		<input
+			class="w-full rounded-r-xl py-1.5 pl-2.5 pr-4 text-sm bg-transparent dark:text-gray-300 outline-none"
+			placeholder={placeholder ? placeholder : $i18n.t('Search')}
+			bind:value
+			on:input={() => {
+				dispatch('input');
+			}}
+			on:focus={() => {
+				focused = true;
+				initTags();
+			}}
+			on:keydown={(e) => {
+				if (e.key === 'Enter') {
+					if (filteredTags.length > 0) {
+						const tagElement = document.getElementById(`search-tag-${selectedIdx}`);
+						tagElement.click();
+						return;
+					}
+
+					if (filteredOptions.length > 0) {
+						const optionElement = document.getElementById(`search-option-${selectedIdx}`);
+						optionElement.click();
+						return;
+					}
+				}
+
+				if (e.key === 'ArrowUp') {
+					e.preventDefault();
+					selectedIdx = Math.max(0, selectedIdx - 1);
+				} else if (e.key === 'ArrowDown') {
+					e.preventDefault();
+
+					if (filteredTags.length > 0) {
+						selectedIdx = Math.min(selectedIdx + 1, filteredTags.length - 1);
+					} else {
+						selectedIdx = Math.min(selectedIdx + 1, filteredOptions.length - 1);
+					}
+				} else {
+					// if the user types something, reset to the top selection.
+					selectedIdx = 0;
+				}
+			}}
+		/>
+	</div>
+
+	{#if focused && (filteredOptions.length > 0 || filteredTags.length > 0)}
+		<!-- svelte-ignore a11y-no-static-element-interactions -->
+		<div
+			class="absolute top-0 mt-8 left-0 right-1 border dark:border-gray-900 bg-gray-50 dark:bg-gray-950 rounded-lg z-10 shadow-lg"
+			in:fade={{ duration: 50 }}
+			on:mouseenter={() => {
+				selectedIdx = null;
+			}}
+			on:mouseleave={() => {
+				selectedIdx = 0;
+			}}
+		>
+			<div class="px-2 py-2 text-xs group">
+				{#if filteredTags.length > 0}
+					<div class="px-1 font-medium dark:text-gray-300 text-gray-700 mb-1">Tags</div>
+
+					<div class="max-h-60 overflow-auto">
+						{#each filteredTags as tag, tagIdx}
+							<button
+								class=" px-1.5 py-0.5 flex gap-1 hover:bg-gray-100 dark:hover:bg-gray-900 w-full rounded {selectedIdx ===
+								tagIdx
+									? 'bg-gray-100 dark:bg-gray-900'
+									: ''}"
+								id="search-tag-{tagIdx}"
+								on:click|stopPropagation={async () => {
+									const words = value.split(' ');
+
+									words.pop();
+									words.push(`tag:${tag.id} `);
+
+									value = words.join(' ');
+
+									dispatch('input');
+								}}
+							>
+								<div
+									class="dark:text-gray-300 text-gray-700 font-medium line-clamp-1 flex-shrink-0"
+								>
+									{tag.name}
+								</div>
+
+								<div class=" text-gray-500 line-clamp-1">
+									{tag.id}
+								</div>
+							</button>
+						{/each}
+					</div>
+				{:else if filteredOptions.length > 0}
+					<div class="px-1 font-medium dark:text-gray-300 text-gray-700 mb-1">
+						{$i18n.t('Search options')}
+					</div>
+
+					<div class=" max-h-60 overflow-auto">
+						{#each filteredOptions as option, optionIdx}
+							<button
+								class=" px-1.5 py-0.5 flex gap-1 hover:bg-gray-100 dark:hover:bg-gray-900 w-full rounded {selectedIdx ===
+								optionIdx
+									? 'bg-gray-100 dark:bg-gray-900'
+									: ''}"
+								id="search-option-{optionIdx}"
+								on:click|stopPropagation={async () => {
+									const words = value.split(' ');
+
+									words.pop();
+									words.push('tag:');
+
+									value = words.join(' ');
+
+									dispatch('input');
+								}}
+							>
+								<div class="dark:text-gray-300 text-gray-700 font-medium">{option.name}</div>
+
+								<div class=" text-gray-500 line-clamp-1">
+									{option.description}
+								</div>
+							</button>
+						{/each}
+					</div>
+				{/if}
+			</div>
+		</div>
+	{/if}
+</div>
diff --git a/src/lib/components/layout/Sidebar/UserMenu.svelte b/src/lib/components/layout/Sidebar/UserMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bddc2115017b7b3797bf17cacf3235b64ba89dac
--- /dev/null
+++ b/src/lib/components/layout/Sidebar/UserMenu.svelte
@@ -0,0 +1,222 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { createEventDispatcher, getContext, onMount } from 'svelte';
+
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { goto } from '$app/navigation';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import { showSettings, activeUserCount, USAGE_POOL, mobile, showSidebar } from '$lib/stores';
+	import { fade, slide } from 'svelte/transition';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { userSignOut } from '$lib/apis/auths';
+
+	const i18n = getContext('i18n');
+
+	export let show = false;
+	export let role = '';
+	export let className = 'max-w-[240px]';
+
+	const dispatch = createEventDispatcher();
+</script>
+
+<DropdownMenu.Root
+	bind:open={show}
+	onOpenChange={(state) => {
+		dispatch('change', state);
+	}}
+>
+	<DropdownMenu.Trigger>
+		<slot />
+	</DropdownMenu.Trigger>
+
+	<slot name="content">
+		<DropdownMenu.Content
+			class="w-full {className} text-sm rounded-xl px-1 py-1.5 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg font-primary"
+			sideOffset={8}
+			side="bottom"
+			align="start"
+			transition={(e) => fade(e, { duration: 100 })}
+		>
+			<button
+				class="flex rounded-md py-2 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+				on:click={async () => {
+					await showSettings.set(true);
+					show = false;
+
+					if ($mobile) {
+						showSidebar.set(false);
+					}
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="1.5"
+						stroke="currentColor"
+						class="w-5 h-5"
+					>
+						<path
+							stroke-linecap="round"
+							stroke-linejoin="round"
+							d="M10.343 3.94c.09-.542.56-.94 1.11-.94h1.093c.55 0 1.02.398 1.11.94l.149.894c.07.424.384.764.78.93.398.164.855.142 1.205-.108l.737-.527a1.125 1.125 0 011.45.12l.773.774c.39.389.44 1.002.12 1.45l-.527.737c-.25.35-.272.806-.107 1.204.165.397.505.71.93.78l.893.15c.543.09.94.56.94 1.109v1.094c0 .55-.397 1.02-.94 1.11l-.893.149c-.425.07-.765.383-.93.78-.165.398-.143.854.107 1.204l.527.738c.32.447.269 1.06-.12 1.45l-.774.773a1.125 1.125 0 01-1.449.12l-.738-.527c-.35-.25-.806-.272-1.203-.107-.397.165-.71.505-.781.929l-.149.894c-.09.542-.56.94-1.11.94h-1.094c-.55 0-1.019-.398-1.11-.94l-.148-.894c-.071-.424-.384-.764-.781-.93-.398-.164-.854-.142-1.204.108l-.738.527c-.447.32-1.06.269-1.45-.12l-.773-.774a1.125 1.125 0 01-.12-1.45l.527-.737c.25-.35.273-.806.108-1.204-.165-.397-.505-.71-.93-.78l-.894-.15c-.542-.09-.94-.56-.94-1.109v-1.094c0-.55.398-1.02.94-1.11l.894-.149c.424-.07.765-.383.93-.78.165-.398.143-.854-.107-1.204l-.527-.738a1.125 1.125 0 01.12-1.45l.773-.773a1.125 1.125 0 011.45-.12l.737.527c.35.25.807.272 1.204.107.397-.165.71-.505.78-.929l.15-.894z"
+						/>
+						<path
+							stroke-linecap="round"
+							stroke-linejoin="round"
+							d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center truncate">{$i18n.t('Settings')}</div>
+			</button>
+
+			<button
+				class="flex rounded-md py-2 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+				on:click={() => {
+					dispatch('show', 'archived-chat');
+					show = false;
+
+					if ($mobile) {
+						showSidebar.set(false);
+					}
+				}}
+			>
+				<div class=" self-center mr-3">
+					<ArchiveBox className="size-5" strokeWidth="1.5" />
+				</div>
+				<div class=" self-center truncate">{$i18n.t('Archived Chats')}</div>
+			</button>
+
+			{#if role === 'admin'}
+				<a
+					class="flex rounded-md py-2 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+					href="/playground"
+					on:click={() => {
+						show = false;
+
+						if ($mobile) {
+							showSidebar.set(false);
+						}
+					}}
+				>
+					<div class=" self-center mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="size-5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M14.25 9.75 16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0 0 20.25 18V6A2.25 2.25 0 0 0 18 3.75H6A2.25 2.25 0 0 0 3.75 6v12A2.25 2.25 0 0 0 6 20.25Z"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center truncate">{$i18n.t('Playground')}</div>
+				</a>
+
+				<a
+					class="flex rounded-md py-2 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+					href="/admin"
+					on:click={() => {
+						show = false;
+
+						if ($mobile) {
+							showSidebar.set(false);
+						}
+					}}
+				>
+					<div class=" self-center mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="w-5 h-5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
+							/>
+						</svg>
+					</div>
+					<div class=" self-center truncate">{$i18n.t('Admin Panel')}</div>
+				</a>
+			{/if}
+
+			<hr class=" border-gray-50 dark:border-gray-850 my-1 p-0" />
+
+			<button
+				class="flex rounded-md py-2 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition"
+				on:click={async () => {
+					await userSignOut();
+					localStorage.removeItem('token');
+					location.href = '/auth';
+					show = false;
+				}}
+			>
+				<div class=" self-center mr-3">
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						viewBox="0 0 20 20"
+						fill="currentColor"
+						class="w-5 h-5"
+					>
+						<path
+							fill-rule="evenodd"
+							d="M3 4.25A2.25 2.25 0 015.25 2h5.5A2.25 2.25 0 0113 4.25v2a.75.75 0 01-1.5 0v-2a.75.75 0 00-.75-.75h-5.5a.75.75 0 00-.75.75v11.5c0 .414.336.75.75.75h5.5a.75.75 0 00.75-.75v-2a.75.75 0 011.5 0v2A2.25 2.25 0 0110.75 18h-5.5A2.25 2.25 0 013 15.75V4.25z"
+							clip-rule="evenodd"
+						/>
+						<path
+							fill-rule="evenodd"
+							d="M6 10a.75.75 0 01.75-.75h9.546l-1.048-.943a.75.75 0 111.004-1.114l2.5 2.25a.75.75 0 010 1.114l-2.5 2.25a.75.75 0 11-1.004-1.114l1.048-.943H6.75A.75.75 0 016 10z"
+							clip-rule="evenodd"
+						/>
+					</svg>
+				</div>
+				<div class=" self-center truncate">{$i18n.t('Sign Out')}</div>
+			</button>
+
+			{#if $activeUserCount}
+				<hr class=" border-gray-50 dark:border-gray-850 my-1 p-0" />
+
+				<Tooltip
+					content={$USAGE_POOL && $USAGE_POOL.length > 0
+						? `${$i18n.t('Running')}: ${$USAGE_POOL.join(', ')} ✨`
+						: ''}
+				>
+					<div class="flex rounded-md py-1.5 px-3 text-xs gap-2.5 items-center">
+						<div class=" flex items-center">
+							<span class="relative flex size-2">
+								<span
+									class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
+								/>
+								<span class="relative inline-flex rounded-full size-2 bg-green-500" />
+							</span>
+						</div>
+
+						<div class=" ">
+							<span class="">
+								{$i18n.t('Active Users')}:
+							</span>
+							<span class=" font-semibold">
+								{$activeUserCount}
+							</span>
+						</div>
+					</div>
+				</Tooltip>
+			{/if}
+
+			<!-- <DropdownMenu.Item class="flex items-center px-3 py-2 text-sm ">
+				<div class="flex items-center">Profile</div>
+			</DropdownMenu.Item> -->
+		</DropdownMenu.Content>
+	</slot>
+</DropdownMenu.Root>
diff --git a/src/lib/components/layout/UpdateInfoToast.svelte b/src/lib/components/layout/UpdateInfoToast.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d7e6b53dd7167d34919c92c051ecceb14b1f6c98
--- /dev/null
+++ b/src/lib/components/layout/UpdateInfoToast.svelte
@@ -0,0 +1,39 @@
+<script lang="ts">
+	import { getContext, createEventDispatcher } from 'svelte';
+
+	const dispatch = createEventDispatcher();
+	const i18n = getContext('i18n');
+
+	import { WEBUI_VERSION } from '$lib/constants';
+	import XMark from '../icons/XMark.svelte';
+
+	export let version = {
+		current: WEBUI_VERSION,
+		latest: WEBUI_VERSION
+	};
+</script>
+
+<div
+	class="flex items-start bg-[#F1F8FE] dark:bg-[#020C1D] border border-[3371D5] dark:border-[#03113B] text-[#3371D5] dark:text-[#6795EC] rounded-lg px-3.5 py-3 text-xs max-w-80 pr-2 w-full shadow-lg"
+>
+	<div class="flex-1 font-medium">
+		{$i18n.t(`A new version (v{{LATEST_VERSION}}) is now available.`, {
+			LATEST_VERSION: version.latest
+		})}
+
+		<a href="https://github.com/open-webui/open-webui/releases" target="_blank" class="underline">
+			{$i18n.t('Update for the latest features and improvements.')}</a
+		>
+	</div>
+
+	<div class=" flex-shrink-0 pr-1">
+		<button
+			class=" hover:text-blue-900 dark:hover:text-blue-300 transition"
+			on:click={() => {
+				dispatch('close');
+			}}
+		>
+			<XMark />
+		</button>
+	</div>
+</div>
diff --git a/src/lib/components/playground/Chat.svelte b/src/lib/components/playground/Chat.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b6c24528313ece7cf2c5b740a1843a4738267b2a
--- /dev/null
+++ b/src/lib/components/playground/Chat.svelte
@@ -0,0 +1,374 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { goto } from '$app/navigation';
+	import { onMount, tick, getContext } from 'svelte';
+
+	import {
+		OLLAMA_API_BASE_URL,
+		OPENAI_API_BASE_URL,
+		WEBUI_API_BASE_URL,
+		WEBUI_BASE_URL
+	} from '$lib/constants';
+	import { WEBUI_NAME, config, user, models, settings } from '$lib/stores';
+
+	import { generateOpenAIChatCompletion } from '$lib/apis/openai';
+
+	import { splitStream } from '$lib/utils';
+	import Collapsible from '../common/Collapsible.svelte';
+
+	import Messages from '$lib/components/playground/Chat/Messages.svelte';
+	import ChevronUp from '../icons/ChevronUp.svelte';
+	import ChevronDown from '../icons/ChevronDown.svelte';
+	import Pencil from '../icons/Pencil.svelte';
+	import Cog6 from '../icons/Cog6.svelte';
+	import Sidebar from '../common/Sidebar.svelte';
+	import ArrowRight from '../icons/ArrowRight.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	let selectedModelId = '';
+	let loading = false;
+	let stopResponseFlag = false;
+
+	let messagesContainerElement: HTMLDivElement;
+
+	let showSystem = false;
+	let showSettings = false;
+
+	let system = '';
+
+	let role = 'user';
+	let message = '';
+
+	let messages = [];
+
+	const scrollToBottom = () => {
+		const element = messagesContainerElement;
+
+		if (element) {
+			element.scrollTop = element?.scrollHeight;
+		}
+	};
+
+	const stopResponse = () => {
+		stopResponseFlag = true;
+		console.log('stopResponse');
+	};
+
+	const chatCompletionHandler = async () => {
+		const model = $models.find((model) => model.id === selectedModelId);
+
+		const [res, controller] = await generateOpenAIChatCompletion(
+			localStorage.token,
+			{
+				model: model.id,
+				stream: true,
+				messages: [
+					system
+						? {
+								role: 'system',
+								content: system
+							}
+						: undefined,
+					...messages
+				].filter((message) => message)
+			},
+			`${WEBUI_BASE_URL}/api`
+		);
+
+		let responseMessage;
+		if (messages.at(-1)?.role === 'assistant') {
+			responseMessage = messages.at(-1);
+		} else {
+			responseMessage = {
+				role: 'assistant',
+				content: ''
+			};
+			messages.push(responseMessage);
+			messages = messages;
+		}
+
+		await tick();
+		const textareaElement = document.getElementById(`assistant-${messages.length - 1}-textarea`);
+
+		if (res && res.ok) {
+			const reader = res.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			while (true) {
+				const { value, done } = await reader.read();
+				if (done || stopResponseFlag) {
+					if (stopResponseFlag) {
+						controller.abort('User: Stop Response');
+					}
+					break;
+				}
+
+				try {
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							console.log(line);
+							if (line === 'data: [DONE]') {
+								// responseMessage.done = true;
+								messages = messages;
+							} else {
+								let data = JSON.parse(line.replace(/^data: /, ''));
+								console.log(data);
+
+								if (responseMessage.content == '' && data.choices[0].delta.content == '\n') {
+									continue;
+								} else {
+									textareaElement.style.height = textareaElement.scrollHeight + 'px';
+
+									responseMessage.content += data.choices[0].delta.content ?? '';
+									messages = messages;
+
+									textareaElement.style.height = textareaElement.scrollHeight + 'px';
+
+									await tick();
+								}
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+				}
+
+				scrollToBottom();
+			}
+		}
+	};
+
+	const addHandler = async () => {
+		if (message) {
+			messages.push({
+				role: role,
+				content: message
+			});
+			messages = messages;
+			message = '';
+			await tick();
+			scrollToBottom();
+		}
+	};
+
+	const submitHandler = async () => {
+		if (selectedModelId) {
+			await addHandler();
+
+			loading = true;
+			await chatCompletionHandler();
+
+			loading = false;
+			stopResponseFlag = false;
+		}
+	};
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			await goto('/');
+		}
+
+		if ($settings?.models) {
+			selectedModelId = $settings?.models[0];
+		} else if ($config?.default_models) {
+			selectedModelId = $config?.default_models.split(',')[0];
+		} else {
+			selectedModelId = '';
+		}
+		loaded = true;
+	});
+</script>
+
+<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
+	<div class="mx-auto w-full md:px-0 h-full relative">
+		<Sidebar bind:show={showSettings} className=" bg-white dark:bg-gray-900" width="300px">
+			<div class="flex flex-col px-5 py-3 text-sm">
+				<div class="flex justify-between items-center mb-2">
+					<div class=" font-medium text-base">Settings</div>
+
+					<div class=" translate-x-1.5">
+						<button
+							class="p-1.5 bg-transparent hover:bg-white/5 transition rounded-lg"
+							on:click={() => {
+								showSettings = !showSettings;
+							}}
+						>
+							<ArrowRight className="size-3" strokeWidth="2.5" />
+						</button>
+					</div>
+				</div>
+
+				<div class="mt-1">
+					<div>
+						<div class=" text-xs font-medium mb-1">Model</div>
+
+						<div class="w-full">
+							<select
+								class="w-full bg-transparent border border-gray-50 dark:border-gray-850 rounded-lg py-1 px-2 -mx-0.5 text-sm outline-none"
+								bind:value={selectedModelId}
+							>
+								{#each $models as model}
+									<option value={model.id} class="bg-gray-50 dark:bg-gray-700">{model.name}</option>
+								{/each}
+							</select>
+						</div>
+					</div>
+				</div>
+			</div>
+		</Sidebar>
+
+		<div class=" flex flex-col h-full px-3.5">
+			<div class="flex w-full items-start gap-1.5">
+				<Collapsible
+					className="w-full flex-1"
+					bind:open={showSystem}
+					buttonClassName="w-full rounded-lg text-sm border border-gray-50 dark:border-gray-850 w-full py-1 px-1.5"
+					grow={true}
+				>
+					<div class="flex gap-2 justify-between items-center">
+						<div class=" flex-shrink-0 font-medium ml-1.5">
+							{$i18n.t('System Instructions')}
+						</div>
+
+						{#if !showSystem}
+							<div class=" flex-1 text-gray-500 line-clamp-1">
+								{system}
+							</div>
+						{/if}
+
+						<div class="flex-shrink-0">
+							<button class="p-1.5 bg-transparent hover:bg-white/5 transition rounded-lg">
+								{#if showSystem}
+									<ChevronUp className="size-3.5" />
+								{:else}
+									<Pencil className="size-3.5" />
+								{/if}
+							</button>
+						</div>
+					</div>
+
+					<div slot="content">
+						<div class="pt-1 px-1.5">
+							<textarea
+								id="system-textarea"
+								class="w-full h-full bg-transparent resize-none outline-none text-sm"
+								bind:value={system}
+								placeholder={$i18n.t("You're a helpful assistant.")}
+								rows="4"
+							/>
+						</div>
+					</div>
+				</Collapsible>
+
+				<div class="translate-y-1">
+					<button
+						class="p-1.5 bg-transparent hover:bg-white/5 transition rounded-lg"
+						on:click={() => {
+							showSettings = !showSettings;
+						}}
+					>
+						<Cog6 />
+					</button>
+				</div>
+			</div>
+
+			<div
+				class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
+				id="messages-container"
+				bind:this={messagesContainerElement}
+			>
+				<div class=" h-full w-full flex flex-col">
+					<div class="flex-1 p-1">
+						<Messages bind:messages />
+					</div>
+				</div>
+			</div>
+
+			<div class="pb-3">
+				<div class="text-xs font-medium text-gray-500 px-2 py-1">
+					{selectedModelId}
+				</div>
+				<div class="border border-gray-50 dark:border-gray-850 w-full px-3 py-2.5 rounded-xl">
+					<div class="py-0.5">
+						<!-- $i18n.t('a user') -->
+						<!-- $i18n.t('an assistant') -->
+						<textarea
+							bind:value={message}
+							class=" w-full h-full bg-transparent resize-none outline-none text-sm"
+							placeholder={$i18n.t(`Enter {{role}} message here`, {
+								role: role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
+							})}
+							on:input={(e) => {
+								e.target.style.height = '';
+								e.target.style.height = Math.min(e.target.scrollHeight, 150) + 'px';
+							}}
+							on:focus={(e) => {
+								e.target.style.height = '';
+								e.target.style.height = Math.min(e.target.scrollHeight, 150) + 'px';
+							}}
+							rows="2"
+						/>
+					</div>
+
+					<div class="flex justify-between">
+						<div>
+							<button
+								class="px-3.5 py-1.5 text-sm font-medium bg-gray-50 hover:bg-gray-100 text-gray-900 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-200 transition rounded-lg"
+								on:click={() => {
+									role = role === 'user' ? 'assistant' : 'user';
+								}}
+							>
+								{#if role === 'user'}
+									{$i18n.t('User')}
+								{:else}
+									{$i18n.t('Assistant')}
+								{/if}
+							</button>
+						</div>
+
+						<div>
+							{#if !loading}
+								<button
+									disabled={message === ''}
+									class="px-3.5 py-1.5 text-sm font-medium disabled:bg-gray-50 dark:disabled:hover:bg-gray-850 disabled:cursor-not-allowed bg-gray-50 hover:bg-gray-100 text-gray-900 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-200 transition rounded-lg"
+									on:click={() => {
+										addHandler();
+										role = role === 'user' ? 'assistant' : 'user';
+									}}
+								>
+									{$i18n.t('Add')}
+								</button>
+
+								<button
+									class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-lg"
+									on:click={() => {
+										submitHandler();
+									}}
+								>
+									{$i18n.t('Run')}
+								</button>
+							{:else}
+								<button
+									class="px-3 py-1.5 text-sm font-medium bg-gray-300 text-black transition rounded-lg"
+									on:click={() => {
+										stopResponse();
+									}}
+								>
+									{$i18n.t('Cancel')}
+								</button>
+							{/if}
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/playground/Chat/Messages.svelte b/src/lib/components/playground/Chat/Messages.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..69574632f6dd51013dbbf75bb35c8f75f68a74cc
--- /dev/null
+++ b/src/lib/components/playground/Chat/Messages.svelte
@@ -0,0 +1,77 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	export let messages = [];
+	let textAreaElement: HTMLTextAreaElement;
+	onMount(() => {
+		messages.forEach((message, idx) => {
+			textAreaElement.style.height = '';
+			textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+		});
+	});
+</script>
+
+<div class="py-3 space-y-3">
+	{#each messages as message, idx}
+		<div class="flex gap-2 group">
+			<div class="flex items-start pt-1">
+				<div
+					class="px-2 py-1 text-sm font-semibold uppercase min-w-[6rem] text-left rounded-lg transition"
+				>
+					{$i18n.t(message.role)}
+				</div>
+			</div>
+
+			<div class="flex-1">
+				<!-- $i18n.t('a user') -->
+				<!-- $i18n.t('an assistant') -->
+				<textarea
+					id="{message.role}-{idx}-textarea"
+					bind:this={textAreaElement}
+					class="w-full bg-transparent outline-none rounded-lg p-2 text-sm resize-none overflow-hidden"
+					placeholder={$i18n.t(`Enter {{role}} message here`, {
+						role: message.role === 'user' ? $i18n.t('a user') : $i18n.t('an assistant')
+					})}
+					rows="1"
+					on:input={(e) => {
+						textAreaElement.style.height = '';
+						textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+					}}
+					on:focus={(e) => {
+						textAreaElement.style.height = '';
+						textAreaElement.style.height = textAreaElement.scrollHeight + 'px';
+
+						// e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
+					}}
+					bind:value={message.content}
+				/>
+			</div>
+
+			<div class=" pt-1">
+				<button
+					class=" group-hover:text-gray-500 dark:text-gray-900 dark:hover:text-gray-300 transition"
+					on:click={() => {
+						messages = messages.filter((message, messageIdx) => messageIdx !== idx);
+					}}
+				>
+					<svg
+						xmlns="http://www.w3.org/2000/svg"
+						fill="none"
+						viewBox="0 0 24 24"
+						stroke-width="2"
+						stroke="currentColor"
+						class="w-5 h-5"
+					>
+						<path
+							stroke-linecap="round"
+							stroke-linejoin="round"
+							d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
+						/>
+					</svg>
+				</button>
+			</div>
+		</div>
+	{/each}
+</div>
diff --git a/src/lib/components/playground/Completions.svelte b/src/lib/components/playground/Completions.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..abc3b8db1695cdf0fab234f8fd05ce16c9af5ded
--- /dev/null
+++ b/src/lib/components/playground/Completions.svelte
@@ -0,0 +1,197 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+
+	import { goto } from '$app/navigation';
+	import { onMount, tick, getContext } from 'svelte';
+
+	import { WEBUI_BASE_URL } from '$lib/constants';
+	import { WEBUI_NAME, config, user, models, settings, showSidebar } from '$lib/stores';
+	import { generateOpenAIChatCompletion } from '$lib/apis/openai';
+
+	import { splitStream } from '$lib/utils';
+	import Selector from '$lib/components/chat/ModelSelector/Selector.svelte';
+	import MenuLines from '../icons/MenuLines.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+	let text = '';
+
+	let selectedModelId = '';
+
+	let loading = false;
+	let stopResponseFlag = false;
+
+	let textCompletionAreaElement: HTMLTextAreaElement;
+
+	const scrollToBottom = () => {
+		const element = textCompletionAreaElement;
+
+		if (element) {
+			element.scrollTop = element?.scrollHeight;
+		}
+	};
+
+	const stopResponse = () => {
+		stopResponseFlag = true;
+		console.log('stopResponse');
+	};
+
+	const textCompletionHandler = async () => {
+		const model = $models.find((model) => model.id === selectedModelId);
+
+		const [res, controller] = await generateOpenAIChatCompletion(
+			localStorage.token,
+			{
+				model: model.id,
+				stream: true,
+				messages: [
+					{
+						role: 'assistant',
+						content: text
+					}
+				]
+			},
+			`${WEBUI_BASE_URL}/api`
+		);
+
+		if (res && res.ok) {
+			const reader = res.body
+				.pipeThrough(new TextDecoderStream())
+				.pipeThrough(splitStream('\n'))
+				.getReader();
+
+			while (true) {
+				const { value, done } = await reader.read();
+				if (done || stopResponseFlag) {
+					if (stopResponseFlag) {
+						controller.abort('User: Stop Response');
+					}
+					break;
+				}
+
+				try {
+					let lines = value.split('\n');
+
+					for (const line of lines) {
+						if (line !== '') {
+							if (line.includes('[DONE]')) {
+								console.log('done');
+							} else {
+								let data = JSON.parse(line.replace(/^data: /, ''));
+								console.log(data);
+
+								text += data.choices[0].delta.content ?? '';
+							}
+						}
+					}
+				} catch (error) {
+					console.log(error);
+				}
+
+				scrollToBottom();
+			}
+		}
+	};
+
+	const submitHandler = async () => {
+		if (selectedModelId) {
+			loading = true;
+			await textCompletionHandler();
+
+			loading = false;
+			stopResponseFlag = false;
+		}
+	};
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			await goto('/');
+		}
+
+		if ($settings?.models) {
+			selectedModelId = $settings?.models[0];
+		} else if ($config?.default_models) {
+			selectedModelId = $config?.default_models.split(',')[0];
+		} else {
+			selectedModelId = '';
+		}
+		loaded = true;
+	});
+</script>
+
+<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
+	<div class="mx-auto w-full md:px-0 h-full">
+		<div class=" flex flex-col h-full px-4">
+			<div class="flex flex-col justify-between mb-1 gap-1">
+				<div class="flex flex-col gap-1 w-full">
+					<div class="flex w-full">
+						<div class="overflow-hidden w-full">
+							<div class="max-w-full">
+								<Selector
+									placeholder={$i18n.t('Select a model')}
+									items={$models.map((model) => ({
+										value: model.id,
+										label: model.name,
+										model: model
+									}))}
+									bind:value={selectedModelId}
+								/>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+
+			<div
+				class=" pt-0.5 pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0"
+				id="messages-container"
+			>
+				<div class=" h-full w-full flex flex-col">
+					<div class="flex-1">
+						<textarea
+							id="text-completion-textarea"
+							bind:this={textCompletionAreaElement}
+							class="w-full h-full p-3 bg-transparent border border-gray-50 dark:border-gray-850 outline-none resize-none rounded-lg text-sm"
+							bind:value={text}
+							placeholder={$i18n.t("You're a helpful assistant.")}
+						/>
+					</div>
+				</div>
+			</div>
+
+			<div class="pb-3 flex justify-end">
+				{#if !loading}
+					<button
+						class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+						on:click={() => {
+							submitHandler();
+						}}
+					>
+						{$i18n.t('Run')}
+					</button>
+				{:else}
+					<button
+						class="px-3 py-1.5 text-sm font-medium bg-gray-300 text-black transition rounded-full"
+						on:click={() => {
+							stopResponse();
+						}}
+					>
+						{$i18n.t('Cancel')}
+					</button>
+				{/if}
+			</div>
+		</div>
+	</div>
+</div>
+
+<style>
+	.scrollbar-hidden::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.scrollbar-hidden {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/playground/Notes.svelte b/src/lib/components/playground/Notes.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9b549e474f04be5a186762755e951598abf3f84f
--- /dev/null
+++ b/src/lib/components/playground/Notes.svelte
@@ -0,0 +1,121 @@
+<script>
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import RichTextInput from '../common/RichTextInput.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import Sparkles from '../icons/Sparkles.svelte';
+	import SparklesSolid from '../icons/SparklesSolid.svelte';
+	import Mic from '../icons/Mic.svelte';
+	import VoiceRecording from '../chat/MessageInput/VoiceRecording.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import { toast } from 'svelte-sonner';
+
+	let name = '';
+	let content = '';
+
+	let voiceInput = false;
+	let loading = false;
+</script>
+
+<div class="relative flex-1 w-full h-full flex justify-center overflow-auto px-5">
+	{#if loading}
+		<div class=" absolute top-0 bottom-0 left-0 right-0 flex">
+			<div class="m-auto">
+				<Spinner />
+			</div>
+		</div>
+	{/if}
+
+	<div class=" w-full flex flex-col gap-2 {loading ? 'opacity-20' : ''}">
+		<div class="flex-shrink-0 w-full flex justify-between items-center">
+			<div class="w-full">
+				<input
+					class="w-full text-2xl font-medium bg-transparent outline-none"
+					type="text"
+					bind:value={name}
+					placeholder={$i18n.t('Title')}
+					required
+				/>
+			</div>
+		</div>
+
+		<div class=" flex-1 w-full h-full">
+			<RichTextInput
+				className=" input-prose-sm"
+				bind:value={content}
+				placeholder={$i18n.t('Write something...')}
+			/>
+		</div>
+	</div>
+
+	<div class="absolute bottom-0 left-0 right-0 p-5 max-w-full flex justify-end">
+		<div class="flex gap-0.5 justify-end w-full">
+			{#if voiceInput}
+				<div class="flex-1 w-full">
+					<VoiceRecording
+						bind:recording={voiceInput}
+						className="p-1 w-full max-w-full"
+						on:cancel={() => {
+							voiceInput = false;
+						}}
+						on:confirm={(e) => {
+							const { text, filename } = e.detail;
+
+							// url is hostname + /cache/audio/transcription/ + filename
+							const url = `${window.location.origin}/cache/audio/transcription/${filename}`;
+
+							// Open in new tab
+
+							if (content.trim() !== '') {
+								content = `${content}\n\n${text}\n\nRecording: ${url}\n\n`;
+							} else {
+								content = `${content}${text}\n\nRecording: ${url}\n\n`;
+							}
+
+							voiceInput = false;
+						}}
+					/>
+				</div>
+			{:else}
+				<Tooltip content={$i18n.t('Voice Input')}>
+					<button
+						class="cursor-pointer p-2.5 flex rounded-full hover:bg-gray-100 dark:hover:bg-gray-850 transition shadow-xl"
+						type="button"
+						on:click={async () => {
+							try {
+								let stream = await navigator.mediaDevices
+									.getUserMedia({ audio: true })
+									.catch(function (err) {
+										toast.error(
+											$i18n.t(`Permission denied when accessing microphone: {{error}}`, {
+												error: err
+											})
+										);
+										return null;
+									});
+
+								if (stream) {
+									voiceInput = true;
+									const tracks = stream.getTracks();
+									tracks.forEach((track) => track.stop());
+								}
+								stream = null;
+							} catch {
+								toast.error($i18n.t('Permission denied when accessing microphone'));
+							}
+						}}
+					>
+						<Mic className="size-4" />
+					</button>
+				</Tooltip>
+			{/if}
+
+			<!-- <button
+				class="cursor-pointer p-2.5 flex rounded-full hover:bg-gray-100 dark:hover:bg-gray-850 transition shadow-xl"
+			>
+				<SparklesSolid className="size-4" />
+			</button> -->
+		</div>
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Knowledge.svelte b/src/lib/components/workspace/Knowledge.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c5ea4ad7ff76f878731d7353e0978bd6340f6e20
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge.svelte
@@ -0,0 +1,197 @@
+<script lang="ts">
+	import Fuse from 'fuse.js';
+
+	import dayjs from 'dayjs';
+	import relativeTime from 'dayjs/plugin/relativeTime';
+	dayjs.extend(relativeTime);
+
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { WEBUI_NAME, knowledge } from '$lib/stores';
+	import {
+		getKnowledgeBases,
+		deleteKnowledgeById,
+		getKnowledgeBaseList
+	} from '$lib/apis/knowledge';
+
+	import { goto } from '$app/navigation';
+
+	import DeleteConfirmDialog from '../common/ConfirmDialog.svelte';
+	import ItemMenu from './Knowledge/ItemMenu.svelte';
+	import Badge from '../common/Badge.svelte';
+	import Search from '../icons/Search.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import { capitalizeFirstLetter } from '$lib/utils';
+	import Tooltip from '../common/Tooltip.svelte';
+
+	let loaded = false;
+
+	let query = '';
+	let selectedItem = null;
+	let showDeleteConfirm = false;
+
+	let fuse = null;
+
+	let knowledgeBases = [];
+	let filteredItems = [];
+
+	$: if (knowledgeBases) {
+		fuse = new Fuse(knowledgeBases, {
+			keys: ['name', 'description']
+		});
+	}
+
+	$: if (fuse) {
+		filteredItems = query
+			? fuse.search(query).map((e) => {
+					return e.item;
+				})
+			: knowledgeBases;
+	}
+
+	const deleteHandler = async (item) => {
+		const res = await deleteKnowledgeById(localStorage.token, item.id).catch((e) => {
+			toast.error(e);
+		});
+
+		if (res) {
+			knowledgeBases = await getKnowledgeBaseList(localStorage.token);
+			knowledge.set(await getKnowledgeBases(localStorage.token));
+			toast.success($i18n.t('Knowledge deleted successfully.'));
+		}
+	};
+
+	onMount(async () => {
+		knowledgeBases = await getKnowledgeBaseList(localStorage.token);
+		loaded = true;
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Knowledge')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<DeleteConfirmDialog
+		bind:show={showDeleteConfirm}
+		on:confirm={() => {
+			deleteHandler(selectedItem);
+		}}
+	/>
+
+	<div class="flex flex-col gap-1 my-1.5">
+		<div class="flex justify-between items-center">
+			<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
+				{$i18n.t('Knowledge')}
+				<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+				<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
+					>{filteredItems.length}</span
+				>
+			</div>
+		</div>
+
+		<div class=" flex w-full space-x-2">
+			<div class="flex flex-1">
+				<div class=" self-center ml-1 mr-3">
+					<Search className="size-3.5" />
+				</div>
+				<input
+					class=" w-full text-sm py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={query}
+					placeholder={$i18n.t('Search Knowledge')}
+				/>
+			</div>
+
+			<div>
+				<button
+					class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+					aria-label={$i18n.t('Create Knowledge')}
+					on:click={() => {
+						goto('/workspace/knowledge/create');
+					}}
+				>
+					<Plus className="size-3.5" />
+				</button>
+			</div>
+		</div>
+	</div>
+
+	<div class="mb-5 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-2">
+		{#each filteredItems as item}
+			<button
+				class=" flex space-x-4 cursor-pointer text-left w-full px-3 py-2 hover:bg-gray-50 dark:hover:bg-gray-850 transition rounded-xl"
+				on:click={() => {
+					if (item?.meta?.document) {
+						toast.error(
+							$i18n.t(
+								'Only collections can be edited, create a new knowledge base to edit/add documents.'
+							)
+						);
+					} else {
+						goto(`/workspace/knowledge/${item.id}`);
+					}
+				}}
+			>
+				<div class=" w-full">
+					<div class="flex items-center justify-between -mt-1">
+						{#if item?.meta?.document}
+							<Badge type="muted" content={$i18n.t('Document')} />
+						{:else}
+							<Badge type="success" content={$i18n.t('Collection')} />
+						{/if}
+
+						<div class=" flex self-center -mr-1 translate-y-1">
+							<ItemMenu
+								on:delete={() => {
+									selectedItem = item;
+									showDeleteConfirm = true;
+								}}
+							/>
+						</div>
+					</div>
+
+					<div class=" self-center flex-1 px-1 mb-1">
+						<div class=" font-semibold line-clamp-1 h-fit">{item.name}</div>
+
+						<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
+							{item.description}
+						</div>
+
+						<div class="mt-3 flex justify-between">
+							<div class="text-xs text-gray-500">
+								<Tooltip
+									content={item?.user?.email ?? $i18n.t('Deleted User')}
+									className="flex shrink-0"
+									placement="top-start"
+								>
+									{$i18n.t('By {{name}}', {
+										name: capitalizeFirstLetter(
+											item?.user?.name ?? item?.user?.email ?? $i18n.t('Deleted User')
+										)
+									})}
+								</Tooltip>
+							</div>
+							<div class=" text-xs text-gray-500 line-clamp-1">
+								{$i18n.t('Updated')}
+								{dayjs(item.updated_at * 1000).fromNow()}
+							</div>
+						</div>
+					</div>
+				</div>
+			</button>
+		{/each}
+	</div>
+
+	<div class=" text-gray-500 text-xs mt-1 mb-2">
+		ⓘ {$i18n.t("Use '#' in the prompt input to load and include your knowledge.")}
+	</div>
+{:else}
+	<div class="w-full h-full flex justify-center items-center">
+		<Spinner />
+	</div>
+{/if}
diff --git a/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5d1e79808e2f07bd6f8d0ea7002afe558a5fd442
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte
@@ -0,0 +1,161 @@
+<script>
+	import { goto } from '$app/navigation';
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { createNewKnowledge, getKnowledgeBases } from '$lib/apis/knowledge';
+	import { toast } from 'svelte-sonner';
+	import { knowledge } from '$lib/stores';
+	import AccessControl from '../common/AccessControl.svelte';
+
+	let loading = false;
+
+	let name = '';
+	let description = '';
+	let accessControl = null;
+
+	const submitHandler = async () => {
+		loading = true;
+
+		if (name.trim() === '' || description.trim() === '') {
+			toast.error($i18n.t('Please fill in all fields.'));
+			name = '';
+			description = '';
+			loading = false;
+			return;
+		}
+
+		const res = await createNewKnowledge(
+			localStorage.token,
+			name,
+			description,
+			accessControl
+		).catch((e) => {
+			toast.error(e);
+		});
+
+		if (res) {
+			toast.success($i18n.t('Knowledge created successfully.'));
+			knowledge.set(await getKnowledgeBases(localStorage.token));
+			goto(`/workspace/knowledge/${res.id}`);
+		}
+
+		loading = false;
+	};
+</script>
+
+<div class="w-full max-h-full">
+	<button
+		class="flex space-x-1"
+		on:click={() => {
+			goto('/workspace/knowledge');
+		}}
+	>
+		<div class=" self-center">
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 20 20"
+				fill="currentColor"
+				class="w-4 h-4"
+			>
+				<path
+					fill-rule="evenodd"
+					d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
+					clip-rule="evenodd"
+				/>
+			</svg>
+		</div>
+		<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
+	</button>
+
+	<form
+		class="flex flex-col max-w-lg mx-auto mt-10 mb-10"
+		on:submit|preventDefault={() => {
+			submitHandler();
+		}}
+	>
+		<div class=" w-full flex flex-col justify-center">
+			<div class=" text-2xl font-medium font-primary mb-2.5">
+				{$i18n.t('Create a knowledge base')}
+			</div>
+
+			<div class="w-full flex flex-col gap-2.5">
+				<div class="w-full">
+					<div class=" text-sm mb-2">{$i18n.t('What are you working on?')}</div>
+
+					<div class="w-full mt-1">
+						<input
+							class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							type="text"
+							bind:value={name}
+							placeholder={$i18n.t('Name your knowledge base')}
+							required
+						/>
+					</div>
+				</div>
+
+				<div>
+					<div class="text-sm mb-2">{$i18n.t('What are you trying to achieve?')}</div>
+
+					<div class=" w-full mt-1">
+						<textarea
+							class="w-full resize-none rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
+							rows="4"
+							bind:value={description}
+							placeholder={$i18n.t('Describe your knowledge base and objectives')}
+							required
+						/>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class="mt-2">
+			<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
+				<AccessControl bind:accessControl />
+			</div>
+		</div>
+
+		<div class="flex justify-end mt-2">
+			<div>
+				<button
+					class=" text-sm px-4 py-2 transition rounded-lg {loading
+						? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
+						: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800'} flex"
+					type="submit"
+					disabled={loading}
+				>
+					<div class=" self-center font-medium">{$i18n.t('Create Knowledge')}</div>
+
+					{#if loading}
+						<div class="ml-1.5 self-center">
+							<svg
+								class=" w-4 h-4"
+								viewBox="0 0 24 24"
+								fill="currentColor"
+								xmlns="http://www.w3.org/2000/svg"
+								><style>
+									.spinner_ajPY {
+										transform-origin: center;
+										animation: spinner_AtaB 0.75s infinite linear;
+									}
+									@keyframes spinner_AtaB {
+										100% {
+											transform: rotate(360deg);
+										}
+									}
+								</style><path
+									d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+									opacity=".25"
+								/><path
+									d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+									class="spinner_ajPY"
+								/></svg
+							>
+						</div>
+					{/if}
+				</button>
+			</div>
+		</div>
+	</form>
+</div>
diff --git a/src/lib/components/workspace/Knowledge/ItemMenu.svelte b/src/lib/components/workspace/Knowledge/ItemMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b4d448b1580b9ace44e22ddb71b6dddcfccf066e
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/ItemMenu.svelte
@@ -0,0 +1,69 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+	import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
+	import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let onClose: Function = () => {};
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+	align="end"
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot
+			><button
+				class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+				type="button"
+				on:click={(e) => {
+					e.stopPropagation();
+					show = true;
+				}}
+			>
+				<EllipsisHorizontal className="size-5" />
+			</button>
+		</slot>
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="end"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('delete');
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cabcff57138ea4e1cb5747d5cad6095a4745896d
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase.svelte
@@ -0,0 +1,871 @@
+<script lang="ts">
+	import Fuse from 'fuse.js';
+	import { toast } from 'svelte-sonner';
+	import { v4 as uuidv4 } from 'uuid';
+	import { PaneGroup, Pane, PaneResizer } from 'paneforge';
+
+	import { onMount, getContext, onDestroy, tick } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { mobile, showSidebar, knowledge as _knowledge } from '$lib/stores';
+
+	import { updateFileDataContentById, uploadFile } from '$lib/apis/files';
+	import {
+		addFileToKnowledgeById,
+		getKnowledgeById,
+		getKnowledgeBases,
+		removeFileFromKnowledgeById,
+		resetKnowledgeById,
+		updateFileFromKnowledgeById,
+		updateKnowledgeById
+	} from '$lib/apis/knowledge';
+
+	import { transcribeAudio } from '$lib/apis/audio';
+	import { blobToFile } from '$lib/utils';
+	import { processFile } from '$lib/apis/retrieval';
+
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import Files from './KnowledgeBase/Files.svelte';
+	import AddFilesPlaceholder from '$lib/components/AddFilesPlaceholder.svelte';
+
+	import AddContentMenu from './KnowledgeBase/AddContentMenu.svelte';
+	import AddTextContentModal from './KnowledgeBase/AddTextContentModal.svelte';
+
+	import SyncConfirmDialog from '../../common/ConfirmDialog.svelte';
+	import RichTextInput from '$lib/components/common/RichTextInput.svelte';
+	import EllipsisVertical from '$lib/components/icons/EllipsisVertical.svelte';
+	import Drawer from '$lib/components/common/Drawer.svelte';
+	import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
+	import LockClosed from '$lib/components/icons/LockClosed.svelte';
+	import AccessControlModal from '../common/AccessControlModal.svelte';
+
+	let largeScreen = true;
+
+	let pane;
+	let showSidepanel = true;
+	let minSize = 0;
+
+	type Knowledge = {
+		id: string;
+		name: string;
+		description: string;
+		data: {
+			file_ids: string[];
+		};
+		files: any[];
+	};
+
+	let id = null;
+	let knowledge: Knowledge | null = null;
+	let query = '';
+
+	let showAddTextContentModal = false;
+	let showSyncConfirmModal = false;
+	let showAccessControlModal = false;
+
+	let inputFiles = null;
+
+	let filteredItems = [];
+	$: if (knowledge && knowledge.files) {
+		fuse = new Fuse(knowledge.files, {
+			keys: ['meta.name', 'meta.description']
+		});
+	}
+
+	$: if (fuse) {
+		filteredItems = query
+			? fuse.search(query).map((e) => {
+					return e.item;
+				})
+			: (knowledge?.files ?? []);
+	}
+
+	let selectedFile = null;
+	let selectedFileId = null;
+
+	$: if (selectedFileId) {
+		const file = (knowledge?.files ?? []).find((file) => file.id === selectedFileId);
+		if (file) {
+			file.data = file.data ?? { content: '' };
+			selectedFile = file;
+		} else {
+			selectedFile = null;
+		}
+	} else {
+		selectedFile = null;
+	}
+
+	let fuse = null;
+	let debounceTimeout = null;
+	let mediaQuery;
+	let dragged = false;
+
+	const createFileFromText = (name, content) => {
+		const blob = new Blob([content], { type: 'text/plain' });
+		const file = blobToFile(blob, `${name}.txt`);
+
+		console.log(file);
+		return file;
+	};
+
+	const uploadFileHandler = async (file) => {
+		console.log(file);
+
+		const tempItemId = uuidv4();
+		const fileItem = {
+			type: 'file',
+			file: '',
+			id: null,
+			url: '',
+			name: file.name,
+			size: file.size,
+			status: 'uploading',
+			error: '',
+			itemId: tempItemId
+		};
+
+		if (fileItem.size == 0) {
+			toast.error($i18n.t('You cannot upload an empty file.'));
+			return null;
+		}
+
+		knowledge.files = [...(knowledge.files ?? []), fileItem];
+
+		// Check if the file is an audio file and transcribe/convert it to text file
+		if (['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/x-m4a'].includes(file['type'])) {
+			const res = await transcribeAudio(localStorage.token, file).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (res) {
+				console.log(res);
+				const blob = new Blob([res.text], { type: 'text/plain' });
+				file = blobToFile(blob, `${file.name}.txt`);
+			}
+		}
+
+		try {
+			const uploadedFile = await uploadFile(localStorage.token, file).catch((e) => {
+				toast.error(e);
+				return null;
+			});
+
+			if (uploadedFile) {
+				console.log(uploadedFile);
+				knowledge.files = knowledge.files.map((item) => {
+					if (item.itemId === tempItemId) {
+						item.id = uploadedFile.id;
+					}
+
+					// Remove temporary item id
+					delete item.itemId;
+					return item;
+				});
+				await addFileHandler(uploadedFile.id);
+			} else {
+				toast.error($i18n.t('Failed to upload file.'));
+			}
+		} catch (e) {
+			toast.error(e);
+		}
+	};
+
+	const uploadDirectoryHandler = async () => {
+		// Check if File System Access API is supported
+		const isFileSystemAccessSupported = 'showDirectoryPicker' in window;
+
+		try {
+			if (isFileSystemAccessSupported) {
+				// Modern browsers (Chrome, Edge) implementation
+				await handleModernBrowserUpload();
+			} else {
+				// Firefox fallback
+				await handleFirefoxUpload();
+			}
+		} catch (error) {
+			handleUploadError(error);
+		}
+	};
+
+	// Helper function to check if a path contains hidden folders
+	const hasHiddenFolder = (path) => {
+		return path.split('/').some((part) => part.startsWith('.'));
+	};
+
+	// Modern browsers implementation using File System Access API
+	const handleModernBrowserUpload = async () => {
+		const dirHandle = await window.showDirectoryPicker();
+		let totalFiles = 0;
+		let uploadedFiles = 0;
+
+		// Function to update the UI with the progress
+		const updateProgress = () => {
+			const percentage = (uploadedFiles / totalFiles) * 100;
+			toast.info(`Upload Progress: ${uploadedFiles}/${totalFiles} (${percentage.toFixed(2)}%)`);
+		};
+
+		// Recursive function to count all files excluding hidden ones
+		async function countFiles(dirHandle) {
+			for await (const entry of dirHandle.values()) {
+				// Skip hidden files and directories
+				if (entry.name.startsWith('.')) continue;
+
+				if (entry.kind === 'file') {
+					totalFiles++;
+				} else if (entry.kind === 'directory') {
+					// Only process non-hidden directories
+					if (!entry.name.startsWith('.')) {
+						await countFiles(entry);
+					}
+				}
+			}
+		}
+
+		// Recursive function to process directories excluding hidden files and folders
+		async function processDirectory(dirHandle, path = '') {
+			for await (const entry of dirHandle.values()) {
+				// Skip hidden files and directories
+				if (entry.name.startsWith('.')) continue;
+
+				const entryPath = path ? `${path}/${entry.name}` : entry.name;
+
+				// Skip if the path contains any hidden folders
+				if (hasHiddenFolder(entryPath)) continue;
+
+				if (entry.kind === 'file') {
+					const file = await entry.getFile();
+					const fileWithPath = new File([file], entryPath, { type: file.type });
+
+					await uploadFileHandler(fileWithPath);
+					uploadedFiles++;
+					updateProgress();
+				} else if (entry.kind === 'directory') {
+					// Only process non-hidden directories
+					if (!entry.name.startsWith('.')) {
+						await processDirectory(entry, entryPath);
+					}
+				}
+			}
+		}
+
+		await countFiles(dirHandle);
+		updateProgress();
+
+		if (totalFiles > 0) {
+			await processDirectory(dirHandle);
+		} else {
+			console.log('No files to upload.');
+		}
+	};
+
+	// Firefox fallback implementation using traditional file input
+	const handleFirefoxUpload = async () => {
+		return new Promise((resolve, reject) => {
+			// Create hidden file input
+			const input = document.createElement('input');
+			input.type = 'file';
+			input.webkitdirectory = true;
+			input.directory = true;
+			input.multiple = true;
+			input.style.display = 'none';
+
+			// Add input to DOM temporarily
+			document.body.appendChild(input);
+
+			input.onchange = async () => {
+				try {
+					const files = Array.from(input.files)
+						// Filter out files from hidden folders
+						.filter((file) => !hasHiddenFolder(file.webkitRelativePath));
+
+					let totalFiles = files.length;
+					let uploadedFiles = 0;
+
+					// Function to update the UI with the progress
+					const updateProgress = () => {
+						const percentage = (uploadedFiles / totalFiles) * 100;
+						toast.info(
+							`Upload Progress: ${uploadedFiles}/${totalFiles} (${percentage.toFixed(2)}%)`
+						);
+					};
+
+					updateProgress();
+
+					// Process all files
+					for (const file of files) {
+						// Skip hidden files (additional check)
+						if (!file.name.startsWith('.')) {
+							const relativePath = file.webkitRelativePath || file.name;
+							const fileWithPath = new File([file], relativePath, { type: file.type });
+
+							await uploadFileHandler(fileWithPath);
+							uploadedFiles++;
+							updateProgress();
+						}
+					}
+
+					// Clean up
+					document.body.removeChild(input);
+					resolve();
+				} catch (error) {
+					reject(error);
+				}
+			};
+
+			input.onerror = (error) => {
+				document.body.removeChild(input);
+				reject(error);
+			};
+
+			// Trigger file picker
+			input.click();
+		});
+	};
+
+	// Error handler
+	const handleUploadError = (error) => {
+		if (error.name === 'AbortError') {
+			toast.info('Directory selection was cancelled');
+		} else {
+			toast.error('Error accessing directory');
+			console.error('Directory access error:', error);
+		}
+	};
+
+	// Helper function to maintain file paths within zip
+	const syncDirectoryHandler = async () => {
+		if ((knowledge?.files ?? []).length > 0) {
+			const res = await resetKnowledgeById(localStorage.token, id).catch((e) => {
+				toast.error(e);
+			});
+
+			if (res) {
+				knowledge = res;
+				toast.success($i18n.t('Knowledge reset successfully.'));
+
+				// Upload directory
+				uploadDirectoryHandler();
+			}
+		} else {
+			uploadDirectoryHandler();
+		}
+	};
+
+	const addFileHandler = async (fileId) => {
+		const updatedKnowledge = await addFileToKnowledgeById(localStorage.token, id, fileId).catch(
+			(e) => {
+				toast.error(e);
+				return null;
+			}
+		);
+
+		if (updatedKnowledge) {
+			knowledge = updatedKnowledge;
+			toast.success($i18n.t('File added successfully.'));
+		} else {
+			toast.error($i18n.t('Failed to add file.'));
+			knowledge.files = knowledge.files.filter((file) => file.id !== fileId);
+		}
+	};
+
+	const deleteFileHandler = async (fileId) => {
+		const updatedKnowledge = await removeFileFromKnowledgeById(
+			localStorage.token,
+			id,
+			fileId
+		).catch((e) => {
+			toast.error(e);
+		});
+
+		if (updatedKnowledge) {
+			knowledge = updatedKnowledge;
+			toast.success($i18n.t('File removed successfully.'));
+		}
+	};
+
+	const updateFileContentHandler = async () => {
+		const fileId = selectedFile.id;
+		const content = selectedFile.data.content;
+
+		const res = updateFileDataContentById(localStorage.token, fileId, content).catch((e) => {
+			toast.error(e);
+		});
+
+		const updatedKnowledge = await updateFileFromKnowledgeById(
+			localStorage.token,
+			id,
+			fileId
+		).catch((e) => {
+			toast.error(e);
+		});
+
+		if (res && updatedKnowledge) {
+			knowledge = updatedKnowledge;
+			toast.success($i18n.t('File content updated successfully.'));
+		}
+	};
+
+	const changeDebounceHandler = () => {
+		console.log('debounce');
+		if (debounceTimeout) {
+			clearTimeout(debounceTimeout);
+		}
+
+		debounceTimeout = setTimeout(async () => {
+			if (knowledge.name.trim() === '' || knowledge.description.trim() === '') {
+				toast.error($i18n.t('Please fill in all fields.'));
+				return;
+			}
+
+			const res = await updateKnowledgeById(localStorage.token, id, {
+				...knowledge,
+				name: knowledge.name,
+				description: knowledge.description,
+				access_control: knowledge.access_control
+			}).catch((e) => {
+				toast.error(e);
+			});
+
+			if (res) {
+				toast.success($i18n.t('Knowledge updated successfully'));
+				_knowledge.set(await getKnowledgeBases(localStorage.token));
+			}
+		}, 1000);
+	};
+
+	const handleMediaQuery = async (e) => {
+		if (e.matches) {
+			largeScreen = true;
+		} else {
+			largeScreen = false;
+		}
+	};
+
+	const onDragOver = (e) => {
+		e.preventDefault();
+
+		// Check if a file is being draggedOver.
+		if (e.dataTransfer?.types?.includes('Files')) {
+			dragged = true;
+		} else {
+			dragged = false;
+		}
+	};
+
+	const onDragLeave = () => {
+		dragged = false;
+	};
+
+	const onDrop = async (e) => {
+		e.preventDefault();
+		dragged = false;
+
+		if (e.dataTransfer?.types?.includes('Files')) {
+			if (e.dataTransfer?.files) {
+				const inputFiles = e.dataTransfer?.files;
+
+				if (inputFiles && inputFiles.length > 0) {
+					for (const file of inputFiles) {
+						await uploadFileHandler(file);
+					}
+				} else {
+					toast.error($i18n.t(`File not found.`));
+				}
+			}
+		}
+	};
+
+	onMount(async () => {
+		// listen to resize 1024px
+		mediaQuery = window.matchMedia('(min-width: 1024px)');
+
+		mediaQuery.addEventListener('change', handleMediaQuery);
+		handleMediaQuery(mediaQuery);
+
+		// Select the container element you want to observe
+		const container = document.getElementById('collection-container');
+
+		// initialize the minSize based on the container width
+		minSize = !largeScreen ? 100 : Math.floor((300 / container.clientWidth) * 100);
+
+		// Create a new ResizeObserver instance
+		const resizeObserver = new ResizeObserver((entries) => {
+			for (let entry of entries) {
+				const width = entry.contentRect.width;
+				// calculate the percentage of 300
+				const percentage = (300 / width) * 100;
+				// set the minSize to the percentage, must be an integer
+				minSize = !largeScreen ? 100 : Math.floor(percentage);
+
+				if (showSidepanel) {
+					if (pane && pane.isExpanded() && pane.getSize() < minSize) {
+						pane.resize(minSize);
+					}
+				}
+			}
+		});
+
+		// Start observing the container's size changes
+		resizeObserver.observe(container);
+
+		if (pane) {
+			pane.expand();
+		}
+
+		id = $page.params.id;
+
+		const res = await getKnowledgeById(localStorage.token, id).catch((e) => {
+			toast.error(e);
+			return null;
+		});
+
+		if (res) {
+			knowledge = res;
+		} else {
+			goto('/workspace/knowledge');
+		}
+
+		const dropZone = document.querySelector('body');
+		dropZone?.addEventListener('dragover', onDragOver);
+		dropZone?.addEventListener('drop', onDrop);
+		dropZone?.addEventListener('dragleave', onDragLeave);
+	});
+
+	onDestroy(() => {
+		mediaQuery?.removeEventListener('change', handleMediaQuery);
+		const dropZone = document.querySelector('body');
+		dropZone?.removeEventListener('dragover', onDragOver);
+		dropZone?.removeEventListener('drop', onDrop);
+		dropZone?.removeEventListener('dragleave', onDragLeave);
+	});
+</script>
+
+{#if dragged}
+	<div
+		class="fixed {$showSidebar
+			? 'left-0 md:left-[260px] md:w-[calc(100%-260px)]'
+			: 'left-0'}  w-full h-full flex z-50 touch-none pointer-events-none"
+		id="dropzone"
+		role="region"
+		aria-label="Drag and Drop Container"
+	>
+		<div class="absolute w-full h-full backdrop-blur bg-gray-800/40 flex justify-center">
+			<div class="m-auto pt-64 flex flex-col justify-center">
+				<div class="max-w-md">
+					<AddFilesPlaceholder>
+						<div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
+							Drop any files here to add to my documents
+						</div>
+					</AddFilesPlaceholder>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
+
+<SyncConfirmDialog
+	bind:show={showSyncConfirmModal}
+	message={$i18n.t(
+		'This will reset the knowledge base and sync all files. Do you wish to continue?'
+	)}
+	on:confirm={() => {
+		syncDirectoryHandler();
+	}}
+/>
+
+<AddTextContentModal
+	bind:show={showAddTextContentModal}
+	on:submit={(e) => {
+		const file = createFileFromText(e.detail.name, e.detail.content);
+		uploadFileHandler(file);
+	}}
+/>
+
+<input
+	id="files-input"
+	bind:files={inputFiles}
+	type="file"
+	multiple
+	hidden
+	on:change={async () => {
+		if (inputFiles && inputFiles.length > 0) {
+			for (const file of inputFiles) {
+				await uploadFileHandler(file);
+			}
+
+			inputFiles = null;
+			const fileInputElement = document.getElementById('files-input');
+
+			if (fileInputElement) {
+				fileInputElement.value = '';
+			}
+		} else {
+			toast.error($i18n.t(`File not found.`));
+		}
+	}}
+/>
+
+<div class="flex flex-col w-full translate-y-1" id="collection-container">
+	{#if id && knowledge}
+		<AccessControlModal
+			bind:show={showAccessControlModal}
+			bind:accessControl={knowledge.access_control}
+			onChange={() => {
+				changeDebounceHandler();
+			}}
+		/>
+		<div class="w-full mb-2.5">
+			<div class=" flex w-full">
+				<div class="flex-1">
+					<div class="flex items-center justify-between w-full px-0.5 mb-1">
+						<div class="w-full">
+							<input
+								type="text"
+								class="text-left w-full font-semibold text-2xl font-primary bg-transparent outline-none"
+								bind:value={knowledge.name}
+								placeholder="Knowledge Name"
+								on:input={() => {
+									changeDebounceHandler();
+								}}
+							/>
+						</div>
+
+						<div class="self-center flex-shrink-0">
+							<button
+								class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
+								type="button"
+								on:click={() => {
+									showAccessControlModal = true;
+								}}
+							>
+								<LockClosed strokeWidth="2.5" className="size-3.5" />
+
+								<div class="text-sm font-medium flex-shrink-0">
+									{$i18n.t('Access')}
+								</div>
+							</button>
+						</div>
+					</div>
+
+					<div class="flex w-full px-1">
+						<input
+							type="text"
+							class="text-left text-xs w-full text-gray-500 bg-transparent outline-none"
+							bind:value={knowledge.description}
+							placeholder="Knowledge Description"
+							on:input={() => {
+								changeDebounceHandler();
+							}}
+						/>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class="flex flex-row flex-1 h-full max-h-full pb-2.5 gap-3">
+			{#if largeScreen}
+				<div class="flex-1 flex justify-start w-full h-full max-h-full">
+					{#if selectedFile}
+						<div class=" flex flex-col w-full h-full max-h-full">
+							<div class="flex-shrink-0 mb-2 flex items-center">
+								{#if !showSidepanel}
+									<div class="-translate-x-2">
+										<button
+											class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
+											on:click={() => {
+												pane.expand();
+											}}
+										>
+											<ChevronLeft strokeWidth="2.5" />
+										</button>
+									</div>
+								{/if}
+
+								<div class=" flex-1 text-xl font-medium">
+									<a
+										class="hover:text-gray-500 hover:dark:text-gray-100 hover:underline flex-grow line-clamp-1"
+										href={selectedFile.id ? `/api/v1/files/${selectedFile.id}/content` : '#'}
+										target="_blank"
+									>
+										{selectedFile?.meta?.name}
+									</a>
+								</div>
+
+								<div>
+									<button
+										class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
+										on:click={() => {
+											updateFileContentHandler();
+										}}
+									>
+										{$i18n.t('Save')}
+									</button>
+								</div>
+							</div>
+
+							<div
+								class=" flex-1 w-full h-full max-h-full text-sm bg-transparent outline-none overflow-y-auto scrollbar-hidden"
+							>
+								{#key selectedFile.id}
+									<RichTextInput
+										className="input-prose-sm"
+										bind:value={selectedFile.data.content}
+										placeholder={$i18n.t('Add content here')}
+										preserveBreaks={true}
+									/>
+								{/key}
+							</div>
+						</div>
+					{:else}
+						<div class="h-full flex w-full">
+							<div class="m-auto text-xs text-center text-gray-200 dark:text-gray-700">
+								{$i18n.t('Drag and drop a file to upload or select a file to view')}
+							</div>
+						</div>
+					{/if}
+				</div>
+			{:else if !largeScreen && selectedFileId !== null}
+				<Drawer
+					className="h-full"
+					show={selectedFileId !== null}
+					on:close={() => {
+						selectedFileId = null;
+					}}
+				>
+					<div class="flex flex-col justify-start h-full max-h-full p-2">
+						<div class=" flex flex-col w-full h-full max-h-full">
+							<div class="flex-shrink-0 mt-1 mb-2 flex items-center">
+								<div class="mr-2">
+									<button
+										class="w-full text-left text-sm p-1.5 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
+										on:click={() => {
+											selectedFileId = null;
+										}}
+									>
+										<ChevronLeft strokeWidth="2.5" />
+									</button>
+								</div>
+								<div class=" flex-1 text-xl line-clamp-1">
+									{selectedFile?.meta?.name}
+								</div>
+
+								<div>
+									<button
+										class="self-center w-fit text-sm py-1 px-2.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-lg"
+										on:click={() => {
+											updateFileContentHandler();
+										}}
+									>
+										{$i18n.t('Save')}
+									</button>
+								</div>
+							</div>
+
+							<div
+								class=" flex-1 w-full h-full max-h-full py-2.5 px-3.5 rounded-lg text-sm bg-transparent overflow-y-auto scrollbar-hidden"
+							>
+								{#key selectedFile.id}
+									<RichTextInput
+										className="input-prose-sm"
+										bind:value={selectedFile.data.content}
+										placeholder={$i18n.t('Add content here')}
+										preserveBreaks={true}
+									/>
+								{/key}
+							</div>
+						</div>
+					</div>
+				</Drawer>
+			{/if}
+
+			<div
+				class="{largeScreen ? 'flex-shrink-0 w-72 max-w-72' : 'flex-1'}
+			flex
+			py-2
+			rounded-2xl
+			border
+			border-gray-50
+			h-full
+			dark:border-gray-850"
+			>
+				<div class=" flex flex-col w-full space-x-2 rounded-lg h-full">
+					<div class="w-full h-full flex flex-col">
+						<div class=" px-3">
+							<div class="flex mb-0.5">
+								<div class=" self-center ml-1 mr-3">
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											fill-rule="evenodd"
+											d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+											clip-rule="evenodd"
+										/>
+									</svg>
+								</div>
+								<input
+									class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+									bind:value={query}
+									placeholder={$i18n.t('Search Collection')}
+									on:focus={() => {
+										selectedFileId = null;
+									}}
+								/>
+
+								<div>
+									<AddContentMenu
+										on:upload={(e) => {
+											if (e.detail.type === 'directory') {
+												uploadDirectoryHandler();
+											} else if (e.detail.type === 'text') {
+												showAddTextContentModal = true;
+											} else {
+												document.getElementById('files-input').click();
+											}
+										}}
+										on:sync={(e) => {
+											showSyncConfirmModal = true;
+										}}
+									/>
+								</div>
+							</div>
+						</div>
+
+						{#if filteredItems.length > 0}
+							<div class=" flex overflow-y-auto h-full w-full scrollbar-hidden text-xs">
+								<Files
+									small
+									files={filteredItems}
+									{selectedFileId}
+									on:click={(e) => {
+										selectedFileId = selectedFileId === e.detail ? null : e.detail;
+									}}
+									on:delete={(e) => {
+										console.log(e.detail);
+
+										selectedFileId = null;
+										deleteFileHandler(e.detail);
+									}}
+								/>
+							</div>
+						{:else}
+							<div class="my-3 flex flex-col justify-center text-center text-gray-500 text-xs">
+								<div>
+									{$i18n.t('No content found')}
+								</div>
+							</div>
+						{/if}
+					</div>
+				</div>
+			</div>
+		</div>
+	{:else}
+		<Spinner />
+	{/if}
+</div>
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/AddContentMenu.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddContentMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..88326ed89886086efde148b045c329ab7a60706c
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddContentMenu.svelte
@@ -0,0 +1,107 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext, createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
+	import BarsArrowUp from '$lib/components/icons/BarsArrowUp.svelte';
+	import FolderOpen from '$lib/components/icons/FolderOpen.svelte';
+	import ArrowPath from '$lib/components/icons/ArrowPath.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let onClose: Function = () => {};
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+	align="end"
+>
+	<Tooltip content={$i18n.t('Add Content')}>
+		<button
+			class=" p-1.5 rounded-xl hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition font-medium text-sm flex items-center space-x-1"
+			on:click={(e) => {
+				e.stopPropagation();
+				show = true;
+			}}
+		>
+			<svg
+				xmlns="http://www.w3.org/2000/svg"
+				viewBox="0 0 16 16"
+				fill="currentColor"
+				class="w-4 h-4"
+			>
+				<path
+					d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
+				/>
+			</svg>
+		</button>
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-44 rounded-xl p-1 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={4}
+			side="bottom"
+			align="end"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('upload', { type: 'files' });
+				}}
+			>
+				<ArrowUpCircle strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Upload files')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('upload', { type: 'directory' });
+				}}
+			>
+				<FolderOpen strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Upload directory')}</div>
+			</DropdownMenu.Item>
+
+			<Tooltip
+				content={$i18n.t(
+					'This option will delete all existing files in the collection and replace them with newly uploaded files.'
+				)}
+				className="w-full"
+			>
+				<DropdownMenu.Item
+					class="flex  gap-2  items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+					on:click={() => {
+						dispatch('sync', { type: 'directory' });
+					}}
+				>
+					<ArrowPath strokeWidth="2" />
+					<div class="flex items-center">{$i18n.t('Sync directory')}</div>
+				</DropdownMenu.Item>
+			</Tooltip>
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					dispatch('upload', { type: 'text' });
+				}}
+			>
+				<BarsArrowUp strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Add text content')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..713bd43176cbc85c6218f9714aca1c7f7f23b273
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/AddTextContentModal.svelte
@@ -0,0 +1,169 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import dayjs from 'dayjs';
+
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import RichTextInput from '$lib/components/common/RichTextInput.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
+	import Mic from '$lib/components/icons/Mic.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import VoiceRecording from '$lib/components/chat/MessageInput/VoiceRecording.svelte';
+	export let show = false;
+
+	let name = 'Untitled';
+	let content = '';
+
+	let voiceInput = false;
+</script>
+
+<Modal size="full" containerClassName="" className="h-full bg-white dark:bg-gray-900" bind:show>
+	<div class="absolute top-0 right-0 p-5">
+		<button
+			class="self-center dark:text-white"
+			type="button"
+			on:click={() => {
+				show = false;
+			}}
+		>
+			<XMark className="size-3.5" />
+		</button>
+	</div>
+	<div class="flex flex-col md:flex-row w-full h-full md:space-x-4 dark:text-gray-200">
+		<form
+			class="flex flex-col w-full h-full"
+			on:submit|preventDefault={() => {
+				if (name.trim() === '' || content.trim() === '') {
+					toast.error($i18n.t('Please fill in all fields.'));
+					name = name.trim();
+					content = content.trim();
+					return;
+				}
+
+				dispatch('submit', {
+					name,
+					content
+				});
+				show = false;
+				name = '';
+				content = '';
+			}}
+		>
+			<div class=" flex-1 w-full h-full flex justify-center overflow-auto px-5 py-4">
+				<div class=" max-w-3xl py-2 md:py-10 w-full flex flex-col gap-2">
+					<div class="flex-shrink-0 w-full flex justify-between items-center">
+						<div class="w-full">
+							<input
+								class="w-full text-3xl font-semibold bg-transparent outline-none"
+								type="text"
+								bind:value={name}
+								placeholder={$i18n.t('Title')}
+								required
+							/>
+						</div>
+					</div>
+
+					<div class=" flex-1 w-full h-full">
+						<RichTextInput
+							bind:value={content}
+							placeholder={$i18n.t('Write something...')}
+							preserveBreaks={true}
+						/>
+					</div>
+				</div>
+			</div>
+
+			<div
+				class="flex flex-row items-center justify-end text-sm font-medium flex-shrink-0 mt-1 p-4 gap-1.5"
+			>
+				<div class="">
+					{#if voiceInput}
+						<div class=" max-w-full w-64">
+							<VoiceRecording
+								bind:recording={voiceInput}
+								className="p-1"
+								on:cancel={() => {
+									voiceInput = false;
+								}}
+								on:confirm={(e) => {
+									const { text, filename } = e.detail;
+									content = `${content}${text} `;
+
+									voiceInput = false;
+								}}
+							/>
+						</div>
+					{:else}
+						<Tooltip content={$i18n.t('Voice Input')}>
+							<button
+								class=" p-2 bg-gray-50 text-gray-700 dark:bg-gray-700 dark:text-white transition rounded-full"
+								type="button"
+								on:click={async () => {
+									try {
+										let stream = await navigator.mediaDevices
+											.getUserMedia({ audio: true })
+											.catch(function (err) {
+												toast.error(
+													$i18n.t(`Permission denied when accessing microphone: {{error}}`, {
+														error: err
+													})
+												);
+												return null;
+											});
+
+										if (stream) {
+											voiceInput = true;
+											const tracks = stream.getTracks();
+											tracks.forEach((track) => track.stop());
+										}
+										stream = null;
+									} catch {
+										toast.error($i18n.t('Permission denied when accessing microphone'));
+									}
+								}}
+							>
+								<Mic className="size-5" />
+							</button>
+						</Tooltip>
+					{/if}
+				</div>
+
+				<div class=" flex-shrink-0">
+					<Tooltip content={$i18n.t('Save')}>
+						<button
+							class=" px-3.5 py-2 bg-black text-white dark:bg-white dark:text-black transition rounded-full"
+							type="submit"
+						>
+							{$i18n.t('Save')}
+						</button>
+					</Tooltip>
+				</div>
+			</div>
+		</form>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/workspace/Knowledge/KnowledgeBase/Files.svelte b/src/lib/components/workspace/Knowledge/KnowledgeBase/Files.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..bdc3371da7b951af979ff06fb8f37cb974debc79
--- /dev/null
+++ b/src/lib/components/workspace/Knowledge/KnowledgeBase/Files.svelte
@@ -0,0 +1,45 @@
+<script lang="ts">
+	import { createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	import FileItem from '$lib/components/common/FileItem.svelte';
+
+	export let selectedFileId = null;
+	export let files = [];
+
+	export let small = false;
+</script>
+
+<div class=" max-h-full flex flex-col w-full">
+	{#each files as file}
+		<div class="mt-1 px-2">
+			<FileItem
+				className="w-full"
+				colorClassName="{selectedFileId === file.id
+					? ' bg-gray-50 dark:bg-gray-850'
+					: 'bg-transparent'} hover:bg-gray-50 dark:hover:bg-gray-850 transition"
+				{small}
+				{file}
+				name={file?.name ?? file?.meta?.name}
+				type="file"
+				size={file?.size ?? file?.meta?.size ?? ''}
+				loading={file.status === 'uploading'}
+				dismissible
+				on:click={() => {
+					if (file.status === 'uploading') {
+						return;
+					}
+
+					dispatch('click', file.id);
+				}}
+				on:dismiss={() => {
+					if (file.status === 'uploading') {
+						return;
+					}
+
+					dispatch('delete', file.id);
+				}}
+			/>
+		</div>
+	{/each}
+</div>
diff --git a/src/lib/components/workspace/Models.svelte b/src/lib/components/workspace/Models.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..df19c00972e7778f2def09404e0f475b0df418fd
--- /dev/null
+++ b/src/lib/components/workspace/Models.svelte
@@ -0,0 +1,504 @@
+<script lang="ts">
+	import { marked } from 'marked';
+
+	import { toast } from 'svelte-sonner';
+	import Sortable from 'sortablejs';
+
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { onMount, getContext, tick } from 'svelte';
+	import { goto } from '$app/navigation';
+	const i18n = getContext('i18n');
+
+	import { WEBUI_NAME, config, mobile, models as _models, settings, user } from '$lib/stores';
+	import {
+		createNewModel,
+		deleteModelById,
+		getModels as getWorkspaceModels,
+		toggleModelById,
+		updateModelById
+	} from '$lib/apis/models';
+
+	import { getModels } from '$lib/apis';
+
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import ModelMenu from './Models/ModelMenu.svelte';
+	import ModelDeleteConfirmDialog from '../common/ConfirmDialog.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import GarbageBin from '../icons/GarbageBin.svelte';
+	import Search from '../icons/Search.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+	import Switch from '../common/Switch.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import { capitalizeFirstLetter } from '$lib/utils';
+
+	let shiftKey = false;
+
+	let importFiles;
+	let modelsImportInputElement: HTMLInputElement;
+	let loaded = false;
+
+	let models = [];
+
+	let filteredModels = [];
+	let selectedModel = null;
+
+	let showModelDeleteConfirm = false;
+
+	$: if (models) {
+		filteredModels = models.filter(
+			(m) => searchValue === '' || m.name.toLowerCase().includes(searchValue.toLowerCase())
+		);
+	}
+
+	let searchValue = '';
+
+	const deleteModelHandler = async (model) => {
+		const res = await deleteModelById(localStorage.token, model.id).catch((e) => {
+			toast.error(e);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t(`Deleted {{name}}`, { name: model.id }));
+		}
+
+		await _models.set(await getModels(localStorage.token));
+		models = await getWorkspaceModels(localStorage.token);
+	};
+
+	const cloneModelHandler = async (model) => {
+		sessionStorage.model = JSON.stringify({
+			...model,
+			id: `${model.id}-clone`,
+			name: `${model.name} (Clone)`
+		});
+		goto('/workspace/models/create');
+	};
+
+	const shareModelHandler = async (model) => {
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+
+		const url = 'https://openwebui.com';
+
+		const tab = await window.open(`${url}/models/create`, '_blank');
+
+		// Define the event handler function
+		const messageHandler = (event) => {
+			if (event.origin !== url) return;
+			if (event.data === 'loaded') {
+				tab.postMessage(JSON.stringify(model), '*');
+
+				// Remove the event listener after handling the message
+				window.removeEventListener('message', messageHandler);
+			}
+		};
+
+		window.addEventListener('message', messageHandler, false);
+	};
+
+	const hideModelHandler = async (model) => {
+		let info = model.info;
+
+		if (!info) {
+			info = {
+				id: model.id,
+				name: model.name,
+				meta: {
+					suggestion_prompts: null
+				},
+				params: {}
+			};
+		}
+
+		info.meta = {
+			...info.meta,
+			hidden: !(info?.meta?.hidden ?? false)
+		};
+
+		console.log(info);
+
+		const res = await updateModelById(localStorage.token, info.id, info);
+
+		if (res) {
+			toast.success(
+				$i18n.t(`Model {{name}} is now {{status}}`, {
+					name: info.id,
+					status: info.meta.hidden ? 'hidden' : 'visible'
+				})
+			);
+		}
+
+		await _models.set(await getModels(localStorage.token));
+		models = await getWorkspaceModels(localStorage.token);
+	};
+
+	const downloadModels = async (models) => {
+		let blob = new Blob([JSON.stringify(models)], {
+			type: 'application/json'
+		});
+		saveAs(blob, `models-export-${Date.now()}.json`);
+	};
+
+	const exportModelHandler = async (model) => {
+		let blob = new Blob([JSON.stringify([model])], {
+			type: 'application/json'
+		});
+		saveAs(blob, `${model.id}-${Date.now()}.json`);
+	};
+
+	onMount(async () => {
+		models = await getWorkspaceModels(localStorage.token);
+
+		loaded = true;
+
+		const onKeyDown = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = true;
+			}
+		};
+
+		const onKeyUp = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = false;
+			}
+		};
+
+		const onBlur = () => {
+			shiftKey = false;
+		};
+
+		window.addEventListener('keydown', onKeyDown);
+		window.addEventListener('keyup', onKeyUp);
+		window.addEventListener('blur', onBlur);
+
+		return () => {
+			window.removeEventListener('keydown', onKeyDown);
+			window.removeEventListener('keyup', onKeyUp);
+			window.removeEventListener('blur', onBlur);
+		};
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Models')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<ModelDeleteConfirmDialog
+		bind:show={showModelDeleteConfirm}
+		on:confirm={() => {
+			deleteModelHandler(selectedModel);
+		}}
+	/>
+
+	<div class="flex flex-col gap-1 my-1.5">
+		<div class="flex justify-between items-center">
+			<div class="flex items-center md:self-center text-xl font-medium px-0.5">
+				{$i18n.t('Models')}
+				<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+				<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
+					>{filteredModels.length}</span
+				>
+			</div>
+		</div>
+
+		<div class=" flex flex-1 items-center w-full space-x-2">
+			<div class="flex flex-1 items-center">
+				<div class=" self-center ml-1 mr-3">
+					<Search className="size-3.5" />
+				</div>
+				<input
+					class=" w-full text-sm py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={searchValue}
+					placeholder={$i18n.t('Search Models')}
+				/>
+			</div>
+
+			<div>
+				<a
+					class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+					href="/workspace/models/create"
+				>
+					<Plus className="size-3.5" />
+				</a>
+			</div>
+		</div>
+	</div>
+
+	<div class=" my-2 mb-5 gap-2 grid lg:grid-cols-2 xl:grid-cols-3" id="model-list">
+		{#each filteredModels as model}
+			<div
+				class=" flex flex-col cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl transition"
+				id="model-item-{model.id}"
+			>
+				<div class="flex gap-4 mt-0.5 mb-0.5">
+					<div class=" w-[44px]">
+						<div
+							class=" rounded-full object-cover {model.is_active
+								? ''
+								: 'opacity-50 dark:opacity-50'} "
+						>
+							<img
+								src={model?.meta?.profile_image_url ?? '/static/favicon.png'}
+								alt="modelfile profile"
+								class=" rounded-full w-full h-auto object-cover"
+							/>
+						</div>
+					</div>
+
+					<a
+						class=" flex flex-1 cursor-pointer w-full"
+						href={`/?models=${encodeURIComponent(model.id)}`}
+					>
+						<div class=" flex-1 self-center {model.is_active ? '' : 'text-gray-500'}">
+							<Tooltip
+								content={marked.parse(model?.meta?.description ?? model.id)}
+								className=" w-fit"
+								placement="top-start"
+							>
+								<div class=" font-semibold line-clamp-1">{model.name}</div>
+							</Tooltip>
+
+							<div class="flex gap-1 text-xs overflow-hidden">
+								<div class="line-clamp-1">
+									{#if (model?.meta?.description ?? '').trim()}
+										{model?.meta?.description}
+									{:else}
+										{model.id}
+									{/if}
+								</div>
+							</div>
+						</div>
+					</a>
+				</div>
+
+				<div class="flex justify-between items-center -mb-0.5 px-0.5">
+					<div class=" text-xs mt-0.5">
+						<Tooltip
+							content={model?.user?.email ?? $i18n.t('Deleted User')}
+							className="flex shrink-0"
+							placement="top-start"
+						>
+							<div class="shrink-0 text-gray-500">
+								{$i18n.t('By {{name}}', {
+									name: capitalizeFirstLetter(
+										model?.user?.name ?? model?.user?.email ?? $i18n.t('Deleted User')
+									)
+								})}
+							</div>
+						</Tooltip>
+					</div>
+
+					<div class="flex flex-row gap-0.5 items-center">
+						{#if shiftKey}
+							<Tooltip content={$i18n.t('Delete')}>
+								<button
+									class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+									type="button"
+									on:click={() => {
+										deleteModelHandler(model);
+									}}
+								>
+									<GarbageBin />
+								</button>
+							</Tooltip>
+						{:else}
+							{#if $user?.role === 'admin' || model.user_id === $user?.id}
+								<a
+									class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+									type="button"
+									href={`/workspace/models/edit?id=${encodeURIComponent(model.id)}`}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										fill="none"
+										viewBox="0 0 24 24"
+										stroke-width="1.5"
+										stroke="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											stroke-linecap="round"
+											stroke-linejoin="round"
+											d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
+										/>
+									</svg>
+								</a>
+							{/if}
+
+							<ModelMenu
+								user={$user}
+								{model}
+								shareHandler={() => {
+									shareModelHandler(model);
+								}}
+								cloneHandler={() => {
+									cloneModelHandler(model);
+								}}
+								exportHandler={() => {
+									exportModelHandler(model);
+								}}
+								hideHandler={() => {
+									hideModelHandler(model);
+								}}
+								deleteHandler={() => {
+									selectedModel = model;
+									showModelDeleteConfirm = true;
+								}}
+								onClose={() => {}}
+							>
+								<button
+									class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+									type="button"
+								>
+									<EllipsisHorizontal className="size-5" />
+								</button>
+							</ModelMenu>
+
+							<div class="ml-1">
+								<Tooltip content={model.is_active ? $i18n.t('Enabled') : $i18n.t('Disabled')}>
+									<Switch
+										bind:state={model.is_active}
+										on:change={async (e) => {
+											toggleModelById(localStorage.token, model.id);
+											_models.set(await getModels(localStorage.token));
+										}}
+									/>
+								</Tooltip>
+							</div>
+						{/if}
+					</div>
+				</div>
+			</div>
+		{/each}
+	</div>
+
+	{#if $user?.role === 'admin'}
+		<div class=" flex justify-end w-full mb-3">
+			<div class="flex space-x-1">
+				<input
+					id="models-import-input"
+					bind:this={modelsImportInputElement}
+					bind:files={importFiles}
+					type="file"
+					accept=".json"
+					hidden
+					on:change={() => {
+						console.log(importFiles);
+
+						let reader = new FileReader();
+						reader.onload = async (event) => {
+							let savedModels = JSON.parse(event.target.result);
+							console.log(savedModels);
+
+							for (const model of savedModels) {
+								if (model?.info ?? false) {
+									if ($_models.find((m) => m.id === model.id)) {
+										await updateModelById(localStorage.token, model.id, model.info).catch(
+											(error) => {
+												return null;
+											}
+										);
+									} else {
+										await createNewModel(localStorage.token, model.info).catch((error) => {
+											return null;
+										});
+									}
+								}
+							}
+
+							await _models.set(await getModels(localStorage.token));
+							models = await getWorkspaceModels(localStorage.token);
+						};
+
+						reader.readAsText(importFiles[0]);
+					}}
+				/>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={() => {
+						modelsImportInputElement.click();
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Models')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-3.5 h-3.5"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={async () => {
+						downloadModels($_models);
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Models')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-3.5 h-3.5"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+			</div>
+		</div>
+	{/if}
+
+	{#if $config?.features.enable_community_sharing}
+		<div class=" my-16">
+			<div class=" text-xl font-medium mb-1 line-clamp-1">
+				{$i18n.t('Made by OpenWebUI Community')}
+			</div>
+
+			<a
+				class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
+				href="https://openwebui.com/#open-webui-community"
+				target="_blank"
+			>
+				<div class=" self-center">
+					<div class=" font-semibold line-clamp-1">{$i18n.t('Discover a model')}</div>
+					<div class=" text-sm line-clamp-1">
+						{$i18n.t('Discover, download, and explore model presets')}
+					</div>
+				</div>
+
+				<div>
+					<div>
+						<ChevronRight />
+					</div>
+				</div>
+			</a>
+		</div>
+	{/if}
+{:else}
+	<div class="w-full h-full flex justify-center items-center">
+		<Spinner />
+	</div>
+{/if}
diff --git a/src/lib/components/workspace/Models/ActionsSelector.svelte b/src/lib/components/workspace/Models/ActionsSelector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8335455edab74179dcec9e736fbe2f88808be5ff
--- /dev/null
+++ b/src/lib/components/workspace/Models/ActionsSelector.svelte
@@ -0,0 +1,59 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let actions = [];
+	export let selectedActionIds = [];
+
+	let _actions = {};
+
+	onMount(() => {
+		_actions = actions.reduce((acc, action) => {
+			acc[action.id] = {
+				...action,
+				selected: selectedActionIds.includes(action.id)
+			};
+
+			return acc;
+		}, {});
+	});
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Actions')}</div>
+	</div>
+
+	<div class=" text-xs dark:text-gray-500">
+		{$i18n.t('To select actions here, add them to the "Functions" workspace first.')}
+	</div>
+
+	<div class="flex flex-col">
+		{#if actions.length > 0}
+			<div class=" flex items-center mt-2 flex-wrap">
+				{#each Object.keys(_actions) as action, actionIdx}
+					<div class=" flex items-center gap-2 mr-3">
+						<div class="self-center flex items-center">
+							<Checkbox
+								state={_actions[action].selected ? 'checked' : 'unchecked'}
+								on:change={(e) => {
+									_actions[action].selected = e.detail === 'checked';
+									selectedActionIds = Object.keys(_actions).filter((t) => _actions[t].selected);
+								}}
+							/>
+						</div>
+
+						<div class=" py-0.5 text-sm w-full capitalize font-medium">
+							<Tooltip content={_actions[action].meta.description}>
+								{_actions[action].name}
+							</Tooltip>
+						</div>
+					</div>
+				{/each}
+			</div>
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Models/Capabilities.svelte b/src/lib/components/workspace/Models/Capabilities.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cbccef1ff2024ea3727292c3906b059045545185
--- /dev/null
+++ b/src/lib/components/workspace/Models/Capabilities.svelte
@@ -0,0 +1,46 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import { marked } from 'marked';
+
+	const i18n = getContext('i18n');
+
+	const helpText = {
+		vision: $i18n.t('Model accepts image inputs'),
+		usage: $i18n.t(
+			'Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.'
+		),
+		citations: $i18n.t('Displays citations in the response')
+	};
+
+	export let capabilities: {
+		vision?: boolean;
+		usage?: boolean;
+		citations?: boolean;
+	} = {};
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Capabilities')}</div>
+	</div>
+	<div class="flex">
+		{#each Object.keys(capabilities) as capability}
+			<div class=" flex items-center gap-2 mr-3">
+				<Checkbox
+					state={capabilities[capability] ? 'checked' : 'unchecked'}
+					on:change={(e) => {
+						capabilities[capability] = e.detail === 'checked';
+					}}
+				/>
+
+				<div class=" py-0.5 text-sm capitalize">
+					<Tooltip content={marked.parse(helpText[capability])}>
+						{$i18n.t(capability)}
+					</Tooltip>
+				</div>
+			</div>
+		{/each}
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Models/FiltersSelector.svelte b/src/lib/components/workspace/Models/FiltersSelector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..92f64c2cf084a67b37deae73186b572e695861f9
--- /dev/null
+++ b/src/lib/components/workspace/Models/FiltersSelector.svelte
@@ -0,0 +1,60 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let filters = [];
+	export let selectedFilterIds = [];
+
+	let _filters = {};
+
+	onMount(() => {
+		_filters = filters.reduce((acc, filter) => {
+			acc[filter.id] = {
+				...filter,
+				selected: selectedFilterIds.includes(filter.id)
+			};
+
+			return acc;
+		}, {});
+	});
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Filters')}</div>
+	</div>
+
+	<div class=" text-xs dark:text-gray-500">
+		{$i18n.t('To select filters here, add them to the "Functions" workspace first.')}
+	</div>
+
+	<!-- TODO: Filer order matters -->
+	<div class="flex flex-col">
+		{#if filters.length > 0}
+			<div class=" flex items-center mt-2 flex-wrap">
+				{#each Object.keys(_filters) as filter, filterIdx}
+					<div class=" flex items-center gap-2 mr-3">
+						<div class="self-center flex items-center">
+							<Checkbox
+								state={_filters[filter].selected ? 'checked' : 'unchecked'}
+								on:change={(e) => {
+									_filters[filter].selected = e.detail === 'checked';
+									selectedFilterIds = Object.keys(_filters).filter((t) => _filters[t].selected);
+								}}
+							/>
+						</div>
+
+						<div class=" py-0.5 text-sm w-full capitalize font-medium">
+							<Tooltip content={_filters[filter].meta.description}>
+								{_filters[filter].name}
+							</Tooltip>
+						</div>
+					</div>
+				{/each}
+			</div>
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Models/Knowledge.svelte b/src/lib/components/workspace/Models/Knowledge.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d2adc50e645d739c07f94be297c5c835f3c53ed8
--- /dev/null
+++ b/src/lib/components/workspace/Models/Knowledge.svelte
@@ -0,0 +1,63 @@
+<script lang="ts">
+	import { getContext } from 'svelte';
+	import Selector from './Knowledge/Selector.svelte';
+	import FileItem from '$lib/components/common/FileItem.svelte';
+
+	export let selectedKnowledge = [];
+	export let collections = [];
+
+	const i18n = getContext('i18n');
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Knowledge')}</div>
+	</div>
+
+	<div class=" text-xs dark:text-gray-500">
+		{$i18n.t('To attach knowledge base here, add them to the "Knowledge" workspace first.')}
+	</div>
+
+	<div class="flex flex-col">
+		{#if selectedKnowledge?.length > 0}
+			<div class=" flex flex-wrap items-center gap-2 mt-2">
+				{#each selectedKnowledge as file, fileIdx}
+					<FileItem
+						{file}
+						name={file.name}
+						type={file?.legacy
+							? `Legacy${file.type ? ` ${file.type}` : ''}`
+							: (file?.type ?? 'Collection')}
+						dismissible
+						on:dismiss={(e) => {
+							selectedKnowledge = selectedKnowledge.filter((_, idx) => idx !== fileIdx);
+						}}
+					/>
+				{/each}
+			</div>
+		{/if}
+
+		<div class="flex flex-wrap text-sm font-medium gap-1.5 mt-2">
+			<Selector
+				on:select={(e) => {
+					const item = e.detail;
+
+					if (!selectedKnowledge.find((k) => k.id === item.id)) {
+						selectedKnowledge = [
+							...selectedKnowledge,
+							{
+								...item
+							}
+						];
+					}
+				}}
+			>
+				<button
+					class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-100 dark:outline-gray-850 rounded-3xl"
+					type="button">{$i18n.t('Select Knowledge')}</button
+				>
+			</Selector>
+		</div>
+		<!-- {knowledge} -->
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Models/Knowledge/Selector.svelte b/src/lib/components/workspace/Models/Knowledge/Selector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..6843c17a08ed00f3d7a39d8db239360f6afbaa4f
--- /dev/null
+++ b/src/lib/components/workspace/Models/Knowledge/Selector.svelte
@@ -0,0 +1,167 @@
+<script lang="ts">
+	import Fuse from 'fuse.js';
+
+	import { DropdownMenu } from 'bits-ui';
+	import { onMount, getContext, createEventDispatcher } from 'svelte';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { knowledge } from '$lib/stores';
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let onClose: Function = () => {};
+
+	let query = '';
+
+	let items = [];
+	let filteredItems = [];
+
+	let fuse = null;
+	$: if (fuse) {
+		filteredItems = query
+			? fuse.search(query).map((e) => {
+					return e.item;
+				})
+			: items;
+	}
+
+	onMount(() => {
+		let legacy_documents = $knowledge.filter((item) => item?.meta?.document);
+		let legacy_collections =
+			legacy_documents.length > 0
+				? [
+						{
+							name: 'All Documents',
+							legacy: true,
+							type: 'collection',
+							description: 'Deprecated (legacy collection), please create a new knowledge base.',
+
+							title: $i18n.t('All Documents'),
+							collection_names: legacy_documents.map((item) => item.id)
+						},
+
+						...legacy_documents
+							.reduce((a, item) => {
+								return [...new Set([...a, ...(item?.meta?.tags ?? []).map((tag) => tag.name)])];
+							}, [])
+							.map((tag) => ({
+								name: tag,
+								legacy: true,
+								type: 'collection',
+								description: 'Deprecated (legacy collection), please create a new knowledge base.',
+
+								collection_names: legacy_documents
+									.filter((item) => (item?.meta?.tags ?? []).map((tag) => tag.name).includes(tag))
+									.map((item) => item.id)
+							}))
+					]
+				: [];
+
+		items = [...$knowledge, ...legacy_collections].map((item) => {
+			return {
+				...item,
+				...(item?.legacy || item?.meta?.legacy || item?.meta?.document ? { legacy: true } : {}),
+				type: item?.meta?.document ? 'document' : 'collection'
+			};
+		});
+
+		fuse = new Fuse(items, {
+			keys: ['name', 'description']
+		});
+	});
+</script>
+
+<Dropdown
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+			query = '';
+		}
+	}}
+>
+	<slot />
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-80 rounded-lg px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-lg"
+			sideOffset={8}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<div class=" flex w-full space-x-2 py-0.5 px-2">
+				<div class="flex flex-1">
+					<div class=" self-center ml-1 mr-3">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 20 20"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+					<input
+						class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+						bind:value={query}
+						placeholder={$i18n.t('Search Knowledge')}
+					/>
+				</div>
+			</div>
+
+			<hr class=" border-gray-50 dark:border-gray-700 my-1.5" />
+
+			<div class="max-h-48 overflow-y-scroll">
+				{#if filteredItems.length === 0}
+					<div class="text-center text-sm text-gray-500 dark:text-gray-400">
+						{$i18n.t('No knowledge found')}
+					</div>
+				{:else}
+					{#each filteredItems as item}
+						<DropdownMenu.Item
+							class="flex gap-2.5 items-center px-3 py-2 text-sm  cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+							on:click={() => {
+								dispatch('select', item);
+							}}
+						>
+							<div class="flex items-center">
+								<div class="flex flex-col">
+									<div class=" w-fit mb-0.5">
+										{#if item.legacy}
+											<div
+												class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1"
+											>
+												Legacy
+											</div>
+										{:else if item?.meta?.document}
+											<div
+												class="bg-gray-500/20 text-gray-700 dark:text-gray-200 rounded uppercase text-xs font-bold px-1"
+											>
+												Document
+											</div>
+										{:else}
+											<div
+												class="bg-green-500/20 text-green-700 dark:text-green-200 rounded uppercase text-xs font-bold px-1"
+											>
+												Collection
+											</div>
+										{/if}
+									</div>
+
+									<div class="line-clamp-1 font-medium pr-0.5">
+										{item.name}
+									</div>
+								</div>
+							</div>
+						</DropdownMenu.Item>
+					{/each}
+				{/if}
+			</div>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Models/ModelEditor.svelte b/src/lib/components/workspace/Models/ModelEditor.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b184d5d5d1823e218738932fa61b3b451d5047dc
--- /dev/null
+++ b/src/lib/components/workspace/Models/ModelEditor.svelte
@@ -0,0 +1,793 @@
+<script lang="ts">
+	import { onMount, getContext, tick } from 'svelte';
+	import { models, tools, functions, knowledge as knowledgeCollections, user } from '$lib/stores';
+
+	import AdvancedParams from '$lib/components/chat/Settings/Advanced/AdvancedParams.svelte';
+	import Tags from '$lib/components/common/Tags.svelte';
+	import Knowledge from '$lib/components/workspace/Models/Knowledge.svelte';
+	import ToolsSelector from '$lib/components/workspace/Models/ToolsSelector.svelte';
+	import FiltersSelector from '$lib/components/workspace/Models/FiltersSelector.svelte';
+	import ActionsSelector from '$lib/components/workspace/Models/ActionsSelector.svelte';
+	import Capabilities from '$lib/components/workspace/Models/Capabilities.svelte';
+	import Textarea from '$lib/components/common/Textarea.svelte';
+	import { getTools } from '$lib/apis/tools';
+	import { getFunctions } from '$lib/apis/functions';
+	import { getKnowledgeBases } from '$lib/apis/knowledge';
+	import AccessControl from '../common/AccessControl.svelte';
+	import { stringify } from 'postcss';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	export let onSubmit: Function;
+	export let onBack: null | Function = null;
+
+	export let model = null;
+	export let edit = false;
+
+	export let preset = true;
+
+	let loading = false;
+	let success = false;
+
+	let filesInputElement;
+	let inputFiles;
+
+	let showAdvanced = false;
+	let showPreview = false;
+
+	let loaded = false;
+
+	// ///////////
+	// model
+	// ///////////
+
+	let id = '';
+	let name = '';
+
+	$: if (!edit) {
+		if (name) {
+			id = name
+				.replace(/\s+/g, '-')
+				.replace(/[^a-zA-Z0-9-]/g, '')
+				.toLowerCase();
+		}
+	}
+
+	let info = {
+		id: '',
+		base_model_id: null,
+		name: '',
+		meta: {
+			profile_image_url: '/static/favicon.png',
+			description: '',
+			suggestion_prompts: null,
+			tags: []
+		},
+		params: {
+			system: ''
+		}
+	};
+
+	let params = {
+		system: ''
+	};
+	let capabilities = {
+		vision: true,
+		usage: undefined,
+		citations: true
+	};
+
+	let knowledge = [];
+	let toolIds = [];
+	let filterIds = [];
+	let actionIds = [];
+
+	let accessControl = {};
+
+	const addUsage = (base_model_id) => {
+		const baseModel = $models.find((m) => m.id === base_model_id);
+
+		if (baseModel) {
+			if (baseModel.owned_by === 'openai') {
+				capabilities.usage = baseModel?.meta?.capabilities?.usage ?? false;
+			} else {
+				delete capabilities.usage;
+			}
+			capabilities = capabilities;
+		}
+	};
+
+	const submitHandler = async () => {
+		loading = true;
+
+		info.id = id;
+		info.name = name;
+
+		if (id === '') {
+			toast.error('Model ID is required.');
+		}
+
+		if (name === '') {
+			toast.error('Model Name is required.');
+		}
+
+		info.access_control = accessControl;
+		info.meta.capabilities = capabilities;
+
+		if (knowledge.length > 0) {
+			info.meta.knowledge = knowledge;
+		} else {
+			if (info.meta.knowledge) {
+				delete info.meta.knowledge;
+			}
+		}
+
+		if (toolIds.length > 0) {
+			info.meta.toolIds = toolIds;
+		} else {
+			if (info.meta.toolIds) {
+				delete info.meta.toolIds;
+			}
+		}
+
+		if (filterIds.length > 0) {
+			info.meta.filterIds = filterIds;
+		} else {
+			if (info.meta.filterIds) {
+				delete info.meta.filterIds;
+			}
+		}
+
+		if (actionIds.length > 0) {
+			info.meta.actionIds = actionIds;
+		} else {
+			if (info.meta.actionIds) {
+				delete info.meta.actionIds;
+			}
+		}
+
+		info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
+		Object.keys(info.params).forEach((key) => {
+			if (info.params[key] === '' || info.params[key] === null) {
+				delete info.params[key];
+			}
+		});
+
+		await onSubmit(info);
+
+		loading = false;
+		success = false;
+	};
+
+	onMount(async () => {
+		await tools.set(await getTools(localStorage.token));
+		await functions.set(await getFunctions(localStorage.token));
+		await knowledgeCollections.set(await getKnowledgeBases(localStorage.token));
+
+		// Scroll to top 'workspace-container' element
+		const workspaceContainer = document.getElementById('workspace-container');
+		if (workspaceContainer) {
+			workspaceContainer.scrollTop = 0;
+		}
+
+		if (model) {
+			console.log(model);
+			name = model.name;
+			await tick();
+
+			id = model.id;
+
+			if (model.base_model_id) {
+				const base_model = $models
+					.filter((m) => !m?.preset && !(m?.arena ?? false))
+					.find((m) => [model.base_model_id, `${model.base_model_id}:latest`].includes(m.id));
+
+				console.log('base_model', base_model);
+
+				if (base_model) {
+					model.base_model_id = base_model.id;
+				} else {
+					model.base_model_id = null;
+				}
+			}
+
+			params = { ...params, ...model?.params };
+			params.stop = params?.stop
+				? (typeof params.stop === 'string' ? params.stop.split(',') : (params?.stop ?? [])).join(
+						','
+					)
+				: null;
+
+			toolIds = model?.meta?.toolIds ?? [];
+			filterIds = model?.meta?.filterIds ?? [];
+			actionIds = model?.meta?.actionIds ?? [];
+			knowledge = (model?.meta?.knowledge ?? []).map((item) => {
+				if (item?.collection_name) {
+					return {
+						id: item.collection_name,
+						name: item.name,
+						legacy: true
+					};
+				} else if (item?.collection_names) {
+					return {
+						name: item.name,
+						type: 'collection',
+						collection_names: item.collection_names,
+						legacy: true
+					};
+				} else {
+					return item;
+				}
+			});
+			capabilities = { ...capabilities, ...(model?.meta?.capabilities ?? {}) };
+
+			if ('access_control' in model) {
+				accessControl = model.access_control;
+			} else {
+				accessControl = {};
+			}
+
+			console.log(model?.access_control);
+			console.log(accessControl);
+
+			info = {
+				...info,
+				...JSON.parse(
+					JSON.stringify(
+						model
+							? model
+							: {
+									id: model.id,
+									name: model.name
+								}
+					)
+				)
+			};
+
+			console.log(model);
+		}
+
+		loaded = true;
+	});
+</script>
+
+{#if loaded}
+	{#if onBack}
+		<button
+			class="flex space-x-1"
+			on:click={() => {
+				onBack();
+			}}
+		>
+			<div class=" self-center">
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="h-4 w-4"
+				>
+					<path
+						fill-rule="evenodd"
+						d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
+						clip-rule="evenodd"
+					/>
+				</svg>
+			</div>
+			<div class=" self-center text-sm font-medium">{'Back'}</div>
+		</button>
+	{/if}
+
+	<div class="w-full max-h-full flex justify-center">
+		<input
+			bind:this={filesInputElement}
+			bind:files={inputFiles}
+			type="file"
+			hidden
+			accept="image/*"
+			on:change={() => {
+				let reader = new FileReader();
+				reader.onload = (event) => {
+					let originalImageUrl = `${event.target.result}`;
+
+					const img = new Image();
+					img.src = originalImageUrl;
+
+					img.onload = function () {
+						const canvas = document.createElement('canvas');
+						const ctx = canvas.getContext('2d');
+
+						// Calculate the aspect ratio of the image
+						const aspectRatio = img.width / img.height;
+
+						// Calculate the new width and height to fit within 100x100
+						let newWidth, newHeight;
+						if (aspectRatio > 1) {
+							newWidth = 250 * aspectRatio;
+							newHeight = 250;
+						} else {
+							newWidth = 250;
+							newHeight = 250 / aspectRatio;
+						}
+
+						// Set the canvas size
+						canvas.width = 250;
+						canvas.height = 250;
+
+						// Calculate the position to center the image
+						const offsetX = (250 - newWidth) / 2;
+						const offsetY = (250 - newHeight) / 2;
+
+						// Draw the image on the canvas
+						ctx.drawImage(img, offsetX, offsetY, newWidth, newHeight);
+
+						// Get the base64 representation of the compressed image
+						const compressedSrc = canvas.toDataURL();
+
+						// Display the compressed image
+						info.meta.profile_image_url = compressedSrc;
+
+						inputFiles = null;
+						filesInputElement.value = '';
+					};
+				};
+
+				if (
+					inputFiles &&
+					inputFiles.length > 0 &&
+					['image/gif', 'image/webp', 'image/jpeg', 'image/png', 'image/svg+xml'].includes(
+						inputFiles[0]['type']
+					)
+				) {
+					reader.readAsDataURL(inputFiles[0]);
+				} else {
+					console.log(`Unsupported File Type '${inputFiles[0]['type']}'.`);
+					inputFiles = null;
+				}
+			}}
+		/>
+
+		{#if !edit || (edit && model)}
+			<form
+				class="flex flex-col md:flex-row w-full gap-3 md:gap-6"
+				on:submit|preventDefault={() => {
+					submitHandler();
+				}}
+			>
+				<div class="self-center md:self-start flex justify-center my-2 flex-shrink-0">
+					<div class="self-center">
+						<button
+							class="rounded-xl flex flex-shrink-0 items-center {info.meta.profile_image_url !==
+							'/static/favicon.png'
+								? 'bg-transparent'
+								: 'bg-white'} shadow-xl group relative"
+							type="button"
+							on:click={() => {
+								filesInputElement.click();
+							}}
+						>
+							{#if info.meta.profile_image_url}
+								<img
+									src={info.meta.profile_image_url}
+									alt="model profile"
+									class="rounded-xl size-72 md:size-60 object-cover shrink-0"
+								/>
+							{:else}
+								<img
+									src="/static/favicon.png"
+									alt="model profile"
+									class=" rounded-xl size-72 md:size-60 object-cover shrink-0"
+								/>
+							{/if}
+
+							<div class="absolute bottom-0 right-0 z-10">
+								<div class="m-1.5">
+									<div
+										class="shadow-xl p-1 rounded-full border-2 border-white bg-gray-800 text-white group-hover:bg-gray-600 transition dark:border-black dark:bg-white dark:group-hover:bg-gray-200 dark:text-black"
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											viewBox="0 0 16 16"
+											fill="currentColor"
+											class="size-5"
+										>
+											<path
+												fill-rule="evenodd"
+												d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
+												clip-rule="evenodd"
+											/>
+										</svg>
+									</div>
+								</div>
+							</div>
+
+							<div
+								class="absolute top-0 bottom-0 left-0 right-0 bg-white dark:bg-black rounded-lg opacity-0 group-hover:opacity-20 transition"
+							></div>
+						</button>
+
+						<div class="flex w-full mt-1 justify-end">
+							<button
+								class="px-2 py-1 text-gray-500 rounded-lg text-xs"
+								on:click={() => {
+									info.meta.profile_image_url = '/static/favicon.png';
+								}}
+								type="button"
+							>
+								Reset Image</button
+							>
+						</div>
+					</div>
+				</div>
+
+				<div class="w-full">
+					<div class="mt-2 my-2 flex flex-col">
+						<div class="flex-1">
+							<div>
+								<input
+									class="text-3xl font-semibold w-full bg-transparent outline-none"
+									placeholder={$i18n.t('Model Name')}
+									bind:value={name}
+									required
+								/>
+							</div>
+						</div>
+
+						<div class="flex-1">
+							<div>
+								<input
+									class="text-xs w-full bg-transparent text-gray-500 outline-none"
+									placeholder={$i18n.t('Model ID')}
+									bind:value={id}
+									disabled={edit}
+									required
+								/>
+							</div>
+						</div>
+					</div>
+
+					{#if preset}
+						<div class="my-1">
+							<div class=" text-sm font-semibold mb-1">{$i18n.t('Base Model (From)')}</div>
+
+							<div>
+								<select
+									class="text-sm w-full bg-transparent outline-none"
+									placeholder="Select a base model (e.g. llama3, gpt-4o)"
+									bind:value={info.base_model_id}
+									on:change={(e) => {
+										addUsage(e.target.value);
+									}}
+									required
+								>
+									<option value={null} class=" text-gray-900"
+										>{$i18n.t('Select a base model')}</option
+									>
+									{#each $models.filter((m) => (model ? m.id !== model.id : true) && !m?.preset && m?.owned_by !== 'arena') as model}
+										<option value={model.id} class=" text-gray-900">{model.name}</option>
+									{/each}
+								</select>
+							</div>
+						</div>
+					{/if}
+
+					<div class="my-1">
+						<div class="mb-1 flex w-full justify-between items-center">
+							<div class=" self-center text-sm font-semibold">{$i18n.t('Description')}</div>
+
+							<button
+								class="p-1 text-xs flex rounded transition"
+								type="button"
+								on:click={() => {
+									if (info.meta.description === null) {
+										info.meta.description = '';
+									} else {
+										info.meta.description = null;
+									}
+								}}
+							>
+								{#if info.meta.description === null}
+									<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+								{:else}
+									<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+								{/if}
+							</button>
+						</div>
+
+						{#if info.meta.description !== null}
+							<Textarea
+								className=" text-sm w-full bg-transparent outline-none resize-none overflow-y-hidden "
+								placeholder={$i18n.t('Add a short description about what this model does')}
+								rows={3}
+								bind:value={info.meta.description}
+							/>
+						{/if}
+					</div>
+
+					<div class="my-1">
+						<div class="">
+							<Tags
+								tags={info?.meta?.tags ?? []}
+								on:delete={(e) => {
+									const tagName = e.detail;
+									info.meta.tags = info.meta.tags.filter((tag) => tag.name !== tagName);
+								}}
+								on:add={(e) => {
+									const tagName = e.detail;
+									if (!(info?.meta?.tags ?? null)) {
+										info.meta.tags = [{ name: tagName }];
+									} else {
+										info.meta.tags = [...info.meta.tags, { name: tagName }];
+									}
+								}}
+							/>
+						</div>
+					</div>
+
+					<div class="my-2">
+						<div class="px-3 py-2 bg-gray-50 dark:bg-gray-950 rounded-lg">
+							<AccessControl bind:accessControl />
+						</div>
+					</div>
+
+					<hr class=" border-gray-50 dark:border-gray-850 my-1.5" />
+
+					<div class="my-2">
+						<div class="flex w-full justify-between">
+							<div class=" self-center text-sm font-semibold">{$i18n.t('Model Params')}</div>
+						</div>
+
+						<div class="mt-2">
+							<div class="my-1">
+								<div class=" text-xs font-semibold mb-2">{$i18n.t('System Prompt')}</div>
+								<div>
+									<Textarea
+										className=" text-sm w-full bg-transparent outline-none resize-none overflow-y-hidden "
+										placeholder={`Write your model system prompt content here\ne.g.) You are Mario from Super Mario Bros, acting as an assistant.`}
+										rows={4}
+										bind:value={info.params.system}
+									/>
+								</div>
+							</div>
+
+							<div class="flex w-full justify-between">
+								<div class=" self-center text-xs font-semibold">
+									{$i18n.t('Advanced Params')}
+								</div>
+
+								<button
+									class="p-1 px-3 text-xs flex rounded transition"
+									type="button"
+									on:click={() => {
+										showAdvanced = !showAdvanced;
+									}}
+								>
+									{#if showAdvanced}
+										<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
+									{:else}
+										<span class="ml-2 self-center">{$i18n.t('Show')}</span>
+									{/if}
+								</button>
+							</div>
+
+							{#if showAdvanced}
+								<div class="my-2">
+									<AdvancedParams
+										admin={true}
+										bind:params
+										on:change={(e) => {
+											info.params = { ...info.params, ...params };
+										}}
+									/>
+								</div>
+							{/if}
+						</div>
+					</div>
+
+					<hr class=" border-gray-50 dark:border-gray-850 my-1" />
+
+					<div class="my-2">
+						<div class="flex w-full justify-between items-center">
+							<div class="flex w-full justify-between items-center">
+								<div class=" self-center text-sm font-semibold">
+									{$i18n.t('Prompt suggestions')}
+								</div>
+
+								<button
+									class="p-1 text-xs flex rounded transition"
+									type="button"
+									on:click={() => {
+										if ((info?.meta?.suggestion_prompts ?? null) === null) {
+											info.meta.suggestion_prompts = [{ content: '' }];
+										} else {
+											info.meta.suggestion_prompts = null;
+										}
+									}}
+								>
+									{#if (info?.meta?.suggestion_prompts ?? null) === null}
+										<span class="ml-2 self-center">{$i18n.t('Default')}</span>
+									{:else}
+										<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
+									{/if}
+								</button>
+							</div>
+
+							{#if (info?.meta?.suggestion_prompts ?? null) !== null}
+								<button
+									class="p-1 px-2 text-xs flex rounded transition"
+									type="button"
+									on:click={() => {
+										if (
+											info.meta.suggestion_prompts.length === 0 ||
+											info.meta.suggestion_prompts.at(-1).content !== ''
+										) {
+											info.meta.suggestion_prompts = [
+												...info.meta.suggestion_prompts,
+												{ content: '' }
+											];
+										}
+									}}
+								>
+									<svg
+										xmlns="http://www.w3.org/2000/svg"
+										viewBox="0 0 20 20"
+										fill="currentColor"
+										class="w-4 h-4"
+									>
+										<path
+											d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
+										/>
+									</svg>
+								</button>
+							{/if}
+						</div>
+
+						{#if info?.meta?.suggestion_prompts}
+							<div class="flex flex-col space-y-1 mt-1 mb-3">
+								{#if info.meta.suggestion_prompts.length > 0}
+									{#each info.meta.suggestion_prompts as prompt, promptIdx}
+										<div class=" flex rounded-lg">
+											<input
+												class=" text-sm w-full bg-transparent outline-none border-r border-gray-50 dark:border-gray-850"
+												placeholder={$i18n.t('Write a prompt suggestion (e.g. Who are you?)')}
+												bind:value={prompt.content}
+											/>
+
+											<button
+												class="px-2"
+												type="button"
+												on:click={() => {
+													info.meta.suggestion_prompts.splice(promptIdx, 1);
+													info.meta.suggestion_prompts = info.meta.suggestion_prompts;
+												}}
+											>
+												<svg
+													xmlns="http://www.w3.org/2000/svg"
+													viewBox="0 0 20 20"
+													fill="currentColor"
+													class="w-4 h-4"
+												>
+													<path
+														d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+													/>
+												</svg>
+											</button>
+										</div>
+									{/each}
+								{:else}
+									<div class="text-xs text-center">No suggestion prompts</div>
+								{/if}
+							</div>
+						{/if}
+					</div>
+
+					<hr class=" border-gray-50 dark:border-gray-850 my-1.5" />
+
+					<div class="my-2">
+						<Knowledge bind:selectedKnowledge={knowledge} collections={$knowledgeCollections} />
+					</div>
+
+					<div class="my-2">
+						<ToolsSelector bind:selectedToolIds={toolIds} tools={$tools} />
+					</div>
+
+					<div class="my-2">
+						<FiltersSelector
+							bind:selectedFilterIds={filterIds}
+							filters={$functions.filter((func) => func.type === 'filter')}
+						/>
+					</div>
+
+					<div class="my-2">
+						<ActionsSelector
+							bind:selectedActionIds={actionIds}
+							actions={$functions.filter((func) => func.type === 'action')}
+						/>
+					</div>
+
+					<div class="my-2">
+						<Capabilities bind:capabilities />
+					</div>
+
+					<div class="my-2 text-gray-300 dark:text-gray-700">
+						<div class="flex w-full justify-between mb-2">
+							<div class=" self-center text-sm font-semibold">{$i18n.t('JSON Preview')}</div>
+
+							<button
+								class="p-1 px-3 text-xs flex rounded transition"
+								type="button"
+								on:click={() => {
+									showPreview = !showPreview;
+								}}
+							>
+								{#if showPreview}
+									<span class="ml-2 self-center">{$i18n.t('Hide')}</span>
+								{:else}
+									<span class="ml-2 self-center">{$i18n.t('Show')}</span>
+								{/if}
+							</button>
+						</div>
+
+						{#if showPreview}
+							<div>
+								<textarea
+									class="text-sm w-full bg-transparent outline-none resize-none"
+									rows="10"
+									value={JSON.stringify(info, null, 2)}
+									disabled
+									readonly
+								/>
+							</div>
+						{/if}
+					</div>
+
+					<div class="my-2 flex justify-end pb-20">
+						<button
+							class=" text-sm px-3 py-2 transition rounded-lg {loading
+								? ' cursor-not-allowed bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black'
+								: 'bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black'} flex w-full justify-center"
+							type="submit"
+							disabled={loading}
+						>
+							<div class=" self-center font-medium">
+								{#if edit}
+									{$i18n.t('Save & Update')}
+								{:else}
+									{$i18n.t('Save & Create')}
+								{/if}
+							</div>
+
+							{#if loading}
+								<div class="ml-1.5 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</div>
+			</form>
+		{/if}
+	</div>
+{/if}
diff --git a/src/lib/components/workspace/Models/ModelMenu.svelte b/src/lib/components/workspace/Models/ModelMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8cd9130a03885172bf9fe518bd6c00d0f18e4ef2
--- /dev/null
+++ b/src/lib/components/workspace/Models/ModelMenu.svelte
@@ -0,0 +1,98 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+	import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let user;
+	export let model;
+
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+
+	export let hideHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Models/ToolsSelector.svelte b/src/lib/components/workspace/Models/ToolsSelector.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5f1b3b482128882f2c076b85f044ed94cfa31798
--- /dev/null
+++ b/src/lib/components/workspace/Models/ToolsSelector.svelte
@@ -0,0 +1,57 @@
+<script lang="ts">
+	import Checkbox from '$lib/components/common/Checkbox.svelte';
+	import { getContext, onMount } from 'svelte';
+
+	export let tools = [];
+
+	let _tools = {};
+
+	export let selectedToolIds = [];
+
+	const i18n = getContext('i18n');
+
+	onMount(() => {
+		_tools = tools.reduce((acc, tool) => {
+			acc[tool.id] = {
+				...tool,
+				selected: selectedToolIds.includes(tool.id)
+			};
+
+			return acc;
+		}, {});
+	});
+</script>
+
+<div>
+	<div class="flex w-full justify-between mb-1">
+		<div class=" self-center text-sm font-semibold">{$i18n.t('Tools')}</div>
+	</div>
+
+	<div class=" text-xs dark:text-gray-500">
+		{$i18n.t('To select toolkits here, add them to the "Tools" workspace first.')}
+	</div>
+
+	<div class="flex flex-col">
+		{#if tools.length > 0}
+			<div class=" flex items-center mt-2 flex-wrap">
+				{#each Object.keys(_tools) as tool, toolIdx}
+					<div class=" flex items-center gap-2 mr-3">
+						<div class="self-center flex items-center">
+							<Checkbox
+								state={_tools[tool].selected ? 'checked' : 'unchecked'}
+								on:change={(e) => {
+									_tools[tool].selected = e.detail === 'checked';
+									selectedToolIds = Object.keys(_tools).filter((t) => _tools[t].selected);
+								}}
+							/>
+						</div>
+
+						<div class=" py-0.5 text-sm w-full capitalize font-medium">
+							{_tools[tool].name}
+						</div>
+					</div>
+				{/each}
+			</div>
+		{/if}
+	</div>
+</div>
diff --git a/src/lib/components/workspace/Prompts.svelte b/src/lib/components/workspace/Prompts.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..f20f549e154978c6768a030d04d0d0da24b33ef4
--- /dev/null
+++ b/src/lib/components/workspace/Prompts.svelte
@@ -0,0 +1,346 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { goto } from '$app/navigation';
+	import { onMount, getContext } from 'svelte';
+	import { WEBUI_NAME, config, prompts as _prompts, user } from '$lib/stores';
+
+	import {
+		createNewPrompt,
+		deletePromptByCommand,
+		getPrompts,
+		getPromptList
+	} from '$lib/apis/prompts';
+
+	import PromptMenu from './Prompts/PromptMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Search from '../icons/Search.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import { capitalizeFirstLetter } from '$lib/utils';
+
+	const i18n = getContext('i18n');
+	let promptsImportInputElement: HTMLInputElement;
+	let loaded = false;
+
+	let importFiles = '';
+	let query = '';
+
+	let prompts = [];
+
+	let showDeleteConfirm = false;
+	let deletePrompt = null;
+
+	let filteredItems = [];
+	$: filteredItems = prompts.filter((p) => query === '' || p.command.includes(query));
+
+	const shareHandler = async (prompt) => {
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+
+		const url = 'https://openwebui.com';
+
+		const tab = await window.open(`${url}/prompts/create`, '_blank');
+		window.addEventListener(
+			'message',
+			(event) => {
+				if (event.origin !== url) return;
+				if (event.data === 'loaded') {
+					tab.postMessage(JSON.stringify(prompt), '*');
+				}
+			},
+			false
+		);
+	};
+
+	const cloneHandler = async (prompt) => {
+		sessionStorage.prompt = JSON.stringify(prompt);
+		goto('/workspace/prompts/create');
+	};
+
+	const exportHandler = async (prompt) => {
+		let blob = new Blob([JSON.stringify([prompt])], {
+			type: 'application/json'
+		});
+		saveAs(blob, `prompt-export-${Date.now()}.json`);
+	};
+
+	const deleteHandler = async (prompt) => {
+		const command = prompt.command;
+		await deletePromptByCommand(localStorage.token, command);
+		await init();
+	};
+
+	const init = async () => {
+		prompts = await getPromptList(localStorage.token);
+		await _prompts.set(await getPrompts(localStorage.token));
+	};
+
+	onMount(async () => {
+		await init();
+		loaded = true;
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Prompts')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<DeleteConfirmDialog
+		bind:show={showDeleteConfirm}
+		title={$i18n.t('Delete prompt?')}
+		on:confirm={() => {
+			deleteHandler(deletePrompt);
+		}}
+	>
+		<div class=" text-sm text-gray-500">
+			{$i18n.t('This will delete')} <span class="  font-semibold">{deletePrompt.command}</span>.
+		</div>
+	</DeleteConfirmDialog>
+
+	<div class="flex flex-col gap-1 my-1.5">
+		<div class="flex justify-between items-center">
+			<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
+				{$i18n.t('Prompts')}
+				<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+				<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
+					>{filteredItems.length}</span
+				>
+			</div>
+		</div>
+
+		<div class=" flex w-full space-x-2">
+			<div class="flex flex-1">
+				<div class=" self-center ml-1 mr-3">
+					<Search className="size-3.5" />
+				</div>
+				<input
+					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={query}
+					placeholder={$i18n.t('Search Prompts')}
+				/>
+			</div>
+
+			<div>
+				<a
+					class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+					href="/workspace/prompts/create"
+				>
+					<Plus className="size-3.5" />
+				</a>
+			</div>
+		</div>
+	</div>
+
+	<div class="mb-5 gap-2 grid lg:grid-cols-2 xl:grid-cols-3">
+		{#each filteredItems as prompt}
+			<div
+				class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl transition"
+			>
+				<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
+					<a href={`/workspace/prompts/edit?command=${encodeURIComponent(prompt.command)}`}>
+						<div class=" flex-1 flex items-center gap-2 self-center">
+							<div class=" font-semibold line-clamp-1 capitalize">{prompt.title}</div>
+							<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
+								{prompt.command}
+							</div>
+						</div>
+
+						<div class=" text-xs px-0.5">
+							<Tooltip
+								content={prompt?.user?.email ?? $i18n.t('Deleted User')}
+								className="flex shrink-0"
+								placement="top-start"
+							>
+								<div class="shrink-0 text-gray-500">
+									{$i18n.t('By {{name}}', {
+										name: capitalizeFirstLetter(
+											prompt?.user?.name ?? prompt?.user?.email ?? $i18n.t('Deleted User')
+										)
+									})}
+								</div>
+							</Tooltip>
+						</div>
+					</a>
+				</div>
+				<div class="flex flex-row gap-0.5 self-center">
+					<a
+						class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+						type="button"
+						href={`/workspace/prompts/edit?command=${encodeURIComponent(prompt.command)}`}
+					>
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+							/>
+						</svg>
+					</a>
+
+					<PromptMenu
+						shareHandler={() => {
+							shareHandler(prompt);
+						}}
+						cloneHandler={() => {
+							cloneHandler(prompt);
+						}}
+						exportHandler={() => {
+							exportHandler(prompt);
+						}}
+						deleteHandler={async () => {
+							deletePrompt = prompt;
+							showDeleteConfirm = true;
+						}}
+						onClose={() => {}}
+					>
+						<button
+							class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+							type="button"
+						>
+							<EllipsisHorizontal className="size-5" />
+						</button>
+					</PromptMenu>
+				</div>
+			</div>
+		{/each}
+	</div>
+
+	{#if $user?.role === 'admin'}
+		<div class=" flex justify-end w-full mb-3">
+			<div class="flex space-x-2">
+				<input
+					id="prompts-import-input"
+					bind:this={promptsImportInputElement}
+					bind:files={importFiles}
+					type="file"
+					accept=".json"
+					hidden
+					on:change={() => {
+						console.log(importFiles);
+
+						const reader = new FileReader();
+						reader.onload = async (event) => {
+							const savedPrompts = JSON.parse(event.target.result);
+							console.log(savedPrompts);
+
+							for (const prompt of savedPrompts) {
+								await createNewPrompt(
+									localStorage.token,
+									prompt.command.charAt(0) === '/' ? prompt.command.slice(1) : prompt.command,
+									prompt.title,
+									prompt.content
+								).catch((error) => {
+									toast.error(error);
+									return null;
+								});
+							}
+
+							prompts = await getPromptList(localStorage.token);
+							await _prompts.set(await getPrompts(localStorage.token));
+						};
+
+						reader.readAsText(importFiles[0]);
+					}}
+				/>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={() => {
+						promptsImportInputElement.click();
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Prompts')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={async () => {
+						// promptsImportInputElement.click();
+						let blob = new Blob([JSON.stringify(prompts)], {
+							type: 'application/json'
+						});
+						saveAs(blob, `prompts-export-${Date.now()}.json`);
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Prompts')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+			</div>
+		</div>
+	{/if}
+
+	{#if $config?.features.enable_community_sharing}
+		<div class=" my-16">
+			<div class=" text-xl font-medium mb-1 line-clamp-1">
+				{$i18n.t('Made by OpenWebUI Community')}
+			</div>
+
+			<a
+				class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
+				href="https://openwebui.com/#open-webui-community"
+				target="_blank"
+			>
+				<div class=" self-center">
+					<div class=" font-semibold line-clamp-1">{$i18n.t('Discover a prompt')}</div>
+					<div class=" text-sm line-clamp-1">
+						{$i18n.t('Discover, download, and explore custom prompts')}
+					</div>
+				</div>
+
+				<div>
+					<div>
+						<ChevronRight />
+					</div>
+				</div>
+			</a>
+		</div>
+	{/if}
+{:else}
+	<div class="w-full h-full flex justify-center items-center">
+		<Spinner />
+	</div>
+{/if}
diff --git a/src/lib/components/workspace/Prompts/PromptEditor.svelte b/src/lib/components/workspace/Prompts/PromptEditor.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9ca0287843877b8d4d841490578790368f2a6f74
--- /dev/null
+++ b/src/lib/components/workspace/Prompts/PromptEditor.svelte
@@ -0,0 +1,206 @@
+<script lang="ts">
+	import { onMount, tick, getContext } from 'svelte';
+
+	import Textarea from '$lib/components/common/Textarea.svelte';
+	import { toast } from 'svelte-sonner';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import AccessControl from '../common/AccessControl.svelte';
+	import LockClosed from '$lib/components/icons/LockClosed.svelte';
+	import AccessControlModal from '../common/AccessControlModal.svelte';
+
+	export let onSubmit: Function;
+	export let edit = false;
+	export let prompt = null;
+
+	const i18n = getContext('i18n');
+
+	let loading = false;
+
+	let title = '';
+	let command = '';
+	let content = '';
+
+	let accessControl = null;
+
+	let showAccessControlModal = false;
+
+	$: if (!edit) {
+		command = title !== '' ? `${title.replace(/\s+/g, '-').toLowerCase()}` : '';
+	}
+
+	const submitHandler = async () => {
+		loading = true;
+
+		if (validateCommandString(command)) {
+			await onSubmit({
+				title,
+				command,
+				content,
+				access_control: accessControl
+			});
+		} else {
+			toast.error(
+				$i18n.t('Only alphanumeric characters and hyphens are allowed in the command string.')
+			);
+		}
+
+		loading = false;
+	};
+
+	const validateCommandString = (inputString) => {
+		// Regular expression to match only alphanumeric characters and hyphen
+		const regex = /^[a-zA-Z0-9-]+$/;
+
+		// Test the input string against the regular expression
+		return regex.test(inputString);
+	};
+
+	onMount(async () => {
+		if (prompt) {
+			title = prompt.title;
+			await tick();
+
+			command = prompt.command.at(0) === '/' ? prompt.command.slice(1) : prompt.command;
+			content = prompt.content;
+
+			accessControl = prompt?.access_control ?? null;
+		}
+	});
+</script>
+
+<AccessControlModal bind:show={showAccessControlModal} bind:accessControl />
+
+<div class="w-full max-h-full flex justify-center">
+	<form
+		class="flex flex-col w-full mb-10"
+		on:submit|preventDefault={() => {
+			submitHandler();
+		}}
+	>
+		<div class="my-2">
+			<Tooltip
+				content={`${$i18n.t('Only alphanumeric characters and hyphens are allowed')} - ${$i18n.t(
+					'Activate this command by typing "/{{COMMAND}}" to chat input.',
+					{
+						COMMAND: command
+					}
+				)}`}
+				placement="bottom-start"
+			>
+				<div class="flex flex-col w-full">
+					<div class="flex items-center">
+						<input
+							class="text-2xl font-semibold w-full bg-transparent outline-none"
+							placeholder={$i18n.t('Title')}
+							bind:value={title}
+							required
+						/>
+
+						<div class="self-center flex-shrink-0">
+							<button
+								class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
+								type="button"
+								on:click={() => {
+									showAccessControlModal = true;
+								}}
+							>
+								<LockClosed strokeWidth="2.5" className="size-3.5" />
+
+								<div class="text-sm font-medium flex-shrink-0">
+									{$i18n.t('Access')}
+								</div>
+							</button>
+						</div>
+					</div>
+
+					<div class="flex gap-0.5 items-center text-xs text-gray-500">
+						<div class="">/</div>
+						<input
+							class=" w-full bg-transparent outline-none"
+							placeholder={$i18n.t('Command')}
+							bind:value={command}
+							required
+							disabled={edit}
+						/>
+					</div>
+				</div>
+			</Tooltip>
+		</div>
+
+		<div class="my-2">
+			<div class="flex w-full justify-between">
+				<div class=" self-center text-sm font-semibold">{$i18n.t('Prompt Content')}</div>
+			</div>
+
+			<div class="mt-2">
+				<div>
+					<Textarea
+						className="text-sm w-full bg-transparent outline-none overflow-y-hidden resize-none"
+						placeholder={$i18n.t('Write a summary in 50 words that summarizes [topic or keyword].')}
+						bind:value={content}
+						rows={6}
+						required
+					/>
+				</div>
+
+				<div class="text-xs text-gray-400 dark:text-gray-500">
+					ⓘ {$i18n.t('Format your variables using brackets like this:')}&nbsp;<span
+						class=" text-gray-600 dark:text-gray-300 font-medium"
+						>{'{{'}{$i18n.t('variable')}{'}}'}</span
+					>.
+					{$i18n.t('Make sure to enclose them with')}
+					<span class=" text-gray-600 dark:text-gray-300 font-medium">{'{{'}</span>
+					{$i18n.t('and')}
+					<span class=" text-gray-600 dark:text-gray-300 font-medium">{'}}'}</span>.
+				</div>
+
+				<div class="text-xs text-gray-400 dark:text-gray-500">
+					{$i18n.t('Utilize')}<span class=" text-gray-600 dark:text-gray-300 font-medium">
+						{` {{CLIPBOARD}}`}</span
+					>
+					{$i18n.t('variable to have them replaced with clipboard content.')}
+				</div>
+			</div>
+		</div>
+
+		<div class="my-4 flex justify-end pb-20">
+			<button
+				class=" text-sm w-full lg:w-fit px-4 py-2 transition rounded-lg {loading
+					? ' cursor-not-allowed bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black'
+					: 'bg-black hover:bg-gray-900 text-white dark:bg-white dark:hover:bg-gray-100 dark:text-black'} flex w-full justify-center"
+				type="submit"
+				disabled={loading}
+			>
+				<div class=" self-center font-medium">{$i18n.t('Save & Create')}</div>
+
+				{#if loading}
+					<div class="ml-1.5 self-center">
+						<svg
+							class=" w-4 h-4"
+							viewBox="0 0 24 24"
+							fill="currentColor"
+							xmlns="http://www.w3.org/2000/svg"
+							><style>
+								.spinner_ajPY {
+									transform-origin: center;
+									animation: spinner_AtaB 0.75s infinite linear;
+								}
+								@keyframes spinner_AtaB {
+									100% {
+										transform: rotate(360deg);
+									}
+								}
+							</style><path
+								d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+								opacity=".25"
+							/><path
+								d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+								class="spinner_ajPY"
+							/></svg
+						>
+					</div>
+				{/if}
+			</button>
+		</div>
+	</form>
+</div>
diff --git a/src/lib/components/workspace/Prompts/PromptMenu.svelte b/src/lib/components/workspace/Prompts/PromptMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9a4177b1edd979597a4c2af4391562eac91f75ac
--- /dev/null
+++ b/src/lib/components/workspace/Prompts/PromptMenu.svelte
@@ -0,0 +1,92 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Tools.svelte b/src/lib/components/workspace/Tools.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c8731dfc4660a90a25464426f84c8595e142e519
--- /dev/null
+++ b/src/lib/components/workspace/Tools.svelte
@@ -0,0 +1,525 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+
+	import { onMount, getContext } from 'svelte';
+	import { WEBUI_NAME, config, prompts, tools as _tools, user } from '$lib/stores';
+	import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts';
+
+	import { goto } from '$app/navigation';
+	import {
+		createNewTool,
+		deleteToolById,
+		exportTools,
+		getToolById,
+		getToolList,
+		getTools
+	} from '$lib/apis/tools';
+	import ArrowDownTray from '../icons/ArrowDownTray.svelte';
+	import Tooltip from '../common/Tooltip.svelte';
+	import ConfirmDialog from '../common/ConfirmDialog.svelte';
+	import ToolMenu from './Tools/ToolMenu.svelte';
+	import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
+	import ValvesModal from './common/ValvesModal.svelte';
+	import ManifestModal from './common/ManifestModal.svelte';
+	import Heart from '../icons/Heart.svelte';
+	import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import GarbageBin from '../icons/GarbageBin.svelte';
+	import Search from '../icons/Search.svelte';
+	import Plus from '../icons/Plus.svelte';
+	import ChevronRight from '../icons/ChevronRight.svelte';
+	import Spinner from '../common/Spinner.svelte';
+	import { capitalizeFirstLetter } from '$lib/utils';
+
+	const i18n = getContext('i18n');
+
+	let shiftKey = false;
+	let loaded = false;
+
+	let toolsImportInputElement: HTMLInputElement;
+	let importFiles;
+
+	let showConfirm = false;
+	let query = '';
+
+	let showManifestModal = false;
+	let showValvesModal = false;
+	let selectedTool = null;
+
+	let showDeleteConfirm = false;
+
+	let tools = [];
+	let filteredItems = [];
+
+	$: filteredItems = tools.filter(
+		(t) =>
+			query === '' ||
+			t.name.toLowerCase().includes(query.toLowerCase()) ||
+			t.id.toLowerCase().includes(query.toLowerCase())
+	);
+
+	const shareHandler = async (tool) => {
+		const item = await getToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
+
+		const url = 'https://openwebui.com';
+
+		const tab = await window.open(`${url}/tools/create`, '_blank');
+
+		// Define the event handler function
+		const messageHandler = (event) => {
+			if (event.origin !== url) return;
+			if (event.data === 'loaded') {
+				tab.postMessage(JSON.stringify(item), '*');
+
+				// Remove the event listener after handling the message
+				window.removeEventListener('message', messageHandler);
+			}
+		};
+
+		window.addEventListener('message', messageHandler, false);
+		console.log(item);
+	};
+
+	const cloneHandler = async (tool) => {
+		const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_tool) {
+			sessionStorage.tool = JSON.stringify({
+				..._tool,
+				id: `${_tool.id}_clone`,
+				name: `${_tool.name} (Clone)`
+			});
+			goto('/workspace/tools/create');
+		}
+	};
+
+	const exportHandler = async (tool) => {
+		const _tool = await getToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (_tool) {
+			let blob = new Blob([JSON.stringify([_tool])], {
+				type: 'application/json'
+			});
+			saveAs(blob, `tool-${_tool.id}-export-${Date.now()}.json`);
+		}
+	};
+
+	const deleteHandler = async (tool) => {
+		const res = await deleteToolById(localStorage.token, tool.id).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Tool deleted successfully'));
+
+			init();
+		}
+	};
+
+	const init = async () => {
+		tools = await getToolList(localStorage.token);
+		_tools.set(await getTools(localStorage.token));
+	};
+
+	onMount(async () => {
+		await init();
+		loaded = true;
+
+		const onKeyDown = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = true;
+			}
+		};
+
+		const onKeyUp = (event) => {
+			if (event.key === 'Shift') {
+				shiftKey = false;
+			}
+		};
+
+		const onBlur = () => {
+			shiftKey = false;
+		};
+
+		window.addEventListener('keydown', onKeyDown);
+		window.addEventListener('keyup', onKeyUp);
+		window.addEventListener('blur', onBlur);
+
+		return () => {
+			window.removeEventListener('keydown', onKeyDown);
+			window.removeEventListener('keyup', onKeyUp);
+			window.removeEventListener('blur', onBlur);
+		};
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Tools')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<div class="flex flex-col gap-1 my-1.5">
+		<div class="flex justify-between items-center">
+			<div class="flex md:self-center text-xl font-medium px-0.5 items-center">
+				{$i18n.t('Tools')}
+				<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-50 dark:bg-gray-850" />
+				<span class="text-lg font-medium text-gray-500 dark:text-gray-300"
+					>{filteredItems.length}</span
+				>
+			</div>
+		</div>
+
+		<div class=" flex w-full space-x-2">
+			<div class="flex flex-1">
+				<div class=" self-center ml-1 mr-3">
+					<Search className="size-3.5" />
+				</div>
+				<input
+					class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-none bg-transparent"
+					bind:value={query}
+					placeholder={$i18n.t('Search Tools')}
+				/>
+			</div>
+
+			<div>
+				<a
+					class=" px-2 py-2 rounded-xl hover:bg-gray-700/10 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition font-medium text-sm flex items-center space-x-1"
+					href="/workspace/tools/create"
+				>
+					<Plus className="size-3.5" />
+				</a>
+			</div>
+		</div>
+	</div>
+
+	<div class="mb-5 gap-2 grid lg:grid-cols-2 xl:grid-cols-3">
+		{#each filteredItems as tool}
+			<div
+				class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl transition"
+			>
+				<a
+					class=" flex flex-1 space-x-3.5 cursor-pointer w-full"
+					href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
+				>
+					<div class="flex items-center text-left">
+						<div class=" flex-1 self-center">
+							<Tooltip content={tool?.meta?.description ?? ''} placement="top-start">
+								<div class=" font-semibold flex items-center gap-1.5">
+									<div
+										class=" text-xs font-bold px-1 rounded uppercase line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+									>
+										TOOL
+									</div>
+
+									{#if tool?.meta?.manifest?.version}
+										<div
+											class="text-xs font-bold px-1 rounded line-clamp-1 bg-gray-500/20 text-gray-700 dark:text-gray-200"
+										>
+											v{tool?.meta?.manifest?.version ?? ''}
+										</div>
+									{/if}
+
+									<div class="line-clamp-1">
+										{tool.name}
+
+										<span class=" text-gray-500 text-xs font-medium flex-shrink-0">{tool.id}</span>
+									</div>
+								</div>
+							</Tooltip>
+
+							<div class="px-0.5">
+								<div class="flex gap-1.5 mt-0.5 mb-0.5">
+									<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
+										{tool.meta.description}
+									</div>
+								</div>
+
+								<div class="text-xs text-gray-500 shrink-0">
+									<Tooltip
+										content={tool?.user?.email ?? $i18n.t('Deleted User')}
+										className="flex shrink-0"
+										placement="top-start"
+									>
+										{$i18n.t('By {{name}}', {
+											name: capitalizeFirstLetter(
+												tool?.user?.name ?? tool?.user?.email ?? $i18n.t('Deleted User')
+											)
+										})}
+									</Tooltip>
+								</div>
+							</div>
+						</div>
+					</div>
+				</a>
+				<div class="flex flex-row gap-0.5 self-center">
+					{#if shiftKey}
+						<Tooltip content={$i18n.t('Delete')}>
+							<button
+								class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								type="button"
+								on:click={() => {
+									deleteHandler(tool);
+								}}
+							>
+								<GarbageBin />
+							</button>
+						</Tooltip>
+					{:else}
+						{#if tool?.meta?.manifest?.funding_url ?? false}
+							<Tooltip content="Support">
+								<button
+									class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+									type="button"
+									on:click={() => {
+										selectedTool = tool;
+										showManifestModal = true;
+									}}
+								>
+									<Heart />
+								</button>
+							</Tooltip>
+						{/if}
+
+						<Tooltip content={$i18n.t('Valves')}>
+							<button
+								class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								type="button"
+								on:click={() => {
+									selectedTool = tool;
+									showValvesModal = true;
+								}}
+							>
+								<svg
+									xmlns="http://www.w3.org/2000/svg"
+									fill="none"
+									viewBox="0 0 24 24"
+									stroke-width="1.5"
+									stroke="currentColor"
+									class="size-4"
+								>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
+									/>
+									<path
+										stroke-linecap="round"
+										stroke-linejoin="round"
+										d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
+									/>
+								</svg>
+							</button>
+						</Tooltip>
+
+						<ToolMenu
+							editHandler={() => {
+								goto(`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`);
+							}}
+							shareHandler={() => {
+								shareHandler(tool);
+							}}
+							cloneHandler={() => {
+								cloneHandler(tool);
+							}}
+							exportHandler={() => {
+								exportHandler(tool);
+							}}
+							deleteHandler={async () => {
+								selectedTool = tool;
+								showDeleteConfirm = true;
+							}}
+							onClose={() => {}}
+						>
+							<button
+								class="self-center w-fit text-sm p-1.5 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
+								type="button"
+							>
+								<EllipsisHorizontal className="size-5" />
+							</button>
+						</ToolMenu>
+					{/if}
+				</div>
+			</div>
+		{/each}
+	</div>
+
+	{#if $user?.role === 'admin'}
+		<div class=" flex justify-end w-full mb-2">
+			<div class="flex space-x-2">
+				<input
+					id="documents-import-input"
+					bind:this={toolsImportInputElement}
+					bind:files={importFiles}
+					type="file"
+					accept=".json"
+					hidden
+					on:change={() => {
+						console.log(importFiles);
+						showConfirm = true;
+					}}
+				/>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={() => {
+						toolsImportInputElement.click();
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Import Tools')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 9.5a.75.75 0 0 1-.75-.75V8.06l-.72.72a.75.75 0 0 1-1.06-1.06l2-2a.75.75 0 0 1 1.06 0l2 2a.75.75 0 1 1-1.06 1.06l-.72-.72v2.69a.75.75 0 0 1-.75.75Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+
+				<button
+					class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
+					on:click={async () => {
+						const _tools = await exportTools(localStorage.token).catch((error) => {
+							toast.error(error);
+							return null;
+						});
+
+						if (_tools) {
+							let blob = new Blob([JSON.stringify(_tools)], {
+								type: 'application/json'
+							});
+							saveAs(blob, `tools-export-${Date.now()}.json`);
+						}
+					}}
+				>
+					<div class=" self-center mr-2 font-medium line-clamp-1">{$i18n.t('Export Tools')}</div>
+
+					<div class=" self-center">
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							viewBox="0 0 16 16"
+							fill="currentColor"
+							class="w-4 h-4"
+						>
+							<path
+								fill-rule="evenodd"
+								d="M4 2a1.5 1.5 0 0 0-1.5 1.5v9A1.5 1.5 0 0 0 4 14h8a1.5 1.5 0 0 0 1.5-1.5V6.621a1.5 1.5 0 0 0-.44-1.06L9.94 2.439A1.5 1.5 0 0 0 8.878 2H4Zm4 3.5a.75.75 0 0 1 .75.75v2.69l.72-.72a.75.75 0 1 1 1.06 1.06l-2 2a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 0 1 1.06-1.06l.72.72V6.25A.75.75 0 0 1 8 5.5Z"
+								clip-rule="evenodd"
+							/>
+						</svg>
+					</div>
+				</button>
+			</div>
+		</div>
+	{/if}
+
+	{#if $config?.features.enable_community_sharing}
+		<div class=" my-16">
+			<div class=" text-xl font-medium mb-1 line-clamp-1">
+				{$i18n.t('Made by OpenWebUI Community')}
+			</div>
+
+			<a
+				class=" flex cursor-pointer items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-850 w-full mb-2 px-3.5 py-1.5 rounded-xl transition"
+				href="https://openwebui.com/#open-webui-community"
+				target="_blank"
+			>
+				<div class=" self-center">
+					<div class=" font-semibold line-clamp-1">{$i18n.t('Discover a tool')}</div>
+					<div class=" text-sm line-clamp-1">
+						{$i18n.t('Discover, download, and explore custom tools')}
+					</div>
+				</div>
+
+				<div>
+					<div>
+						<ChevronRight />
+					</div>
+				</div>
+			</a>
+		</div>
+	{/if}
+
+	<DeleteConfirmDialog
+		bind:show={showDeleteConfirm}
+		title={$i18n.t('Delete tool?')}
+		on:confirm={() => {
+			deleteHandler(selectedTool);
+		}}
+	>
+		<div class=" text-sm text-gray-500">
+			{$i18n.t('This will delete')} <span class="  font-semibold">{selectedTool.name}</span>.
+		</div>
+	</DeleteConfirmDialog>
+
+	<ValvesModal bind:show={showValvesModal} type="tool" id={selectedTool?.id ?? null} />
+	<ManifestModal bind:show={showManifestModal} manifest={selectedTool?.meta?.manifest ?? {}} />
+
+	<ConfirmDialog
+		bind:show={showConfirm}
+		on:confirm={() => {
+			const reader = new FileReader();
+			reader.onload = async (event) => {
+				const _tools = JSON.parse(event.target.result);
+				console.log(_tools);
+
+				for (const tool of _tools) {
+					const res = await createNewTool(localStorage.token, tool).catch((error) => {
+						toast.error(error);
+						return null;
+					});
+				}
+
+				toast.success($i18n.t('Tool imported successfully'));
+				tools.set(await getTools(localStorage.token));
+			};
+
+			reader.readAsText(importFiles[0]);
+		}}
+	>
+		<div class="text-sm text-gray-500">
+			<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
+				<div>{$i18n.t('Please carefully review the following warnings:')}</div>
+
+				<ul class=" mt-1 list-disc pl-4 text-xs">
+					<li>
+						{$i18n.t('Tools have a function calling system that allows arbitrary code execution')}.
+					</li>
+					<li>{$i18n.t('Do not install tools from sources you do not fully trust.')}</li>
+				</ul>
+			</div>
+
+			<div class="my-3">
+				{$i18n.t(
+					'I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.'
+				)}
+			</div>
+		</div>
+	</ConfirmDialog>
+{:else}
+	<div class="w-full h-full flex justify-center items-center">
+		<Spinner />
+	</div>
+{/if}
diff --git a/src/lib/components/workspace/Tools/ToolMenu.svelte b/src/lib/components/workspace/Tools/ToolMenu.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..93a26b118df7a77cd11636ee0f048c604dfc662d
--- /dev/null
+++ b/src/lib/components/workspace/Tools/ToolMenu.svelte
@@ -0,0 +1,117 @@
+<script lang="ts">
+	import { DropdownMenu } from 'bits-ui';
+	import { flyAndScale } from '$lib/utils/transitions';
+	import { getContext } from 'svelte';
+
+	import Dropdown from '$lib/components/common/Dropdown.svelte';
+	import GarbageBin from '$lib/components/icons/GarbageBin.svelte';
+	import Pencil from '$lib/components/icons/Pencil.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Tags from '$lib/components/chat/Tags.svelte';
+	import Share from '$lib/components/icons/Share.svelte';
+	import ArchiveBox from '$lib/components/icons/ArchiveBox.svelte';
+	import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
+	import ArrowDownTray from '$lib/components/icons/ArrowDownTray.svelte';
+
+	const i18n = getContext('i18n');
+
+	export let editHandler: Function;
+	export let shareHandler: Function;
+	export let cloneHandler: Function;
+	export let exportHandler: Function;
+	export let deleteHandler: Function;
+	export let onClose: Function;
+
+	let show = false;
+</script>
+
+<Dropdown
+	bind:show
+	on:change={(e) => {
+		if (e.detail === false) {
+			onClose();
+		}
+	}}
+>
+	<Tooltip content={$i18n.t('More')}>
+		<slot />
+	</Tooltip>
+
+	<div slot="content">
+		<DropdownMenu.Content
+			class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow"
+			sideOffset={-2}
+			side="bottom"
+			align="start"
+			transition={flyAndScale}
+		>
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					editHandler();
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					fill="none"
+					viewBox="0 0 24 24"
+					stroke-width="1.5"
+					stroke="currentColor"
+					class="w-4 h-4"
+				>
+					<path
+						stroke-linecap="round"
+						stroke-linejoin="round"
+						d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L6.832 19.82a4.5 4.5 0 01-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 011.13-1.897L16.863 4.487zm0 0L19.5 7.125"
+					/>
+				</svg>
+
+				<div class="flex items-center">{$i18n.t('Edit')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800  rounded-md"
+				on:click={() => {
+					shareHandler();
+				}}
+			>
+				<Share />
+				<div class="flex items-center">{$i18n.t('Share')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					cloneHandler();
+				}}
+			>
+				<DocumentDuplicate />
+
+				<div class="flex items-center">{$i18n.t('Clone')}</div>
+			</DropdownMenu.Item>
+
+			<DropdownMenu.Item
+				class="flex gap-2 items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					exportHandler();
+				}}
+			>
+				<ArrowDownTray />
+
+				<div class="flex items-center">{$i18n.t('Export')}</div>
+			</DropdownMenu.Item>
+
+			<hr class="border-gray-100 dark:border-gray-800 my-1" />
+
+			<DropdownMenu.Item
+				class="flex  gap-2  items-center px-3 py-2 text-sm  font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
+				on:click={() => {
+					deleteHandler();
+				}}
+			>
+				<GarbageBin strokeWidth="2" />
+				<div class="flex items-center">{$i18n.t('Delete')}</div>
+			</DropdownMenu.Item>
+		</DropdownMenu.Content>
+	</div>
+</Dropdown>
diff --git a/src/lib/components/workspace/Tools/ToolkitEditor.svelte b/src/lib/components/workspace/Tools/ToolkitEditor.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..603c074ff7c3be5ef3d05e48a9ffaabef7ce0e49
--- /dev/null
+++ b/src/lib/components/workspace/Tools/ToolkitEditor.svelte
@@ -0,0 +1,341 @@
+<script>
+	import { getContext, createEventDispatcher, onMount, tick } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import CodeEditor from '$lib/components/common/CodeEditor.svelte';
+	import { goto } from '$app/navigation';
+	import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
+	import Badge from '$lib/components/common/Badge.svelte';
+	import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import LockClosed from '$lib/components/icons/LockClosed.svelte';
+	import AccessControlModal from '../common/AccessControlModal.svelte';
+
+	const dispatch = createEventDispatcher();
+
+	let formElement = null;
+	let loading = false;
+
+	let showConfirm = false;
+	let showAccessControlModal = false;
+
+	export let edit = false;
+	export let clone = false;
+
+	export let id = '';
+	export let name = '';
+	export let meta = {
+		description: ''
+	};
+	export let content = '';
+	export let accessControl = null;
+
+	let _content = '';
+
+	$: if (content) {
+		updateContent();
+	}
+
+	const updateContent = () => {
+		_content = content;
+	};
+
+	$: if (name && !edit && !clone) {
+		id = name.replace(/\s+/g, '_').toLowerCase();
+	}
+
+	let codeEditor;
+	let boilerplate = `import os
+import requests
+from datetime import datetime
+
+
+class Tools:
+    def __init__(self):
+        pass
+
+    # Add your custom tools using pure Python code here, make sure to add type hints
+    # Use Sphinx-style docstrings to document your tools, they will be used for generating tools specifications
+    # Please refer to function_calling_filter_pipeline.py file from pipelines project for an example
+
+    def get_user_name_and_email_and_id(self, __user__: dict = {}) -> str:
+        """
+        Get the user name, Email and ID from the user object.
+        """
+
+        # Do not include :param for __user__ in the docstring as it should not be shown in the tool's specification
+        # The session user object will be passed as a parameter when the function is called
+
+        print(__user__)
+        result = ""
+
+        if "name" in __user__:
+            result += f"User: {__user__['name']}"
+        if "id" in __user__:
+            result += f" (ID: {__user__['id']})"
+        if "email" in __user__:
+            result += f" (Email: {__user__['email']})"
+
+        if result == "":
+            result = "User: Unknown"
+
+        return result
+
+    def get_current_time(self) -> str:
+        """
+        Get the current time in a more human-readable format.
+        :return: The current time.
+        """
+
+        now = datetime.now()
+        current_time = now.strftime("%I:%M:%S %p")  # Using 12-hour format with AM/PM
+        current_date = now.strftime(
+            "%A, %B %d, %Y"
+        )  # Full weekday, month name, day, and year
+
+        return f"Current Date and Time = {current_date}, {current_time}"
+
+    def calculator(self, equation: str) -> str:
+        """
+        Calculate the result of an equation.
+        :param equation: The equation to calculate.
+        """
+
+        # Avoid using eval in production code
+        # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
+        try:
+            result = eval(equation)
+            return f"{equation} = {result}"
+        except Exception as e:
+            print(e)
+            return "Invalid equation"
+
+    def get_current_weather(self, city: str) -> str:
+        """
+        Get the current weather for a given city.
+        :param city: The name of the city to get the weather for.
+        :return: The current weather information or an error message.
+        """
+        api_key = os.getenv("OPENWEATHER_API_KEY")
+        if not api_key:
+            return (
+                "API key is not set in the environment variable 'OPENWEATHER_API_KEY'."
+            )
+
+        base_url = "http://api.openweathermap.org/data/2.5/weather"
+        params = {
+            "q": city,
+            "appid": api_key,
+            "units": "metric",  # Optional: Use 'imperial' for Fahrenheit
+        }
+
+        try:
+            response = requests.get(base_url, params=params)
+            response.raise_for_status()  # Raise HTTPError for bad responses (4xx and 5xx)
+            data = response.json()
+
+            if data.get("cod") != 200:
+                return f"Error fetching weather data: {data.get('message')}"
+
+            weather_description = data["weather"][0]["description"]
+            temperature = data["main"]["temp"]
+            humidity = data["main"]["humidity"]
+            wind_speed = data["wind"]["speed"]
+
+            return f"Weather in {city}: {temperature}°C"
+        except requests.RequestException as e:
+            return f"Error fetching weather data: {str(e)}"
+`;
+
+	const saveHandler = async () => {
+		loading = true;
+		dispatch('save', {
+			id,
+			name,
+			meta,
+			content,
+			access_control: accessControl
+		});
+	};
+
+	const submitHandler = async () => {
+		if (codeEditor) {
+			content = _content;
+			await tick();
+
+			const res = await codeEditor.formatPythonCodeHandler();
+			await tick();
+
+			content = _content;
+			await tick();
+
+			if (res) {
+				console.log('Code formatted successfully');
+
+				saveHandler();
+			}
+		}
+	};
+</script>
+
+<AccessControlModal bind:show={showAccessControlModal} bind:accessControl />
+
+<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
+	<div class="mx-auto w-full md:px-0 h-full">
+		<form
+			bind:this={formElement}
+			class=" flex flex-col max-h-[100dvh] h-full"
+			on:submit|preventDefault={() => {
+				if (edit) {
+					submitHandler();
+				} else {
+					showConfirm = true;
+				}
+			}}
+		>
+			<div class="flex flex-col flex-1 overflow-auto h-0">
+				<div class="w-full mb-2 flex flex-col gap-0.5">
+					<div class="flex w-full items-center">
+						<div class=" flex-shrink-0 mr-2">
+							<Tooltip content={$i18n.t('Back')}>
+								<button
+									class="w-full text-left text-sm py-1.5 px-1 rounded-lg dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-gray-850"
+									on:click={() => {
+										goto('/workspace/tools');
+									}}
+									type="button"
+								>
+									<ChevronLeft strokeWidth="2.5" />
+								</button>
+							</Tooltip>
+						</div>
+
+						<div class="flex-1">
+							<Tooltip content={$i18n.t('e.g. My Tools')} placement="top-start">
+								<input
+									class="w-full text-2xl font-semibold bg-transparent outline-none"
+									type="text"
+									placeholder={$i18n.t('Tool Name')}
+									bind:value={name}
+									required
+								/>
+							</Tooltip>
+						</div>
+
+						<div class="self-center flex-shrink-0">
+							<button
+								class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
+								type="button"
+								on:click={() => {
+									showAccessControlModal = true;
+								}}
+							>
+								<LockClosed strokeWidth="2.5" className="size-3.5" />
+
+								<div class="text-sm font-medium flex-shrink-0">
+									{$i18n.t('Access')}
+								</div>
+							</button>
+						</div>
+					</div>
+
+					<div class=" flex gap-2 px-1 items-center">
+						{#if edit}
+							<div class="text-sm text-gray-500 flex-shrink-0">
+								{id}
+							</div>
+						{:else}
+							<Tooltip className="w-full" content={$i18n.t('e.g. my_tools')} placement="top-start">
+								<input
+									class="w-full text-sm disabled:text-gray-500 bg-transparent outline-none"
+									type="text"
+									placeholder={$i18n.t('Tool ID')}
+									bind:value={id}
+									required
+									disabled={edit}
+								/>
+							</Tooltip>
+						{/if}
+
+						<Tooltip
+							className="w-full self-center items-center flex"
+							content={$i18n.t('e.g. Tools for performing various operations')}
+							placement="top-start"
+						>
+							<input
+								class="w-full text-sm bg-transparent outline-none"
+								type="text"
+								placeholder={$i18n.t('Tool Description')}
+								bind:value={meta.description}
+								required
+							/>
+						</Tooltip>
+					</div>
+				</div>
+
+				<div class="mb-2 flex-1 overflow-auto h-0 rounded-lg">
+					<CodeEditor
+						bind:this={codeEditor}
+						value={content}
+						{boilerplate}
+						lang="python"
+						on:change={(e) => {
+							_content = e.detail.value;
+						}}
+						on:save={() => {
+							if (formElement) {
+								formElement.requestSubmit();
+							}
+						}}
+					/>
+				</div>
+
+				<div class="pb-3 flex justify-between">
+					<div class="flex-1 pr-3">
+						<div class="text-xs text-gray-500 line-clamp-2">
+							<span class=" font-semibold dark:text-gray-200">{$i18n.t('Warning:')}</span>
+							{$i18n.t('Tools are a function calling system with arbitrary code execution')} <br />—
+							<span class=" font-medium dark:text-gray-400"
+								>{$i18n.t(`don't install random tools from sources you don't trust.`)}</span
+							>
+						</div>
+					</div>
+
+					<button
+						class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
+						type="submit"
+					>
+						{$i18n.t('Save')}
+					</button>
+				</div>
+			</div>
+		</form>
+	</div>
+</div>
+
+<ConfirmDialog
+	bind:show={showConfirm}
+	on:confirm={() => {
+		submitHandler();
+	}}
+>
+	<div class="text-sm text-gray-500">
+		<div class=" bg-yellow-500/20 text-yellow-700 dark:text-yellow-200 rounded-lg px-4 py-3">
+			<div>{$i18n.t('Please carefully review the following warnings:')}</div>
+
+			<ul class=" mt-1 list-disc pl-4 text-xs">
+				<li>
+					{$i18n.t('Tools have a function calling system that allows arbitrary code execution.')}
+				</li>
+				<li>{$i18n.t('Do not install tools from sources you do not fully trust.')}</li>
+			</ul>
+		</div>
+
+		<div class="my-3">
+			{$i18n.t(
+				'I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.'
+			)}
+		</div>
+	</div>
+</ConfirmDialog>
diff --git a/src/lib/components/workspace/common/AccessControl.svelte b/src/lib/components/workspace/common/AccessControl.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..be2c8a2afbeeb30c2757ea1cb1f5ef61b9d87461
--- /dev/null
+++ b/src/lib/components/workspace/common/AccessControl.svelte
@@ -0,0 +1,208 @@
+<script lang="ts">
+	import { getContext, onMount } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { getGroups } from '$lib/apis/groups';
+	import Tooltip from '$lib/components/common/Tooltip.svelte';
+	import Plus from '$lib/components/icons/Plus.svelte';
+	import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte';
+	import XMark from '$lib/components/icons/XMark.svelte';
+
+	export let onChange: Function = () => {};
+
+	export let accessControl = null;
+
+	let selectedGroupId = '';
+	let groups = [];
+
+	onMount(async () => {
+		groups = await getGroups(localStorage.token);
+
+		if (accessControl === null) {
+			accessControl = null;
+		} else {
+			accessControl = {
+				read: {
+					group_ids: accessControl?.read?.group_ids ?? [],
+					user_ids: accessControl?.read?.user_ids ?? []
+				},
+				write: {
+					group_ids: accessControl?.write?.group_ids ?? [],
+					user_ids: accessControl?.write?.user_ids ?? []
+				}
+			};
+		}
+	});
+
+	$: onChange(accessControl);
+</script>
+
+<div class=" rounded-lg flex flex-col gap-2">
+	<div class="">
+		<div class=" text-sm font-semibold mb-1">{$i18n.t('Visibility')}</div>
+
+		<div class="flex gap-2.5 items-center mb-1">
+			<div>
+				<div class=" p-2 bg-black/5 dark:bg-white/5 rounded-full">
+					{#if accessControl !== null}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="w-5 h-5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
+							/>
+						</svg>
+					{:else}
+						<svg
+							xmlns="http://www.w3.org/2000/svg"
+							fill="none"
+							viewBox="0 0 24 24"
+							stroke-width="1.5"
+							stroke="currentColor"
+							class="w-5 h-5"
+						>
+							<path
+								stroke-linecap="round"
+								stroke-linejoin="round"
+								d="M6.115 5.19l.319 1.913A6 6 0 008.11 10.36L9.75 12l-.387.775c-.217.433-.132.956.21 1.298l1.348 1.348c.21.21.329.497.329.795v1.089c0 .426.24.815.622 1.006l.153.076c.433.217.956.132 1.298-.21l.723-.723a8.7 8.7 0 002.288-4.042 1.087 1.087 0 00-.358-1.099l-1.33-1.108c-.251-.21-.582-.299-.905-.245l-1.17.195a1.125 1.125 0 01-.98-.314l-.295-.295a1.125 1.125 0 010-1.591l.13-.132a1.125 1.125 0 011.3-.21l.603.302a.809.809 0 001.086-1.086L14.25 7.5l1.256-.837a4.5 4.5 0 001.528-1.732l.146-.292M6.115 5.19A9 9 0 1017.18 4.64M6.115 5.19A8.965 8.965 0 0112 3c1.929 0 3.716.607 5.18 1.64"
+							/>
+						</svg>
+					{/if}
+				</div>
+			</div>
+
+			<div>
+				<select
+					id="models"
+					class="outline-none bg-transparent text-sm font-medium rounded-lg block w-fit pr-10 max-w-full placeholder-gray-400"
+					value={accessControl !== null ? 'private' : 'public'}
+					on:change={(e) => {
+						if (e.target.value === 'public') {
+							accessControl = null;
+						} else {
+							accessControl = {
+								read: {
+									group_ids: []
+								}
+							};
+						}
+					}}
+				>
+					<option class=" text-gray-700" value="private" selected>Private</option>
+					<option class=" text-gray-700" value="public" selected>Public</option>
+				</select>
+
+				<div class=" text-xs text-gray-400 font-medium">
+					{#if accessControl !== null}
+						{$i18n.t('Only select users and groups with permission can access')}
+					{:else}
+						{$i18n.t('Accessible to all users')}
+					{/if}
+				</div>
+			</div>
+		</div>
+	</div>
+
+	{#if accessControl !== null}
+		{@const accessGroups = groups.filter((group) =>
+			accessControl.read.group_ids.includes(group.id)
+		)}
+		<div>
+			<div class="">
+				<div class="flex justify-between mb-1.5">
+					<div class="text-sm font-semibold">
+						{$i18n.t('Groups')}
+					</div>
+				</div>
+
+				<div class="flex flex-col gap-2">
+					{#if accessGroups.length > 0}
+						{#each accessGroups as group}
+							<div class="flex items-center gap-3 justify-between text-xs w-full transition">
+								<div class="flex items-center gap-1.5 w-full font-medium">
+									<div>
+										<UserCircleSolid className="size-4" />
+									</div>
+
+									<div>
+										{group.name}
+									</div>
+								</div>
+
+								<div class="w-full flex justify-end">
+									<button
+										class=" rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-850 transition"
+										type="button"
+										on:click={() => {
+											accessControl.read.group_ids = accessControl.read.group_ids.filter(
+												(id) => id !== group.id
+											);
+										}}
+									>
+										<XMark />
+									</button>
+								</div>
+							</div>
+						{/each}
+					{:else}
+						<div class="flex items-center justify-center">
+							<div class="text-gray-500 text-xs text-center py-2 px-10">
+								{$i18n.t('No groups with access, add a group to grant access')}
+							</div>
+						</div>
+					{/if}
+				</div>
+			</div>
+
+			<hr class=" my-2 border-black/5 dark:border-white/5" />
+
+			<div class="mb-1">
+				<div class="flex w-full">
+					<div class="flex flex-1 items-center">
+						<div class="w-full">
+							<select
+								class="outline-none bg-transparent text-sm font-medium rounded-lg block w-full pr-10 max-w-full dark:placeholder-gray-700"
+								bind:value={selectedGroupId}
+							>
+								<option class=" text-gray-700" value="" disabled selected
+									>{$i18n.t('Select a group')}</option
+								>
+								{#each groups.filter((group) => !accessControl.read.group_ids.includes(group.id)) as group}
+									<option class=" text-gray-700" value={group.id}>{group.name}</option>
+								{/each}
+							</select>
+						</div>
+						<div>
+							<Tooltip content={$i18n.t('Add Group')}>
+								<button
+									class=" p-1 rounded-xl bg-transparent dark:hover:bg-white/5 hover:bg-black/5 transition font-medium text-sm flex items-center space-x-1"
+									type="button"
+									on:click={() => {
+										if (selectedGroupId !== '') {
+											accessControl.read.group_ids = [
+												...accessControl.read.group_ids,
+												selectedGroupId
+											];
+
+											selectedGroupId = '';
+										}
+									}}
+								>
+									<Plus className="size-3.5" />
+								</button>
+							</Tooltip>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	{/if}
+</div>
diff --git a/src/lib/components/workspace/common/AccessControlModal.svelte b/src/lib/components/workspace/common/AccessControlModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..edde237dc8d1a881560cfca115364f659fdb9858
--- /dev/null
+++ b/src/lib/components/workspace/common/AccessControlModal.svelte
@@ -0,0 +1,43 @@
+<script>
+	import { getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import Modal from '$lib/components/common/Modal.svelte';
+	import AccessControl from './AccessControl.svelte';
+
+	export let show = false;
+	export let accessControl = null;
+
+	export let onChange = () => {};
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-100 px-5 pt-3 pb-1">
+			<div class=" text-lg font-medium self-center font-primary">
+				{$i18n.t('Access Control')}
+			</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="w-full px-5 pb-4 dark:text-white">
+			<AccessControl bind:accessControl {onChange} />
+		</div>
+	</div>
+</Modal>
diff --git a/src/lib/components/workspace/common/ManifestModal.svelte b/src/lib/components/workspace/common/ManifestModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0e646e4097f15dfe0a2051120533b44fbe2ccc08
--- /dev/null
+++ b/src/lib/components/workspace/common/ManifestModal.svelte
@@ -0,0 +1,104 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+
+	import Modal from '../../common/Modal.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+	export let manifest = {};
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Show your support!')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						show = false;
+					}}
+				>
+					<div class="px-1 text-sm">
+						<div class="my-2">
+							{$i18n.t(
+								'The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.'
+							)}
+						</div>
+
+						<div class="my-2">
+							{$i18n.t(
+								'Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.'
+							)}
+						</div>
+
+						<hr class="dark:border-gray-800 my-3" />
+						<div class="my-2">
+							{$i18n.t('Support this plugin:')}
+							<a
+								href={manifest.funding_url}
+								target="_blank"
+								class="underline text-blue-400 hover:text-blue-300">{manifest.funding_url}</a
+							>
+						</div>
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<button
+							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center"
+							type="submit"
+						>
+							{$i18n.t('Done')}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/components/workspace/common/ValvesModal.svelte b/src/lib/components/workspace/common/ValvesModal.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..1f23c510ec0305dfc2ba168ba9d30cc64cf94040
--- /dev/null
+++ b/src/lib/components/workspace/common/ValvesModal.svelte
@@ -0,0 +1,202 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { createEventDispatcher } from 'svelte';
+	import { onMount, getContext } from 'svelte';
+	import { addUser } from '$lib/apis/auths';
+
+	import Modal from '../../common/Modal.svelte';
+	import {
+		getFunctionValvesById,
+		getFunctionValvesSpecById,
+		updateFunctionValvesById
+	} from '$lib/apis/functions';
+	import { getToolValvesById, getToolValvesSpecById, updateToolValvesById } from '$lib/apis/tools';
+	import Spinner from '../../common/Spinner.svelte';
+	import Switch from '$lib/components/common/Switch.svelte';
+	import Valves from '$lib/components/common/Valves.svelte';
+
+	const i18n = getContext('i18n');
+	const dispatch = createEventDispatcher();
+
+	export let show = false;
+
+	export let type = 'tool';
+	export let id = null;
+
+	let saving = false;
+	let loading = false;
+
+	let valvesSpec = null;
+	let valves = {};
+
+	const submitHandler = async () => {
+		saving = true;
+
+		if (valvesSpec) {
+			// Convert string to array
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim());
+				}
+			}
+
+			let res = null;
+
+			if (type === 'tool') {
+				res = await updateToolValvesById(localStorage.token, id, valves).catch((error) => {
+					toast.error(error);
+				});
+			} else if (type === 'function') {
+				res = await updateFunctionValvesById(localStorage.token, id, valves).catch((error) => {
+					toast.error(error);
+				});
+			}
+
+			if (res) {
+				toast.success('Valves updated successfully');
+				dispatch('save');
+			}
+		}
+
+		saving = false;
+	};
+
+	const initHandler = async () => {
+		loading = true;
+		valves = {};
+		valvesSpec = null;
+
+		if (type === 'tool') {
+			valves = await getToolValvesById(localStorage.token, id);
+			valvesSpec = await getToolValvesSpecById(localStorage.token, id);
+		} else if (type === 'function') {
+			valves = await getFunctionValvesById(localStorage.token, id);
+			valvesSpec = await getFunctionValvesSpecById(localStorage.token, id);
+		}
+
+		if (!valves) {
+			valves = {};
+		}
+
+		if (valvesSpec) {
+			// Convert array to string
+			for (const property in valvesSpec.properties) {
+				if (valvesSpec.properties[property]?.type === 'array') {
+					valves[property] = (valves[property] ?? []).join(',');
+				}
+			}
+		}
+
+		loading = false;
+	};
+
+	$: if (show) {
+		initHandler();
+	}
+</script>
+
+<Modal size="sm" bind:show>
+	<div>
+		<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
+			<div class=" text-lg font-medium self-center">{$i18n.t('Valves')}</div>
+			<button
+				class="self-center"
+				on:click={() => {
+					show = false;
+				}}
+			>
+				<svg
+					xmlns="http://www.w3.org/2000/svg"
+					viewBox="0 0 20 20"
+					fill="currentColor"
+					class="w-5 h-5"
+				>
+					<path
+						d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
+					/>
+				</svg>
+			</button>
+		</div>
+
+		<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
+			<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
+				<form
+					class="flex flex-col w-full"
+					on:submit|preventDefault={() => {
+						submitHandler();
+					}}
+				>
+					<div class="px-1">
+						{#if !loading}
+							<Valves {valvesSpec} bind:valves />
+						{:else}
+							<Spinner className="size-5" />
+						{/if}
+					</div>
+
+					<div class="flex justify-end pt-3 text-sm font-medium">
+						<button
+							class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg flex flex-row space-x-1 items-center {saving
+								? ' cursor-not-allowed'
+								: ''}"
+							type="submit"
+							disabled={saving}
+						>
+							{$i18n.t('Save')}
+
+							{#if saving}
+								<div class="ml-2 self-center">
+									<svg
+										class=" w-4 h-4"
+										viewBox="0 0 24 24"
+										fill="currentColor"
+										xmlns="http://www.w3.org/2000/svg"
+										><style>
+											.spinner_ajPY {
+												transform-origin: center;
+												animation: spinner_AtaB 0.75s infinite linear;
+											}
+											@keyframes spinner_AtaB {
+												100% {
+													transform: rotate(360deg);
+												}
+											}
+										</style><path
+											d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
+											opacity=".25"
+										/><path
+											d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
+											class="spinner_ajPY"
+										/></svg
+									>
+								</div>
+							{/if}
+						</button>
+					</div>
+				</form>
+			</div>
+		</div>
+	</div>
+</Modal>
+
+<style>
+	input::-webkit-outer-spin-button,
+	input::-webkit-inner-spin-button {
+		/* display: none; <- Crashes Chrome on hover */
+		-webkit-appearance: none;
+		margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
+	}
+
+	.tabs::-webkit-scrollbar {
+		display: none; /* for Chrome, Safari and Opera */
+	}
+
+	.tabs {
+		-ms-overflow-style: none; /* IE and Edge */
+		scrollbar-width: none; /* Firefox */
+	}
+
+	input[type='number'] {
+		-moz-appearance: textfield; /* Firefox */
+	}
+</style>
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..700bd3c4278eee7b8b94ee0916e39417ec6d5f9c
--- /dev/null
+++ b/src/lib/constants.ts
@@ -0,0 +1,103 @@
+import { browser, dev } from '$app/environment';
+// import { version } from '../../package.json';
+
+export const APP_NAME = 'Open WebUI';
+
+export const WEBUI_HOSTNAME = browser ? (dev ? `${location.hostname}:8080` : ``) : '';
+export const WEBUI_BASE_URL = browser ? (dev ? `http://${WEBUI_HOSTNAME}` : ``) : ``;
+export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`;
+
+export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama`;
+export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai`;
+export const AUDIO_API_BASE_URL = `${WEBUI_BASE_URL}/audio/api/v1`;
+export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
+export const RETRIEVAL_API_BASE_URL = `${WEBUI_BASE_URL}/retrieval/api/v1`;
+
+export const WEBUI_VERSION = APP_VERSION;
+export const WEBUI_BUILD_HASH = APP_BUILD_HASH;
+export const REQUIRED_OLLAMA_VERSION = '0.1.16';
+
+export const SUPPORTED_FILE_TYPE = [
+	'application/epub+zip',
+	'application/pdf',
+	'text/plain',
+	'text/csv',
+	'text/xml',
+	'text/html',
+	'text/x-python',
+	'text/css',
+	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+	'application/octet-stream',
+	'application/x-javascript',
+	'text/markdown',
+	'audio/mpeg',
+	'audio/wav',
+	'audio/ogg',
+	'audio/x-m4a'
+];
+
+export const SUPPORTED_FILE_EXTENSIONS = [
+	'md',
+	'rst',
+	'go',
+	'py',
+	'java',
+	'sh',
+	'bat',
+	'ps1',
+	'cmd',
+	'js',
+	'ts',
+	'css',
+	'cpp',
+	'hpp',
+	'h',
+	'c',
+	'cs',
+	'htm',
+	'html',
+	'sql',
+	'log',
+	'ini',
+	'pl',
+	'pm',
+	'r',
+	'dart',
+	'dockerfile',
+	'env',
+	'php',
+	'hs',
+	'hsc',
+	'lua',
+	'nginxconf',
+	'conf',
+	'm',
+	'mm',
+	'plsql',
+	'perl',
+	'rb',
+	'rs',
+	'db2',
+	'scala',
+	'bash',
+	'swift',
+	'vue',
+	'svelte',
+	'doc',
+	'docx',
+	'pdf',
+	'csv',
+	'txt',
+	'xls',
+	'xlsx',
+	'pptx',
+	'ppt',
+	'msg'
+];
+
+export const PASTED_TEXT_CHARACTER_LIMIT = 1000;
+
+// Source: https://kit.svelte.dev/docs/modules#$env-static-public
+// This feature, akin to $env/static/private, exclusively incorporates environment variables
+// that are prefixed with config.kit.env.publicPrefix (usually set to PUBLIC_).
+// Consequently, these variables can be securely exposed to client-side code.
diff --git a/src/lib/i18n/index.ts b/src/lib/i18n/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..172c42f91bdc3fca84c460676147167448cfbe44
--- /dev/null
+++ b/src/lib/i18n/index.ts
@@ -0,0 +1,79 @@
+import i18next from 'i18next';
+import resourcesToBackend from 'i18next-resources-to-backend';
+import LanguageDetector from 'i18next-browser-languagedetector';
+import type { i18n as i18nType } from 'i18next';
+import { writable } from 'svelte/store';
+
+const createI18nStore = (i18n: i18nType) => {
+	const i18nWritable = writable(i18n);
+
+	i18n.on('initialized', () => {
+		i18nWritable.set(i18n);
+	});
+	i18n.on('loaded', () => {
+		i18nWritable.set(i18n);
+	});
+	i18n.on('added', () => i18nWritable.set(i18n));
+	i18n.on('languageChanged', () => {
+		i18nWritable.set(i18n);
+	});
+	return i18nWritable;
+};
+
+const createIsLoadingStore = (i18n: i18nType) => {
+	const isLoading = writable(false);
+
+	// if loaded resources are empty || {}, set loading to true
+	i18n.on('loaded', (resources) => {
+		// console.log('loaded:', resources);
+		Object.keys(resources).length !== 0 && isLoading.set(false);
+	});
+
+	// if resources failed loading, set loading to true
+	i18n.on('failedLoading', () => {
+		isLoading.set(true);
+	});
+
+	return isLoading;
+};
+
+export const initI18n = (defaultLocale: string | undefined) => {
+	let detectionOrder = defaultLocale
+		? ['querystring', 'localStorage']
+		: ['querystring', 'localStorage', 'navigator'];
+	let fallbackDefaultLocale = defaultLocale ? [defaultLocale] : ['en-US'];
+
+	const loadResource = (language: string, namespace: string) =>
+		import(`./locales/${language}/${namespace}.json`);
+
+	i18next
+		.use(resourcesToBackend(loadResource))
+		.use(LanguageDetector)
+		.init({
+			debug: false,
+			detection: {
+				order: detectionOrder,
+				caches: ['localStorage'],
+				lookupQuerystring: 'lang',
+				lookupLocalStorage: 'locale'
+			},
+			fallbackLng: {
+				default: fallbackDefaultLocale
+			},
+			ns: 'translation',
+			returnEmptyString: false,
+			interpolation: {
+				escapeValue: false // not needed for svelte as it escapes by default
+			}
+		});
+};
+
+const i18n = createI18nStore(i18next);
+const isLoadingStore = createIsLoadingStore(i18next);
+
+export const getLanguages = async () => {
+	const languages = (await import(`./locales/languages.json`)).default;
+	return languages;
+};
+export default i18n;
+export const isLoading = isLoadingStore;
diff --git a/src/lib/i18n/locales/ar-BH/translation.json b/src/lib/i18n/locales/ar-BH/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..1f43be7eff55a62cc1177eb5a42e3a84a82b4106
--- /dev/null
+++ b/src/lib/i18n/locales/ar-BH/translation.json
@@ -0,0 +1,1029 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' أو '-1' لا توجد انتهاء",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "( `sh webui.sh --api`مثال)",
+	"(latest)": "(الأخير)",
+	"{{ models }}": "{{ نماذج }}",
+	"{{user}}'s Chats": "دردشات {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} مطلوب",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "يتم استخدام نموذج المهمة عند تنفيذ مهام مثل إنشاء عناوين للدردشات واستعلامات بحث الويب",
+	"a user": "مستخدم",
+	"About": "عن",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "الحساب",
+	"Account Activation Pending": "",
+	"Accurate information": "معلومات دقيقة",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "أضف",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "أضف وصفا موجزا حول ما يفعله هذا النموذج",
+	"Add a tag": "أضافة تاق",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "أضافة مطالبة مخصصه",
+	"Add Files": "إضافة ملفات",
+	"Add Group": "",
+	"Add Memory": "إضافة ذكرايات",
+	"Add Model": "اضافة موديل",
+	"Add Tag": "",
+	"Add Tags": "اضافة تاق",
+	"Add text content": "",
+	"Add User": "اضافة مستخدم",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "سيؤدي ضبط هذه الإعدادات إلى تطبيق التغييرات بشكل عام على كافة المستخدمين",
+	"admin": "المشرف",
+	"Admin": "",
+	"Admin Panel": "لوحة التحكم",
+	"Admin Settings": "اعدادات المشرف",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "التعليمات المتقدمة",
+	"Advanced Params": "المعلمات المتقدمة",
+	"All chats": "",
+	"All Documents": "جميع الملفات",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "يستطيع حذف المحادثات",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "هل تملك حساب ؟",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "مساعد",
+	"and": "و",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
+	"API Base URL": "API الرابط الرئيسي",
+	"API Key": "API مفتاح",
+	"API Key created.": "API تم أنشاء المفتاح",
+	"API keys": "مفاتيح واجهة برمجة التطبيقات",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "أبريل",
+	"Archive": "الأرشيف",
+	"Archive All Chats": "أرشفة جميع الدردشات",
+	"Archived Chats": "الأرشيف المحادثات",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "هل أنت متأكد ؟",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "أرفق ملف",
+	"Attention to detail": "انتبه للتفاصيل",
+	"Attribute for Username": "",
+	"Audio": "صوتي",
+	"August": "أغسطس",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "النسخ التلقائي للاستجابة إلى الحافظة",
+	"Auto-playback response": "استجابة التشغيل التلقائي",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 الرابط الرئيسي",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 الرابط مطلوب",
+	"Available list": "",
+	"available!": "متاح",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "خلف",
+	"Bad Response": "استجابة خطاء",
+	"Banners": "لافتات",
+	"Base Model (From)": "النموذج الأساسي (من)",
+	"Batch Size (num_batch)": "",
+	"before": "قبل",
+	"Being lazy": "كون كسول",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "مفتاح واجهة برمجة تطبيقات البحث الشجاع",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "تجاوز التحقق من SSL للموقع",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "اللغاء",
+	"Capabilities": "قدرات",
+	"Certificate Path": "",
+	"Change Password": "تغير الباسورد",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "المحادثة",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "UI الدردشة",
+	"Chat Controls": "",
+	"Chat direction": "اتجاه المحادثة",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "المحادثات",
+	"Check Again": "تحقق مرة اخرى",
+	"Check for updates": "تحقق من التحديثات",
+	"Checking for updates...": "البحث عن تحديثات",
+	"Choose a model before saving...": "أختار موديل قبل الحفظ",
+	"Chunk Overlap": "Chunk تداخل",
+	"Chunk Params": "Chunk المتغيرات",
+	"Chunk Size": "Chunk حجم",
+	"Ciphers": "",
+	"Citation": "اقتباس",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "أضغط هنا للمساعدة",
+	"Click here to": "أضغط هنا الانتقال",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "أضغط هنا للاختيار",
+	"Click here to select a csv file.": "أضغط هنا للاختيار ملف csv",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "أضغط هنا",
+	"Click on the user role button to change a user's role.": "أضغط على أسم الصلاحيات لتغيرها للمستخدم",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "استنساخ",
+	"Close": "أغلق",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "مجموعة",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI الرابط الافتراضي",
+	"ComfyUI Base URL is required.": "ComfyUI الرابط مطلوب",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "الأوامر",
+	"Completions": "",
+	"Concurrent Requests": "الطلبات المتزامنة",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "تأكيد كلمة المرور",
+	"Confirm your action": "",
+	"Connections": "اتصالات",
+	"Contact Admin for WebUI Access": "",
+	"Content": "الاتصال",
+	"Content Extraction": "",
+	"Context Length": "طول السياق",
+	"Continue Response": "متابعة الرد",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "تم نسخ عنوان URL للدردشة المشتركة إلى الحافظة",
+	"Copied to clipboard": "",
+	"Copy": "نسخ",
+	"Copy last code block": "انسخ كتلة التعليمات البرمجية الأخيرة",
+	"Copy last response": "انسخ الرد الأخير",
+	"Copy Link": "أنسخ الرابط",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "تم النسخ إلى الحافظة بنجاح",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "إنشاء نموذج",
+	"Create Account": "إنشاء حساب",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "عمل مفتاح جديد",
+	"Create new secret key": "عمل سر جديد",
+	"Created at": "أنشئت في",
+	"Created At": "أنشئت من",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "الموديل المختار",
+	"Current Password": "كلمة السر الحالية",
+	"Custom": "مخصص",
+	"Dark": "مظلم",
+	"Database": "قاعدة البيانات",
+	"December": "ديسمبر",
+	"Default": "الإفتراضي",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "(SentenceTransformers) الإفتراضي",
+	"Default Model": "النموذج الافتراضي",
+	"Default model updated": "الإفتراضي تحديث الموديل",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "الإفتراضي Prompt الاقتراحات",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "الإفتراضي صلاحيات المستخدم",
+	"Delete": "حذف",
+	"Delete a model": "حذف الموديل",
+	"Delete All Chats": "حذف جميع الدردشات",
+	"Delete All Models": "",
+	"Delete chat": "حذف المحادثه",
+	"Delete Chat": "حذف المحادثه.",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "أحذف هذا الرابط",
+	"Delete tool?": "",
+	"Delete User": "حذف المستخدم",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} حذف",
+	"Deleted {{name}}": "حذف {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "وصف",
+	"Didn't fully follow instructions": "لم أتبع التعليمات بشكل كامل",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "اكتشف نموذجا",
+	"Discover a prompt": "اكتشاف موجه",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "اكتشاف وتنزيل واستكشاف المطالبات المخصصة",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "اكتشاف وتنزيل واستكشاف الإعدادات المسبقة للنموذج",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "اعرض اسم المستخدم بدلاً منك في الدردشة",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "المستند",
+	"Documentation": "",
+	"Documents": "مستندات",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "لا يجري أي اتصالات خارجية، وتظل بياناتك آمنة على الخادم المستضاف محليًا.",
+	"Don't have an account?": "ليس لديك حساب؟",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "لا أحب النمط",
+	"Done": "",
+	"Download": "تحميل",
+	"Download canceled": "تم اللغاء التحميل",
+	"Download Database": "تحميل قاعدة البيانات",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "أسقط أية ملفات هنا لإضافتها إلى المحادثة",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. الوحدات الزمنية الصالحة هي 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "تعديل",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "تعديل المستخدم",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "البريد",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "نموذج التضمين",
+	"Embedding Model Engine": "تضمين محرك النموذج",
+	"Embedding model set to \"{{embedding_model}}\"": "تم تعيين نموذج التضمين على \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "تمكين مشاركة المجتمع",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "تفعيل عمليات التسجيل الجديدة",
+	"Enable Web Search": "تمكين بحث الويب",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "تأكد من أن ملف CSV الخاص بك يتضمن 4 أعمدة بهذا الترتيب: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "أدخل رسالة {{role}} هنا",
+	"Enter a detail about yourself for your LLMs to recall": "ادخل معلومات عنك تريد أن يتذكرها الموديل",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "أدخل مفتاح واجهة برمجة تطبيقات البحث الشجاع",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "أدخل الChunk Overlap",
+	"Enter Chunk Size": "أدخل Chunk الحجم",
+	"Enter description": "",
+	"Enter Github Raw URL": "أدخل عنوان URL ل Github Raw",
+	"Enter Google PSE API Key": "أدخل مفتاح واجهة برمجة تطبيقات PSE من Google",
+	"Enter Google PSE Engine Id": "أدخل معرف محرك PSE من Google",
+	"Enter Image Size (e.g. 512x512)": "(e.g. 512x512) أدخل حجم الصورة ",
+	"Enter Jina API Key": "",
+	"Enter language codes": "أدخل كود اللغة",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "(e.g. {{modelTag}}) أدخل الموديل تاق",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "أدخل النتيجة",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "أدخل عنوان URL لاستعلام Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "أدخل مفتاح واجهة برمجة تطبيقات Serper",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "أدخل مفتاح واجهة برمجة تطبيقات Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "أدخل تسلسل التوقف",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "أدخل Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "الرابط (e.g. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "URL (e.g. http://localhost:11434)",
+	"Enter Your Email": "أدخل البريد الاكتروني",
+	"Enter Your Full Name": "أدخل الاسم كامل",
+	"Enter your message": "",
+	"Enter Your Password": "ادخل كلمة المرور",
+	"Enter Your Role": "أدخل الصلاحيات",
+	"Enter Your Username": "",
+	"Error": "خطأ",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "تجريبي",
+	"Explore the cosmos": "",
+	"Export": "تصدير",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "تصدير جميع الدردشات (جميع المستخدمين)",
+	"Export chat (.json)": "",
+	"Export Chats": "تصدير جميع الدردشات",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "نماذج التصدير",
+	"Export Presets": "",
+	"Export Prompts": "مطالبات التصدير",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "فشل في إنشاء مفتاح API.",
+	"Failed to read clipboard contents": "فشل في قراءة محتويات الحافظة",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "فبراير",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "لا تتردد في إضافة تفاصيل محددة",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "وضع الملف",
+	"File not found.": "لم يتم العثور على الملف.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "تم اكتشاف انتحال بصمة الإصبع: غير قادر على استخدام الأحرف الأولى كصورة رمزية. الافتراضي لصورة الملف الشخصي الافتراضية.",
+	"Fluidly stream large external response chunks": "دفق قطع الاستجابة الخارجية الكبيرة بسلاسة",
+	"Focus chat input": "التركيز على إدخال الدردشة",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "اتبعت التعليمات على أكمل وجه",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "عقوبة التردد",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "عام",
+	"General Settings": "الاعدادات العامة",
+	"Generate Image": "",
+	"Generating search query": "إنشاء استعلام بحث",
+	"Generation Info": "معلومات الجيل",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "استجابة جيدة",
+	"Google PSE API Key": "مفتاح واجهة برمجة تطبيقات PSE من Google",
+	"Google PSE Engine Id": "معرف محرك PSE من Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "الساعة:الدقائق صباحا/مساء",
+	"Haptic Feedback": "",
+	"has no conversations.": "ليس لديه محادثات.",
+	"Hello, {{name}}": " {{name}} مرحبا",
+	"Help": "مساعدة",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "أخفاء",
+	"Host": "",
+	"How can I help you today?": "كيف استطيع مساعدتك اليوم؟",
+	"How would you rate this response?": "",
+	"Hybrid Search": "البحث الهجين",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "توليد الصور (تجريبي)",
+	"Image Generation Engine": "محرك توليد الصور",
+	"Image Settings": "إعدادات الصورة",
+	"Images": "الصور",
+	"Import Chats": "استيراد الدردشات",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "استيراد النماذج",
+	"Import Presets": "",
+	"Import Prompts": "مطالبات الاستيراد",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "قم بتضمين علامة `-api` عند تشغيل Stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "معلومات",
+	"Input commands": "إدخال الأوامر",
+	"Install from Github URL": "التثبيت من عنوان URL لجيثب",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "واجهه المستخدم",
+	"Invalid file format.": "",
+	"Invalid Tag": "تاق غير صالحة",
+	"January": "يناير",
+	"Jina API Key": "",
+	"join our Discord for help.": "انضم إلى Discord للحصول على المساعدة.",
+	"JSON": "JSON",
+	"JSON Preview": "معاينة JSON",
+	"July": "يوليو",
+	"June": "يونيو",
+	"JWT Expiration": "JWT تجريبي",
+	"JWT Token": "JWT Token",
+	"Keep Alive": "Keep Alive",
+	"Key": "",
+	"Keyboard shortcuts": "اختصارات لوحة المفاتيح",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "اللغة",
+	"Last Active": "آخر نشاط",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "فاتح",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "يمكن أن تصدر بعض الأخطاء. لذلك يجب التحقق من المعلومات المهمة",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "من جهة اليسار إلى اليمين",
+	"Made by OpenWebUI Community": "OpenWebUI تم إنشاؤه بواسطة مجتمع ",
+	"Make sure to enclose them with": "تأكد من إرفاقها",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "إدارة خطوط الأنابيب",
+	"March": "مارس",
+	"Max Tokens (num_predict)": "ماكس توكنز (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "يمكن تنزيل 3 نماذج كحد أقصى في وقت واحد. الرجاء معاودة المحاولة في وقت لاحق.",
+	"May": "مايو",
+	"Memories accessible by LLMs will be shown here.": "سيتم عرض الذكريات التي يمكن الوصول إليها بواسطة LLMs هنا.",
+	"Memory": "الذاكرة",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "لن تتم مشاركة الرسائل التي ترسلها بعد إنشاء الرابط الخاص بك. سيتمكن المستخدمون الذين لديهم عنوان URL من عرض الدردشة المشتركة",
+	"Min P": "",
+	"Minimum Score": "الحد الأدنى من النقاط",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "تم تحميل النموذج '{{modelName}}' بنجاح",
+	"Model '{{modelTag}}' is already in queue for downloading.": "النموذج '{{modelTag}}' موجود بالفعل في قائمة الانتظار للتحميل",
+	"Model {{modelId}} not found": "لم يتم العثور على النموذج {{modelId}}.",
+	"Model {{modelName}} is not vision capable": "نموذج {{modelName}} غير قادر على الرؤية",
+	"Model {{name}} is now {{status}}": "نموذج {{name}} هو الآن {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "تم اكتشاف مسار نظام الملفات النموذجي. الاسم المختصر للنموذج مطلوب للتحديث، ولا يمكن الاستمرار.",
+	"Model Filtering": "",
+	"Model ID": "رقم الموديل",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "لم تختار موديل",
+	"Model Params": "معلمات النموذج",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "محتوى الملف النموذجي",
+	"Models": "الموديلات",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "المزيد",
+	"Name": "الأسم",
+	"Name your knowledge base": "",
+	"New Chat": "دردشة جديدة",
+	"New folder": "",
+	"New Password": "كلمة المرور الجديدة",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "لا توجد نتايج",
+	"No search query generated": "لم يتم إنشاء استعلام بحث",
+	"No source available": "لا يوجد مصدر متاح",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "اي",
+	"Not factually correct": "ليس صحيحا من حيث الواقع",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "ملاحظة: إذا قمت بتعيين الحد الأدنى من النقاط، فلن يؤدي البحث إلا إلى إرجاع المستندات التي لها نقاط أكبر من أو تساوي الحد الأدنى من النقاط.",
+	"Notes": "",
+	"Notifications": "إشعارات",
+	"November": "نوفمبر",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (أولاما)",
+	"OAuth ID": "",
+	"October": "اكتوبر",
+	"Off": "أغلاق",
+	"Okay, Let's Go!": "حسنا دعنا نذهب!",
+	"OLED Dark": "OLED داكن",
+	"Ollama": "Ollama",
+	"Ollama API": "أولاما API",
+	"Ollama API disabled": "أولاما API معطلة",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama الاصدار",
+	"On": "تشغيل",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "يُسمح فقط بالأحرف الأبجدية الرقمية والواصلات في سلسلة الأمر.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "خطاء! يبدو أن عنوان URL غير صالح. يرجى التحقق مرة أخرى والمحاولة مرة أخرى.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "خطاء! أنت تستخدم طريقة غير مدعومة (الواجهة الأمامية فقط). يرجى تقديم واجهة WebUI من الواجهة الخلفية.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "فتح محادثة جديده",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API إعدادات",
+	"OpenAI API Key is required.": "OpenAI API.مطلوب مفتاح ",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
+	"or": "أو",
+	"Organize your users": "",
+	"Other": "آخر",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "الباسورد",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF ملف (.pdf)",
+	"PDF Extract Images (OCR)": "PDF أستخرج الصور (OCR)",
+	"pending": "قيد الانتظار",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "{{error}} تم رفض الإذن عند الوصول إلى الميكروفون ",
+	"Permissions": "",
+	"Personalization": "التخصيص",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "خطوط الانابيب",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "صمامات خطوط الأنابيب",
+	"Plain text (.txt)": "نص عادي (.txt)",
+	"Playground": "مكان التجربة",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "موقف ايجابي",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "أخر 30 يوم",
+	"Previous 7 days": "أخر 7 أيام",
+	"Profile Image": "صورة الملف الشخصي",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "موجه (على سبيل المثال: أخبرني بحقيقة ممتعة عن الإمبراطورية الرومانية)",
+	"Prompt Content": "محتوى عاجل",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "اقتراحات سريعة",
+	"Prompt updated successfully": "",
+	"Prompts": "مطالبات",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com \"{{searchValue}}\" أسحب من ",
+	"Pull a model from Ollama.com": "Ollama.com سحب الموديل من ",
+	"Query Generation Prompt": "",
+	"Query Params": "Query Params",
+	"RAG Template": "RAG تنمبلت",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "أقراء لي",
+	"Record voice": "سجل صوت",
+	"Redirecting you to OpenWebUI Community": "OpenWebUI إعادة توجيهك إلى مجتمع ",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "رفض عندما لا ينبغي أن يكون",
+	"Regenerate": "تجديد",
+	"Release Notes": "ملاحظات الإصدار",
+	"Relevance": "",
+	"Remove": "إزالة",
+	"Remove Model": "حذف الموديل",
+	"Rename": "إعادة تسمية",
+	"Reorder Models": "",
+	"Repeat Last N": "N كرر آخر",
+	"Request Mode": "وضع الطلب",
+	"Reranking Model": "إعادة تقييم النموذج",
+	"Reranking model disabled": "تم تعطيل نموذج إعادة الترتيب",
+	"Reranking model set to \"{{reranking_model}}\"": "تم ضبط نموذج إعادة الترتيب على \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "منصب",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "من اليمين إلى اليسار",
+	"Run": "",
+	"Running": "",
+	"Save": "حفظ",
+	"Save & Create": "حفظ وإنشاء",
+	"Save & Update": "حفظ وتحديث",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "لم يعد حفظ سجلات الدردشة مباشرة في مساحة تخزين متصفحك مدعومًا. يرجى تخصيص بعض الوقت لتنزيل وحذف سجلات الدردشة الخاصة بك عن طريق النقر على الزر أدناه. لا تقلق، يمكنك بسهولة إعادة استيراد سجلات الدردشة الخاصة بك إلى الواجهة الخلفية من خلاله",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "البحث",
+	"Search a model": "البحث عن موديل",
+	"Search Base": "",
+	"Search Chats": "البحث في الدردشات",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "نماذج البحث",
+	"Search options": "",
+	"Search Prompts": "أبحث حث",
+	"Search Result Count": "عدد نتائج البحث",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_zero": "تم البحث في {{count}} sites_zero",
+	"Searched {{count}} sites_one": "تم البحث في {{count}} sites_one",
+	"Searched {{count}} sites_two": "تم البحث في {{count}} sites_two",
+	"Searched {{count}} sites_few": "تم البحث في {{count}} sites_few",
+	"Searched {{count}} sites_many": "تم البحث في {{count}} sites_many",
+	"Searched {{count}} sites_other": "تم البحث في {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "عنوان URL لاستعلام Searxng",
+	"See readme.md for instructions": "readme.md للحصول على التعليمات",
+	"See what's new": "ما الجديد",
+	"Seed": "Seed",
+	"Select a base model": "حدد نموذجا أساسيا",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "أختار الموديل",
+	"Select a pipeline": "حدد مسارا",
+	"Select a pipeline url": "حدد عنوان URL لخط الأنابيب",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": " أختار موديل",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "النموذج (النماذج) المحددة لا تدعم مدخلات الصور",
+	"Semantic distance to query": "",
+	"Send": "تم",
+	"Send a Message": "يُرجى إدخال طلبك هنا",
+	"Send message": "يُرجى إدخال طلبك هنا.",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "سبتمبر",
+	"Serper API Key": "مفتاح واجهة برمجة تطبيقات سيربر",
+	"Serply API Key": "",
+	"Serpstack API Key": "مفتاح واجهة برمجة تطبيقات Serpstack",
+	"Server connection verified": "تم التحقق من اتصال الخادم",
+	"Set as default": "الافتراضي",
+	"Set CFG Scale": "",
+	"Set Default Model": "تفعيد الموديل الافتراضي",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ضبط نموذج المتجهات (على سبيل المثال: {{model}})",
+	"Set Image Size": "حجم الصورة",
+	"Set reranking model (e.g. {{model}})": "ضبط نموذج إعادة الترتيب (على سبيل المثال: {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "ضبط الخطوات",
+	"Set Task Model": "تعيين نموذج المهمة",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "ضبط الصوت",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "الاعدادات",
+	"Settings saved successfully!": "تم حفظ الاعدادات بنجاح",
+	"Share": "كشاركة",
+	"Share Chat": "مشاركة الدردشة",
+	"Share to OpenWebUI Community": "OpenWebUI شارك في مجتمع",
+	"Show": "عرض",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "إظهار الاختصارات",
+	"Show your support!": "",
+	"Showcased creativity": "أظهر الإبداع",
+	"Sign in": "تسجيل الدخول",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "تسجيل الخروج",
+	"Sign up": "تسجيل",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "المصدر",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "{{error}} خطأ في التعرف على الكلام",
+	"Speech-to-Text Engine": "محرك تحويل الكلام إلى نص",
+	"Stop": "",
+	"Stop Sequence": "وقف التسلسل",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT اعدادات",
+	"Subtitle (e.g. about the Roman Empire)": "(e.g. about the Roman Empire) الترجمة",
+	"Success": "نجاح",
+	"Successfully updated.": "تم التحديث بنجاح",
+	"Suggested": "مقترحات",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "النظام",
+	"System Instructions": "",
+	"System Prompt": "محادثة النظام",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "أخبرنا المزيد:",
+	"Temperature": "درجة حرارة",
+	"Template": "نموذج",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "محرك تحويل النص إلى كلام",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "شكرا لملاحظاتك!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "يجب أن تكون النتيجة قيمة تتراوح بين 0.0 (0%) و1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "الثيم",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "وهذا يضمن حفظ محادثاتك القيمة بشكل آمن في قاعدة بياناتك الخلفية. شكرًا لك!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "شرح شامل",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "ملاحضة: قم بتحديث عدة فتحات متغيرة على التوالي عن طريق الضغط على مفتاح tab في مدخلات الدردشة بعد كل استبدال.",
+	"Title": "العنوان",
+	"Title (e.g. Tell me a fun fact)": "(e.g. Tell me a fun fact) العناون",
+	"Title Auto-Generation": "توليد تلقائي للعنوان",
+	"Title cannot be an empty string.": "العنوان مطلوب",
+	"Title Generation Prompt": "موجه إنشاء العنوان",
+	"TLS": "",
+	"To access the available model names for downloading,": "للوصول إلى أسماء الموديلات المتاحة للتنزيل،",
+	"To access the GGUF models available for downloading,": "للوصول إلى الموديلات GGUF المتاحة للتنزيل،",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "اليوم",
+	"Toggle settings": "فتح وأغلاق الاعدادات",
+	"Toggle sidebar": "فتح وأغلاق الشريط الجانبي",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "هل تواجه مشكلة في الوصول",
+	"TTS Model": "",
+	"TTS Settings": "TTS اعدادات",
+	"TTS Voice": "",
+	"Type": "نوع",
+	"Type Hugging Face Resolve (Download) URL": "اكتب عنوان URL لحل مشكلة الوجه (تنزيل).",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}خطاء أوه! حدثت مشكلة في الاتصال بـ ",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "تحديث ونسخ الرابط",
+	"Update for the latest features and improvements.": "",
+	"Update password": "تحديث كلمة المرور",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "GGUF رفع موديل نوع",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "تحميل الملفات",
+	"Upload Pipeline": "",
+	"Upload Progress": "جاري التحميل",
+	"URL": "",
+	"URL Mode": "رابط الموديل",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gravatar أستخدم",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Initials أستخدم",
+	"use_mlock (Ollama)": "use_mlock (أولاما)",
+	"use_mmap (Ollama)": "use_mmap (أولاما)",
+	"user": "مستخدم",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "المستخدمين",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "يستخدم",
+	"Valid time units:": "وحدات زمنية صالحة:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "المتغير",
+	"variable to have them replaced with clipboard content.": "متغير لاستبدالها بمحتوى الحافظة.",
+	"Version": "إصدار",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "تحذير",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "تحذير: إذا قمت بتحديث أو تغيير نموذج التضمين الخاص بك، فستحتاج إلى إعادة استيراد كافة المستندات.",
+	"Web": "Web",
+	"Web API": "",
+	"Web Loader Settings": "Web تحميل اعدادات",
+	"Web Search": "بحث الويب",
+	"Web Search Engine": "محرك بحث الويب",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook الرابط",
+	"WebUI Settings": "WebUI اعدادات",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "ما هو الجديد",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "مساحة العمل",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "اكتب اقتراحًا سريعًا (على سبيل المثال، من أنت؟)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "اكتب ملخصًا في 50 كلمة يلخص [الموضوع أو الكلمة الرئيسية]",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "أمس",
+	"You": "انت",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "لا تملك محادثات محفوظه",
+	"You have shared this chat": "تم مشاركة هذه المحادثة",
+	"You're a helpful assistant.": "مساعدك المفيد هنا",
+	"You're now logged in.": "لقد قمت الآن بتسجيل الدخول.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube تحميل اعدادات"
+}
diff --git a/src/lib/i18n/locales/bg-BG/translation.json b/src/lib/i18n/locales/bg-BG/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..c0bd91f9f9329e5cd139b4470eeb33c35e3efae9
--- /dev/null
+++ b/src/lib/i18n/locales/bg-BG/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' за неограничен срок.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(например `sh webui.sh --api`)",
+	"(latest)": "(последна)",
+	"{{ models }}": "{{ модели }}",
+	"{{user}}'s Chats": "{{user}}'s чатове",
+	"{{webUIName}} Backend Required": "{{webUIName}} Изисква се Бекенд",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Моделът на задачите се използва при изпълнение на задачи като генериране на заглавия за чатове и заявки за търсене в мрежата",
+	"a user": "потребител",
+	"About": "Относно",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Акаунт",
+	"Account Activation Pending": "",
+	"Accurate information": "Точни информация",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "Добавяне",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Добавете кратко описание за това какво прави този модел",
+	"Add a tag": "Добавяне на таг",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Добавяне на собствен промпт",
+	"Add Files": "Добавяне на Файлове",
+	"Add Group": "",
+	"Add Memory": "Добавяне на Памет",
+	"Add Model": "Добавяне на Модел",
+	"Add Tag": "",
+	"Add Tags": "добавяне на тагове",
+	"Add text content": "",
+	"Add User": "Добавяне на потребител",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "При промяна на тези настройки промените се прилагат за всички потребители.",
+	"admin": "админ",
+	"Admin": "",
+	"Admin Panel": "Панел на Администратор",
+	"Admin Settings": "Настройки на Администратор",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Разширени Параметри",
+	"Advanced Params": "Разширени параметри",
+	"All chats": "",
+	"All Documents": "Всички Документи",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Позволи Изтриване на Чат",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Вече имате акаунт? ",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "асистент",
+	"and": "и",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "и създай нов общ линк.",
+	"API Base URL": "API Базов URL",
+	"API Key": "API Ключ",
+	"API Key created.": "API Ключ създаден.",
+	"API keys": "API Ключове",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Април",
+	"Archive": "Архивирани Чатове",
+	"Archive All Chats": "Архив Всички чатове",
+	"Archived Chats": "Архивирани Чатове",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Сигурни ли сте?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Прикачване на файл",
+	"Attention to detail": "Внимание към детайлите",
+	"Attribute for Username": "",
+	"Audio": "Аудио",
+	"August": "Август",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Аувтоматично копиране на отговор в клипборда",
+	"Auto-playback response": "Аувтоматично възпроизвеждане на Отговора",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Базов URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Базов URL е задължителен.",
+	"Available list": "",
+	"available!": "наличен!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Назад",
+	"Bad Response": "Невалиден отговор от API",
+	"Banners": "Банери",
+	"Base Model (From)": "Базов модел (от)",
+	"Batch Size (num_batch)": "",
+	"before": "преди",
+	"Being lazy": "Да бъдеш мързелив",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Смел ключ за API за търсене",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Изключване на SSL проверката за сайтове",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Отказ",
+	"Capabilities": "Възможности",
+	"Certificate Path": "",
+	"Change Password": "Промяна на Парола",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Чат",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "UI за чат бублон",
+	"Chat Controls": "",
+	"Chat direction": "Направление на чата",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Чатове",
+	"Check Again": "Проверете Още Веднъж",
+	"Check for updates": "Проверка за актуализации",
+	"Checking for updates...": "Проверка за актуализации...",
+	"Choose a model before saving...": "Изберете модел преди запазване...",
+	"Chunk Overlap": "Chunk Overlap",
+	"Chunk Params": "Chunk Params",
+	"Chunk Size": "Chunk Size",
+	"Ciphers": "",
+	"Citation": "Цитат",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Натиснете тук за помощ.",
+	"Click here to": "Натиснете тук за",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Натиснете тук, за да изберете",
+	"Click here to select a csv file.": "Натиснете тук, за да изберете csv файл.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "натиснете тук.",
+	"Click on the user role button to change a user's role.": "Натиснете върху бутона за промяна на ролята на потребителя.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Клонинг",
+	"Close": "Затвори",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Колекция",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL е задължително.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Команда",
+	"Completions": "",
+	"Concurrent Requests": "Едновременни искания",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Потвърди Парола",
+	"Confirm your action": "",
+	"Connections": "Връзки",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Съдържание",
+	"Content Extraction": "",
+	"Context Length": "Дължина на Контекста",
+	"Continue Response": "Продължи отговора",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Копирана е връзката за чат!",
+	"Copied to clipboard": "",
+	"Copy": "Копирай",
+	"Copy last code block": "Копиране на последен код блок",
+	"Copy last response": "Копиране на последен отговор",
+	"Copy Link": "Копиране на връзка",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Копирането в клипборда беше успешно!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Създаване на модел",
+	"Create Account": "Създаване на Акаунт",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Създаване на нов ключ",
+	"Create new secret key": "Създаване на нов секретен ключ",
+	"Created at": "Създадено на",
+	"Created At": "Създадено на",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Текущ модел",
+	"Current Password": "Текуща Парола",
+	"Custom": "Персонализиран",
+	"Dark": "Тъмен",
+	"Database": "База данни",
+	"December": "Декември",
+	"Default": "По подразбиране",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "По подразбиране (SentenceTransformers)",
+	"Default Model": "Модел по подразбиране",
+	"Default model updated": "Моделът по подразбиране е обновен",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Промпт Предложения по подразбиране",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Роля на потребителя по подразбиране",
+	"Delete": "Изтриване",
+	"Delete a model": "Изтриване на модел",
+	"Delete All Chats": "Изтриване на всички чатове",
+	"Delete All Models": "",
+	"Delete chat": "Изтриване на чат",
+	"Delete Chat": "Изтриване на Чат",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "Изтриване на този линк",
+	"Delete tool?": "",
+	"Delete User": "Изтриване на потребител",
+	"Deleted {{deleteModelTag}}": "Изтрито {{deleteModelTag}}",
+	"Deleted {{name}}": "Изтрито {{име}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Описание",
+	"Didn't fully follow instructions": "Не следва инструкциите",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Открийте модел",
+	"Discover a prompt": "Откриване на промпт",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Откриване, сваляне и преглед на персонализирани промптове",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Откриване, сваляне и преглед на пресетове на модели",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Показване на потребителското име вместо Вие в чата",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Документ",
+	"Documentation": "",
+	"Documents": "Документи",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "няма външни връзки, и вашите данни остават сигурни на локално назначен сървър.",
+	"Don't have an account?": "Нямате акаунт?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Не харесваш стила?",
+	"Done": "",
+	"Download": "Изтегляне отменено",
+	"Download canceled": "Изтегляне отменено",
+	"Download Database": "Сваляне на база данни",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Пускане на файлове тук, за да ги добавите в чата",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр. '30с','10м'. Валидни единици са 'с', 'м', 'ч'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Редактиране",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Редактиране на потребител",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Имейл",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Модел за вграждане",
+	"Embedding Model Engine": "Модел за вграждане",
+	"Embedding model set to \"{{embedding_model}}\"": "Модел за вграждане е настроен на \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Разрешаване на споделяне в общност",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Вклюване на Нови Потребители",
+	"Enable Web Search": "Разрешаване на търсене в уеб",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Уверете се, че вашият CSV файл включва 4 колони в следния ред: Име, Имейл, Парола, Роля.",
+	"Enter {{role}} message here": "Въведете съобщение за {{role}} тук",
+	"Enter a detail about yourself for your LLMs to recall": "Въведете подробности за себе си, за да се herinnerат вашите LLMs",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Въведете Brave Search API ключ",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Въведете Chunk Overlap",
+	"Enter Chunk Size": "Въведете Chunk Size",
+	"Enter description": "",
+	"Enter Github Raw URL": "Въведете URL адреса на Github Raw",
+	"Enter Google PSE API Key": "Въведете Google PSE API ключ",
+	"Enter Google PSE Engine Id": "Въведете идентификатор на двигателя на Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Въведете размер на изображението (напр. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Въведете кодове на езика",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Въведете таг на модел (напр. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Въведете оценка",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Въведете URL адреса на заявката на Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Въведете Serper API ключ",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Въведете Serpstack API ключ",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Въведете стоп последователност",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Въведете Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Въведете URL (напр. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Въведете URL (напр. http://localhost:11434)",
+	"Enter Your Email": "Въведете имейл",
+	"Enter Your Full Name": "Въведете вашето пълно име",
+	"Enter your message": "",
+	"Enter Your Password": "Въведете вашата парола",
+	"Enter Your Role": "Въведете вашата роля",
+	"Enter Your Username": "",
+	"Error": "Грешка",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Експериментално",
+	"Explore the cosmos": "",
+	"Export": "Износ",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Експортване на всички чатове (За всички потребители)",
+	"Export chat (.json)": "",
+	"Export Chats": "Експортване на чатове",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Експортиране на модели",
+	"Export Presets": "",
+	"Export Prompts": "Експортване на промптове",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Неуспешно създаване на API ключ.",
+	"Failed to read clipboard contents": "Грешка при четене на съдържанието от клипборда",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "Февруари",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Feel free to add specific details",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Файл Мод",
+	"File not found.": "Файл не е намерен.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Потвърждаване на отпечатък: Не може да се използва инициализационна буква като аватар. Потребителят се връща към стандартна аватарка.",
+	"Fluidly stream large external response chunks": "Плавно предаване на големи части от външен отговор",
+	"Focus chat input": "Фокусиране на чат вход",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Следвайте инструкциите перфектно",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Наказание за честота",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Основни",
+	"General Settings": "Основни Настройки",
+	"Generate Image": "",
+	"Generating search query": "Генериране на заявка за търсене",
+	"Generation Info": "Информация за Генерация",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Добра отговор",
+	"Google PSE API Key": "Google PSE API ключ",
+	"Google PSE Engine Id": "Идентификатор на двигателя на Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "няма разговори.",
+	"Hello, {{name}}": "Здравей, {{name}}",
+	"Help": "Помощ",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Скрий",
+	"Host": "",
+	"How can I help you today?": "Как мога да ви помогна днес?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybrid Search",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Генерация на изображения (Експериментално)",
+	"Image Generation Engine": "Двигател за генериране на изображения",
+	"Image Settings": "Настройки на изображения",
+	"Images": "Изображения",
+	"Import Chats": "Импортване на чатове",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Импортиране на модели",
+	"Import Presets": "",
+	"Import Prompts": "Импортване на промптове",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Включете флага `--api`, когато стартирате stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Информация",
+	"Input commands": "Въведете команди",
+	"Install from Github URL": "Инсталиране от URL адреса на Github",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Интерфейс",
+	"Invalid file format.": "",
+	"Invalid Tag": "Невалиден тег",
+	"January": "Януари",
+	"Jina API Key": "",
+	"join our Discord for help.": "свържете се с нашия Discord за помощ.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON Преглед",
+	"July": "Июл",
+	"June": "Июн",
+	"JWT Expiration": "JWT Expiration",
+	"JWT Token": "JWT Token",
+	"Keep Alive": "Keep Alive",
+	"Key": "",
+	"Keyboard shortcuts": "Клавиши за бърз достъп",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Език",
+	"Last Active": "Последни активни",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Светъл",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLMs могат да правят грешки. Проверете важните данни.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Направено от OpenWebUI общността",
+	"Make sure to enclose them with": "Уверете се, че са заключени с",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Управление на тръбопроводи",
+	"March": "Март",
+	"Max Tokens (num_predict)": "Макс токени (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 модели могат да бъдат сваляни едновременно. Моля, опитайте отново по-късно.",
+	"May": "Май",
+	"Memories accessible by LLMs will be shown here.": "Мемории достъпни от LLMs ще бъдат показани тук.",
+	"Memory": "Мемория",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Съобщенията, които изпращате след създаването на връзката, няма да бъдат споделяни. Потребителите с URL адреса ще могат да видят споделения чат.",
+	"Min P": "",
+	"Minimum Score": "Минимална оценка",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Моделът '{{modelName}}' беше успешно свален.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Моделът '{{modelTag}}' е вече в очакване за сваляне.",
+	"Model {{modelId}} not found": "Моделът {{modelId}} не е намерен",
+	"Model {{modelName}} is not vision capable": "Моделът {{modelName}} не може да се вижда",
+	"Model {{name}} is now {{status}}": "Моделът {{name}} сега е {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Открит е път до файловата система на модела. За актуализацията се изисква съкратено име на модела, не може да продължи.",
+	"Model Filtering": "",
+	"Model ID": "ИД на модел",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Не е избран модел",
+	"Model Params": "Модел Params",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Съдържание на модфайл",
+	"Models": "Модели",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Повече",
+	"Name": "Име",
+	"Name your knowledge base": "",
+	"New Chat": "Нов чат",
+	"New folder": "",
+	"New Password": "Нова парола",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Няма намерени резултати",
+	"No search query generated": "Не е генерирана заявка за търсене",
+	"No source available": "Няма наличен източник",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Никой",
+	"Not factually correct": "Не е фактологически правилно",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Забележка: Ако зададете минимален резултат, търсенето ще върне само документи с резултат, по-голям или равен на минималния резултат.",
+	"Notes": "",
+	"Notifications": "Десктоп Известия",
+	"November": "Ноември",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Октомври",
+	"Off": "Изкл.",
+	"Okay, Let's Go!": "ОК, Нека започваме!",
+	"OLED Dark": "OLED тъмно",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API деактивиран",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama Версия",
+	"On": "Вкл.",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Само алфанумерични знаци и тире са разрешени в командния низ.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Изглежда URL адресът е невалиден. Моля, проверете отново и опитайте пак.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Използвате неподдържан метод (само фронтенд). Моля, сервирайте WebUI от бекенда.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Отвори нов чат",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API Config",
+	"OpenAI API Key is required.": "OpenAI API ключ е задължителен.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Key е задължителен.",
+	"or": "или",
+	"Organize your users": "",
+	"Other": "Other",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Парола",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF документ (.pdf)",
+	"PDF Extract Images (OCR)": "PDF Extract Images (OCR)",
+	"pending": "в очакване",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
+	"Permissions": "",
+	"Personalization": "Персонализация",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Тръбопроводи",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Тръбопроводи Вентили",
+	"Plain text (.txt)": "Plain text (.txt)",
+	"Playground": "Плейграунд",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Позитивна ативност",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Предыдущите 30 дни",
+	"Previous 7 days": "Предыдущите 7 дни",
+	"Profile Image": "Профилна снимка",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Промпт (напр. Обмисли ме забавна факт за Римската империя)",
+	"Prompt Content": "Съдържание на промпта",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Промпт предложения",
+	"Prompt updated successfully": "",
+	"Prompts": "Промптове",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Извади \"{{searchValue}}\" от Ollama.com",
+	"Pull a model from Ollama.com": "Издърпайте модел от Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Query Параметри",
+	"RAG Template": "RAG Шаблон",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Прочети на Голос",
+	"Record voice": "Записване на глас",
+	"Redirecting you to OpenWebUI Community": "Пренасочване към OpenWebUI общността",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "Отказано, когато не трябва да бъде",
+	"Regenerate": "Регенериране",
+	"Release Notes": "Бележки по изданието",
+	"Relevance": "",
+	"Remove": "Изтриване",
+	"Remove Model": "Изтриване на модела",
+	"Rename": "Преименуване",
+	"Reorder Models": "",
+	"Repeat Last N": "Repeat Last N",
+	"Request Mode": "Request Mode",
+	"Reranking Model": "Reranking Model",
+	"Reranking model disabled": "Reranking model disabled",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking model set to \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Роля",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "Запис",
+	"Save & Create": "Запис & Създаване",
+	"Save & Update": "Запис & Актуализиране",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Запазването на чат логове директно в хранилището на вашия браузър вече не се поддържа. Моля, отделете малко време, за да изтеглите и изтриете чат логовете си, като щракнете върху бутона по-долу. Не се притеснявайте, можете лесно да импортирате отново чат логовете си в бекенда чрез",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Търси",
+	"Search a model": "Търси модел",
+	"Search Base": "",
+	"Search Chats": "Търсене на чатове",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Търсене на модели",
+	"Search options": "",
+	"Search Prompts": "Търси Промптове",
+	"Search Result Count": "Брой резултати от търсенето",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Търси се в {{count}} sites_one",
+	"Searched {{count}} sites_other": "Търси се в {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL адрес на заявка на Searxng",
+	"See readme.md for instructions": "Виж readme.md за инструкции",
+	"See what's new": "Виж какво е новото",
+	"Seed": "Seed",
+	"Select a base model": "Изберете базов модел",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Изберете модел",
+	"Select a pipeline": "Изберете тръбопровод",
+	"Select a pipeline url": "Избор на URL адрес на канал",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Изберете модел",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "Избраният(те) модел(и) не поддържа въвеждане на изображения",
+	"Semantic distance to query": "",
+	"Send": "Изпрати",
+	"Send a Message": "Изпращане на Съобщение",
+	"Send message": "Изпращане на съобщение",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Септември",
+	"Serper API Key": "Serper API ключ",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack API ключ",
+	"Server connection verified": "Server connection verified",
+	"Set as default": "Задай по подразбиране",
+	"Set CFG Scale": "",
+	"Set Default Model": "Задай Модел По Подразбиране",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Задай embedding model (e.g. {{model}})",
+	"Set Image Size": "Задай Размер на Изображението",
+	"Set reranking model (e.g. {{model}})": "Задай reranking model (e.g. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Задай Стъпки",
+	"Set Task Model": "Задаване на модел на задача",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Задай Глас",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Настройки",
+	"Settings saved successfully!": "Настройките са запазени успешно!",
+	"Share": "Подели",
+	"Share Chat": "Подели Чат",
+	"Share to OpenWebUI Community": "Споделите с OpenWebUI Общността",
+	"Show": "Покажи",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Покажи",
+	"Show your support!": "",
+	"Showcased creativity": "Показана креативност",
+	"Sign in": "Вписване",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Изход",
+	"Sign up": "Регистрация",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Източник",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Speech recognition error: {{error}}",
+	"Speech-to-Text Engine": "Speech-to-Text Engine",
+	"Stop": "",
+	"Stop Sequence": "Stop Sequence",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT Настройки",
+	"Subtitle (e.g. about the Roman Empire)": "Подтитул (напр. за Римска империя)",
+	"Success": "Успех",
+	"Successfully updated.": "Успешно обновено.",
+	"Suggested": "Препоръчано",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Система",
+	"System Instructions": "",
+	"System Prompt": "Системен Промпт",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Повече информация:",
+	"Temperature": "Температура",
+	"Template": "Шаблон",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Text-to-Speech Engine",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Благодарим ви за вашия отзив!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "The score should be a value between 0.0 (0%) and 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Тема",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Това гарантира, че ценните ви разговори се запазват сигурно във вашата бекенд база данни. Благодарим ви!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Това е подробно описание.",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Съвет: Актуализирайте няколко слота за променливи последователно, като натискате клавиша Tab в чат входа след всяка подмяна.",
+	"Title": "Заглавие",
+	"Title (e.g. Tell me a fun fact)": "Заглавие (напр. Моля, кажете ми нещо забавно)",
+	"Title Auto-Generation": "Автоматично Генериране на Заглавие",
+	"Title cannot be an empty string.": "Заглавието не може да бъде празно.",
+	"Title Generation Prompt": "Промпт за Генериране на Заглавие",
+	"TLS": "",
+	"To access the available model names for downloading,": "За да получите достъп до наличните имена на модели за изтегляне,",
+	"To access the GGUF models available for downloading,": "За да получите достъп до GGUF моделите, налични за изтегляне,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "днес",
+	"Toggle settings": "Toggle settings",
+	"Toggle sidebar": "Toggle sidebar",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Проблеми с достъпът до Ollama?",
+	"TTS Model": "",
+	"TTS Settings": "TTS Настройки",
+	"TTS Voice": "",
+	"Type": "Вид",
+	"Type Hugging Face Resolve (Download) URL": "Въведете Hugging Face Resolve (Download) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "О, не! Възникна проблем при свързването с {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Обнови и копирай връзка",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Обновяване на парола",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Качване на GGUF модел",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Качване на файлове",
+	"Upload Pipeline": "",
+	"Upload Progress": "Прогрес на качването",
+	"URL": "",
+	"URL Mode": "URL Mode",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Използвайте Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Използвайте Инициали",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "потребител",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Потребители",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Използване",
+	"Valid time units:": "Валидни единици за време:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "променлива",
+	"variable to have them replaced with clipboard content.": "променливи да се заменят съдържанието от клипборд.",
+	"Version": "Версия",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Предупреждение",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Предупреждение: Ако актуализирате или промените вашия модел за вграждане, трябва да повторите импортирането на всички документи.",
+	"Web": "Уеб",
+	"Web API": "",
+	"Web Loader Settings": "Настройки за зареждане на уеб",
+	"Web Search": "Търсене в уеб",
+	"Web Search Engine": "Уеб търсачка",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Уебхук URL",
+	"WebUI Settings": "WebUI Настройки",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Какво е новото в",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Работно пространство",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Напиши предложение за промпт (напр. Кой сте вие?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Напиши описание в 50 знака, което описва [тема или ключова дума].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "вчера",
+	"You": "вие",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Нямате архивирани разговори.",
+	"You have shared this chat": "Вие сте споделели този чат",
+	"You're a helpful assistant.": "Вие сте полезен асистент.",
+	"You're now logged in.": "Сега, вие влязохте в системата.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Loader Settings"
+}
diff --git a/src/lib/i18n/locales/bn-BD/translation.json b/src/lib/i18n/locales/bn-BD/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..ad42df5f3f09dc40eae6475a0f979632aa661fe4
--- /dev/null
+++ b/src/lib/i18n/locales/bn-BD/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' অথবা অনির্দিষ্টকাল মেয়াদের জন্য '-1' ",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(যেমন `sh webui.sh --api`)",
+	"(latest)": "(সর্বশেষ)",
+	"{{ models }}": "{{ মডেল}}",
+	"{{user}}'s Chats": "{{user}}র চ্যাটস",
+	"{{webUIName}} Backend Required": "{{webUIName}} ব্যাকএন্ড আবশ্যক",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "চ্যাট এবং ওয়েব অনুসন্ধান প্রশ্নের জন্য শিরোনাম তৈরি করার মতো কাজগুলি সম্পাদন করার সময় একটি টাস্ক মডেল ব্যবহার করা হয়",
+	"a user": "একজন ব্যাবহারকারী",
+	"About": "সম্পর্কে",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "একাউন্ট",
+	"Account Activation Pending": "",
+	"Accurate information": "সঠিক তথ্য",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "যোগ করুন",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "এই মডেলটি কী করে সে সম্পর্কে একটি সংক্ষিপ্ত বিবরণ যুক্ত করুন",
+	"Add a tag": "একটি ট্যাগ যোগ করুন",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "একটি কাস্টম প্রম্পট যোগ করুন",
+	"Add Files": "ফাইল যোগ করুন",
+	"Add Group": "",
+	"Add Memory": "মেমোরি যোগ করুন",
+	"Add Model": "মডেল যোগ করুন",
+	"Add Tag": "",
+	"Add Tags": "ট্যাগ যোগ করুন",
+	"Add text content": "",
+	"Add User": "ইউজার যোগ করুন",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "এই সেটিংগুলো পরিবর্তন করলে তা সব ইউজারের উপরেই প্রয়োগ করা হবে",
+	"admin": "এডমিন",
+	"Admin": "",
+	"Admin Panel": "এডমিন প্যানেল",
+	"Admin Settings": "এডমিন সেটিংস",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "এডভান্সড প্যারামিটার্স",
+	"Advanced Params": "অ্যাডভান্সড প্যারাম",
+	"All chats": "",
+	"All Documents": "সব ডকুমেন্ট",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "চ্যাট ডিলিট করতে দিন",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "আগে থেকেই একাউন্ট আছে?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "একটা এসিস্ট্যান্ট",
+	"and": "এবং",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
+	"API Base URL": "এপিআই বেজ ইউআরএল",
+	"API Key": "এপিআই কোড",
+	"API Key created.": "একটি এপিআই কোড তৈরি করা হয়েছে.",
+	"API keys": "এপিআই কোডস",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "আপ্রিল",
+	"Archive": "আর্কাইভ",
+	"Archive All Chats": "আর্কাইভ করুন সকল চ্যাট",
+	"Archived Chats": "চ্যাট ইতিহাস সংরক্ষণাগার",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "আপনি নিশ্চিত?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "ফাইল যুক্ত করুন",
+	"Attention to detail": "বিস্তারিত বিশেষতা",
+	"Attribute for Username": "",
+	"Audio": "অডিও",
+	"August": "আগস্ট",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "রেসপন্সগুলো স্বয়ংক্রিভাবে ক্লিপবোর্ডে কপি হবে",
+	"Auto-playback response": "রেসপন্স অটো-প্লেব্যাক",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 বেজ ইউআরএল",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 বেজ ইউআরএল আবশ্যক",
+	"Available list": "",
+	"available!": "উপলব্ধ!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "পেছনে",
+	"Bad Response": "খারাপ প্রতিক্রিয়া",
+	"Banners": "ব্যানার",
+	"Base Model (From)": "বেস মডেল (থেকে)",
+	"Batch Size (num_batch)": "",
+	"before": "পূর্ববর্তী",
+	"Being lazy": "অলস হওয়া",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "সাহসী অনুসন্ধান API কী",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "ওয়েবসাইটের জন্য SSL যাচাই বাতিল করুন",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "বাতিল",
+	"Capabilities": "সক্ষমতা",
+	"Certificate Path": "",
+	"Change Password": "পাসওয়ার্ড পরিবর্তন করুন",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "চ্যাট",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "চ্যাট বাবল UI",
+	"Chat Controls": "",
+	"Chat direction": "চ্যাট দিকনির্দেশ",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "চ্যাটসমূহ",
+	"Check Again": "আবার চেক করুন",
+	"Check for updates": "নতুন আপডেট আছে কিনা চেক করুন",
+	"Checking for updates...": "নতুন আপডেট আছে কিনা চেক করা হচ্ছে...",
+	"Choose a model before saving...": "সেভ করার আগে একটি মডেল নির্বাচন করুন",
+	"Chunk Overlap": "চাঙ্ক ওভারল্যাপ",
+	"Chunk Params": "চাঙ্ক প্যারামিটার্স",
+	"Chunk Size": "চাঙ্ক সাইজ",
+	"Ciphers": "",
+	"Citation": "উদ্ধৃতি",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "সাহায্যের জন্য এখানে ক্লিক করুন",
+	"Click here to": "এখানে ক্লিক করুন",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "নির্বাচন করার জন্য এখানে ক্লিক করুন",
+	"Click here to select a csv file.": "একটি csv ফাইল নির্বাচন করার জন্য এখানে ক্লিক করুন",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "এখানে ক্লিক করুন",
+	"Click on the user role button to change a user's role.": "ইউজারের পদবি পরিবর্তন করার জন্য ইউজারের পদবি বাটনে ক্লিক করুন",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "ক্লোন",
+	"Close": "বন্ধ",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "সংগ্রহ",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL আবশ্যক।",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "কমান্ড",
+	"Completions": "",
+	"Concurrent Requests": "সমকালীন অনুরোধ",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "পাসওয়ার্ড নিশ্চিত করুন",
+	"Confirm your action": "",
+	"Connections": "কানেকশনগুলো",
+	"Contact Admin for WebUI Access": "",
+	"Content": "বিষয়বস্তু",
+	"Content Extraction": "",
+	"Context Length": "কনটেক্সটের দৈর্ঘ্য",
+	"Continue Response": "যাচাই করুন",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "শেয়ারকৃত কথা-ব্যবহারের URL ক্লিপবোর্ডে কপি করা হয়েছে!",
+	"Copied to clipboard": "",
+	"Copy": "অনুলিপি",
+	"Copy last code block": "সর্বশেষ কোড ব্লক কপি করুন",
+	"Copy last response": "সর্বশেষ রেসপন্স কপি করুন",
+	"Copy Link": "লিংক কপি করুন",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "ক্লিপবোর্ডে কপি করা সফল হয়েছে",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "একটি মডেল তৈরি করুন",
+	"Create Account": "একাউন্ট তৈরি করুন",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "একটি নতুন কী তৈরি করুন",
+	"Create new secret key": "একটি নতুন সিক্রেট কী তৈরি করুন",
+	"Created at": "নির্মানকাল",
+	"Created At": "নির্মানকাল",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "বর্তমান মডেল",
+	"Current Password": "বর্তমান পাসওয়ার্ড",
+	"Custom": "কাস্টম",
+	"Dark": "ডার্ক",
+	"Database": "ডেটাবেজ",
+	"December": "ডেসেম্বর",
+	"Default": "ডিফল্ট",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "ডিফল্ট (SentenceTransformers)",
+	"Default Model": "ডিফল্ট মডেল",
+	"Default model updated": "ডিফল্ট মডেল আপডেট হয়েছে",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "ডিফল্ট প্রম্পট সাজেশন",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "ইউজারের ডিফল্ট পদবি",
+	"Delete": "মুছে ফেলুন",
+	"Delete a model": "একটি মডেল মুছে ফেলুন",
+	"Delete All Chats": "সব চ্যাট মুছে ফেলুন",
+	"Delete All Models": "",
+	"Delete chat": "চ্যাট মুছে ফেলুন",
+	"Delete Chat": "চ্যাট মুছে ফেলুন",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "এই লিংক মুছে ফেলুন",
+	"Delete tool?": "",
+	"Delete User": "ইউজার মুছে ফেলুন",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} মুছে ফেলা হয়েছে",
+	"Deleted {{name}}": "{{name}} মোছা হয়েছে",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "বিবরণ",
+	"Didn't fully follow instructions": "ইনস্ট্রাকশন সম্পূর্ণ অনুসরণ করা হয়নি",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "একটি মডেল আবিষ্কার করুন",
+	"Discover a prompt": "একটি প্রম্পট খুঁজে বের করুন",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "কাস্টম প্রম্পটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "মডেল প্রিসেটগুলো আবিস্কার, ডাউনলোড এবং এক্সপ্লোর করুন",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "চ্যাটে 'আপনি'-র পরবর্তে ইউজারনেম দেখান",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "ডকুমেন্ট",
+	"Documentation": "",
+	"Documents": "ডকুমেন্টসমূহ",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "কোন এক্সটার্নাল কানেকশন তৈরি করে না, এবং আপনার ডেটা আর লোকালি হোস্টেড সার্ভারেই নিরাপদে থাকে।",
+	"Don't have an account?": "একাউন্ট নেই?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "স্টাইল পছন্দ করেন না",
+	"Done": "",
+	"Download": "ডাউনলোড",
+	"Download canceled": "ডাউনলোড বাতিল করা হয়েছে",
+	"Download Database": "ডেটাবেজ ডাউনলোড করুন",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "আলোচনায় যুক্ত করার জন্য যে কোন ফাইল এখানে ড্রপ করুন",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "যেমন '30s','10m'. সময়ের অনুমোদিত অনুমোদিত এককগুলি হচ্ছে 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "এডিট করুন",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "ইউজার এডিট করুন",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "ইমেইল",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "ইমেজ ইমেবডিং মডেল",
+	"Embedding Model Engine": "ইমেজ ইমেবডিং মডেল ইঞ্জিন",
+	"Embedding model set to \"{{embedding_model}}\"": "ইমেজ ইমেবডিং মডেল সেট করা হয়েছে - \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "সম্প্রদায় শেয়ারকরণ সক্ষম করুন",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "নতুন সাইনআপ চালু করুন",
+	"Enable Web Search": "ওয়েব অনুসন্ধান সক্ষম করুন",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "আপনার সিএসভি ফাইলটিতে এই ক্রমে 4 টি কলাম অন্তর্ভুক্ত রয়েছে তা নিশ্চিত করুন: নাম, ইমেল, পাসওয়ার্ড, ভূমিকা।.",
+	"Enter {{role}} message here": "{{role}} মেসেজ এখানে লিখুন",
+	"Enter a detail about yourself for your LLMs to recall": "আপনার এলএলএমগুলি স্মরণ করার জন্য নিজের সম্পর্কে একটি বিশদ লিখুন",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "সাহসী অনুসন্ধান API কী লিখুন",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "চাঙ্ক ওভারল্যাপ লিখুন",
+	"Enter Chunk Size": "চাংক সাইজ লিখুন",
+	"Enter description": "",
+	"Enter Github Raw URL": "গিটহাব কাঁচা URL লিখুন",
+	"Enter Google PSE API Key": "গুগল পিএসই এপিআই কী লিখুন",
+	"Enter Google PSE Engine Id": "গুগল পিএসই ইঞ্জিন আইডি লিখুন",
+	"Enter Image Size (e.g. 512x512)": "ছবির মাপ লিখুন (যেমন 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "ল্যাঙ্গুয়েজ কোড লিখুন",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "মডেল ট্যাগ লিখুন (e.g. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "স্কোর দিন",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Searxng ক্যোয়ারী URL লিখুন",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper API কী লিখুন",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Serpstack API কী লিখুন",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "স্টপ সিকোয়েন্স লিখুন",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"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://localhost:11434)": "ইউআরএল দিন (যেমন http://localhost:11434)",
+	"Enter Your Email": "আপনার ইমেইল লিখুন",
+	"Enter Your Full Name": "আপনার পূর্ণ নাম লিখুন",
+	"Enter your message": "",
+	"Enter Your Password": "আপনার পাসওয়ার্ড লিখুন",
+	"Enter Your Role": "আপনার রোল লিখুন",
+	"Enter Your Username": "",
+	"Error": "ত্রুটি",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "পরিক্ষামূলক",
+	"Explore the cosmos": "",
+	"Export": "রপ্তানি",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "সব চ্যাট এক্সপোর্ট করুন (সব ইউজারের)",
+	"Export chat (.json)": "",
+	"Export Chats": "চ্যাটগুলো এক্সপোর্ট করুন",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "রপ্তানি মডেল",
+	"Export Presets": "",
+	"Export Prompts": "প্রম্পটগুলো একপোর্ট করুন",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "API Key তৈরি করা যায়নি।",
+	"Failed to read clipboard contents": "ক্লিপবোর্ডের বিষয়বস্তু পড়া সম্ভব হয়নি",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "ফেব্রুয়ারি",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "নির্দিষ্ট বিবরণ যোগ করতে বিনা দ্বিধায়",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "ফাইল মোড",
+	"File not found.": "ফাইল পাওয়া যায়নি",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "ফিঙ্গারপ্রিন্ট স্পুফিং ধরা পড়েছে: অ্যাভাটার হিসেবে নামের আদ্যক্ষর ব্যবহার করা যাচ্ছে না। ডিফল্ট প্রোফাইল পিকচারে ফিরিয়ে নেয়া হচ্ছে।",
+	"Fluidly stream large external response chunks": "বড় এক্সটার্নাল রেসপন্স চাঙ্কগুলো মসৃণভাবে প্রবাহিত করুন",
+	"Focus chat input": "চ্যাট ইনপুট ফোকাস করুন",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "নির্দেশাবলী নিখুঁতভাবে অনুসরণ করা হয়েছে",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "ফ্রিকোয়েন্সি পেনাল্টি",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "সাধারণ",
+	"General Settings": "সাধারণ সেটিংসমূহ",
+	"Generate Image": "",
+	"Generating search query": "অনুসন্ধান ক্যোয়ারী তৈরি করা হচ্ছে",
+	"Generation Info": "জেনারেশন ইনফো",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "ভালো সাড়া",
+	"Google PSE API Key": "গুগল পিএসই এপিআই কী",
+	"Google PSE Engine Id": "গুগল পিএসই ইঞ্জিন আইডি",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "কোন কনভার্সেশন আছে না।",
+	"Hello, {{name}}": "হ্যালো, {{name}}",
+	"Help": "সহায়তা",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "লুকান",
+	"Host": "",
+	"How can I help you today?": "আপনাকে আজ কিভাবে সাহায্য করতে পারি?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "হাইব্রিড অনুসন্ধান",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "ইমেজ জেনারেশন (পরিক্ষামূলক)",
+	"Image Generation Engine": "ইমেজ জেনারেশন ইঞ্জিন",
+	"Image Settings": "ছবির সেটিংসমূহ",
+	"Images": "ছবিসমূহ",
+	"Import Chats": "চ্যাটগুলি ইমপোর্ট করুন",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "মডেল আমদানি করুন",
+	"Import Presets": "",
+	"Import Prompts": "প্রম্পটগুলো ইমপোর্ট করুন",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui চালু করার সময় `--api` ফ্ল্যাগ সংযুক্ত করুন",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "তথ্য",
+	"Input commands": "ইনপুট কমান্ডস",
+	"Install from Github URL": "Github URL থেকে ইনস্টল করুন",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "ইন্টারফেস",
+	"Invalid file format.": "",
+	"Invalid Tag": "অবৈধ ট্যাগ",
+	"January": "জানুয়ারী",
+	"Jina API Key": "",
+	"join our Discord for help.": "সাহায্যের জন্য আমাদের Discord-এ যুক্ত হোন",
+	"JSON": "JSON",
+	"JSON Preview": "JSON প্রিভিউ",
+	"July": "জুলাই",
+	"June": "জুন",
+	"JWT Expiration": "JWT-র মেয়াদ",
+	"JWT Token": "JWT টোকেন",
+	"Keep Alive": "সচল রাখুন",
+	"Key": "",
+	"Keyboard shortcuts": "কিবোর্ড শর্টকাটসমূহ",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "ভাষা",
+	"Last Active": "সর্বশেষ সক্রিয়",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "লাইট",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLM ভুল করতে পারে। গুরুত্বপূর্ণ তথ্য যাচাই করে নিন।",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "OpenWebUI কমিউনিটিকর্তৃক নির্মিত",
+	"Make sure to enclose them with": "এটা দিয়ে বন্ধনী দিতে ভুলবেন না",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "পাইপলাইন পরিচালনা করুন",
+	"March": "মার্চ",
+	"Max Tokens (num_predict)": "সর্বোচ্চ টোকেন (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "একসঙ্গে সর্বোচ্চ তিনটি মডেল ডাউনলোড করা যায়। দয়া করে পরে আবার চেষ্টা করুন।",
+	"May": "মে",
+	"Memories accessible by LLMs will be shown here.": "LLMs দ্বারা অ্যাক্সেসযোগ্য মেমোরিগুলি এখানে দেখানো হবে।",
+	"Memory": "মেমোরি",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "আপনার লিঙ্ক তৈরি করার পরে আপনার পাঠানো বার্তাগুলি শেয়ার করা হবে না। ইউআরএল ব্যবহারকারীরা শেয়ার করা চ্যাট দেখতে পারবেন।",
+	"Min P": "",
+	"Minimum Score": "Minimum Score",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' মডেল সফলভাবে ডাউনলোড হয়েছে।",
+	"Model '{{modelTag}}' is already in queue for downloading.": "{{modelTag}} ডাউনলোডের জন্য আগে থেকেই অপেক্ষমান আছে।",
+	"Model {{modelId}} not found": "{{modelId}} মডেল পাওয়া যায়নি",
+	"Model {{modelName}} is not vision capable": "মডেল {{modelName}} দৃষ্টি সক্ষম নয়",
+	"Model {{name}} is now {{status}}": "মডেল {{name}} এখন {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "মডেল ফাইলসিস্টেম পাথ পাওয়া গেছে। আপডেটের জন্য মডেলের শর্টনেম আবশ্যক, এগিয়ে যাওয়া যাচ্ছে না।",
+	"Model Filtering": "",
+	"Model ID": "মডেল ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "মডেল নির্বাচন করা হয়নি",
+	"Model Params": "মডেল প্যারাম",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "মডেলফাইল কনটেন্ট",
+	"Models": "মডেলসমূহ",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "আরো",
+	"Name": "নাম",
+	"Name your knowledge base": "",
+	"New Chat": "নতুন চ্যাট",
+	"New folder": "",
+	"New Password": "নতুন পাসওয়ার্ড",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "কোন ফলাফল পাওয়া যায়নি",
+	"No search query generated": "কোনও অনুসন্ধান ক্যোয়ারী উত্পন্ন হয়নি",
+	"No source available": "কোন উৎস পাওয়া যায়নি",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "কোনোটিই নয়",
+	"Not factually correct": "তথ্যগত দিক থেকে সঠিক নয়",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "দ্রষ্টব্য: আপনি যদি ন্যূনতম স্কোর সেট করেন তবে অনুসন্ধানটি কেবলমাত্র ন্যূনতম স্কোরের চেয়ে বেশি বা সমান স্কোর সহ নথিগুলি ফেরত দেবে।",
+	"Notes": "",
+	"Notifications": "নোটিফিকেশনসমূহ",
+	"November": "নভেম্বর",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (ওলামা)",
+	"OAuth ID": "",
+	"October": "অক্টোবর",
+	"Off": "বন্ধ",
+	"Okay, Let's Go!": "ঠিক আছে, চলুন যাই!",
+	"OLED Dark": "OLED ডার্ক",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API নিষ্ক্রিয় করা হয়েছে",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama ভার্সন",
+	"On": "চালু",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "কমান্ড স্ট্রিং-এ শুধুমাত্র ইংরেজি অক্ষর, সংখ্যা এবং হাইফেন ব্যবহার করা যাবে।",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "ওহ, মনে হচ্ছে ইউআরএলটা ইনভ্যালিড। দয়া করে আর চেক করে চেষ্টা করুন।",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "আপনি একটা আনসাপোর্টেড পদ্ধতি (শুধু ফ্রন্টএন্ড) ব্যবহার করছেন। দয়া করে WebUI ব্যাকএন্ড থেকে চালনা করুন।",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "নতুন চ্যাট খুলুন",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI এপিআই",
+	"OpenAI API Config": "OpenAI এপিআই কনফিগ",
+	"OpenAI API Key is required.": "OpenAI API কোড আবশ্যক",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Key আবশ্যক",
+	"or": "অথবা",
+	"Organize your users": "",
+	"Other": "অন্যান্য",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "পাসওয়ার্ড",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF ডকুমেন্ট (.pdf)",
+	"PDF Extract Images (OCR)": "পিডিএফ এর ছবি থেকে লেখা বের করুন (OCR)",
+	"pending": "অপেক্ষমান",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "মাইক্রোফোন ব্যবহারের অনুমতি পাওয়া যায়নি: {{error}}",
+	"Permissions": "",
+	"Personalization": "ডিজিটাল বাংলা",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "পাইপলাইন",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "পাইপলাইন ভালভ",
+	"Plain text (.txt)": "প্লায়েন টেক্সট (.txt)",
+	"Playground": "খেলাঘর",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "পজিটিভ আক্রমণ",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "পূর্ব ৩০ দিন",
+	"Previous 7 days": "পূর্ব ৭ দিন",
+	"Profile Image": "প্রোফাইল ইমেজ",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "প্রম্প্ট (উদাহরণস্বরূপ, আমি রোমান ইমপার্টের সম্পর্কে একটি উপস্থিতি জানতে বল)",
+	"Prompt Content": "প্রম্পট কন্টেন্ট",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "প্রম্পট সাজেশনসমূহ",
+	"Prompt updated successfully": "",
+	"Prompts": "প্রম্পটসমূহ",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com থেকে \"{{searchValue}}\" টানুন",
+	"Pull a model from Ollama.com": "Ollama.com থেকে একটি টেনে আনুন আনুন",
+	"Query Generation Prompt": "",
+	"Query Params": "Query প্যারামিটারসমূহ",
+	"RAG Template": "RAG টেম্পলেট",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "পড়াশোনা করুন",
+	"Record voice": "ভয়েস রেকর্ড করুন",
+	"Redirecting you to OpenWebUI Community": "আপনাকে OpenWebUI কমিউনিটিতে পাঠানো হচ্ছে",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "যদি উপযুক্ত নয়, তবে রেজিগেনেট করা হচ্ছে",
+	"Regenerate": "রেজিগেনেট করুন",
+	"Release Notes": "রিলিজ নোটসমূহ",
+	"Relevance": "",
+	"Remove": "রিমুভ করুন",
+	"Remove Model": "মডেল রিমুভ করুন",
+	"Rename": "রেনেম",
+	"Reorder Models": "",
+	"Repeat Last N": "রিপিট Last N",
+	"Request Mode": "রিকোয়েস্ট মোড",
+	"Reranking Model": "রির্যাক্টিং মডেল",
+	"Reranking model disabled": "রির্যাক্টিং মডেল নিষ্ক্রিয় করা",
+	"Reranking model set to \"{{reranking_model}}\"": "রির ্যাঙ্কিং মডেল \"{{reranking_model}}\" -এ সেট করা আছে",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "পদবি",
+	"Rosé Pine": "রোজ পাইন",
+	"Rosé Pine Dawn": "ভোরের রোজ পাইন",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "সংরক্ষণ",
+	"Save & Create": "সংরক্ষণ এবং তৈরি করুন",
+	"Save & Update": "সংরক্ষণ এবং আপডেট করুন",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "মাধ্যমে",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "অনুসন্ধান",
+	"Search a model": "মডেল অনুসন্ধান করুন",
+	"Search Base": "",
+	"Search Chats": "চ্যাট অনুসন্ধান করুন",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "অনুসন্ধান মডেল",
+	"Search options": "",
+	"Search Prompts": "প্রম্পটসমূহ অনুসন্ধান করুন",
+	"Search Result Count": "অনুসন্ধানের ফলাফল গণনা",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "{{কাউন্ট}} অনুসন্ধান করা হয়েছে sites_one",
+	"Searched {{count}} sites_other": "{{কাউন্ট}} অনুসন্ধান করা হয়েছে sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng ক্যোয়ারী URL",
+	"See readme.md for instructions": "নির্দেশিকার জন্য readme.md দেখুন",
+	"See what's new": "নতুন কী আছে দেখুন",
+	"Seed": "সীড",
+	"Select a base model": "একটি বেস মডেল নির্বাচন করুন",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "একটি মডেল নির্বাচন করুন",
+	"Select a pipeline": "একটি পাইপলাইন নির্বাচন করুন",
+	"Select a pipeline url": "একটি পাইপলাইন URL নির্বাচন করুন",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "মডেল নির্বাচন করুন",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "নির্বাচিত মডেল(গুলি) চিত্র ইনপুট সমর্থন করে না",
+	"Semantic distance to query": "",
+	"Send": "পাঠান",
+	"Send a Message": "একটি মেসেজ পাঠান",
+	"Send message": "মেসেজ পাঠান",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "সেপ্টেম্বর",
+	"Serper API Key": "Serper API Key",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack API Key",
+	"Server connection verified": "সার্ভার কানেকশন যাচাই করা হয়েছে",
+	"Set as default": "ডিফল্ট হিসেবে নির্ধারণ করুন",
+	"Set CFG Scale": "",
+	"Set Default Model": "ডিফল্ট মডেল নির্ধারণ করুন",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ইমেম্বিং মডেল নির্ধারণ করুন (উদাহরণ {{model}})",
+	"Set Image Size": "ছবির সাইজ নির্ধারণ করুন",
+	"Set reranking model (e.g. {{model}})": "রি-র্যাংকিং মডেল নির্ধারণ করুন (উদাহরণ {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "পরবর্তী ধাপসমূহ",
+	"Set Task Model": "টাস্ক মডেল সেট করুন",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "কন্ঠস্বর নির্ধারণ করুন",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "সেটিংসমূহ",
+	"Settings saved successfully!": "সেটিংগুলো সফলভাবে সংরক্ষিত হয়েছে",
+	"Share": "শেয়ার করুন",
+	"Share Chat": "চ্যাট শেয়ার করুন",
+	"Share to OpenWebUI Community": "OpenWebUI কমিউনিটিতে শেয়ার করুন",
+	"Show": "দেখান",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "শর্টকাটগুলো দেখান",
+	"Show your support!": "",
+	"Showcased creativity": "সৃজনশীলতা প্রদর্শন",
+	"Sign in": "সাইন ইন",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "সাইন আউট",
+	"Sign up": "সাইন আপ",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "উৎস",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "স্পিচ রিকগনিশনে সমস্যা: {{error}}",
+	"Speech-to-Text Engine": "স্পিচ-টু-টেক্সট ইঞ্জিন",
+	"Stop": "",
+	"Stop Sequence": "সিকোয়েন্স থামান",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT সেটিংস",
+	"Subtitle (e.g. about the Roman Empire)": "সাবটাইটল (রোমান ইম্পার্টের সম্পর্কে)",
+	"Success": "সফল",
+	"Successfully updated.": "সফলভাবে আপডেট হয়েছে",
+	"Suggested": "প্রস্তাবিত",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "সিস্টেম",
+	"System Instructions": "",
+	"System Prompt": "সিস্টেম প্রম্পট",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "আরও বলুন:",
+	"Temperature": "তাপমাত্রা",
+	"Template": "টেম্পলেট",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "টেক্সট-টু-স্পিচ ইঞ্জিন",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "আপনার মতামত ধন্যবাদ!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "স্কোর একটি 0.0 (0%) এবং 1.0 (100%) এর মধ্যে একটি মান হওয়া উচিত।",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "থিম",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "এটা নিশ্চিত করে যে, আপনার গুরুত্বপূর্ণ আলোচনা নিরাপদে আপনার ব্যাকএন্ড ডেটাবেজে সংরক্ষিত আছে। ধন্যবাদ!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "পুঙ্খানুপুঙ্খ ব্যাখ্যা",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "পরামর্শ: একাধিক ভেরিয়েবল স্লট একের পর এক রিপ্লেস করার জন্য চ্যাট ইনপুটে কিবোর্ডের Tab বাটন ব্যবহার করুন।",
+	"Title": "শিরোনাম",
+	"Title (e.g. Tell me a fun fact)": "শিরোনাম (একটি উপস্থিতি বিবরণ জানান)",
+	"Title Auto-Generation": "স্বয়ংক্রিয় শিরোনামগঠন",
+	"Title cannot be an empty string.": "শিরোনাম অবশ্যই একটি পাশাপাশি শব্দ হতে হবে।",
+	"Title Generation Prompt": "শিরোনামগঠন প্রম্পট",
+	"TLS": "",
+	"To access the available model names for downloading,": "ডাউনলোডের জন্য এভেইলএবল মডেলের নামগুলো এক্সেস করতে,",
+	"To access the GGUF models available for downloading,": "ডাউলোডের জন্য এভেইলএবল GGUF মডেলগুলো এক্সেস করতে,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "আজ",
+	"Toggle settings": "সেটিংস টোগল",
+	"Toggle sidebar": "সাইডবার টোগল",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama এক্সেস করতে সমস্যা হচ্ছে?",
+	"TTS Model": "",
+	"TTS Settings": "TTS সেটিংসমূহ",
+	"TTS Voice": "",
+	"Type": "টাইপ",
+	"Type Hugging Face Resolve (Download) URL": "Hugging Face থেকে ডাউনলোড করার ইউআরএল টাইপ করুন",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "ওহ-হো! {{provider}} এর সাথে কানেকশনে সমস্যা হয়েছে।",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "আপডেট এবং লিংক কপি করুন",
+	"Update for the latest features and improvements.": "",
+	"Update password": "পাসওয়ার্ড আপডেট করুন",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "একটি GGUF মডেল আপলোড করুন",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "ফাইল আপলোড করুন",
+	"Upload Pipeline": "",
+	"Upload Progress": "আপলোড হচ্ছে",
+	"URL": "",
+	"URL Mode": "ইউআরএল মোড",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gravatar ব্যবহার করুন",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "নামের আদ্যক্ষর ব্যবহার করুন",
+	"use_mlock (Ollama)": "use_mlock (ওলামা)",
+	"use_mmap (Ollama)": "use_mmap (ওলামা)",
+	"user": "ব্যবহারকারী",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "ব্যাবহারকারীগণ",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "ইউটিলাইজ",
+	"Valid time units:": "সময়ের গ্রহণযোগ্য এককসমূহ:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "ভেরিয়েবল",
+	"variable to have them replaced with clipboard content.": "ক্লিপবোর্ডের কন্টেন্ট দিয়ে যেই ভেরিয়েবল রিপ্লেস করা যাবে।",
+	"Version": "ভার্সন",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "সতর্কীকরণ",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "সতর্কীকরণ: আপনি যদি আপনার এম্বেডিং মডেল আপডেট বা পরিবর্তন করেন, তাহলে আপনাকে সমস্ত নথি পুনরায় আমদানি করতে হবে।.",
+	"Web": "ওয়েব",
+	"Web API": "",
+	"Web Loader Settings": "ওয়েব লোডার সেটিংস",
+	"Web Search": "ওয়েব অনুসন্ধান",
+	"Web Search Engine": "ওয়েব সার্চ ইঞ্জিন",
+	"Web Search Query Generation": "",
+	"Webhook URL": "ওয়েবহুক URL",
+	"WebUI Settings": "WebUI সেটিংসমূহ",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "এতে নতুন কী",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "ওয়ার্কস্পেস",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "একটি প্রম্পট সাজেশন লিখুন (যেমন Who are you?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "৫০ শব্দের মধ্যে [topic or keyword] এর একটি সারসংক্ষেপ লিখুন।",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "আগামী",
+	"You": "আপনি",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "আপনার কোনও আর্কাইভ করা কথোপকথন নেই।",
+	"You have shared this chat": "আপনি এই চ্যাটটি শেয়ার করেছেন",
+	"You're a helpful assistant.": "আপনি একজন উপকারী এসিস্ট্যান্ট",
+	"You're now logged in.": "আপনি এখন লগইন করা অবস্থায় আছেন",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube লোডার সেটিংস"
+}
diff --git a/src/lib/i18n/locales/ca-ES/translation.json b/src/lib/i18n/locales/ca-ES/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a2337e98638bdbb15cd5d60d40bd21ad17ad81ed
--- /dev/null
+++ b/src/lib/i18n/locales/ca-ES/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' perquè no caduqui mai.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(p. ex. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)",
+	"(latest)": "(últim)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Els xats de {{user}}",
+	"{{webUIName}} Backend Required": "El Backend de {{webUIName}} és necessari",
+	"*Prompt node ID(s) are required for image generation": "*Els identificadors de nodes d'indicacions són necessaris per a la generació d'imatges",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Hi ha una nova versió disponible (v{{LATEST_VERSION}}).",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de tasca s'utilitza quan es realitzen tasques com ara generar títols per a xats i consultes de cerca per a la web",
+	"a user": "un usuari",
+	"About": "Sobre",
+	"Access": "Accés",
+	"Access Control": "Control d'accés",
+	"Accessible to all users": "Accessible a tots els usuaris",
+	"Account": "Compte",
+	"Account Activation Pending": "Activació del compte pendent",
+	"Accurate information": "Informació precisa",
+	"Actions": "Accions",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activa aquest comanda escrivint \"{{COMMAND}}\" en el xat",
+	"Active Users": "Usuaris actius",
+	"Add": "Afegir",
+	"Add a model ID": "Afegir un ID de model",
+	"Add a short description about what this model does": "Afegeix una breu descripció sobre què fa aquest model",
+	"Add a tag": "Afegir una etiqueta",
+	"Add Arena Model": "Afegir model de l'Arena",
+	"Add Connection": "Afegir connexió",
+	"Add Content": "Afegir contingut",
+	"Add content here": "Afegir contingut aquí",
+	"Add custom prompt": "Afegir una indicació personalitzada",
+	"Add Files": "Afegir arxius",
+	"Add Group": "Afegir grup",
+	"Add Memory": "Afegir memòria",
+	"Add Model": "Afegir un model",
+	"Add Tag": "Afegir etiqueta",
+	"Add Tags": "Afegir etiquetes",
+	"Add text content": "Afegir contingut de text",
+	"Add User": "Afegir un usuari",
+	"Add User Group": "Afegir grup d'usuaris",
+	"Adjusting these settings will apply changes universally to all users.": "Si ajustes aquesta preferència, els canvis s'aplicaran de manera universal a tots els usuaris.",
+	"admin": "administrador",
+	"Admin": "Administrador",
+	"Admin Panel": "Panell d'administració",
+	"Admin Settings": "Preferències d'administració",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Els administradors tenen accés a totes les eines en tot moment; els usuaris necessiten eines assignades per model a l'espai de treball.",
+	"Advanced Parameters": "Paràmetres avançats",
+	"Advanced Params": "Paràmetres avançats",
+	"All chats": "Tots els xats",
+	"All Documents": "Tots els documents",
+	"All models deleted successfully": "Tots els models s'han eliminat correctament",
+	"Allow Chat Delete": "Permetre eliminar el xat",
+	"Allow Chat Deletion": "Permetre la supressió del xat",
+	"Allow Chat Edit": "Permetre editar el xat",
+	"Allow File Upload": "Permetre la pujada d'arxius",
+	"Allow non-local voices": "Permetre veus no locals",
+	"Allow Temporary Chat": "Permetre el xat temporal",
+	"Allow User Location": "Permetre la ubicació de l'usuari",
+	"Allow Voice Interruption in Call": "Permetre la interrupció de la veu en una trucada",
+	"Already have an account?": "Ja tens un compte?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternativa al top_p, i pretén garantir un equilibri de qualitat i varietat. El paràmetre p representa la probabilitat mínima que es consideri un token, en relació amb la probabilitat del token més probable. Per exemple, amb p=0,05 i el token més probable amb una probabilitat de 0,9, es filtren els logits amb un valor inferior a 0,045. (Per defecte: 0.0)",
+	"Amazing": "Al·lucinant",
+	"an assistant": "un assistent",
+	"and": "i",
+	"and {{COUNT}} more": "i {{COUNT}} més",
+	"and create a new shared link.": "i crear un nou enllaç compartit.",
+	"API Base URL": "URL Base de l'API",
+	"API Key": "clau API",
+	"API Key created.": "clau API creada.",
+	"API keys": "Claus de l'API",
+	"Application DN": "DN d'aplicació",
+	"Application DN Password": "Contrasenya del DN d'aplicació",
+	"applies to all users with the \"user\" role": "s'aplica a tots els usuaris amb el rol \"usuari\"",
+	"April": "Abril",
+	"Archive": "Arxiu",
+	"Archive All Chats": "Arxiva tots els xats",
+	"Archived Chats": "Xats arxivats",
+	"archived-chat-export": "archived-chat-export",
+	"Are you sure you want to unarchive all archived chats?": "Estàs segur que vols desarxivar tots els xats arxivats?",
+	"Are you sure?": "Estàs segur?",
+	"Arena Models": "Models de l'Arena",
+	"Artifacts": "Artefactes",
+	"Ask a question": "Fer una pregunta",
+	"Assistant": "Assistent",
+	"Attach file": "Adjuntar arxiu",
+	"Attention to detail": "Atenció al detall",
+	"Attribute for Username": "Atribut per al Nom d'usuari",
+	"Audio": "Àudio",
+	"August": "Agost",
+	"Authenticate": "Autenticar",
+	"Auto-Copy Response to Clipboard": "Copiar la resposta automàticament al porta-retalls",
+	"Auto-playback response": "Reproduir la resposta automàticament",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "Cadena d'autenticació de l'API d'AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "URL Base d'AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base d'AUTOMATIC1111.",
+	"Available list": "Llista de disponibles",
+	"available!": "disponible!",
+	"Awful": "Terrible",
+	"Azure AI Speech": "Azure AI Speech",
+	"Azure Region": "Regió d'Azure",
+	"Back": "Enrere",
+	"Bad Response": "Resposta errònia",
+	"Banners": "Banners",
+	"Base Model (From)": "Model base (des de)",
+	"Batch Size (num_batch)": "Mida del lot (num_batch)",
+	"before": "abans",
+	"Being lazy": "Essent mandrós",
+	"Bing Search V7 Endpoint": "Punt de connexió a Bing Search V7",
+	"Bing Search V7 Subscription Key": "Clau de subscripció a Bing Search V7",
+	"Brave Search API Key": "Clau API de Brave Search",
+	"By {{name}}": "Per {{name}}",
+	"Bypass SSL verification for Websites": "Desactivar la verificació SSL per a l'accés a Internet",
+	"Call": "Trucada",
+	"Call feature is not supported when using Web STT engine": "La funció de trucada no s'admet quan s'utilitza el motor Web STT",
+	"Camera": "Càmera",
+	"Cancel": "Cancel·lar",
+	"Capabilities": "Capacitats",
+	"Certificate Path": "Camí del certificat",
+	"Change Password": "Canviar la contrasenya",
+	"Character": "Personatge",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Traça noves fronteres",
+	"Chat": "Xat",
+	"Chat Background Image": "Imatge de fons del xat",
+	"Chat Bubble UI": "Chat Bubble UI",
+	"Chat Controls": "Controls de xat",
+	"Chat direction": "Direcció del xat",
+	"Chat Overview": "Vista general del xat",
+	"Chat Permissions": "Permisos del xat",
+	"Chat Tags Auto-Generation": "Generació automàtica d'etiquetes del xat",
+	"Chats": "Xats",
+	"Check Again": "Comprovar-ho de nou",
+	"Check for updates": "Comprovar si hi ha actualitzacions",
+	"Checking for updates...": "Comprovant actualitzacions...",
+	"Choose a model before saving...": "Triar un model abans de desar...",
+	"Chunk Overlap": "Solapament de blocs",
+	"Chunk Params": "Paràmetres dels blocs",
+	"Chunk Size": "Mida del bloc",
+	"Ciphers": "Xifradors",
+	"Citation": "Cita",
+	"Clear memory": "Esborrar la memòria",
+	"click here": "prem aquí",
+	"Click here for filter guides.": "Clica aquí per filtrar les guies.",
+	"Click here for help.": "Clica aquí per obtenir ajuda.",
+	"Click here to": "Clic aquí per",
+	"Click here to download user import template file.": "Fes clic aquí per descarregar l'arxiu de plantilla d'importació d'usuaris",
+	"Click here to learn more about faster-whisper and see the available models.": "Clica aquí per obtenir més informació sobre faster-whisper i veure els models disponibles.",
+	"Click here to select": "Clica aquí per seleccionar",
+	"Click here to select a csv file.": "Clica aquí per seleccionar un fitxer csv.",
+	"Click here to select a py file.": "Clica aquí per seleccionar un fitxer py.",
+	"Click here to upload a workflow.json file.": "Clica aquí per pujar un arxiu workflow.json",
+	"click here.": "clica aquí.",
+	"Click on the user role button to change a user's role.": "Clica sobre el botó de rol d'usuari per canviar el rol d'un usuari.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permís d'escriptura al porta-retalls denegat. Comprova els ajustos de navegador per donar l'accés necessari.",
+	"Clone": "Clonar",
+	"Close": "Tancar",
+	"Code execution": "Execució de codi",
+	"Code formatted successfully": "Codi formatat correctament",
+	"Collection": "Col·lecció",
+	"Color": "Color",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL base de ComfyUI",
+	"ComfyUI Base URL is required.": "L'URL base de ComfyUI és obligatòria.",
+	"ComfyUI Workflow": "Flux de treball de ComfyUI",
+	"ComfyUI Workflow Nodes": "Nodes del flux de treball de ComfyUI",
+	"Command": "Comanda",
+	"Completions": "Completaments",
+	"Concurrent Requests": "Peticions simultànies",
+	"Configure": "Configurar",
+	"Configure Models": "Configurar models",
+	"Confirm": "Confirmar",
+	"Confirm Password": "Confirmar la contrasenya",
+	"Confirm your action": "Confirma la teva acció",
+	"Connections": "Connexions",
+	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
+	"Content": "Contingut",
+	"Content Extraction": "Extracció de contingut",
+	"Context Length": "Mida del context",
+	"Continue Response": "Continuar la resposta",
+	"Continue with {{provider}}": "Continuar amb {{provider}}",
+	"Continue with Email": "Continuar amb el correu",
+	"Continue with LDAP": "Continuar amb LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Controlar com es divideix el text del missatge per a les sol·licituds TTS. 'Puntuació' divideix en frases, 'paràgrafs' divideix en paràgrafs i 'cap' manté el missatge com una cadena única.",
+	"Controls": "Controls",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Controlar l'equilibri entre la coherència i la diversitat de la sortida. Un valor més baix donarà lloc a un text més enfocat i coherent. (Per defecte: 5.0)",
+	"Copied": "Copiat",
+	"Copied shared chat URL to clipboard!": "S'ha copiat l'URL compartida al porta-retalls!",
+	"Copied to clipboard": "Copiat al porta-retalls",
+	"Copy": "Copiar",
+	"Copy last code block": "Copiar l'últim bloc de codi",
+	"Copy last response": "Copiar l'última resposta",
+	"Copy Link": "Copiar l'enllaç",
+	"Copy to clipboard": "Copiar al porta-retalls",
+	"Copying to clipboard was successful!": "La còpia al porta-retalls s'ha realitzat correctament",
+	"Create": "Crear",
+	"Create a knowledge base": "Crear una base de coneixement",
+	"Create a model": "Crear un model",
+	"Create Account": "Crear un compte",
+	"Create Admin Account": "Crear un compte d'Administrador",
+	"Create Group": "Crear grup",
+	"Create Knowledge": "Crear Coneixement",
+	"Create new key": "Crear una nova clau",
+	"Create new secret key": "Crear una nova clau secreta",
+	"Created at": "Creat el",
+	"Created At": "Creat el",
+	"Created by": "Creat per",
+	"CSV Import": "Importar CSV",
+	"Current Model": "Model actual",
+	"Current Password": "Contrasenya actual",
+	"Custom": "Personalitzat",
+	"Dark": "Fosc",
+	"Database": "Base de dades",
+	"December": "Desembre",
+	"Default": "Per defecte",
+	"Default (Open AI)": "Per defecte (Open AI)",
+	"Default (SentenceTransformers)": "Per defecte (SentenceTransformers)",
+	"Default Model": "Model per defecte",
+	"Default model updated": "Model per defecte actualitzat",
+	"Default Models": "Models per defecte",
+	"Default permissions": "Permisos per defecte",
+	"Default permissions updated successfully": "Permisos per defecte actualitzats correctament",
+	"Default Prompt Suggestions": "Suggeriments d'indicació per defecte",
+	"Default to 389 or 636 if TLS is enabled": "Per defecte 389 o 636 si TLS està habilitat",
+	"Default to ALL": "Per defecte TOTS",
+	"Default User Role": "Rol d'usuari per defecte",
+	"Delete": "Eliminar",
+	"Delete a model": "Eliminar un model",
+	"Delete All Chats": "Eliminar tots els xats",
+	"Delete All Models": "Eliminar tots els models",
+	"Delete chat": "Eliminar xat",
+	"Delete Chat": "Eliminar xat",
+	"Delete chat?": "Eliminar el xat?",
+	"Delete folder?": "Eliminar la carpeta?",
+	"Delete function?": "Eliminar funció?",
+	"Delete prompt?": "Eliminar indicació?",
+	"delete this link": "Eliminar aquest enllaç",
+	"Delete tool?": "Eliminar eina?",
+	"Delete User": "Eliminar usuari",
+	"Deleted {{deleteModelTag}}": "S'ha eliminat {{deleteModelTag}}",
+	"Deleted {{name}}": "S'ha eliminat {{name}}",
+	"Deleted User": "Usuari eliminat",
+	"Describe your knowledge base and objectives": "Descriu la teva base de coneixement i objectius",
+	"Description": "Descripció",
+	"Didn't fully follow instructions": "No s'han seguit les instruccions completament",
+	"Disabled": "Deshabilitat",
+	"Discover a function": "Descobrir una funció",
+	"Discover a model": "Descobrir un model",
+	"Discover a prompt": "Descobrir una indicació",
+	"Discover a tool": "Descobrir una eina",
+	"Discover wonders": "Descobrir meravelles",
+	"Discover, download, and explore custom functions": "Descobrir, descarregar i explorar funcions personalitzades",
+	"Discover, download, and explore custom prompts": "Descobrir, descarregar i explorar indicacions personalitzades",
+	"Discover, download, and explore custom tools": "Descobrir, descarregar i explorar eines personalitzades",
+	"Discover, download, and explore model presets": "Descobrir, descarregar i explorar models preconfigurats",
+	"Dismissible": "Descartable",
+	"Display": "Mostrar",
+	"Display Emoji in Call": "Mostrar emojis a la trucada",
+	"Display the username instead of You in the Chat": "Mostrar el nom d'usuari en lloc de 'Tu' al xat",
+	"Displays citations in the response": "Mostra les referències a la resposta",
+	"Dive into knowledge": "Aprofundir en el coneixement",
+	"Do not install functions from sources you do not fully trust.": "No instal·lis funcions de fonts en què no confiïs plenament.",
+	"Do not install tools from sources you do not fully trust.": "No instal·lis eines de fonts en què no confiïs plenament.",
+	"Document": "Document",
+	"Documentation": "Documentació",
+	"Documents": "Documents",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "no realitza connexions externes, i les teves dades romanen segures al teu servidor allotjat localment.",
+	"Don't have an account?": "No tens un compte?",
+	"don't install random functions from sources you don't trust.": "no instal·lis funcions aleatòries de fonts en què no confiïs.",
+	"don't install random tools from sources you don't trust.": "no instal·lis eines aleatòries de fonts en què no confiïs.",
+	"Don't like the style": "No t'agrada l'estil?",
+	"Done": "Fet",
+	"Download": "Descarregar",
+	"Download canceled": "Descàrrega cancel·lada",
+	"Download Database": "Descarregar la base de dades",
+	"Drag and drop a file to upload or select a file to view": "Arrossegar un arxiu per pujar o escull un arxiu a veure",
+	"Draw": "Dibuixar",
+	"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "p. ex. Un filtre per eliminar paraules malsonants del text",
+	"e.g. My Filter": "p. ex. El meu filtre",
+	"e.g. My Tools": "p. ex. Les meves eines",
+	"e.g. my_filter": "p. ex. els_meus_filtres",
+	"e.g. my_tools": "p. ex. les_meves_eines",
+	"e.g. Tools for performing various operations": "p. ex. Eines per dur a terme operacions",
+	"Edit": "Editar",
+	"Edit Arena Model": "Editar model de l'Arena",
+	"Edit Connection": "Editar la connexió",
+	"Edit Default Permissions": "Editar el permisos per defecte",
+	"Edit Memory": "Editar la memòria",
+	"Edit User": "Editar l'usuari",
+	"Edit User Group": "Editar el grup d'usuaris",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Correu electrònic",
+	"Embark on adventures": "Embarcar en aventures",
+	"Embedding Batch Size": "Mida del lot d'incrustació",
+	"Embedding Model": "Model d'incrustació",
+	"Embedding Model Engine": "Motor de model d'incrustació",
+	"Embedding model set to \"{{embedding_model}}\"": "Model d'incrustació configurat a \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Activar l'autenticació amb clau API",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Activar el bloqueig de memòria (mlock) per evitar que les dades del model s'intercanviïn fora de la memòria RAM. Aquesta opció bloqueja el conjunt de pàgines de treball del model a la memòria RAM, assegurant-se que no s'intercanviaran al disc. Això pot ajudar a mantenir el rendiment evitant errors de pàgina i garantint un accés ràpid a les dades.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Activar l'assignació de memòria (mmap) per carregar les dades del model. Aquesta opció permet que el sistema utilitzi l'emmagatzematge en disc com a extensió de la memòria RAM tractant els fitxers de disc com si estiguessin a la memòria RAM. Això pot millorar el rendiment del model permetent un accés més ràpid a les dades. Tanmateix, és possible que no funcioni correctament amb tots els sistemes i pot consumir una quantitat important d'espai en disc.",
+	"Enable Message Rating": "Permetre la qualificació de missatges",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Activar el mostreig de Mirostat per controlar la perplexitat. (Per defecte: 0, 0 = Inhabilitat, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Permetre nous registres",
+	"Enable Web Search": "Activar la cerca web",
+	"Enabled": "Habilitat",
+	"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.",
+	"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 api auth string (e.g. username:password)": "Entra la cadena d'autenticació api (p. ex. nom d'usuari:contrasenya)",
+	"Enter Application DN": "Introdueix el DN d'aplicació",
+	"Enter Application DN Password": "Introdueix la contrasenya del DN d'aplicació",
+	"Enter Bing Search V7 Endpoint": "Introdueix el punt de connexió de Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Introdueix la clau de subscripció de Bing Search V7",
+	"Enter Brave Search API Key": "Introdueix la clau API de Brave Search",
+	"Enter certificate path": "Introdueix el camí del certificat",
+	"Enter CFG Scale (e.g. 7.0)": "Entra l'escala CFG (p.ex. 7.0)",
+	"Enter Chunk Overlap": "Introdueix la mida de solapament de blocs",
+	"Enter Chunk Size": "Introdueix la mida del bloc",
+	"Enter description": "Introdueix la descripció",
+	"Enter Github Raw URL": "Introdueix l'URL en brut de Github",
+	"Enter Google PSE API Key": "Introdueix la clau API de Google PSE",
+	"Enter Google PSE Engine Id": "Introdueix l'identificador del motor PSE de Google",
+	"Enter Image Size (e.g. 512x512)": "Introdueix la mida de la imatge (p. ex. 512x512)",
+	"Enter Jina API Key": "Introdueix la clau API de Jina",
+	"Enter language codes": "Introdueix els codis de llenguatge",
+	"Enter Model ID": "Introdueix l'identificador del model",
+	"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
+	"Enter Mojeek Search API Key": "Introdueix la clau API de Mojeek Search",
+	"Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Introdueix el mostrejador (p.ex. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Entra el programador (p.ex. Karras)",
+	"Enter Score": "Introdueix la puntuació",
+	"Enter SearchApi API Key": "Introdueix la clau API SearchApi",
+	"Enter SearchApi Engine": "Introdueix el motor SearchApi",
+	"Enter Searxng Query URL": "Introdueix l'URL de consulta de Searxng",
+	"Enter Seed": "Introdueix la llavor",
+	"Enter Serper API Key": "Introdueix la clau API Serper",
+	"Enter Serply API Key": "Introdueix la clau API Serply",
+	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
+	"Enter server host": "Introdueix el servidor",
+	"Enter server label": "Introdueix l'etiqueta del servidor",
+	"Enter server port": "Introdueix el port del servidor",
+	"Enter stop sequence": "Introdueix la seqüència de parada",
+	"Enter system prompt": "Introdueix la indicació de sistema",
+	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
+	"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
+	"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://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
+	"Enter Your Email": "Introdueix el teu correu electrònic",
+	"Enter Your Full Name": "Introdueix el teu nom complet",
+	"Enter your message": "Introdueix el teu missatge",
+	"Enter Your Password": "Introdueix la teva contrasenya",
+	"Enter Your Role": "Introdueix el teu rol",
+	"Enter Your Username": "Introdueix el teu nom d'usuari",
+	"Error": "Error",
+	"ERROR": "ERROR",
+	"Evaluations": "Avaluacions",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Exemple: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Exemple: TOTS",
+	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName o uid o userPrincipalName",
+	"Exclude": "Excloure",
+	"Experimental": "Experimental",
+	"Explore the cosmos": "Explorar el cosmos",
+	"Export": "Exportar",
+	"Export All Archived Chats": "Exportar tots els xats arxivats",
+	"Export All Chats (All Users)": "Exportar tots els xats (Tots els usuaris)",
+	"Export chat (.json)": "Exportar el xat (.json)",
+	"Export Chats": "Exportar els xats",
+	"Export Config to JSON File": "Exportar la configuració a un arxiu JSON",
+	"Export Functions": "Exportar funcions",
+	"Export Models": "Exportar els models",
+	"Export Presets": "Exportar les configuracions",
+	"Export Prompts": "Exportar les indicacions",
+	"Export to CSV": "Exportar a CSV",
+	"Export Tools": "Exportar les eines",
+	"External Models": "Models externs",
+	"Failed to add file.": "No s'ha pogut afegir l'arxiu.",
+	"Failed to create API Key.": "No s'ha pogut crear la clau API.",
+	"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
+	"Failed to save models configuration": "No s'ha pogut desar la configuració dels models",
+	"Failed to update settings": "No s'han pogut actualitzar les preferències",
+	"Failed to upload file.": "No s'ha pogut pujar l'arxiu.",
+	"February": "Febrer",
+	"Feedback History": "Històric de comentaris",
+	"Feedbacks": "Comentaris",
+	"Feel free to add specific details": "Sent-te lliure d'afegir detalls específics",
+	"File": "Arxiu",
+	"File added successfully.": "L'arxiu s'ha afegit correctament.",
+	"File content updated successfully.": "El contingut de l'arxiu s'ha actualitzat correctament.",
+	"File Mode": "Mode d'arxiu",
+	"File not found.": "No s'ha trobat l'arxiu.",
+	"File removed successfully.": "Arxiu eliminat correctament.",
+	"File size should not exceed {{maxSize}} MB.": "La mida del fitxer no ha de superar els {{maxSize}} MB.",
+	"Files": "Arxius",
+	"Filter is now globally disabled": "El filtre ha estat desactivat globalment",
+	"Filter is now globally enabled": "El filtre ha estat activat globalment",
+	"Filters": "Filtres",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "S'ha detectat la suplantació d'identitat de l'empremta digital: no es poden utilitzar les inicials com a avatar. S'estableix la imatge de perfil predeterminada.",
+	"Fluidly stream large external response chunks": "Transmetre amb fluïdesa grans trossos de resposta externa",
+	"Focus chat input": "Estableix el focus a l'entrada del xat",
+	"Folder deleted successfully": "Carpeta eliminada correctament",
+	"Folder name cannot be empty": "El nom de la carpeta no pot ser buit",
+	"Folder name cannot be empty.": "El nom de la carpeta no pot ser buit.",
+	"Folder name updated successfully": "Nom de la carpeta actualitzat correctament",
+	"Followed instructions perfectly": "S'han seguit les instruccions perfectament",
+	"Forge new paths": "Crea nous camins",
+	"Form": "Formulari",
+	"Format your variables using brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
+	"Frequency Penalty": "Penalització per freqüència",
+	"Function": "Funció",
+	"Function created successfully": "La funció s'ha creat correctament",
+	"Function deleted successfully": "La funció s'ha eliminat correctament",
+	"Function Description": "Descripció de la funció",
+	"Function ID": "ID de la funció",
+	"Function is now globally disabled": "La funció ha estat desactivada globalment",
+	"Function is now globally enabled": "La funció ha estat activada globalment",
+	"Function Name": "Nom de la funció",
+	"Function updated successfully": "La funció s'ha actualitzat correctament",
+	"Functions": "Funcions",
+	"Functions allow arbitrary code execution": "Les funcions permeten l'execució de codi arbitrari",
+	"Functions allow arbitrary code execution.": "Les funcions permeten l'execució de codi arbitrari.",
+	"Functions imported successfully": "Les funcions s'han importat correctament",
+	"General": "General",
+	"General Settings": "Preferències generals",
+	"Generate Image": "Generar imatge",
+	"Generating search query": "Generant consulta",
+	"Generation Info": "Informació sobre la generació",
+	"Get started": "Començar",
+	"Get started with {{WEBUI_NAME}}": "Començar amb {{WEBUI_NAME}}",
+	"Global": "Global",
+	"Good Response": "Bona resposta",
+	"Google PSE API Key": "Clau API PSE de Google",
+	"Google PSE Engine Id": "Identificador del motor PSE de Google",
+	"Group created successfully": "El grup s'ha creat correctament",
+	"Group deleted successfully": "El grup s'ha eliminat correctament",
+	"Group Description": "Descripció del grup",
+	"Group Name": "Nom del grup",
+	"Group updated successfully": "Grup actualitzat correctament",
+	"Groups": "Grups",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Retorn hàptic",
+	"has no conversations.": "no té converses.",
+	"Hello, {{name}}": "Hola, {{name}}",
+	"Help": "Ajuda",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Ajuda'ns a crear la millor taula de classificació de la comunitat compartint el teu historial de comentaris!",
+	"Hex Color": "Color hexadecimal",
+	"Hex Color - Leave empty for default color": "Color hexadecimal - Deixar buit per a color per defecte",
+	"Hide": "Amaga",
+	"Host": "Servidor",
+	"How can I help you today?": "Com et puc ajudar avui?",
+	"How would you rate this response?": "Com avaluaries aquesta resposta?",
+	"Hybrid Search": "Cerca híbrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Afirmo que he llegit i entenc les implicacions de la meva acció. Soc conscient dels riscos associats a l'execució de codi arbitrari i he verificat la fiabilitat de la font.",
+	"ID": "ID",
+	"Ignite curiosity": "Despertar la curiositat",
+	"Image Generation (Experimental)": "Generació d'imatges (Experimental)",
+	"Image Generation Engine": "Motor de generació d'imatges",
+	"Image Settings": "Preferències d'imatges",
+	"Images": "Imatges",
+	"Import Chats": "Importar xats",
+	"Import Config from JSON File": "Importar la configuració des d'un arxiu JSON",
+	"Import Functions": "Importar funcions",
+	"Import Models": "Importar models",
+	"Import Presets": "Importar configuracions",
+	"Import Prompts": "Importar indicacions",
+	"Import Tools": "Importar eines",
+	"Include": "Incloure",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Inclou `--api-auth` quan executis stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Inclou `--api` quan executis stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Influeix amb la rapidesa amb què l'algoritme respon als comentaris del text generat. Una taxa d'aprenentatge més baixa donarà lloc a ajustos més lents, mentre que una taxa d'aprenentatge més alta farà que l'algorisme sigui més sensible. (Per defecte: 0,1)",
+	"Info": "Informació",
+	"Input commands": "Entra comandes",
+	"Install from Github URL": "Instal·lar des de l'URL de Github",
+	"Instant Auto-Send After Voice Transcription": "Enviament automàtic després de la transcripció de veu",
+	"Interface": "Interfície",
+	"Invalid file format.": "Format d'arxiu no vàlid.",
+	"Invalid Tag": "Etiqueta no vàlida",
+	"January": "Gener",
+	"Jina API Key": "Clau API de Jina",
+	"join our Discord for help.": "uneix-te al nostre Discord per obtenir ajuda.",
+	"JSON": "JSON",
+	"JSON Preview": "Vista prèvia del document JSON",
+	"July": "Juliol",
+	"June": "Juny",
+	"JWT Expiration": "Caducitat del JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Manté actiu",
+	"Key": "Clau",
+	"Keyboard shortcuts": "Dreceres de teclat",
+	"Knowledge": "Coneixement",
+	"Knowledge Access": "Accés al coneixement",
+	"Knowledge created successfully.": "Coneixement creat correctament.",
+	"Knowledge deleted successfully.": "Coneixement eliminat correctament.",
+	"Knowledge reset successfully.": "Coneixement restablert correctament.",
+	"Knowledge updated successfully": "Coneixement actualitzat correctament.",
+	"Label": "Etiqueta",
+	"Landing Page Mode": "Mode de la pàgina d'entrada",
+	"Language": "Idioma",
+	"Last Active": "Activitat recent",
+	"Last Modified": "Modificació",
+	"LDAP": "LDAP",
+	"LDAP server updated": "Servidor LDAP actualitzat",
+	"Leaderboard": "Tauler de classificació",
+	"Leave empty for unlimited": "Deixar-ho buit per il·limitat",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Deixar-ho buit per incloure tots els models del punt de connexió \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Deixar-ho buit per incloure tots els models del punt de connexió \"{{URL}}/models\"",
+	"Leave empty to include all models or select specific models": "Deixa-ho en blanc per incloure tots els models o selecciona models específics",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Deixa-ho en blanc per utilitzar la indicació predeterminada o introdueix una indicació personalitzada",
+	"Light": "Clar",
+	"Listening...": "Escoltant...",
+	"LLMs can make mistakes. Verify important information.": "Els models de llenguatge poden cometre errors. Verifica la informació important.",
+	"Local": "Local",
+	"Local Models": "Models locals",
+	"Lost": "Perdut",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Creat per la Comunitat OpenWebUI",
+	"Make sure to enclose them with": "Assegura't d'envoltar-los amb",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Assegura't d'exportar un fitxer workflow.json com a format API des de ComfyUI.",
+	"Manage": "Gestionar",
+	"Manage Arena Models": "Gestionar els models de l'Arena",
+	"Manage Ollama": "Gestionar Ollama",
+	"Manage Ollama API Connections": "Gestionar les connexions a l'API d'Ollama",
+	"Manage OpenAI API Connections": "Gestionar les connexions a l'API d'OpenAI",
+	"Manage Pipelines": "Gestionar les Pipelines",
+	"March": "Març",
+	"Max Tokens (num_predict)": "Nombre màxim de Tokens (num_predict)",
+	"Max Upload Count": "Nombre màxim de càrregues",
+	"Max Upload Size": "Mida màxima de càrrega",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es poden descarregar un màxim de 3 models simultàniament. Si us plau, prova-ho més tard.",
+	"May": "Maig",
+	"Memories accessible by LLMs will be shown here.": "Les memòries accessibles pels models de llenguatge es mostraran aquí.",
+	"Memory": "Memòria",
+	"Memory added successfully": "Memòria afegida correctament",
+	"Memory cleared successfully": "Memòria eliminada correctament",
+	"Memory deleted successfully": "Memòria eliminada correctament",
+	"Memory updated successfully": "Memòria actualitzada correctament",
+	"Merge Responses": "Fusionar les respostes",
+	"Message rating should be enabled to use this feature": "La classificació dels missatges s'hauria d'activar per utilitzar aquesta funció",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Els missatges enviats després de crear el teu enllaç no es compartiran. Els usuaris amb l'URL podran veure el xat compartit.",
+	"Min P": "Min P",
+	"Minimum Score": "Puntuació mínima",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Eta de Mirostat",
+	"Mirostat Tau": "Tau de Mirostat",
+	"MMMM DD, YYYY": "DD de MMMM, YYYY",
+	"MMMM DD, YYYY HH:mm": "DD de MMMM, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD de MMMM, YYYY HH:mm:ss, A",
+	"Model": "Model",
+	"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat correctament.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
+	"Model {{modelId}} not found": "No s'ha trobat el model {{modelId}}",
+	"Model {{modelName}} is not vision capable": "El model {{modelName}} no és capaç de visió",
+	"Model {{name}} is now {{status}}": "El model {{name}} ara és {{status}}",
+	"Model accepts image inputs": "El model accepta entrades d'imatge",
+	"Model created successfully!": "Model creat correctament",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "S'ha detectat el camí del sistema de fitxers del model. És necessari un nom curt del model per actualitzar, no es pot continuar.",
+	"Model Filtering": "Filtrat de models",
+	"Model ID": "Identificador del model",
+	"Model IDs": "Identificadors del model",
+	"Model Name": "Nom del model",
+	"Model not selected": "Model no seleccionat",
+	"Model Params": "Paràmetres del model",
+	"Model Permissions": "Permisos dels models",
+	"Model updated successfully": "Model actualitzat correctament",
+	"Modelfile Content": "Contingut del Modelfile",
+	"Models": "Models",
+	"Models Access": "Accés als models",
+	"Models configuration saved successfully": "La configuració dels models s'ha desat correctament",
+	"Mojeek Search API Key": "Clau API de Mojeek Search",
+	"more": "més",
+	"More": "Més",
+	"Name": "Nom",
+	"Name your knowledge base": "Anomena la teva base de coneixement",
+	"New Chat": "Nou xat",
+	"New folder": "Nova carpeta",
+	"New Password": "Nova contrasenya",
+	"No content found": "No s'ha trobat contingut",
+	"No content to speak": "No hi ha contingut per parlar",
+	"No distance available": "No hi ha distància disponible",
+	"No feedbacks found": "No s'han trobat comentaris",
+	"No file selected": "No s'ha escollit cap fitxer",
+	"No files found.": "No s'han trobat arxius.",
+	"No groups with access, add a group to grant access": "No hi ha cap grup amb accés, afegeix un grup per concedir accés",
+	"No HTML, CSS, or JavaScript content found.": "No s'ha trobat contingut HTML, CSS o JavaScript.",
+	"No knowledge found": "No s'ha trobat Coneixement",
+	"No model IDs": "No hi ha IDs de model",
+	"No models found": "No s'han trobat models",
+	"No models selected": "No s'ha seleccionat cap model",
+	"No results found": "No s'han trobat resultats",
+	"No search query generated": "No s'ha generat cap consulta",
+	"No source available": "Sense font disponible",
+	"No users were found.": "No s'han trobat usuaris",
+	"No valves to update": "No hi ha cap Valve per actualitzar",
+	"None": "Cap",
+	"Not factually correct": "No és clarament correcte",
+	"Not helpful": "No ajuda",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Si s'estableix una puntuació mínima, la cerca només retornarà documents amb una puntuació major o igual a la puntuació mínima.",
+	"Notes": "Notes",
+	"Notifications": "Notificacions",
+	"November": "Novembre",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Octubre",
+	"Off": "Desactivat",
+	"Okay, Let's Go!": "D'acord, som-hi!",
+	"OLED Dark": "OLED Fosc",
+	"Ollama": "Ollama",
+	"Ollama API": "API d'Ollama",
+	"Ollama API disabled": "API d'Ollama desactivada",
+	"Ollama API settings updated": "La configuració de l'API d'Ollama s'ha actualitzat",
+	"Ollama Version": "Versió d'Ollama",
+	"On": "Activat",
+	"Only alphanumeric characters and hyphens are allowed": "Només es permeten caràcters alfanumèrics i guions",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la comanda.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Només es poden editar col·leccions, crea una nova base de coneixement per editar/afegir documents.",
+	"Only select users and groups with permission can access": "Només hi poden accedir usuaris i grups seleccionats amb permís",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL no és vàlida. Si us plau, revisa-la i torna-ho a provar.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Ui! Encara hi ha fitxers pujant-se. Si us plau, espera que finalitzi la càrrega.",
+	"Oops! There was an error in the previous response.": "Ui! Hi ha hagut un error a la resposta anterior.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ui! Estàs utilitzant un mètode no suportat (només frontend). Si us plau, serveix la WebUI des del backend.",
+	"Open file": "Obrir arxiu",
+	"Open in full screen": "Obrir en pantalla complerta",
+	"Open new chat": "Obre un xat nou",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI utilitza faster-whisper internament.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI utilitza incrustacions de SpeechT5 i CMU Arctic.",
+	"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 API": "API d'OpenAI",
+	"OpenAI API Config": "Configuració de l'API d'OpenAI",
+	"OpenAI API Key is required.": "Es requereix la clau API d'OpenAI.",
+	"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
+	"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
+	"or": "o",
+	"Organize your users": "Organitza els teus usuaris",
+	"Other": "Altres",
+	"OUTPUT": "SORTIDA",
+	"Output format": "Format de sortida",
+	"Overview": "Vista general",
+	"page": "pàgina",
+	"Password": "Contrasenya",
+	"Paste Large Text as File": "Enganxa un text llarg com a fitxer",
+	"PDF document (.pdf)": "Document PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)",
+	"pending": "pendent",
+	"Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia",
+	"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}}",
+	"Permissions": "Permisos",
+	"Personalization": "Personalització",
+	"Pin": "Fixar",
+	"Pinned": "Fixat",
+	"Pioneer insights": "Perspectives pioneres",
+	"Pipeline deleted successfully": "Pipeline eliminada correctament",
+	"Pipeline downloaded successfully": "Pipeline descarregada correctament",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "No s'ha detectat Pipelines",
+	"Pipelines Valves": "Vàlvules de les Pipelines",
+	"Plain text (.txt)": "Text pla (.txt)",
+	"Playground": "Zona de jocs",
+	"Please carefully review the following warnings:": "Si us plau, revisa els següents avisos amb cura:",
+	"Please enter a prompt": "Si us plau, entra una indicació",
+	"Please fill in all fields.": "Emplena tots els camps, si us plau.",
+	"Please select a model first.": "",
+	"Please select a reason": "Si us plau, selecciona una raó",
+	"Port": "Port",
+	"Positive attitude": "Actitud positiva",
+	"Prefix ID": "Identificador del prefix",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "L'identificador de prefix s'utilitza per evitar conflictes amb altres connexions afegint un prefix als ID de model; deixa'l en blanc per desactivar-lo.",
+	"Previous 30 days": "30 dies anteriors",
+	"Previous 7 days": "7 dies anteriors",
+	"Profile Image": "Imatge de perfil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Indicació (p.ex. Digues-me quelcom divertit sobre l'Imperi Romà)",
+	"Prompt Content": "Contingut de la indicació",
+	"Prompt created successfully": "Indicació creada correctament",
+	"Prompt suggestions": "Suggeriments d'indicacions",
+	"Prompt updated successfully": "Indicació actualitzada correctament",
+	"Prompts": "Indicacions",
+	"Prompts Access": "Accés a les indicacions",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Obtenir \"{{searchValue}}\" de Ollama.com",
+	"Pull a model from Ollama.com": "Obtenir un model d'Ollama.com",
+	"Query Generation Prompt": "Indicació per a generació de consulta",
+	"Query Params": "Paràmetres de consulta",
+	"RAG Template": "Plantilla RAG",
+	"Rating": "Valoració",
+	"Re-rank models by topic similarity": "Reclassificar els models per similitud de temes",
+	"Read Aloud": "Llegir en veu alta",
+	"Record voice": "Enregistrar la veu",
+	"Redirecting you to OpenWebUI Community": "Redirigint-te a la comunitat OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Redueix la probabilitat de generar ximpleries. Un valor més alt (p. ex. 100) donarà respostes més diverses, mentre que un valor més baix (p. ex. 10) serà més conservador. (Per defecte: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Fes referència a tu mateix com a \"Usuari\" (p. ex., \"L'usuari està aprenent espanyol\")",
+	"References from": "Referències de",
+	"Refused when it shouldn't have": "Refusat quan no hauria d'haver estat",
+	"Regenerate": "Regenerar",
+	"Release Notes": "Notes de la versió",
+	"Relevance": "Rellevància",
+	"Remove": "Eliminar",
+	"Remove Model": "Eliminar el model",
+	"Rename": "Canviar el nom",
+	"Reorder Models": "Reordenar els models",
+	"Repeat Last N": "Repeteix els darrers N",
+	"Request Mode": "Mode de sol·licitud",
+	"Reranking Model": "Model de reavaluació",
+	"Reranking model disabled": "Model de reavaluació desactivat",
+	"Reranking model set to \"{{reranking_model}}\"": "Model de reavaluació establert a \"{{reranking_model}}\"",
+	"Reset": "Restableix",
+	"Reset All Models": "Restablir tots els models",
+	"Reset Upload Directory": "Restableix el directori de pujades",
+	"Reset Vector Storage/Knowledge": "Restableix el Repositori de vectors/Coneixement",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Les notifications de resposta no es poden activar perquè els permisos del lloc web han estat rebutjats. Comprova les preferències del navegador per donar l'accés necessari.",
+	"Response splitting": "Divisió de la resposta",
+	"Result": "Resultat",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Entrada de text ric per al xat",
+	"RK": "RK",
+	"Role": "Rol",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Albada Rosé Pine",
+	"RTL": "RTL",
+	"Run": "Executar",
+	"Running": "S'està executant",
+	"Save": "Desar",
+	"Save & Create": "Desar i crear",
+	"Save & Update": "Desar i actualitzar",
+	"Save As Copy": "Desar com a còpia",
+	"Save Tag": "Desar l'etiqueta",
+	"Saved": "Desat",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Desar els registres de xat directament a l'emmagatzematge del teu navegador ja no està suportat. Si us plau, descarregr i elimina els registres de xat fent clic al botó de sota. No et preocupis, pots tornar a importar fàcilment els teus registres de xat al backend a través de",
+	"Scroll to bottom when switching between branches": "Desplaçar a la part inferior quan es canviï de branca",
+	"Search": "Cercar",
+	"Search a model": "Cercar un model",
+	"Search Base": "Base de cerca",
+	"Search Chats": "Cercar xats",
+	"Search Collection": "Cercar col·leccions",
+	"Search Filters": "Filtres de cerca",
+	"search for tags": "cercar etiquetes",
+	"Search Functions": "Cercar funcions",
+	"Search Knowledge": "Cercar coneixement",
+	"Search Models": "Cercar models",
+	"Search options": "Opcions de cerca",
+	"Search Prompts": "Cercar indicacions",
+	"Search Result Count": "Recompte de resultats de cerca",
+	"Search the web": "Cercar la web",
+	"Search Tools": "Cercar eines",
+	"SearchApi API Key": "Clau API de SearchApi",
+	"SearchApi Engine": "Motor de SearchApi",
+	"Searched {{count}} sites_one": "S'ha cercat {{count}} una pàgina",
+	"Searched {{count}} sites_many": "S'han cercat {{count}} pàgines",
+	"Searched {{count}} sites_other": "S'han cercat {{count}} pàgines",
+	"Searching \"{{searchQuery}}\"": "Cercant \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Cercant \"{{searchQuery}}\" al coneixement",
+	"Searxng Query URL": "URL de consulta de Searxng",
+	"See readme.md for instructions": "Consulta l'arxiu readme.md per obtenir instruccions",
+	"See what's new": "Veure què hi ha de nou",
+	"Seed": "Llavor",
+	"Select a base model": "Seleccionar un model base",
+	"Select a engine": "Seleccionar un motor",
+	"Select a function": "Seleccionar una funció",
+	"Select a group": "Seleccionar un grup",
+	"Select a model": "Seleccionar un model",
+	"Select a pipeline": "Seleccionar una Pipeline",
+	"Select a pipeline url": "Seleccionar l'URL d'una Pipeline",
+	"Select a tool": "Seleccionar una eina",
+	"Select Engine": "Seleccionar el motor",
+	"Select Knowledge": "Seleccionar coneixement",
+	"Select model": "Seleccionar un model",
+	"Select only one model to call": "Seleccionar només un model per trucar",
+	"Selected model(s) do not support image inputs": "El(s) model(s) seleccionats no admeten l'entrada d'imatges",
+	"Semantic distance to query": "Distància semàntica a la pregunta",
+	"Send": "Enviar",
+	"Send a Message": "Enviar un missatge",
+	"Send message": "Enviar missatge",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Envia `stream_options: { include_usage: true }` a la sol·licitud.\nEls proveïdors compatibles retornaran la informació d'ús del token a la resposta quan s'estableixi.",
+	"September": "Setembre",
+	"Serper API Key": "Clau API de Serper",
+	"Serply API Key": "Clau API de Serply",
+	"Serpstack API Key": "Clau API de Serpstack",
+	"Server connection verified": "Connexió al servidor verificada",
+	"Set as default": "Establir com a predeterminat",
+	"Set CFG Scale": "Establir l'escala CFG",
+	"Set Default Model": "Establir el model predeterminat",
+	"Set embedding model": "Establir el model d'incrustació",
+	"Set embedding model (e.g. {{model}})": "Establir el model d'incrustació (p.ex. {{model}})",
+	"Set Image Size": "Establir la mida de la image",
+	"Set reranking model (e.g. {{model}})": "Establir el model de reavaluació (p.ex. {{model}})",
+	"Set Sampler": "Establir el mostrejador",
+	"Set Scheduler": "Establir el programador",
+	"Set Steps": "Establir el nombre de passos",
+	"Set Task Model": "Establir el model de tasca",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Establir el nombre de dispositius GPU utilitzats per al càlcul. Aquesta opció controla quants dispositius GPU (si estan disponibles) s'utilitzen per processar les sol·licituds entrants. Augmentar aquest valor pot millorar significativament el rendiment dels models optimitzats per a l'acceleració de la GPU, però també pot consumir més energia i recursos de GPU.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Establir el nombre de fils de treball utilitzats per al càlcul. Aquesta opció controla quants fils s'utilitzen per processar les sol·licituds entrants simultàniament. Augmentar aquest valor pot millorar el rendiment amb càrregues de treball de concurrència elevada, però també pot consumir més recursos de CPU.",
+	"Set Voice": "Establir la veu",
+	"Set whisper model": "Establir el model whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Establir fins a quin punt el model mira enrere per evitar la repetició. (Per defecte: 64, 0 = desactivat, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Establir amb quina força penalitzar les repeticions. Un valor més alt (p. ex., 1,5) penalitzarà les repeticions amb més força, mentre que un valor més baix (p. ex., 0,9) serà més indulgent. (Per defecte: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Establir la llavor del nombre aleatori que s'utilitzarà per a la generació. Establir-ho a un número específic farà que el model generi el mateix text per a la mateixa sol·licitud. (Per defecte: aleatori)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Estableix la mida de la finestra de context utilitzada per generar el següent token. (Per defecte: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Establir les seqüències d'aturada a utilitzar. Quan es trobi aquest patró, el LLM deixarà de generar text. Es poden establir diversos patrons de parada especificant diversos paràmetres de parada separats en un fitxer model.",
+	"Settings": "Preferències",
+	"Settings saved successfully!": "Les preferències s'han desat correctament",
+	"Share": "Compartir",
+	"Share Chat": "Compartir el xat",
+	"Share to OpenWebUI Community": "Compartir amb la comunitat OpenWebUI",
+	"Show": "Mostrar",
+	"Show \"What's New\" modal on login": "Veure 'Què hi ha de nou' a l'entrada",
+	"Show Admin Details in Account Pending Overlay": "Mostrar els detalls de l'administrador a la superposició del compte pendent",
+	"Show shortcuts": "Mostrar dreceres",
+	"Show your support!": "Mostra el teu suport!",
+	"Showcased creativity": "Creativitat mostrada",
+	"Sign in": "Iniciar sessió",
+	"Sign in to {{WEBUI_NAME}}": "Iniciar sessió a {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Iniciar sessió a {{WEBUI_NAME}} amb LDAP",
+	"Sign Out": "Tancar sessió",
+	"Sign up": "Registrar-se",
+	"Sign up to {{WEBUI_NAME}}": "Registrar-se a {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Iniciant sessió a {{WEBUI_NAME}}",
+	"Source": "Font",
+	"Speech Playback Speed": "Velocitat de la parla",
+	"Speech recognition error: {{error}}": "Error de reconeixement de veu: {{error}}",
+	"Speech-to-Text Engine": "Motor de veu a text",
+	"Stop": "Atura",
+	"Stop Sequence": "Atura la seqüència",
+	"Stream Chat Response": "Fer streaming de la resposta del xat",
+	"STT Model": "Model SST",
+	"STT Settings": "Preferències de STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtítol (per exemple, sobre l'Imperi Romà)",
+	"Success": "Èxit",
+	"Successfully updated.": "Actualitzat correctament.",
+	"Suggested": "Suggerit",
+	"Support": "Dona suport",
+	"Support this plugin:": "Dona suport a aquest complement:",
+	"Sync directory": "Sincronitzar directori",
+	"System": "Sistema",
+	"System Instructions": "Instruccions de sistema",
+	"System Prompt": "Indicació del Sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Indicació per a la generació d'etiquetes",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "El mostreig sense cua s'utilitza per reduir l'impacte de tokens menys probables de la sortida. Un valor més alt (p. ex., 2,0) reduirà més l'impacte, mentre que un valor d'1,0 desactiva aquesta configuració. (per defecte: 1)",
+	"Tap to interrupt": "Prem per interrompre",
+	"Tavily API Key": "Clau API de Tavily",
+	"Tell us more:": "Dona'ns més informació:",
+	"Temperature": "Temperatura",
+	"Template": "Plantilla",
+	"Temporary Chat": "Xat temporal",
+	"Text Splitter": "Separador de text",
+	"Text-to-Speech Engine": "Motor de text a veu",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Gràcies pel teu comentari!",
+	"The Application Account DN you bind with for search": "El DN del compte d'aplicació per realitzar la cerca",
+	"The base to search for users": "La base per cercar usuaris",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "La mida del lot determina quantes sol·licituds de text es processen alhora. Una mida de lot més gran pot augmentar el rendiment i la velocitat del model, però també requereix més memòria. (Per defecte: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Els desenvolupadors d'aquest complement són voluntaris apassionats de la comunitat. Si trobeu útil aquest complement, considereu contribuir al seu desenvolupament.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "La classificació d'avaluació es basa en el sistema de qualificació Elo i s'actualitza en temps real.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "L'atribut LDAP que mapeja el nom d'usuari amb l'usuari que vol iniciar sessió",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "La classificació està actualment en versió beta i és possible que s'ajustin els càlculs de la puntuació a mesura que es perfeccioni l'algorisme.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "La mida màxima del fitxer en MB. Si la mida del fitxer supera aquest límit, el fitxer no es carregarà.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "El nombre màxim de fitxers que es poden utilitzar alhora al xat. Si el nombre de fitxers supera aquest límit, els fitxers no es penjaran.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "El valor de puntuació hauria de ser entre 0.0 (0%) i 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "La temperatura del model. Augmentar la temperatura farà que el model respongui de manera més creativa. (Per defecte: 0,8)",
+	"Theme": "Tema",
+	"Thinking...": "Pensant...",
+	"This action cannot be undone. Do you wish to continue?": "Aquesta acció no es pot desfer. Vols continuar?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden desades de manera segura a la teva base de dades. Gràcies!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Aquesta és una funció experimental, és possible que no funcioni com s'espera i està subjecta a canvis en qualsevol moment.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Aquesta opció controla quants tokens es conserven en actualitzar el context. Per exemple, si s'estableix en 2, es conservaran els darrers 2 tokens del context de conversa. Preservar el context pot ajudar a mantenir la continuïtat d'una conversa, però pot reduir la capacitat de respondre a nous temes. (Per defecte: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Aquesta opció estableix el nombre màxim de tokens que el model pot generar en la seva resposta. Augmentar aquest límit permet que el model proporcioni respostes més llargues, però també pot augmentar la probabilitat que es generi contingut poc útil o irrellevant. (Per defecte: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Aquesta opció eliminarà tots els fitxers existents de la col·lecció i els substituirà per fitxers recentment penjats.",
+	"This response was generated by \"{{model}}\"": "Aquesta resposta l'ha generat el model \"{{model}}\"",
+	"This will delete": "Això eliminarà",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Això eliminarà <strong>{{NAME}}</strong> i <strong>tots els continguts</strong>.",
+	"This will delete all models including custom models": "Això eliminarà tots els models incloent els personalitzats",
+	"This will delete all models including custom models and cannot be undone.": "Això eliminarà tots els models incloent els personalitzats i no es pot desfer",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Això restablirà la base de coneixement i sincronitzarà tots els fitxers. Vols continuar?",
+	"Thorough explanation": "Explicació en detall",
+	"Tika": "Tika",
+	"Tika Server URL required.": "La URL del servidor Tika és obligatòria.",
+	"Tiktoken": "Tiktoken",
+	"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 (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
+	"Title Auto-Generation": "Generació automàtica de títol",
+	"Title cannot be an empty string.": "El títol no pot ser una cadena buida.",
+	"Title Generation Prompt": "Indicació de generació de títol",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Per accedir als noms dels models disponibles per descarregar,",
+	"To access the GGUF models available for downloading,": "Per accedir als models GGUF disponibles per descarregar,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Per accedir a la WebUI, poseu-vos en contacte amb l'administrador. Els administradors poden gestionar els estats dels usuaris des del tauler d'administració.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Per adjuntar la base de coneixement aquí, afegiu-la primer a l'espai de treball \"Coneixement\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Per protegir la privadesa, només es comparteixen puntuacions, identificadors de models, etiquetes i metadades dels comentaris; els registres de xat romanen privats i no s'inclouen.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Per seleccionar accions aquí, afegeix-les primer a l'espai de treball \"Funcions\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Per seleccionar filtres aquí, afegeix-los primer a l'espai de treball \"Funcions\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Per seleccionar kits d'eines aquí, afegeix-los primer a l'espai de treball \"Eines\".",
+	"Toast notifications for new updates": "Notificacions Toast de noves actualitzacions",
+	"Today": "Avui",
+	"Toggle settings": "Alterna preferències",
+	"Toggle sidebar": "Alterna la barra lateral",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens a mantenir en l'actualització del context (num_keep)",
+	"Too verbose": "Massa explicit",
+	"Tool created successfully": "Eina creada correctament",
+	"Tool deleted successfully": "Eina eliminada correctament",
+	"Tool Description": "Descripció de l'eina",
+	"Tool ID": "ID de l'eina",
+	"Tool imported successfully": "Eina importada correctament",
+	"Tool Name": "Nom de l'eina",
+	"Tool updated successfully": "Eina actualitzada correctament",
+	"Tools": "Eines",
+	"Tools Access": "Accés a les eines",
+	"Tools are a function calling system with arbitrary code execution": "Les eines són un sistema de crida a funcions amb execució de codi arbitrari",
+	"Tools have a function calling system that allows arbitrary code execution": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari",
+	"Tools have a function calling system that allows arbitrary code execution.": "Les eines disposen d'un sistema de crida a funcions que permet execució de codi arbitrari.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformadors",
+	"Trouble accessing Ollama?": "Problemes en accedir a Ollama?",
+	"TTS Model": "Model TTS",
+	"TTS Settings": "Preferències de TTS",
+	"TTS Voice": "Veu TTS",
+	"Type": "Tipus",
+	"Type Hugging Face Resolve (Download) URL": "Escriu l'URL de Resolució (Descàrrega) de Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Desarxivar tot",
+	"Unarchive All Archived Chats": "Desarxivar tots els xats arxivats",
+	"Unarchive Chat": "Desarxivar xat",
+	"Unlock mysteries": "Desbloqueja els misteris",
+	"Unpin": "Alliberar",
+	"Unravel secrets": "Descobreix els secrets",
+	"Untagged": "Sense etiquetes",
+	"Update": "Actualitzar",
+	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
+	"Update for the latest features and improvements.": "Actualitza per a les darreres característiques i millores.",
+	"Update password": "Actualitzar la contrasenya",
+	"Updated": "Actualitzat",
+	"Updated at": "Actualitzat el",
+	"Updated At": "Actualitzat el",
+	"Upload": "Pujar",
+	"Upload a GGUF model": "Pujar un model GGUF",
+	"Upload directory": "Pujar directori",
+	"Upload files": "Pujar fitxers",
+	"Upload Files": "Pujar fitxers",
+	"Upload Pipeline": "Pujar una Pipeline",
+	"Upload Progress": "Progrés de càrrega",
+	"URL": "URL",
+	"URL Mode": "Mode URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Utilitza '#' a l'entrada de la indicació per carregar i incloure els teus coneixements.",
+	"Use Gravatar": "Utilitzar Gravatar",
+	"Use groups to group your users and assign permissions.": "Utilitza grups per agrupar els usuaris i assignar permisos.",
+	"Use Initials": "Utilitzar inicials",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "usuari",
+	"User": "Usuari",
+	"User location successfully retrieved.": "Ubicació de l'usuari obtinguda correctament",
+	"Username": "Nom d'usuari",
+	"Users": "Usuaris",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "S'utilitza el model d'Arena predeterminat amb tots els models. Clica el botó més per afegir models personalitzats.",
+	"Utilize": "Utilitzar",
+	"Valid time units:": "Unitats de temps vàlides:",
+	"Valves": "Valves",
+	"Valves updated": "Valves actualitzat",
+	"Valves updated successfully": "Valves actualitat correctament",
+	"variable": "variable",
+	"variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.",
+	"Version": "Versió",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Versió {{selectedVersion}} de {{totalVersions}}",
+	"Visibility": "Visibilitat",
+	"Voice": "Veu",
+	"Voice Input": "Entrada de veu",
+	"Warning": "Avís",
+	"Warning:": "Avís:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Avís: Habilitar això permetrà als usuaris penjar codi arbitrari al servidor.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avís: Si s'actualitza o es canvia el model d'incrustació, s'hauran de tornar a importar tots els documents.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Preferències del carregador web",
+	"Web Search": "Cerca la web",
+	"Web Search Engine": "Motor de cerca de la web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL del webhook",
+	"WebUI Settings": "Preferències de WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI farà peticions a \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI farà peticions a \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Què intentes aconseguir?",
+	"What are you working on?": "En què estàs treballant?",
+	"What’s New in": "Què hi ha de nou a",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Quan està activat, el model respondrà a cada missatge de xat en temps real, generant una resposta tan bon punt l'usuari envia un missatge. Aquest mode és útil per a aplicacions de xat en directe, però pot afectar el rendiment en maquinari més lent.",
+	"wherever you are": "allà on estiguis",
+	"Whisper (Local)": "Whisper (local)",
+	"Why?": "Per què?",
+	"Widescreen Mode": "Mode de pantalla ampla",
+	"Won": "Ha guanyat",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funciona juntament amb top-k. Un valor més alt (p. ex., 0,95) donarà lloc a un text més divers, mentre que un valor més baix (p. ex., 0,5) generarà un text més concentrat i conservador. (Per defecte: 0,9)",
+	"Workspace": "Espai de treball",
+	"Workspace Permissions": "Permisos de l'espai de treball",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència d'indicació (p. ex. Qui ets?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
+	"Write something...": "Escriu quelcom...",
+	"Write your model template content here": "Introdueix el contingut de la plantilla del teu model aquí",
+	"Yesterday": "Ahir",
+	"You": "Tu",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Només pots xatejar amb un màxim de {{maxCount}} fitxers alhora.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Pots personalitzar les teves interaccions amb els models de llenguatge afegint memòries mitjançant el botó 'Gestiona' que hi ha a continuació, fent-les més útils i adaptades a tu.",
+	"You cannot upload an empty file.": "No es pot pujar un ariux buit.",
+	"You do not have permission to upload files.": "No tens permisos per pujar arxius.",
+	"You have no archived conversations.": "No tens converses arxivades.",
+	"You have shared this chat": "Has compartit aquest xat",
+	"You're a helpful assistant.": "Ets un assistent útil.",
+	"You're now logged in.": "Ara estàs connectat.",
+	"Your account status is currently pending activation.": "El compte està actualment pendent d'activació",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Tota la teva contribució anirà directament al desenvolupador del complement; Open WebUI no se'n queda cap percentatge. Tanmateix, la plataforma de finançament escollida pot tenir les seves pròpies comissions.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Preferències del carregador de Youtube"
+}
diff --git a/src/lib/i18n/locales/ceb-PH/translation.json b/src/lib/i18n/locales/ceb-PH/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..fe9f67ad513d4a645598350cc220c0b7b33ef3b9
--- /dev/null
+++ b/src/lib/i18n/locales/ceb-PH/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' para walay expiration.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(pananglitan `sh webui.sh --api`)",
+	"(latest)": "",
+	"{{ models }}": "",
+	"{{user}}'s Chats": "",
+	"{{webUIName}} Backend Required": "Backend {{webUIName}} gikinahanglan",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "",
+	"a user": "usa ka user",
+	"About": "Mahitungod sa",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Account",
+	"Account Activation Pending": "",
+	"Accurate information": "",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "",
+	"Add a tag": "Pagdugang og tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Pagdugang og custom prompt",
+	"Add Files": "Idugang ang mga file",
+	"Add Group": "",
+	"Add Memory": "",
+	"Add Model": "",
+	"Add Tag": "",
+	"Add Tags": "idugang ang mga tag",
+	"Add text content": "",
+	"Add User": "",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ang pag-adjust niini nga mga setting magamit ang mga pagbag-o sa tanan nga tiggamit.",
+	"admin": "Administrator",
+	"Admin": "",
+	"Admin Panel": "Admin Panel",
+	"Admin Settings": "Mga setting sa administratibo",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "advanced settings",
+	"Advanced Params": "",
+	"All chats": "",
+	"All Documents": "",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Tugoti nga mapapas ang mga chat",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Naa na kay account ?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "usa ka katabang",
+	"and": "Ug",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "",
+	"API Base URL": "API Base URL",
+	"API Key": "yawe sa API",
+	"API Key created.": "",
+	"API keys": "",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "",
+	"Archive": "",
+	"Archive All Chats": "",
+	"Archived Chats": "pagrekord sa chat",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Sigurado ka ?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Ilakip ang usa ka file",
+	"Attention to detail": "Pagtagad sa mga detalye",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Awtomatikong kopya sa tubag sa clipboard",
+	"Auto-playback response": "Autoplay nga tubag",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "Base URL AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Ang AUTOMATIC1111 base URL gikinahanglan.",
+	"Available list": "",
+	"available!": "magamit!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Balik",
+	"Bad Response": "",
+	"Banners": "",
+	"Base Model (From)": "",
+	"Batch Size (num_batch)": "",
+	"before": "",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Pagkanselar",
+	"Capabilities": "",
+	"Certificate Path": "",
+	"Change Password": "Usba ang password",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Panaghisgot",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "",
+	"Chat Controls": "",
+	"Chat direction": "",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Mga panaghisgot",
+	"Check Again": "Susiha pag-usab",
+	"Check for updates": "Susiha ang mga update",
+	"Checking for updates...": "Pagsusi alang sa mga update...",
+	"Choose a model before saving...": "Pagpili og template sa dili pa i-save...",
+	"Chunk Overlap": "Block overlap",
+	"Chunk Params": "Mga Setting sa Block",
+	"Chunk Size": "Gidak-on sa block",
+	"Ciphers": "",
+	"Citation": "Mga kinutlo",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "I-klik dinhi alang sa tabang.",
+	"Click here to": "",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "I-klik dinhi aron makapili",
+	"Click here to select a csv file.": "",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "I-klik dinhi.",
+	"Click on the user role button to change a user's role.": "I-klik ang User Role button aron usbon ang role sa user.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "",
+	"Close": "Suod nga",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Koleksyon",
+	"Color": "",
+	"ComfyUI": "",
+	"ComfyUI Base URL": "",
+	"ComfyUI Base URL is required.": "",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Pag-order",
+	"Completions": "",
+	"Concurrent Requests": "",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Kumpirma ang password",
+	"Confirm your action": "",
+	"Connections": "Mga koneksyon",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Kontento",
+	"Content Extraction": "",
+	"Context Length": "Ang gitas-on sa konteksto",
+	"Continue Response": "",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "",
+	"Copied to clipboard": "",
+	"Copy": "",
+	"Copy last code block": "Kopyaha ang katapusang bloke sa code",
+	"Copy last response": "Kopyaha ang kataposang tubag",
+	"Copy Link": "",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Ang pagkopya sa clipboard malampuson!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "",
+	"Create Account": "Paghimo og account",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "",
+	"Create new secret key": "",
+	"Created at": "Gihimo ang",
+	"Created At": "",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Kasamtangang modelo",
+	"Current Password": "Kasamtangang Password",
+	"Custom": "Custom",
+	"Dark": "Ngitngit",
+	"Database": "Database",
+	"December": "",
+	"Default": "Pinaagi sa default",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "",
+	"Default Model": "",
+	"Default model updated": "Gi-update nga default template",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Default nga prompt nga mga sugyot",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Default nga Papel sa Gumagamit",
+	"Delete": "",
+	"Delete a model": "Pagtangtang sa usa ka template",
+	"Delete All Chats": "",
+	"Delete All Models": "",
+	"Delete chat": "Pagtangtang sa panaghisgot",
+	"Delete Chat": "",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "",
+	"Delete tool?": "",
+	"Delete User": "",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gipapas",
+	"Deleted {{name}}": "",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Deskripsyon",
+	"Didn't fully follow instructions": "",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "",
+	"Discover a prompt": "Pagkaplag usa ka prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Pagdiskubre, pag-download ug pagsuhid sa mga naandan nga pag-aghat",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Pagdiskobre, pag-download, ug pagsuhid sa mga preset sa template",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Ipakita ang username imbes nga 'Ikaw' sa Panaghisgutan",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Dokumento",
+	"Documentation": "",
+	"Documents": "Mga dokumento",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "wala maghimo ug eksternal nga koneksyon, ug ang imong data nagpabiling luwas sa imong lokal nga host server.",
+	"Don't have an account?": "Wala kay account ?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "",
+	"Done": "",
+	"Download": "",
+	"Download canceled": "",
+	"Download Database": "I-download ang database",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Ihulog ang bisan unsang file dinhi aron idugang kini sa panag-istoryahanay",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "I-edit ang tiggamit",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "E-mail",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "I-enable ang bag-ong mga rehistro",
+	"Enable Web Search": "",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
+	"Enter {{role}} message here": "Pagsulod sa mensahe {{role}} dinhi",
+	"Enter a detail about yourself for your LLMs to recall": "",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Pagsulod sa block overlap",
+	"Enter Chunk Size": "Isulod ang block size",
+	"Enter description": "",
+	"Enter Github Raw URL": "",
+	"Enter Google PSE API Key": "",
+	"Enter Google PSE Engine Id": "",
+	"Enter Image Size (e.g. 512x512)": "Pagsulod sa gidak-on sa hulagway (pananglitan 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Pagsulod sa template tag (e.g. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Pagsulod sa gidaghanon sa mga lakang (e.g. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "",
+	"Enter Seed": "",
+	"Enter Serper API Key": "",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Pagsulod sa katapusan nga han-ay",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Pagsulod sa Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Pagsulod sa URL (e.g. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "",
+	"Enter Your Email": "Pagsulod sa imong e-mail address",
+	"Enter Your Full Name": "Ibutang ang imong tibuok nga ngalan",
+	"Enter your message": "",
+	"Enter Your Password": "Ibutang ang imong password",
+	"Enter Your Role": "",
+	"Enter Your Username": "",
+	"Error": "",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Eksperimento",
+	"Explore the cosmos": "",
+	"Export": "",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "I-export ang tanan nga mga chat (Tanan nga tiggamit)",
+	"Export chat (.json)": "",
+	"Export Chats": "I-export ang mga chat",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "",
+	"Export Presets": "",
+	"Export Prompts": "Export prompts",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "",
+	"Failed to read clipboard contents": "Napakyas sa pagbasa sa sulod sa clipboard",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "File mode",
+	"File not found.": "Wala makit-an ang file.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Fluidly stream large external response chunks": "Hapsay nga paghatud sa daghang mga tipik sa eksternal nga mga tubag",
+	"Focus chat input": "Pag-focus sa entry sa diskusyon",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Heneral",
+	"General Settings": "kinatibuk-ang mga setting",
+	"Generate Image": "",
+	"Generating search query": "",
+	"Generation Info": "",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "",
+	"Google PSE API Key": "",
+	"Google PSE Engine Id": "",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "",
+	"Haptic Feedback": "",
+	"has no conversations.": "",
+	"Hello, {{name}}": "Maayong buntag, {{name}}",
+	"Help": "",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Tagoa",
+	"Host": "",
+	"How can I help you today?": "Unsaon nako pagtabang kanimo karon?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Pagmugna og hulagway (Eksperimento)",
+	"Image Generation Engine": "Makina sa paghimo og imahe",
+	"Image Settings": "Mga Setting sa Imahen",
+	"Images": "Mga hulagway",
+	"Import Chats": "Import nga mga chat",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "",
+	"Import Presets": "",
+	"Import Prompts": "Import prompt",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Iapil ang `--api` nga bandila kung nagdagan nga stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "",
+	"Input commands": "Pagsulod sa input commands",
+	"Install from Github URL": "",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Interface",
+	"Invalid file format.": "",
+	"Invalid Tag": "",
+	"January": "",
+	"Jina API Key": "",
+	"join our Discord for help.": "Apil sa among Discord alang sa tabang.",
+	"JSON": "JSON",
+	"JSON Preview": "",
+	"July": "",
+	"June": "",
+	"JWT Expiration": "Pag-expire sa JWT",
+	"JWT Token": "JWT token",
+	"Keep Alive": "Padayon nga aktibo",
+	"Key": "",
+	"Keyboard shortcuts": "Mga shortcut sa keyboard",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Pinulongan",
+	"Last Active": "",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Kahayag",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "Ang mga LLM mahimong masayop. ",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "",
+	"Made by OpenWebUI Community": "Gihimo sa komunidad sa OpenWebUI",
+	"Make sure to enclose them with": "Siguruha nga palibutan sila",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "",
+	"March": "",
+	"Max Tokens (num_predict)": "",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Ang labing taas nga 3 nga mga disenyo mahimong ma-download nga dungan. ",
+	"May": "",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
+	"Min P": "",
+	"Minimum Score": "",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Ang modelo'{{modelName}}' malampuson nga na-download.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Ang modelo'{{modelTag}}' naa na sa pila para ma-download.",
+	"Model {{modelId}} not found": "Modelo {{modelId}} wala makit-an",
+	"Model {{modelName}} is not vision capable": "",
+	"Model {{name}} is now {{status}}": "",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
+	"Model Filtering": "",
+	"Model ID": "",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Wala gipili ang modelo",
+	"Model Params": "",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Mga sulod sa template file",
+	"Models": "Mga modelo",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "",
+	"Name": "Ngalan",
+	"Name your knowledge base": "",
+	"New Chat": "Bag-ong diskusyon",
+	"New folder": "",
+	"New Password": "Bag-ong Password",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "",
+	"No search query generated": "",
+	"No source available": "Walay tinubdan nga anaa",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "",
+	"Not factually correct": "",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
+	"Notes": "",
+	"Notifications": "Mga pahibalo sa desktop",
+	"November": "",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "",
+	"Off": "Napuo",
+	"Okay, Let's Go!": "Okay, lakaw na!",
+	"OLED Dark": "",
+	"Ollama": "",
+	"Ollama API": "",
+	"Ollama API disabled": "",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama nga bersyon",
+	"On": "Gipaandar",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Ang alphanumeric nga mga karakter ug hyphen lang ang gitugotan sa command string.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! ",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! ",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Ablihi ang bag-ong diskusyon",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "",
+	"OpenAI API Key is required.": "Ang yawe sa OpenAI API gikinahanglan.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "",
+	"or": "O",
+	"Organize your users": "",
+	"Other": "",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Password",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "",
+	"PDF Extract Images (OCR)": "PDF Image Extraction (OCR)",
+	"pending": "gipugngan",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Gidili ang pagtugot sa dihang nag-access sa mikropono: {{error}}",
+	"Permissions": "",
+	"Personalization": "",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "",
+	"Playground": "Dulaanan",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "",
+	"Previous 7 days": "",
+	"Profile Image": "",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
+	"Prompt Content": "Ang sulod sa prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Maabtik nga mga Sugyot",
+	"Prompt updated successfully": "",
+	"Prompts": "Mga aghat",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "Pagkuha ug template gikan sa Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Mga parameter sa pangutana",
+	"RAG Template": "RAG nga modelo",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "",
+	"Record voice": "Irekord ang tingog",
+	"Redirecting you to OpenWebUI Community": "Gi-redirect ka sa komunidad sa OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "",
+	"Regenerate": "",
+	"Release Notes": "Release Notes",
+	"Relevance": "",
+	"Remove": "",
+	"Remove Model": "",
+	"Rename": "",
+	"Reorder Models": "",
+	"Repeat Last N": "Balika ang katapusang N",
+	"Request Mode": "Query mode",
+	"Reranking Model": "",
+	"Reranking model disabled": "",
+	"Reranking model set to \"{{reranking_model}}\"": "",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Papel",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Aube Pine Rosé",
+	"RTL": "",
+	"Run": "",
+	"Running": "",
+	"Save": "Tipigi",
+	"Save & Create": "I-save ug Paghimo",
+	"Save & Update": "I-save ug I-update",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Ang pag-save sa mga chat log direkta sa imong browser storage dili na suportado. ",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Pagpanukiduki",
+	"Search a model": "",
+	"Search Base": "",
+	"Search Chats": "",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "",
+	"Search options": "",
+	"Search Prompts": "Pangitaa ang mga prompt",
+	"Search Result Count": "",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "",
+	"Searched {{count}} sites_other": "",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "",
+	"See readme.md for instructions": "Tan-awa ang readme.md alang sa mga panudlo",
+	"See what's new": "Tan-awa unsay bag-o",
+	"Seed": "Binhi",
+	"Select a base model": "",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Pagpili og modelo",
+	"Select a pipeline": "",
+	"Select a pipeline url": "",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Pagpili og modelo",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "",
+	"Semantic distance to query": "",
+	"Send": "",
+	"Send a Message": "Magpadala ug mensahe",
+	"Send message": "Magpadala ug mensahe",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "",
+	"Serper API Key": "",
+	"Serply API Key": "",
+	"Serpstack API Key": "",
+	"Server connection verified": "Gipamatud-an nga koneksyon sa server",
+	"Set as default": "Define pinaagi sa default",
+	"Set CFG Scale": "",
+	"Set Default Model": "Ibutang ang default template",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "",
+	"Set Image Size": "Ibutang ang gidak-on sa hulagway",
+	"Set reranking model (e.g. {{model}})": "",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Ipasabot ang mga lakang",
+	"Set Task Model": "",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Ibutang ang tingog",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Mga setting",
+	"Settings saved successfully!": "Malampuson nga na-save ang mga setting!",
+	"Share": "",
+	"Share Chat": "",
+	"Share to OpenWebUI Community": "Ipakigbahin sa komunidad sa OpenWebUI",
+	"Show": "Pagpakita",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Ipakita ang mga shortcut",
+	"Show your support!": "",
+	"Showcased creativity": "",
+	"Sign in": "Para maka log in",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Pag-sign out",
+	"Sign up": "Pagrehistro",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Tinubdan",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Sayop sa pag-ila sa tingog: {{error}}",
+	"Speech-to-Text Engine": "Engine sa pag-ila sa tingog",
+	"Stop": "",
+	"Stop Sequence": "Pagkasunod-sunod sa pagsira",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "Mga setting sa STT",
+	"Subtitle (e.g. about the Roman Empire)": "",
+	"Success": "Kalampusan",
+	"Successfully updated.": "Malampuson nga na-update.",
+	"Suggested": "",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Sistema",
+	"System Instructions": "",
+	"System Prompt": "Madasig nga Sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "",
+	"Temperature": "Temperatura",
+	"Template": "Modelo",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Text-to-speech nga makina",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Kini nagsiguro nga ang imong bililhon nga mga panag-istoryahanay luwas nga natipig sa imong backend database. ",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Sugyot: Pag-update sa daghang variable nga lokasyon nga sunud-sunod pinaagi sa pagpindot sa tab key sa chat entry pagkahuman sa matag puli.",
+	"Title": "Titulo",
+	"Title (e.g. Tell me a fun fact)": "",
+	"Title Auto-Generation": "Awtomatikong paghimo sa titulo",
+	"Title cannot be an empty string.": "",
+	"Title Generation Prompt": "Madasig nga henerasyon sa titulo",
+	"TLS": "",
+	"To access the available model names for downloading,": "Aron ma-access ang mga ngalan sa modelo nga ma-download,",
+	"To access the GGUF models available for downloading,": "Aron ma-access ang mga modelo sa GGUF nga magamit alang sa pag-download,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "",
+	"Toggle settings": "I-toggle ang mga setting",
+	"Toggle sidebar": "I-toggle ang sidebar",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Ibabaw nga P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Adunay mga problema sa pag-access sa Ollama?",
+	"TTS Model": "",
+	"TTS Settings": "Mga Setting sa TTS",
+	"TTS Voice": "",
+	"Type": "",
+	"Type Hugging Face Resolve (Download) URL": "Pagsulod sa resolusyon (pag-download) URL Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh!  {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "",
+	"Update for the latest features and improvements.": "",
+	"Update password": "I-update ang password",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Pag-upload ug modelo sa GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "",
+	"Upload Pipeline": "",
+	"Upload Progress": "Pag-uswag sa Pag-upload",
+	"URL": "",
+	"URL Mode": "URL mode",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Paggamit sa Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "tiggamit",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Mga tiggamit",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Sa paggamit",
+	"Valid time units:": "Balido nga mga yunit sa oras:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "variable",
+	"variable to have them replaced with clipboard content.": "variable aron pulihan kini sa mga sulud sa clipboard.",
+	"Version": "Bersyon",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
+	"Web": "Web",
+	"Web API": "",
+	"Web Loader Settings": "",
+	"Web Search": "",
+	"Web Search Engine": "",
+	"Web Search Query Generation": "",
+	"Webhook URL": "",
+	"WebUI Settings": "Mga Setting sa WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Unsay bag-o sa",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Pagsulat og gisugyot nga prompt (eg. Kinsa ka?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Pagsulat og 50 ka pulong nga summary nga nagsumaryo [topic o keyword].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "",
+	"You": "",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "",
+	"You have shared this chat": "",
+	"You're a helpful assistant.": "Usa ka ka mapuslanon nga katabang",
+	"You're now logged in.": "Konektado ka na karon.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "",
+	"Youtube Loader Settings": ""
+}
diff --git a/src/lib/i18n/locales/cs-CZ/translation.json b/src/lib/i18n/locales/cs-CZ/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..2ec6671c975042a44ccca9ecc2e47c690adba13f
--- /dev/null
+++ b/src/lib/i18n/locales/cs-CZ/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' nebo '-1' pro žádné vypršení platnosti.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(např. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(např. `sh webui.sh --api`)",
+	"(latest)": "Nejnovější",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}}'s konverzace",
+	"{{webUIName}} Backend Required": "Požadován {{webUIName}} Backend",
+	"*Prompt node ID(s) are required for image generation": "*Jsou vyžadovány IDs pro prompt node pro generování obrázků",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Nová verze (v{{LATEST_VERSION}}) je nyní k dispozici.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model úloh se používá při provádění úloh, jako je generování názvů pro chaty a vyhledávací dotazy na webu.",
+	"a user": "uživatel",
+	"About": "O programu",
+	"Access": "Přístup",
+	"Access Control": "",
+	"Accessible to all users": "Přístupné pro všecny uživatele",
+	"Account": "Účet",
+	"Account Activation Pending": "Čeká na aktivaci účtu",
+	"Accurate information": "Přesné informace",
+	"Actions": "Akce",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Aktivujte tenhle příkaz napsáním \"/{{COMMAND}}\" do chat inputu",
+	"Active Users": "Aktivní uživatelé",
+	"Add": "Přidat",
+	"Add a model ID": "Přidat ID modelu",
+	"Add a short description about what this model does": "Přidejte krátký popis toho, co tento model dělá.",
+	"Add a tag": "Přidat štítek",
+	"Add Arena Model": "Přidat Arena model",
+	"Add Connection": "Přidat připojení",
+	"Add Content": "Přidat obsah",
+	"Add content here": "Přidat obsah zde",
+	"Add custom prompt": "Přidání vlastního promptu",
+	"Add Files": "Přidat soubory",
+	"Add Group": "Přidat skupinu",
+	"Add Memory": "Přidat paměť",
+	"Add Model": "Přidat model",
+	"Add Tag": "Přidat štítek",
+	"Add Tags": "Přidat štítky",
+	"Add text content": "Přidejte textový obsah",
+	"Add User": "Přidat uživatele",
+	"Add User Group": "Přidatg skupinu uživatelů",
+	"Adjusting these settings will apply changes universally to all users.": "Úprava těchto nastavení se projeví univerzálně u všech uživatelů.",
+	"admin": "amin",
+	"Admin": "Admin",
+	"Admin Panel": "Adminis panel",
+	"Admin Settings": "Nastavení admina",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administrátoři mají přístup ke všem nástrojům kdykoliv; uživatelé potřebují mít nástroje přiřazené podle modelu ve workspace.",
+	"Advanced Parameters": "Pokročilé parametry",
+	"Advanced Params": "Pokročilé parametry",
+	"All chats": "Všechny chaty",
+	"All Documents": "Všechny dokumenty",
+	"All models deleted successfully": "Všechny modely úspěšně odstráněny",
+	"Allow Chat Delete": "Povolit odstranění chatu",
+	"Allow Chat Deletion": "Povolit odstranění chatu",
+	"Allow Chat Edit": "Povolit úpravu chatu",
+	"Allow File Upload": "Povolit nahrávat soubory",
+	"Allow non-local voices": "Povolit ne-místní hlasy",
+	"Allow Temporary Chat": "Povolit dočasný chat",
+	"Allow User Location": "Povolit uživatelskou polohu",
+	"Allow Voice Interruption in Call": "Povolit přerušení hlasu při hovoru",
+	"Already have an account?": "Už máte účet?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "asistent",
+	"and": "a",
+	"and {{COUNT}} more": "a {{COUNT}} další/ch",
+	"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
+	"API Base URL": "Základní URL adresa API",
+	"API Key": "Klíč API",
+	"API Key created.": "API klíč byl vytvořen.",
+	"API keys": "API klíče",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Duben",
+	"Archive": "Archivovat",
+	"Archive All Chats": "Archivovat všechny chaty",
+	"Archived Chats": "Archivované chaty",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Jste si jistý?",
+	"Arena Models": "Arena modely",
+	"Artifacts": "Artefakty",
+	"Ask a question": "Zeptejte se na otázku",
+	"Assistant": "Ano, jak vám mohu pomoci?",
+	"Attach file": "Připojit soubor",
+	"Attention to detail": "Pozornost k detailům",
+	"Attribute for Username": "",
+	"Audio": "Zvuk",
+	"August": "Srpen",
+	"Authenticate": "Autentikace",
+	"Auto-Copy Response to Clipboard": "Automatické kopírování odpovědi do schránky",
+	"Auto-playback response": "Automatická odpověď při přehrávání",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "Výchozí URL pro AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Vyžaduje se základní URL pro AUTOMATIC1111.",
+	"Available list": "Dostupný seznam",
+	"available!": "k dispozici!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI syntéza řeči",
+	"Azure Region": "Azure oblast",
+	"Back": "Zpět",
+	"Bad Response": "Špatná odezva",
+	"Banners": "Bannery",
+	"Base Model (From)": "Základní model (z)",
+	"Batch Size (num_batch)": "Batch size (num_batch)",
+	"before": "před",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Klíč API pro Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Obcházení ověření SSL pro webové stránky",
+	"Call": "Volání",
+	"Call feature is not supported when using Web STT engine": "Funkce pro volání není podporována při použití Web STT engine.",
+	"Camera": "Kamera",
+	"Cancel": "Zrušit",
+	"Capabilities": "Schopnosti",
+	"Certificate Path": "",
+	"Change Password": "Změnit heslo",
+	"Character": "Znak",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "Obrázek pozadí chatu",
+	"Chat Bubble UI": "Uživatelské rozhraní chatu (Chat Bubble UI)",
+	"Chat Controls": "Ovládání chatu",
+	"Chat direction": "Směr chatu",
+	"Chat Overview": "Přehled chatu",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "Automatické generování značek chatu",
+	"Chats": "Chaty.",
+	"Check Again": "Zkontroluj znovu",
+	"Check for updates": "Zkontrolovat aktualizace",
+	"Checking for updates...": "Kontrola aktualizací...",
+	"Choose a model before saving...": "Vyberte model před uložením...",
+	"Chunk Overlap": "",
+	"Chunk Params": "",
+	"Chunk Size": "",
+	"Ciphers": "",
+	"Citation": "Odkaz",
+	"Clear memory": "Vymazat paměť",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klikněte zde pro nápovědu.",
+	"Click here to": "Klikněte sem pro",
+	"Click here to download user import template file.": "Klikněte zde pro stažení šablony souboru pro import uživatelů.",
+	"Click here to learn more about faster-whisper and see the available models.": "Klikněte sem a zjistěte více o faster-whisper a podívejte se na dostupné modely.",
+	"Click here to select": "Klikněte sem pro výběr",
+	"Click here to select a csv file.": "Klikněte zde pro výběr souboru typu csv.",
+	"Click here to select a py file.": "Klikněte sem pro výběr {{py}} souboru.",
+	"Click here to upload a workflow.json file.": "Klikněte sem pro nahrání souboru workflow.json.",
+	"click here.": "klikněte sem.",
+	"Click on the user role button to change a user's role.": "Klikněte na tlačítko role uživatele, abyste změnili roli uživatele.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Přístup k zápisu do schránky byl zamítnut. Prosím, zkontrolujte nastavení svého prohlížeče a udělte potřebný přístup.",
+	"Clone": "Klonovat",
+	"Close": "Zavřít",
+	"Code execution": "Provádění kódu",
+	"Code formatted successfully": "Kód byl úspěšně naformátován.",
+	"Collection": "",
+	"Color": "Barva",
+	"ComfyUI": "ComfyUI.",
+	"ComfyUI Base URL": "Základní URL ComfyUI",
+	"ComfyUI Base URL is required.": "Je vyžadována základní URL pro ComfyUI.",
+	"ComfyUI Workflow": "Pracovní postup ComfyUI",
+	"ComfyUI Workflow Nodes": "Pracovní uzly ComfyUI",
+	"Command": "Příkaz",
+	"Completions": "Doplnění",
+	"Concurrent Requests": "Současné požadavky",
+	"Configure": "Konfigurovat",
+	"Configure Models": "Konfigurovat modely",
+	"Confirm": "Potvrdit",
+	"Confirm Password": "Potvrzení hesla",
+	"Confirm your action": "Potvrďte svoji akci",
+	"Connections": "Připojení",
+	"Contact Admin for WebUI Access": "Kontaktujte administrátora pro přístup k webovému rozhraní.",
+	"Content": "Obsah",
+	"Content Extraction": "Extrahování obsahu",
+	"Context Length": "Délka kontextu",
+	"Continue Response": "Pokračovat v odpovědi",
+	"Continue with {{provider}}": "Pokračovat s {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Řízení, jak se text zprávy rozděluje pro požadavky TTS. 'Punctuation' rozděluje text na věty, 'paragraphs' rozděluje text na odstavce a 'none' udržuje zprávu jako jeden celý řetězec.",
+	"Controls": "Ovládací prvky",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Zkopírováno",
+	"Copied shared chat URL to clipboard!": "URL sdíleného chatu zkopírován do schránky!",
+	"Copied to clipboard": "Zkopírováno do schránky",
+	"Copy": "Kopírovat",
+	"Copy last code block": "Zkopírujte poslední blok kódu",
+	"Copy last response": "Zkopírujte poslední odpověď",
+	"Copy Link": "Kopírovat odkaz",
+	"Copy to clipboard": "Kopírovat do schránky",
+	"Copying to clipboard was successful!": "Kopírování do schránky bylo úspěšné!",
+	"Create": "Vytvořit",
+	"Create a knowledge base": "Vytvořit knowledge base",
+	"Create a model": "Vytvořte model",
+	"Create Account": "Vytvořit účet",
+	"Create Admin Account": "Vytvořit admin účet",
+	"Create Group": "Vytvořit skupinu",
+	"Create Knowledge": "Vytvořit knowledge",
+	"Create new key": "Vytvořit nový klíč",
+	"Create new secret key": "Vytvořte nový tajný klíč",
+	"Created at": "Vytvořeno dne",
+	"Created At": "Vytvořeno dne",
+	"Created by": "Vytvořeno uživatelem",
+	"CSV Import": "CSV import",
+	"Current Model": "Aktuální model",
+	"Current Password": "Aktuální heslo",
+	"Custom": "Na míru",
+	"Dark": "Tmavý",
+	"Database": "Databáze",
+	"December": "Prosinec",
+	"Default": "Výchozí hodnoty nebo nastavení.",
+	"Default (Open AI)": "Výchozí (Open AI)",
+	"Default (SentenceTransformers)": "Výchozí (SentenceTransformers)",
+	"Default Model": "Výchozí model",
+	"Default model updated": "Výchozí model aktualizován.",
+	"Default Models": "Výchozí modely",
+	"Default permissions": "Výchozí povolení",
+	"Default permissions updated successfully": "Výchozí povolení úspěšne aktualizovány",
+	"Default Prompt Suggestions": "Výchozí návrhy promptů",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Výchozí uživatelská role",
+	"Delete": "Smazat",
+	"Delete a model": "Odstranit model.",
+	"Delete All Chats": "Odstranit všechny konverzace",
+	"Delete All Models": "",
+	"Delete chat": "Smazat chat",
+	"Delete Chat": "Smazat chat",
+	"Delete chat?": "Smazat konverzaci?",
+	"Delete folder?": "Smazat složku?",
+	"Delete function?": "Funkce pro odstranění?",
+	"Delete prompt?": "Smazat prompt?",
+	"delete this link": "smazat tento odkaz",
+	"Delete tool?": "Odstranit nástroj?",
+	"Delete User": "Smazat uživatele",
+	"Deleted {{deleteModelTag}}": "Smazáno {{deleteModelTag}}",
+	"Deleted {{name}}": "Smazáno {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Popis",
+	"Didn't fully follow instructions": "Nenásledovali jste přesně všechny instrukce.",
+	"Disabled": "Zakázáno",
+	"Discover a function": "Objevit funkci",
+	"Discover a model": "Objevte model",
+	"Discover a prompt": "Objevte prompt",
+	"Discover a tool": "Objevte nástroj",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Objevujte, stahujte a zkoumejte vlastní funkce",
+	"Discover, download, and explore custom prompts": "Objevte, stáhněte a prozkoumejte vlastní prompty.",
+	"Discover, download, and explore custom tools": "Objevujte, stahujte a prozkoumávejte vlastní nástroje",
+	"Discover, download, and explore model presets": "Objevte, stáhněte a prozkoumejte přednastavení modelů",
+	"Dismissible": "Odstranitelné",
+	"Display": "",
+	"Display Emoji in Call": "Zobrazení emoji během hovoru",
+	"Display the username instead of You in the Chat": "Zobrazit uživatelské jméno místo \"Vás\" v chatu",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Neinstalujte funkce ze zdrojů, kterým plně nedůvěřujete.",
+	"Do not install tools from sources you do not fully trust.": "Neinstalujte nástroje ze zdrojů, kterým plně nedůvěřujete.",
+	"Document": "Dokument",
+	"Documentation": "Dokumentace",
+	"Documents": "Dokumenty",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "nevytváří žádná externí připojení a vaše data zůstávají bezpečně na vašem lokálním serveru.",
+	"Don't have an account?": "Nemáte účet?",
+	"don't install random functions from sources you don't trust.": "Neinstalujte náhodné funkce ze zdrojů, kterým nedůvěřujete.",
+	"don't install random tools from sources you don't trust.": "Neinstalujte náhodné nástroje ze zdrojů, kterým nedůvěřujete.",
+	"Don't like the style": "Nelíbí se mi ten styl.",
+	"Done": "Hotovo.",
+	"Download": "Stáhnout",
+	"Download canceled": "Stahování zrušeno",
+	"Download Database": "Stáhnout databázi",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "Namalovat",
+	"Drop any files here to add to the conversation": "Sem přetáhněte libovolné soubory, které chcete přidat do konverzace",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "např. '30s','10m'. Platné časové jednotky jsou 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Upravit",
+	"Edit Arena Model": "Upravit Arena Model",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Upravit paměť",
+	"Edit User": "Upravit uživatele",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-mail",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Vkládací model (Embedding Model)",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "Model vkládání nastaven na \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Povolit sdílení komunity",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Povolit hodnocení zpráv",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Povolit nové registrace",
+	"Enable Web Search": "Povolit webové vyhledávání",
+	"Enabled": "Povoleno",
+	"Engine": "Engine",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Ujistěte se, že váš CSV soubor obsahuje 4 sloupce v tomto pořadí: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Zadejte zprávu {{role}} sem",
+	"Enter a detail about yourself for your LLMs to recall": "Zadejte podrobnost o sobě, kterou si vaše LLM mají pamatovat.",
+	"Enter api auth string (e.g. username:password)": "Zadejte autentizační řetězec API (např. uživatelské_jméno:heslo)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Zadejte API klíč pro Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "Zadejte měřítko CFG (např. 7.0)",
+	"Enter Chunk Overlap": "Zadejte překryv části",
+	"Enter Chunk Size": "Zadejte velikost bloku",
+	"Enter description": "Zadejte popis",
+	"Enter Github Raw URL": "Zadejte URL adresu Github Raw",
+	"Enter Google PSE API Key": "Zadejte klíč rozhraní API Google PSE",
+	"Enter Google PSE Engine Id": "Zadejte ID vyhledávacího mechanismu Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Zadejte velikost obrázku (např. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Zadejte kódy jazyků",
+	"Enter Model ID": "Zadejte ID modelu",
+	"Enter model tag (e.g. {{modelTag}})": "Zadejte označení modelu (např. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Zadejte počet kroků (např. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Zadejte vzorkovač (např. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Zadejte plánovač (např. Karras)",
+	"Enter Score": "Zadejte skóre",
+	"Enter SearchApi API Key": "Zadejte API klíč pro SearchApi",
+	"Enter SearchApi Engine": "Zadejte vyhledávací stroj SearchApi",
+	"Enter Searxng Query URL": "Zadejte URL dotazu Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Zadejte Serper API klíč",
+	"Enter Serply API Key": "Zadejte API klíč pro Serply",
+	"Enter Serpstack API Key": "Zadejte klíč API pro Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Zadejte ukončovací sekvenci",
+	"Enter system prompt": "Vložte systémový prompt",
+	"Enter Tavily API Key": "Zadejte API klíč Tavily",
+	"Enter Tika Server URL": "Zadejte URL serveru Tika",
+	"Enter Top K": "Zadejte horní K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Zadejte URL (např. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Zadejte URL (např. http://localhost:11434)",
+	"Enter Your Email": "Zadejte svůj email",
+	"Enter Your Full Name": "Zadejte své plné jméno",
+	"Enter your message": "Zadejte svou zprávu",
+	"Enter Your Password": "Zadejte své heslo",
+	"Enter Your Role": "Zadejte svou roli",
+	"Enter Your Username": "",
+	"Error": "Chyba",
+	"ERROR": "Chyba",
+	"Evaluations": "Hodnocení",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "Vyloučit",
+	"Experimental": "Experimentální",
+	"Explore the cosmos": "",
+	"Export": "Exportovat",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exportovat všechny chaty (všichni uživatelé)",
+	"Export chat (.json)": "Exportovat chat (.json)",
+	"Export Chats": "Exportovat chaty",
+	"Export Config to JSON File": "Exportujte konfiguraci do souboru JSON",
+	"Export Functions": "Exportovat funkce",
+	"Export Models": "Export modelů",
+	"Export Presets": "",
+	"Export Prompts": "Exportovat prompty",
+	"Export to CSV": "",
+	"Export Tools": "Exportní nástroje",
+	"External Models": "Externí modely",
+	"Failed to add file.": "Nepodařilo se přidat soubor.",
+	"Failed to create API Key.": "Nepodařilo se vytvořit API klíč.",
+	"Failed to read clipboard contents": "Nepodařilo se přečíst obsah schránky",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Nepodařilo se aktualizovat nastavení",
+	"Failed to upload file.": "Nepodařilo se nahrát soubor.",
+	"February": "Únor",
+	"Feedback History": "Historie zpětné vazby",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Neváhejte přidat konkrétní detaily.",
+	"File": "Soubor",
+	"File added successfully.": "Soubor byl úspěšně přidán.",
+	"File content updated successfully.": "Obsah souboru byl úspěšně aktualizován.",
+	"File Mode": "Režim souboru",
+	"File not found.": "Soubor nenalezen.",
+	"File removed successfully.": "Soubor byl úspěšně odstraněn.",
+	"File size should not exceed {{maxSize}} MB.": "Velikost souboru by neměla překročit {{maxSize}} MB.",
+	"Files": "Soubory",
+	"Filter is now globally disabled": "Filtr je nyní globálně zakázán",
+	"Filter is now globally enabled": "Filtr je nyní globálně povolen.",
+	"Filters": "Filtry",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Detekováno padělání otisku prstu: Není možné použít iniciály jako avatar. Používá se výchozí profilový obrázek.",
+	"Fluidly stream large external response chunks": "Plynule streamujte velké externí části odpovědí",
+	"Focus chat input": "Zaměřte se na vstup chatu",
+	"Folder deleted successfully": "Složka byla úspěšně smazána",
+	"Folder name cannot be empty": "Název složky nesmí být prázdný",
+	"Folder name cannot be empty.": "Název složky nesmí být prázdný.",
+	"Folder name updated successfully": "Název složky byl úspěšně aktualizován.",
+	"Followed instructions perfectly": "Dodržel pokyny dokonale.",
+	"Forge new paths": "",
+	"Form": "Formulář",
+	"Format your variables using brackets like this:": "Formátujte své proměnné pomocí závorek takto:",
+	"Frequency Penalty": "Penalizace frekvence",
+	"Function": "Funkce",
+	"Function created successfully": "Funkce byla úspěšně vytvořena.",
+	"Function deleted successfully": "Funkce byla úspěšně odstraněna",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Funkce je nyní globálně zakázána.",
+	"Function is now globally enabled": "Funkce je nyní globálně povolena.",
+	"Function Name": "",
+	"Function updated successfully": "Funkce byla úspěšně aktualizována.",
+	"Functions": "Funkce",
+	"Functions allow arbitrary code execution": "Funkce umožňují vykonávat libovolný kód.",
+	"Functions allow arbitrary code execution.": "Funkce umožňují provádění libovolného kódu.",
+	"Functions imported successfully": "Funkce byly úspěšně importovány",
+	"General": "Obecný",
+	"General Settings": "Obecná nastavení",
+	"Generate Image": "Vygenerovat obrázek",
+	"Generating search query": "Generování vyhledávacího dotazu",
+	"Generation Info": "Informace o generaci",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Globální",
+	"Good Response": "Dobrý Odezva",
+	"Google PSE API Key": "Klíč API pro Google PSE (Programmatically Search Engine)",
+	"Google PSE Engine Id": "Google PSE Engine Id (Identifikátor vyhledávacího modulu Google PSE)",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "hh:mm dop./odp.",
+	"Haptic Feedback": "Haptická zpětná vazba",
+	"has no conversations.": "nemá žádné konverzace.",
+	"Hello, {{name}}": "Ahoj, {{name}}",
+	"Help": "Pomoc",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Pomozte nám vytvořit nejlepší komunitní žebříček sdílením historie vaší zpětné vazby!",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Schovej",
+	"Host": "",
+	"How can I help you today?": "Jak vám mohu dnes pomoci?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybridní vyhledávání",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Beru na vědomí, že jsem si přečetl a chápu důsledky svých činů. Jsem si vědom rizik spojených s vykonáváním libovolného kódu a ověřil jsem důvěryhodnost zdroje.",
+	"ID": "ID",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generování obrázků (experimentální)",
+	"Image Generation Engine": "Engine pro generování obrázků",
+	"Image Settings": "Nastavení obrázku",
+	"Images": "Obrázky",
+	"Import Chats": "Importovat konverzace",
+	"Import Config from JSON File": "Importování konfigurace z JSON souboru",
+	"Import Functions": "Načítání funkcí",
+	"Import Models": "Importování modelů",
+	"Import Presets": "",
+	"Import Prompts": "Importovat Prompty",
+	"Import Tools": "Importovat nástroje",
+	"Include": "Zahrnout",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Zahrňte přepínač `--api-auth` při spuštění stable-diffusion-webui.",
+	"Include `--api` flag when running stable-diffusion-webui": "Při spuštění stable-diffusion-webui zahrňte příznak `--api`.",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Info",
+	"Input commands": "Vstupní příkazy",
+	"Install from Github URL": "Instalace z URL adresy Githubu",
+	"Instant Auto-Send After Voice Transcription": "Okamžité automatické odeslání po přepisu hlasu",
+	"Interface": "Rozhraní",
+	"Invalid file format.": "Neplatný formát souboru.",
+	"Invalid Tag": "Neplatný tag",
+	"January": "Leden",
+	"Jina API Key": "",
+	"join our Discord for help.": "připojte se k našemu Discordu pro pomoc.",
+	"JSON": "JSON",
+	"JSON Preview": "Náhled JSON",
+	"July": "Červenec",
+	"June": "červen",
+	"JWT Expiration": "Vypršení platnosti JWT (JSON Web Token)",
+	"JWT Token": "JWT Token (JSON Web Token)",
+	"Keep Alive": "Udržovat spojení",
+	"Key": "",
+	"Keyboard shortcuts": "Klávesové zkratky",
+	"Knowledge": "Znalosti",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Znalost úspěšně vytvořena.",
+	"Knowledge deleted successfully.": "Znalosti byly úspěšně odstraněny.",
+	"Knowledge reset successfully.": "Úspěšné obnovení znalostí.",
+	"Knowledge updated successfully": "Znalosti úspěšně aktualizovány",
+	"Label": "",
+	"Landing Page Mode": "Režim vstupní stránky",
+	"Language": "Jazyk",
+	"Last Active": "Naposledy aktivní",
+	"Last Modified": "Poslední změna",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "Žebříček",
+	"Leave empty for unlimited": "Nechte prázdné pro neomezeně",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "Nechte prázdné pro zahrnutí všech modelů nebo vyberte konkrétní modely.",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Nechte prázdné pro použití výchozího podnětu, nebo zadejte vlastní podnět.",
+	"Light": "Světlo",
+	"Listening...": "Poslouchání...",
+	"LLMs can make mistakes. Verify important information.": "LLM mohou dělat chyby. Ověřte si důležité informace.",
+	"Local": "",
+	"Local Models": "Lokální modely",
+	"Lost": "Ztracený",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Vytvořeno komunitou OpenWebUI",
+	"Make sure to enclose them with": "Ujistěte se, že jsou uzavřeny pomocí",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Ujistěte se, že exportujete soubor workflow.json ve formátu API z ComfyUI.",
+	"Manage": "Spravovat",
+	"Manage Arena Models": "Správa modelů v Arena",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Správa pipelines",
+	"March": "Březen",
+	"Max Tokens (num_predict)": "Maximální počet tokenů (num_predict)",
+	"Max Upload Count": "Maximální počet nahrání",
+	"Max Upload Size": "Maximální velikost nahrávání",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximálně 3 modely mohou být staženy současně. Prosím zkuste to znovu později.",
+	"May": "květen",
+	"Memories accessible by LLMs will be shown here.": "Vzpomínky přístupné LLMs budou zobrazeny zde.",
+	"Memory": "Paměť",
+	"Memory added successfully": "Paměť byla úspěšně přidána.",
+	"Memory cleared successfully": "Paměť byla úspěšně vymazána.",
+	"Memory deleted successfully": "Paměť byla úspěšně smazána",
+	"Memory updated successfully": "Paměť úspěšně aktualizována",
+	"Merge Responses": "Sloučit odpovědi",
+	"Message rating should be enabled to use this feature": "Hodnocení zpráv musí být povoleno, aby bylo možné tuto funkci používat.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Zprávy, které odešlete po vytvoření odkazu, nebudou sdíleny. Uživatelé s URL budou moci zobrazit sdílený chat.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimální skóre",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, RRRR",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, RRRR HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Model",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model „{{modelName}}“ byl úspěšně stažen.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je již zařazen do fronty pro stahování.",
+	"Model {{modelId}} not found": "Model {{modelId}} nebyl nalezen",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} není schopen zpracovávat vizuální data.",
+	"Model {{name}} is now {{status}}": "Model {{name}} je nyní {{status}}.",
+	"Model accepts image inputs": "Model přijímá vstupy ve formě obrázků",
+	"Model created successfully!": "Model byl úspěšně vytvořen!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Detekována cesta v\u00a0souborovém systému. Je vyžadován krátký název modelu pro aktualizaci, nelze pokračovat.",
+	"Model Filtering": "",
+	"Model ID": "ID modelu",
+	"Model IDs": "",
+	"Model Name": "Název modelu",
+	"Model not selected": "Model nebyl vybrán",
+	"Model Params": "Parametry modelu",
+	"Model Permissions": "",
+	"Model updated successfully": "Model byl úspěšně aktualizován",
+	"Modelfile Content": "Obsah souboru modelfile",
+	"Models": "Modely",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "více",
+	"More": "Více",
+	"Name": "Jméno",
+	"Name your knowledge base": "",
+	"New Chat": "Nový chat",
+	"New folder": "Nová složka",
+	"New Password": "Nové heslo",
+	"No content found": "Nebyly nalezeny žádné obsahové informace.",
+	"No content to speak": "Žádný obsah k diskusi.",
+	"No distance available": "Není dostupná žádná vzdálenost",
+	"No feedbacks found": "Žádná zpětná vazba nenalezena",
+	"No file selected": "Nebyl vybrán žádný soubor",
+	"No files found.": "Nebyly nalezeny žádné soubory.",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "Nebyl nalezen žádný obsah HTML, CSS ani JavaScriptu.",
+	"No knowledge found": "Nebyly nalezeny žádné znalosti",
+	"No model IDs": "",
+	"No models found": "Nebyly nalezeny žádné modely",
+	"No models selected": "",
+	"No results found": "Nebyly nalezeny žádné výsledky",
+	"No search query generated": "Nebyl vygenerován žádný vyhledávací dotaz.",
+	"No source available": "Není k dispozici žádný zdroj.",
+	"No users were found.": "",
+	"No valves to update": "Žádné ventily k aktualizaci",
+	"None": "Žádný",
+	"Not factually correct": "Není fakticky správné",
+	"Not helpful": "Nepomocné",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Poznámka: Pokud nastavíte minimální skóre, vyhledávání vrátí pouze dokumenty s hodnocením, které je větší nebo rovno zadanému minimálnímu skóre.",
+	"Notes": "Poznámky",
+	"Notifications": "Oznámení",
+	"November": "Listopad",
+	"num_gpu (Ollama)": "Počet GPU (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Říjen",
+	"Off": "Vypnuto",
+	"Okay, Let's Go!": "Dobře, pojďme na to!",
+	"OLED Dark": "OLED Dark",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API rozhraní Ollama je zakázáno.",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Verze Ollama",
+	"On": "Na",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Příkazový řetězec smí obsahovat pouze alfanumerické znaky a pomlčky.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Pouze kolekce mohou být upravovány, pro úpravu/přidání dokumentů vytvořte novou znalostní bázi.",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Jejda! Vypadá to, že URL adresa je neplatná. Prosím, zkontrolujte ji a zkuste to znovu.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Jejda! Některé soubory se stále nahrávají. Prosím, počkejte, až bude nahrávání dokončeno.",
+	"Oops! There was an error in the previous response.": "Jejda! V předchozí odpovědi došlo k chybě.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Jejda! Používáte nepodporovanou metodu (pouze frontend). Prosím, spusťte WebUI ze serverové části (backendu).",
+	"Open file": "Otevřít soubor",
+	"Open in full screen": "Otevřít na celou obrazovku",
+	"Open new chat": "Otevřít nový chat",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI interně používá faster-whisper.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Verze Open WebUI (v{{OPEN_WEBUI_VERSION}}) je nižší než požadovaná verze (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI je výzkumná organizace zaměřená na umělou inteligenci, která je známá vývojem pokročilých jazykových modelů, jako je například GPT. Tyto modely se využívají v různých aplikacích, včetně konverzačních agentů a jazykových nástrojů.",
+	"OpenAI API": "OpenAI API je rozhraní aplikačního programování, které umožňuje vývojářům integrovat pokročilé jazykové modely do svých aplikací.",
+	"OpenAI API Config": "Konfigurace API OpenAI",
+	"OpenAI API Key is required.": "Je vyžadován klíč OpenAI API.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Je vyžadován odkaz/adresa URL nebo klíč OpenAI.",
+	"or": "nebo",
+	"Organize your users": "",
+	"Other": "Jiné",
+	"OUTPUT": "VÝSTUP",
+	"Output format": "Formát výstupu",
+	"Overview": "Přehled",
+	"page": "stránka",
+	"Password": "Heslo",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF dokument (.pdf)",
+	"PDF Extract Images (OCR)": "Extrahování obrázků z PDF (OCR)",
+	"pending": "čeká na vyřízení",
+	"Permission denied when accessing media devices": "Odmítnutí povolení při přístupu k mediálním zařízením",
+	"Permission denied when accessing microphone": "Přístup k mikrofonu byl odepřen",
+	"Permission denied when accessing microphone: {{error}}": "Oprávnění zamítnuto při přístupu k mikrofonu: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalizace",
+	"Pin": "Kolík",
+	"Pinned": "Připnuto",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline byla úspěšně odstraněna",
+	"Pipeline downloaded successfully": "Kanál byl úspěšně stažen",
+	"Pipelines": "",
+	"Pipelines Not Detected": "Přenosové kanály nebyly detekovány",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "Čistý text (.txt)",
+	"Playground": "",
+	"Please carefully review the following warnings:": "Prosím, pečlivě si přečtěte následující upozornění:",
+	"Please enter a prompt": "Prosím, zadejte zadání.",
+	"Please fill in all fields.": "Prosím, vyplňte všechna pole.",
+	"Please select a model first.": "",
+	"Please select a reason": "Prosím vyberte důvod",
+	"Port": "",
+	"Positive attitude": "Pozitivní přístup",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Předchozích 30 dnů",
+	"Previous 7 days": "Předchozích 7 dní",
+	"Profile Image": "Profilový obrázek",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (např. Řekni mi zábavný fakt o Římské říši)",
+	"Prompt Content": "Obsah promptu",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Návrhy výzev",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompty",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Stáhněte \"{{searchValue}}\" z Ollama.com",
+	"Pull a model from Ollama.com": "Stáhněte model z Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parametry dotazu",
+	"RAG Template": "Šablona RAG",
+	"Rating": "Hodnocení",
+	"Re-rank models by topic similarity": "Znovu seřaďte modely podle podobnosti témat.",
+	"Read Aloud": "Číst nahlas",
+	"Record voice": "Nahrát hlas",
+	"Redirecting you to OpenWebUI Community": "Přesměrování na komunitu OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Odkazujte na sebe jako na \"uživatele\" (např. \"Uživatel se učí španělsky\").",
+	"References from": "Reference z",
+	"Refused when it shouldn't have": "Odmítnuto, když nemělo být.",
+	"Regenerate": "Regenerovat",
+	"Release Notes": "Záznamy o vydání",
+	"Relevance": "Relevance",
+	"Remove": "Odebrat",
+	"Remove Model": "Odebrat model",
+	"Rename": "Přejmenovat",
+	"Reorder Models": "",
+	"Repeat Last N": "Opakovat posledních N",
+	"Request Mode": "Režim žádosti",
+	"Reranking Model": "Model pro přehodnocení pořadí",
+	"Reranking model disabled": "Přeřazovací model je deaktivován",
+	"Reranking model set to \"{{reranking_model}}\"": "Model pro přeřazení nastaven na \"{{reranking_model}}\"",
+	"Reset": "režim Reset",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Resetovat adresář nahrávání",
+	"Reset Vector Storage/Knowledge": "Resetování úložiště vektorů/znalostí",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Oznámení o odpovědích nelze aktivovat, protože oprávnění webu byla zamítnuta. Navštivte nastavení svého prohlížeče a udělte potřebný přístup.",
+	"Response splitting": "Rozdělení odpovědi",
+	"Result": "Výsledek",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Vstup pro chat ve formátu Rich Text",
+	"RK": "RK",
+	"Role": "Funkci",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Spustit",
+	"Running": "Spuštění",
+	"Save": "Uložit",
+	"Save & Create": "Uložit a Vytvořit",
+	"Save & Update": "Uložit a aktualizovat",
+	"Save As Copy": "Uložit jako kopii",
+	"Save Tag": "Uložit štítek",
+	"Saved": "Uloženo",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Ukládání záznamů chatu přímo do úložiště vašeho prohlížeče již není podporováno. Věnujte prosím chvíli stažení a smazání svých záznamů chatu kliknutím na tlačítko níže. Nemějte obavy, můžete snadno znovu importovat své záznamy chatu na backend prostřednictvím",
+	"Scroll to bottom when switching between branches": "Přejít na konec při přepínání mezi větvemi.",
+	"Search": "Vyhledávání",
+	"Search a model": "Vyhledat model",
+	"Search Base": "",
+	"Search Chats": "Vyhledávání v chatu",
+	"Search Collection": "Hledat kolekci",
+	"Search Filters": "",
+	"search for tags": "hledání značek",
+	"Search Functions": "Vyhledávací funkce",
+	"Search Knowledge": "Vyhledávání znalostí",
+	"Search Models": "Vyhledávací modely",
+	"Search options": "",
+	"Search Prompts": "Vyhledávací dotazy",
+	"Search Result Count": "Počet výsledků hledání",
+	"Search the web": "",
+	"Search Tools": "Nástroje pro vyhledávání",
+	"SearchApi API Key": "Klíč API pro SearchApi",
+	"SearchApi Engine": "Vyhledávací engine API",
+	"Searched {{count}} sites_one": "Prohledáno {{count}} stránek_one",
+	"Searched {{count}} sites_few": "",
+	"Searched {{count}} sites_many": "",
+	"Searched {{count}} sites_other": "Prohledáno {{count}} stránek_jiných",
+	"Searching \"{{searchQuery}}\"": "Hledání \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Vyhledávání znalostí pro \"{{searchQuery}}\"",
+	"Searxng Query URL": "Adresa URL dotazu Searxng",
+	"See readme.md for instructions": "Podívejte se do {{readme.md}} pro pokyny.",
+	"See what's new": "Podívejte se, co je nového",
+	"Seed": "Semínko",
+	"Select a base model": "Vyberte základní model",
+	"Select a engine": "Vyberte engine",
+	"Select a function": "Vyberte funkci",
+	"Select a group": "",
+	"Select a model": "Vyberte model",
+	"Select a pipeline": "Vyberte pipeline",
+	"Select a pipeline url": "Vyberte URL adresu kanálu",
+	"Select a tool": "Vyberte nástroj",
+	"Select Engine": "Vyberte engine",
+	"Select Knowledge": "Vybrat znalosti",
+	"Select model": "Vyberte model",
+	"Select only one model to call": "Vyberte pouze jeden model, který chcete použít",
+	"Selected model(s) do not support image inputs": "Vybraný(é) model(y) nepodporují vstupy v podobě obrázků.",
+	"Semantic distance to query": "Semantická vzdálenost k dotazu",
+	"Send": "Odeslat",
+	"Send a Message": "Odeslat zprávu",
+	"Send message": "Odeslat zprávu",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Odešle `stream_options: { include_usage: true }` v žádosti. Podporovaní poskytovatelé vrátí informace o využití tokenů v odpovědi, když je tato možnost nastavena.",
+	"September": "Září",
+	"Serper API Key": "Klíč API pro Serper",
+	"Serply API Key": "Serply API klíč",
+	"Serpstack API Key": "Klíč API pro Serpstack",
+	"Server connection verified": "Připojení k serveru ověřeno",
+	"Set as default": "Nastavit jako výchozí",
+	"Set CFG Scale": "Nastavte hodnotu CFG Scale",
+	"Set Default Model": "Nastavení výchozího modelu",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Nastavte model vkládání (např. {{model}})",
+	"Set Image Size": "Nastavení velikosti obrázku",
+	"Set reranking model (e.g. {{model}})": "Nastavte model pro přehodnocení (např. {{model}})",
+	"Set Sampler": "Nastavení vzorkovače",
+	"Set Scheduler": "Nastavení plánovače",
+	"Set Steps": "Nastavení kroků",
+	"Set Task Model": "Nastavit model úkolu",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Nastavit hlas",
+	"Set whisper model": "Nastavit model whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Nastavení",
+	"Settings saved successfully!": "Nastavení byla úspěšně uložena!",
+	"Share": "Sdílet",
+	"Share Chat": "Sdílet chat",
+	"Share to OpenWebUI Community": "Sdílet s komunitou OpenWebUI",
+	"Show": "Zobrazit",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Zobrazit podrobnosti administrátora v překryvném okně s čekajícím účtem",
+	"Show shortcuts": "Zobrazit klávesové zkratky",
+	"Show your support!": "Vyjadřete svou podporu!",
+	"Showcased creativity": "Předvedená kreativita",
+	"Sign in": "Přihlásit se",
+	"Sign in to {{WEBUI_NAME}}": "Přihlásit se do {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Odhlásit se",
+	"Sign up": "Zaregistrovat se",
+	"Sign up to {{WEBUI_NAME}}": "Zaregistrujte se na {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Přihlašování do {{WEBUI_NAME}}",
+	"Source": "Zdroj",
+	"Speech Playback Speed": "Rychlost přehrávání řeči",
+	"Speech recognition error: {{error}}": "Chyba rozpoznávání řeči: {{error}}",
+	"Speech-to-Text Engine": "Motor převodu řeči na text",
+	"Stop": "Zastavit",
+	"Stop Sequence": "Sekvence Zastavení",
+	"Stream Chat Response": "Odezva chatu Stream",
+	"STT Model": "Model rozpoznávání řeči na text (STT)",
+	"STT Settings": "Nastavení STT (Rozpoznávání řeči)",
+	"Subtitle (e.g. about the Roman Empire)": "Titulky (např. o Římské říši)",
+	"Success": "Úspěch",
+	"Successfully updated.": "Úspěšně aktualizováno.",
+	"Suggested": "Navrhované",
+	"Support": "Podpora",
+	"Support this plugin:": "Podpořte tento plugin:",
+	"Sync directory": "Synchronizovat adresář",
+	"System": "System",
+	"System Instructions": "",
+	"System Prompt": "Systémový prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Prompt pro generování značek",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Klepněte pro přerušení",
+	"Tavily API Key": "Klíč API pro Tavily",
+	"Tell us more:": "Řekněte nám více.",
+	"Temperature": "",
+	"Template": "Šablona",
+	"Temporary Chat": "Dočasný chat",
+	"Text Splitter": "Rozdělovač textu",
+	"Text-to-Speech Engine": "Stroj pro převod textu na řeč",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Děkujeme za vaši zpětnou vazbu!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Vývojáři stojící za tímto pluginem jsou zapálení dobrovolníci z komunity. Pokud považujete tento plugin za užitečný, zvažte příspěvek k jeho vývoji.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Hodnotící žebříček je založen na systému hodnocení Elo a je aktualizován v reálném čase.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Žebříček je v současné době v beta verzi a můžeme upravit výpočty hodnocení, jak budeme zdokonalovat algoritmus.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Maximální velikost souboru v MB. Pokud velikost souboru překročí tento limit, soubor nebude nahrán.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Maximální počet souborů, které mohou být použity najednou v chatu. Pokud počet souborů překročí tento limit, soubory nebudou nahrány.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Skóre by mělo být hodnotou mezi 0,0 (0%) a 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Téma",
+	"Thinking...": "Přemýšlím...",
+	"This action cannot be undone. Do you wish to continue?": "Tuto akci nelze vrátit zpět. Přejete si pokračovat?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "To zajišťuje, že vaše cenné konverzace jsou bezpečně uloženy ve vaší backendové databázi. Děkujeme!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Jedná se o experimentální funkci, nemusí fungovat podle očekávání a může být kdykoliv změněna.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Tato volba odstraní všechny existující soubory ve sbírce a nahradí je nově nahranými soubory.",
+	"This response was generated by \"{{model}}\"": "Tato odpověď byla vygenerována pomocí \"{{model}}\"",
+	"This will delete": "Tohle odstraní",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tímto dojde k odstranění <strong>{{NAME}}</strong> a <strong>všech jeho obsahů</strong>.",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Toto obnoví znalostní databázi a synchronizuje všechny soubory. Přejete si pokračovat?",
+	"Thorough explanation": "Obsáhlé vysvětlení",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Je vyžadována URL adresa serveru Tika.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Aktualizujte postupně více proměnných slotů stisknutím klávesy Tab v chatu po každé náhradě.",
+	"Title": "Název",
+	"Title (e.g. Tell me a fun fact)": "Název (např. Řekni mi zajímavost)",
+	"Title Auto-Generation": "Automatické generování názvu",
+	"Title cannot be an empty string.": "Název nemůže být prázdným řetězcem.",
+	"Title Generation Prompt": "Generování názvu promptu",
+	"TLS": "",
+	"To access the available model names for downloading,": "Pro získání dostupných názvů modelů ke stažení,",
+	"To access the GGUF models available for downloading,": "Pro přístup k modelům GGUF dostupným pro stažení,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pro přístup k WebUI se prosím obraťte na administrátora. Administrátoři mohou spravovat stavy uživatelů z Admin Panelu.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Chcete-li zde připojit znalostní databázi, nejprve ji přidejte do workspace \"Knowledge\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Aby byla chráněna vaše soukromí, z vaší zpětné vazby jsou sdílena pouze hodnocení, ID modelů, značky a metadata – vaše chatové záznamy zůstávají soukromé a nejsou zahrnuty.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat akce, nejprve je přidejte do pracovní plochy \"Functions\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Chcete-li zde vybrat filtry, nejprve je přidejte do workspace „Functions“.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Chcete-li zde vybrat nástroje, přidejte je nejprve do workspace \"Tools\".",
+	"Toast notifications for new updates": "Oznámení ve formě toastů pro nové aktualizace",
+	"Today": "Dnes",
+	"Toggle settings": "Přepnout nastavení",
+	"Toggle sidebar": "Přepnout postranní panel",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokeny, které si ponechat při obnovení kontextu (num_keep)",
+	"Too verbose": "Příliš upovídané",
+	"Tool created successfully": "Nástroj byl úspěšně vytvořen.",
+	"Tool deleted successfully": "Nástroj byl úspěšně smazán.",
+	"Tool Description": "",
+	"Tool ID": "ID nástroje",
+	"Tool imported successfully": "Nástroj byl úspěšně importován",
+	"Tool Name": "",
+	"Tool updated successfully": "Nástroj byl úspěšně aktualizován.",
+	"Tools": "Nástroje",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Nástroje jsou systémem pro volání funkcí s vykonáváním libovolného kódu.",
+	"Tools have a function calling system that allows arbitrary code execution": "Nástroje mají systém volání funkcí, který umožňuje libovolné spouštění kódu.",
+	"Tools have a function calling system that allows arbitrary code execution.": "Nástroje mají systém volání funkcí, který umožňuje spuštění libovolného kódu.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Máte potíže s přístupem k Ollama?",
+	"TTS Model": "Model převodu textu na řeč (TTS)",
+	"TTS Settings": "Nastavení TTS (Text-to-Speech)",
+	"TTS Voice": "TTS hlas",
+	"Type": "Napište",
+	"Type Hugging Face Resolve (Download) URL": "Zadejte URL pro úspěšné stažení z Hugging Face.",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Jejda! Došlo k problému s připojením k poskytovateli {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Odarchivovat všechny",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Odepnout",
+	"Unravel secrets": "",
+	"Untagged": "Nebyla označena",
+	"Update": "Aktualizovat",
+	"Update and Copy Link": "Aktualizovat a zkopírovat odkaz",
+	"Update for the latest features and improvements.": "Aktualizace pro nejnovější funkce a vylepšení.",
+	"Update password": "Aktualizovat heslo",
+	"Updated": "Aktualizováno",
+	"Updated at": "Aktualizováno dne",
+	"Updated At": "Aktualizováno dne",
+	"Upload": "Nahrát",
+	"Upload a GGUF model": "Nahrát model ve formátu GGUF",
+	"Upload directory": "Nahrát adresář",
+	"Upload files": "Nahrát soubory",
+	"Upload Files": "Nahrát soubory",
+	"Upload Pipeline": "Nahrát pipeline",
+	"Upload Progress": "Průběh nahrávání",
+	"URL": "",
+	"URL Mode": "Režim URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Použijte '#' ve vstupu příkazu k načtení a zahrnutí vašich znalostí.",
+	"Use Gravatar": "Použití Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Použijte iniciály",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "uživatel",
+	"User": "Uživatel",
+	"User location successfully retrieved.": "Umístění uživatele bylo úspěšně získáno.",
+	"Username": "Uživatelské jméno",
+	"Users": "Uživatelé",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Použití výchozího modelu arény se všemi modely. Kliknutím na tlačítko plus přidejte vlastní modely.",
+	"Utilize": "Využít",
+	"Valid time units:": "Platné časové jednotky:",
+	"Valves": "Ventily",
+	"Valves updated": "Ventily aktualizovány",
+	"Valves updated successfully": "Ventily byly úspěšně aktualizovány.",
+	"variable": "proměnná",
+	"variable to have them replaced with clipboard content.": "proměnnou, aby byl jejich obsah nahrazen obsahem schránky.",
+	"Version": "Verze",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Verze {{selectedVersion}} z {{totalVersions}}",
+	"Visibility": "Viditelnost",
+	"Voice": "Hlas",
+	"Voice Input": "Hlasový vstup",
+	"Warning": "Varování",
+	"Warning:": "Upozornění:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Varování: Pokud aktualizujete nebo změníte svůj model vkládání, budete muset všechny dokumenty znovu importovat.",
+	"Web": "Web",
+	"Web API": "Webové API",
+	"Web Loader Settings": "Nastavení Web Loaderu",
+	"Web Search": "Vyhledávání na webu",
+	"Web Search Engine": "Webový vyhledávač",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "Nastavení WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Co je nového v",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "kdekoliv jste",
+	"Whisper (Local)": "Whisper (Lokálně)",
+	"Why?": "Proč?",
+	"Widescreen Mode": "Režim širokoúhlého zobrazení",
+	"Won": "Vyhrál",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Navrhněte dotaz (např. Kdo jsi?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Napište shrnutí na 50 slov, které shrnuje [téma nebo klíčové slovo].",
+	"Write something...": "Napište něco...",
+	"Write your model template content here": "",
+	"Yesterday": "Včera",
+	"You": "Vy",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Můžete komunikovat pouze s maximálně {{maxCount}} soubor(y) najednou.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Můžete personalizovat své interakce s LLM pomocí přidávání vzpomínek prostřednictvím tlačítka 'Spravovat' níže, což je učiní pro vás užitečnějšími a lépe přizpůsobenými.",
+	"You cannot upload an empty file.": "Nemůžete nahrát prázdný soubor.",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Nemáte žádné archivované konverzace.",
+	"You have shared this chat": "Sdíleli jste tento chat.",
+	"You're a helpful assistant.": "Jste užitečný asistent.",
+	"You're now logged in.": "Nyní jste přihlášen(a).",
+	"Your account status is currently pending activation.": "Stav vašeho účtu je nyní čekající na aktivaci.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Váš celý příspěvek půjde přímo vývojáři pluginu; Open WebUI si nebere žádné procento. Vybraná platforma pro financování však může mít vlastní poplatky.",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "Nastavení YouTube loaderu"
+}
diff --git a/src/lib/i18n/locales/da-DK/translation.json b/src/lib/i18n/locales/da-DK/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..5775250f4b43afc406e34d25dda6d8e3b8700d23
--- /dev/null
+++ b/src/lib/i18n/locales/da-DK/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' for ingen udløb",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(f.eks. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(f.eks. `sh webui.sh --api`)",
+	"(latest)": "(seneste)",
+	"{{ models }}": "{{ modeller }}",
+	"{{user}}'s Chats": "{{user}}s chats",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend kræves",
+	"*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) er påkrævet for at kunne generere billeder",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "En ny version (v{{LATEST_VERSION}}) er nu tilgængelig.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "En 'task model' bliver brugt til at opgaver såsom at generere overskrifter til chats eller internetsøgninger",
+	"a user": "en bruger",
+	"About": "Information",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Profil",
+	"Account Activation Pending": "Aktivering af profil afventer",
+	"Accurate information": "Profilinformation",
+	"Actions": "Handlinger",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktive brugere",
+	"Add": "Tilføj",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "En kort beskrivelse af hvad denne model gør",
+	"Add a tag": "Tilføj et tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "Tilføj indhold",
+	"Add content here": "Tilføj indhold her",
+	"Add custom prompt": "Tilføj en special-prompt",
+	"Add Files": "Tilføj filer",
+	"Add Group": "",
+	"Add Memory": "Tilføj hukommelse",
+	"Add Model": "Tilføj model",
+	"Add Tag": "Tilføj tag",
+	"Add Tags": "Tilføj tags",
+	"Add text content": "Tilføj tekst",
+	"Add User": "Tilføj bruger",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ændringer af disse indstillinger har konsekvenser for alle brugere.",
+	"admin": "administrator",
+	"Admin": "Administrator",
+	"Admin Panel": "Administrationspanel",
+	"Admin Settings": "Administrationsindstillinger",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratorer har adgang til alle værktøjer altid; brugere skal tilføjes værktøjer pr. model i hvert workspace.",
+	"Advanced Parameters": "Advancerede indstillinger",
+	"Advanced Params": "Advancerede indstillinger",
+	"All chats": "",
+	"All Documents": "Alle dokumenter",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Tillad sletning af chats",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Tillad ikke-lokale stemmer",
+	"Allow Temporary Chat": "Tillad midlertidig chat",
+	"Allow User Location": "Tillad bruger-lokation",
+	"Allow Voice Interruption in Call": "Tillad afbrydelser i stemme i opkald",
+	"Already have an account?": "Har du allerede en profil?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "en assistent",
+	"and": "og",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "og lav et nyt link til deling",
+	"API Base URL": "API Base URL",
+	"API Key": "API nøgle",
+	"API Key created.": "API nøgle lavet",
+	"API keys": "API nøgler",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "april",
+	"Archive": "Arkiv",
+	"Archive All Chats": "Arkiver alle chats",
+	"Archived Chats": "Arkiverede chats",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Er du sikker?",
+	"Arena Models": "",
+	"Artifacts": "Artifakter",
+	"Ask a question": "Stil et spørgsmål",
+	"Assistant": "",
+	"Attach file": "Vedhæft fil",
+	"Attention to detail": "Detajleorientering",
+	"Attribute for Username": "",
+	"Audio": "Lyd",
+	"August": "august",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Automatisk kopiering af svar til udklipsholder",
+	"Auto-playback response": "Automatisk afspil svar",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL er påkrævet.",
+	"Available list": "Tilgængelige lister",
+	"available!": "tilgængelig!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI Speech",
+	"Azure Region": "Azure Region",
+	"Back": "Tilbage",
+	"Bad Response": "Problem i response",
+	"Banners": "Bannere",
+	"Base Model (From)": "Base Model (Fra)",
+	"Batch Size (num_batch)": "Batch størrelse (num_batch)",
+	"before": "før",
+	"Being lazy": "At være doven",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API nøgle",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Forbigå SSL verifikation på websider",
+	"Call": "Opkald",
+	"Call feature is not supported when using Web STT engine": "Opkaldsfunktion er ikke understøttet for Web STT engine",
+	"Camera": "Kamera",
+	"Cancel": "Afbryd",
+	"Capabilities": "Funktioner",
+	"Certificate Path": "",
+	"Change Password": "Skift password",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "Chat baggrundsbillede",
+	"Chat Bubble UI": "Chat Bubble UI",
+	"Chat Controls": "Chat indstillinger",
+	"Chat direction": "Chat retning",
+	"Chat Overview": "Chat overblik",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chats",
+	"Check Again": "Tjek igen",
+	"Check for updates": "Søg efter opdateringer",
+	"Checking for updates...": "Søger efter opdateringer",
+	"Choose a model before saving...": "Vælg en model før du gemmer",
+	"Chunk Overlap": "Chunk overlap",
+	"Chunk Params": "Chunk parametre",
+	"Chunk Size": "Chunk størrelse",
+	"Ciphers": "",
+	"Citation": "Citat",
+	"Clear memory": "Slet hukommelse",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klik her for hjælp",
+	"Click here to": "Klik her for at",
+	"Click here to download user import template file.": "Klik her for at downloade bruger import template fil.",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Klik her for at vælge",
+	"Click here to select a csv file.": "Klik her for at vælge en csv fil",
+	"Click here to select a py file.": "Klik her for at vælge en py fil",
+	"Click here to upload a workflow.json file.": "Klik her for at uploade en workflow.json fil",
+	"click here.": "klik her.",
+	"Click on the user role button to change a user's role.": "Klik på bruger ikonet for at ændre brugerens rolle.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skriveadgang til udklipsholderen ikke tilladt. Tjek venligst indstillingerne i din browser for at give adgang.",
+	"Clone": "Klon",
+	"Close": "Luk",
+	"Code execution": "",
+	"Code formatted successfully": "Kode formateret korrekt",
+	"Collection": "Samling",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL er påkrævet.",
+	"ComfyUI Workflow": "ComfyUI Workflow",
+	"ComfyUI Workflow Nodes": "ComfyUI Workflow Nodes",
+	"Command": "Kommando",
+	"Completions": "",
+	"Concurrent Requests": "Concurrent requests",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Bekræft",
+	"Confirm Password": "Bekræft password",
+	"Confirm your action": "Bekræft din handling",
+	"Connections": "Forbindelser",
+	"Contact Admin for WebUI Access": "Kontakt din administrator for adgang til WebUI",
+	"Content": "Indhold",
+	"Content Extraction": "Udtræk af indhold",
+	"Context Length": "Kontekst længde",
+	"Continue Response": "Fortsæt svar",
+	"Continue with {{provider}}": "Fortsæt med {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontroller hvordan beskedens tekst bliver splittet til TTS requests. 'Punctuation' (tegnsætning) splitter i sætninger, 'paragraphs' splitter i paragraffer, og 'none' beholder beskeden som en samlet streng.",
+	"Controls": "Indstillinger",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Kopieret",
+	"Copied shared chat URL to clipboard!": "Link til deling kopieret til udklipsholder",
+	"Copied to clipboard": "Kopieret til udklipsholder",
+	"Copy": "Kopier",
+	"Copy last code block": "Kopier seneste kode",
+	"Copy last response": "Kopier senester svar",
+	"Copy Link": "Kopier link",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Kopieret til udklipsholder!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Lav en model",
+	"Create Account": "Opret profil",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "Opret Viden",
+	"Create new key": "Opret en ny nøgle",
+	"Create new secret key": "Opret en ny hemmelig nøgle",
+	"Created at": "Oprettet",
+	"Created At": "Oprettet",
+	"Created by": "Oprettet af",
+	"CSV Import": "Importer CSV",
+	"Current Model": "Nuværende model",
+	"Current Password": "Nuværende password",
+	"Custom": "Custom",
+	"Dark": "Mørk",
+	"Database": "Database",
+	"December": "december",
+	"Default": "Standard",
+	"Default (Open AI)": "Standard (Open AI)",
+	"Default (SentenceTransformers)": "Standard (SentenceTransformers)",
+	"Default Model": "Standard model",
+	"Default model updated": "Standard model opdateret",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Standardforslag til prompt",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Brugers rolle som standard",
+	"Delete": "Slet",
+	"Delete a model": "Slet en model",
+	"Delete All Chats": "Slet alle chats",
+	"Delete All Models": "",
+	"Delete chat": "Slet chat",
+	"Delete Chat": "Slet chat",
+	"Delete chat?": "Slet chat?",
+	"Delete folder?": "",
+	"Delete function?": "Slet funktion?",
+	"Delete prompt?": "Slet prompt?",
+	"delete this link": "slet dette link",
+	"Delete tool?": "Slet værktøj?",
+	"Delete User": "Slet bruger",
+	"Deleted {{deleteModelTag}}": "Slettede {{deleteModelTag}}",
+	"Deleted {{name}}": "Slettede {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Beskrivelse",
+	"Didn't fully follow instructions": "Fulgte ikke instruktioner",
+	"Disabled": "Inaktiv",
+	"Discover a function": "Find en funktion",
+	"Discover a model": "Find en model",
+	"Discover a prompt": "Find en prompt",
+	"Discover a tool": "Find et værktøj",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Find, download og udforsk unikke funktioner",
+	"Discover, download, and explore custom prompts": "Find, download og udforsk unikke prompts",
+	"Discover, download, and explore custom tools": "Find, download og udforsk unikke værktøjer",
+	"Discover, download, and explore model presets": "Find, download og udforsk modelindstillinger",
+	"Dismissible": "Kan afvises",
+	"Display": "",
+	"Display Emoji in Call": "Vis emoji i chat",
+	"Display the username instead of You in the Chat": "Vis brugernavn i stedet for Dig i chatten",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Lad være med at installere funktioner fra kilder, som du ikke stoler på.",
+	"Do not install tools from sources you do not fully trust.": "Lad være med at installere værktøjer fra kilder, som du ikke stoler på.",
+	"Document": "Dokument",
+	"Documentation": "Dokumentation",
+	"Documents": "Dokumenter",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "laver ikke eksterne kald, og din data bliver sikkert på din egen lokalt hostede server.",
+	"Don't have an account?": "Har du ikke en profil?",
+	"don't install random functions from sources you don't trust.": "lad være med at installere tilfældige funktioner fra kilder, som du ikke stoler på.",
+	"don't install random tools from sources you don't trust.": "lad være med at installere tilfældige værktøjer fra kilder, som du ikke stoler på.",
+	"Don't like the style": "Kan du ikke lide stilen",
+	"Done": "Færdig",
+	"Download": "Download",
+	"Download canceled": "Download afbrudt",
+	"Download Database": "Download database",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Upload filer her for at tilføje til samtalen",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "f.eks. '30s', '10m'. Tilladte værdier er 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Rediger",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Rediger hukommelse",
+	"Edit User": "Rediger bruger",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Embedding Batch størrelse",
+	"Embedding Model": "Embedding Model",
+	"Embedding Model Engine": "Embedding Model engine",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding model sat til \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Aktiver deling til Community",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Aktiver rating af besked",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Aktiver nye signups",
+	"Enable Web Search": "Aktiver websøgning",
+	"Enabled": "Aktiveret",
+	"Engine": "engine",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Sørg for at din CSV-fil indeholder 4 kolonner in denne rækkefølge: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Indtast {{role}} besked her",
+	"Enter a detail about yourself for your LLMs to recall": "Indtast en detalje om dig selv, som dine LLMs kan huske",
+	"Enter api auth string (e.g. username:password)": "Indtast api-godkendelsesstreng (f.eks. brugernavn:adgangskode)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Indtast Brave Search API-nøgle",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "Indtast CFG-skala (f.eks. 7.0)",
+	"Enter Chunk Overlap": "Indtast overlapning af tekststykker",
+	"Enter Chunk Size": "Indtast størrelse af tekststykker",
+	"Enter description": "",
+	"Enter Github Raw URL": "Indtast Github Raw URL",
+	"Enter Google PSE API Key": "Indtast Google PSE API-nøgle",
+	"Enter Google PSE Engine Id": "Indtast Google PSE Engine ID",
+	"Enter Image Size (e.g. 512x512)": "Indtast billedstørrelse (f.eks. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Indtast sprogkoder",
+	"Enter Model ID": "Indtast model-ID",
+	"Enter model tag (e.g. {{modelTag}})": "Indtast modelmærke (f.eks. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Indtast sampler (f.eks. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Indtast scheduler (f.eks. Karras)",
+	"Enter Score": "Indtast score",
+	"Enter SearchApi API Key": "Indtast SearchApi API-nøgle",
+	"Enter SearchApi Engine": "Indtast SearchApi-engine",
+	"Enter Searxng Query URL": "Indtast Searxng-forespørgsels-URL",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Indtast Serper API-nøgle",
+	"Enter Serply API Key": "Indtast Serply API-nøgle",
+	"Enter Serpstack API Key": "Indtast Serpstack API-nøgle",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Indtast stopsekvens",
+	"Enter system prompt": "Indtast systemprompt",
+	"Enter Tavily API Key": "Indtast Tavily API-nøgle",
+	"Enter Tika Server URL": "Indtast Tika Server URL",
+	"Enter Top K": "Indtast Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Indtast URL (f.eks. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Indtast URL (f.eks. http://localhost:11434)",
+	"Enter Your Email": "Indtast din e-mail",
+	"Enter Your Full Name": "Indtast dit fulde navn",
+	"Enter your message": "Indtast din besked",
+	"Enter Your Password": "Indtast din adgangskode",
+	"Enter Your Role": "Indtast din rolle",
+	"Enter Your Username": "",
+	"Error": "Fejl",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Eksperimentel",
+	"Explore the cosmos": "",
+	"Export": "Eksportér",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Eksportér alle chats (alle brugere)",
+	"Export chat (.json)": "Eksportér chat (.json)",
+	"Export Chats": "Eksportér chats",
+	"Export Config to JSON File": "Eksportér konfiguration til JSON-fil",
+	"Export Functions": "Eksportér funktioner",
+	"Export Models": "Eksportér modeller",
+	"Export Presets": "",
+	"Export Prompts": "Eksportér prompts",
+	"Export to CSV": "",
+	"Export Tools": "Eksportér værktøjer",
+	"External Models": "Eksterne modeller",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Kunne ikke oprette API-nøgle.",
+	"Failed to read clipboard contents": "Kunne ikke læse indholdet af udklipsholderen",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Kunne ikke opdatere indstillinger",
+	"Failed to upload file.": "Kunne ikke uploade fil.",
+	"February": "Februar",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Du er velkommen til at tilføje specifikke detaljer",
+	"File": "Fil",
+	"File added successfully.": "Fil tilføjet.",
+	"File content updated successfully.": "Filens indhold er opdateret.",
+	"File Mode": "Filtilstand",
+	"File not found.": "Filen blev ikke fundet.",
+	"File removed successfully.": "Fil fjernet.",
+	"File size should not exceed {{maxSize}} MB.": "Filstørrelsen må ikke overstige {{maxSize}} MB.",
+	"Files": "Filer",
+	"Filter is now globally disabled": "Filter er nu globalt deaktiveret",
+	"Filter is now globally enabled": "Filter er nu globalt aktiveret",
+	"Filters": "Filtre",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeraftryksspoofing registreret: Kan ikke bruge initialer som avatar. Bruger standard profilbillede.",
+	"Fluidly stream large external response chunks": "Stream store eksterne svar chunks flydende",
+	"Focus chat input": "Fokuser på chatinput",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Fulgte instruktionerne perfekt",
+	"Forge new paths": "",
+	"Form": "Formular",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Hyppighedsstraf",
+	"Function": "",
+	"Function created successfully": "Funktion oprettet.",
+	"Function deleted successfully": "Funktion slettet.",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Funktionen er nu globalt deaktiveret",
+	"Function is now globally enabled": "Funktionen er nu globalt aktiveret",
+	"Function Name": "",
+	"Function updated successfully": "Funktion opdateret.",
+	"Functions": "Funktioner",
+	"Functions allow arbitrary code execution": "Funktioner tillader kørsel af vilkårlig kode",
+	"Functions allow arbitrary code execution.": "Funktioner tillader kørsel af vilkårlig kode.",
+	"Functions imported successfully": "Funktioner importeret.",
+	"General": "Generelt",
+	"General Settings": "Generelle indstillinger",
+	"Generate Image": "Generer billede",
+	"Generating search query": "Genererer søgeforespørgsel",
+	"Generation Info": "Genereringsinfo",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Global",
+	"Good Response": "Godt svar",
+	"Google PSE API Key": "Google PSE API-nøgle",
+	"Google PSE Engine Id": "Google PSE Engine-ID",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Haptisk feedback",
+	"has no conversations.": "har ingen samtaler.",
+	"Hello, {{name}}": "Hej {{name}}",
+	"Help": "Hjælp",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Skjul",
+	"Host": "",
+	"How can I help you today?": "Hvordan kan jeg hjælpe dig i dag?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybrid søgning",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Jeg anerkender, at jeg har læst og forstået konsekvenserne af min handling. Jeg er opmærksom på de risici, der er forbundet med at udføre vilkårlig kode, og jeg har verificeret kildens troværdighed.",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Billedgenerering (eksperimentel)",
+	"Image Generation Engine": "Billedgenereringsengine",
+	"Image Settings": "Billedindstillinger",
+	"Images": "Billeder",
+	"Import Chats": "Importer chats",
+	"Import Config from JSON File": "Importer konfiguration fra JSON-fil",
+	"Import Functions": "Importer funktioner",
+	"Import Models": "Importer modeller",
+	"Import Presets": "",
+	"Import Prompts": "Importer prompts",
+	"Import Tools": "Importer værktøjer",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Inkluder `--api-auth` flag, når du kører stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Inkluder `--api` flag, når du kører stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Info",
+	"Input commands": "Inputkommandoer",
+	"Install from Github URL": "Installer fra Github URL",
+	"Instant Auto-Send After Voice Transcription": "Øjeblikkelig automatisk afsendelse efter stemmetransskription",
+	"Interface": "Grænseflade",
+	"Invalid file format.": "",
+	"Invalid Tag": "Ugyldigt tag",
+	"January": "Januar",
+	"Jina API Key": "",
+	"join our Discord for help.": "tilslut dig vores Discord for at få hjælp.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON-forhåndsvisning",
+	"July": "Juli",
+	"June": "Juni",
+	"JWT Expiration": "JWT-udløb",
+	"JWT Token": "JWT-token",
+	"Keep Alive": "Hold i live",
+	"Key": "",
+	"Keyboard shortcuts": "Tastaturgenveje",
+	"Knowledge": "Viden",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Viden oprettet.",
+	"Knowledge deleted successfully.": "Viden slettet.",
+	"Knowledge reset successfully.": "Viden nulstillet.",
+	"Knowledge updated successfully": "Viden opdateret.",
+	"Label": "",
+	"Landing Page Mode": "Landing Page-tilstand",
+	"Language": "Sprog",
+	"Last Active": "Sidst aktiv",
+	"Last Modified": "Sidst ændret",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "Lad stå tomt for ubegrænset",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Lad stå tomt for at bruge standardprompten, eller indtast en brugerdefineret prompt",
+	"Light": "Lys",
+	"Listening...": "Lytter...",
+	"LLMs can make mistakes. Verify important information.": "LLM'er kan lave fejl. Bekræft vigtige oplysninger.",
+	"Local": "",
+	"Local Models": "Lokale modeller",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Lavet af OpenWebUI Community",
+	"Make sure to enclose them with": "Sørg for at omslutte dem med",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Sørg for at eksportere en workflow.json-fil som API-format fra ComfyUI.",
+	"Manage": "Administrer",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Administrer pipelines",
+	"March": "Marts",
+	"Max Tokens (num_predict)": "Maks. tokens (num_predict)",
+	"Max Upload Count": "Maks. uploadantal",
+	"Max Upload Size": "Maks. uploadstørrelse",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Højst 3 modeller kan downloades samtidigt. Prøv igen senere.",
+	"May": "Maj",
+	"Memories accessible by LLMs will be shown here.": "Minder, der er tilgængelige for LLM'er, vises her.",
+	"Memory": "Hukommelse",
+	"Memory added successfully": "Hukommelse tilføjet.",
+	"Memory cleared successfully": "Hukommelse ryddet.",
+	"Memory deleted successfully": "Hukommelse slettet.",
+	"Memory updated successfully": "Hukommelse opdateret.",
+	"Merge Responses": "Flet svar",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Beskeder, du sender efter at have oprettet dit link, deles ikke. Brugere med URL'en vil kunne se den delte chat.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimumscore",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' er blevet downloadet.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' er allerede i kø til download.",
+	"Model {{modelId}} not found": "Model {{modelId}} ikke fundet",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} understøtter ikke billeder",
+	"Model {{name}} is now {{status}}": "Model {{name}} er nu {{status}}",
+	"Model accepts image inputs": "Model accepterer billedinput",
+	"Model created successfully!": "Model oprettet!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model filsystemsti registreret. Modelkortnavn er påkrævet til opdatering, kan ikke fortsætte.",
+	"Model Filtering": "",
+	"Model ID": "Model-ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model ikke valgt",
+	"Model Params": "Modelparametre",
+	"Model Permissions": "",
+	"Model updated successfully": "Model opdateret.",
+	"Modelfile Content": "Modelfilindhold",
+	"Models": "Modeller",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Mere",
+	"Name": "Navn",
+	"Name your knowledge base": "",
+	"New Chat": "Ny chat",
+	"New folder": "",
+	"New Password": "Ny adgangskode",
+	"No content found": "",
+	"No content to speak": "Intet indhold at tale",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Ingen fil valgt",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "Intet HTML-, CSS- eller JavaScript-indhold fundet.",
+	"No knowledge found": "Ingen viden fundet",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Ingen resultater fundet",
+	"No search query generated": "Ingen søgeforespørgsel genereret",
+	"No source available": "Ingen kilde tilgængelig",
+	"No users were found.": "",
+	"No valves to update": "Ingen ventiler at opdatere",
+	"None": "Ingen",
+	"Not factually correct": "Ikke faktuelt korrekt",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Bemærk: Hvis du angiver en minimumscore, returnerer søgningen kun dokumenter med en score, der er større end eller lig med minimumscoren.",
+	"Notes": "",
+	"Notifications": "Notifikationer",
+	"November": "November",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth-ID",
+	"October": "Oktober",
+	"Off": "Fra",
+	"Okay, Let's Go!": "Okay, lad os gå!",
+	"OLED Dark": "OLED Mørk",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API deaktiveret",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama-version",
+	"On": "Til",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Kun alfanumeriske tegn og bindestreger er tilladt i kommandostrengen.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Kun samlinger kan redigeres, opret en ny vidensbase for at redigere/tilføje dokumenter.",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! URL'en ser ud til at være ugyldig. Tjek den igen, og prøv igen.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ups! Du bruger en metode, der ikke understøttes (kun frontend). Kør WebUI fra backend.",
+	"Open file": "Åbn fil",
+	"Open in full screen": "Åbn i fuld skærm",
+	"Open new chat": "Åbn ny chat",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open WebUI-version (v{{OPEN_WEBUI_VERSION}}) er lavere end den krævede version (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API-konfiguration",
+	"OpenAI API Key is required.": "OpenAI API-nøgle er påkrævet.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/nøgle påkrævet.",
+	"or": "eller",
+	"Organize your users": "",
+	"Other": "Andet",
+	"OUTPUT": "",
+	"Output format": "Outputformat",
+	"Overview": "Oversigt",
+	"page": "side",
+	"Password": "Adgangskode",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF-dokument (.pdf)",
+	"PDF Extract Images (OCR)": "Udtræk billeder fra PDF (OCR)",
+	"pending": "afventer",
+	"Permission denied when accessing media devices": "Tilladelse nægtet ved adgang til medieenheder",
+	"Permission denied when accessing microphone": "Tilladelse nægtet ved adgang til mikrofon",
+	"Permission denied when accessing microphone: {{error}}": "Tilladelse nægtet ved adgang til mikrofon: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalisering",
+	"Pin": "Fastgør",
+	"Pinned": "Fastgjort",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline slettet.",
+	"Pipeline downloaded successfully": "Pipeline downloadet.",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Pipelines ikke registreret",
+	"Pipelines Valves": "Pipelines-ventiler",
+	"Plain text (.txt)": "Almindelig tekst (.txt)",
+	"Playground": "Legeplads",
+	"Please carefully review the following warnings:": "Gennemgå omhyggeligt følgende advarsler:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "Udfyld alle felter.",
+	"Please select a model first.": "",
+	"Please select a reason": "Vælg en årsag",
+	"Port": "",
+	"Positive attitude": "Positiv holdning",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Seneste 30 dage",
+	"Previous 7 days": "Seneste 7 dage",
+	"Profile Image": "Profilbillede",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (f.eks. Fortæl mig en sjov kendsgerning om Romerriget)",
+	"Prompt Content": "Promptindhold",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Promptforslag",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompts",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Hent \"{{searchValue}}\" fra Ollama.com",
+	"Pull a model from Ollama.com": "Hent en model fra Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Forespørgselsparametre",
+	"RAG Template": "RAG-skabelon",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Læs højt",
+	"Record voice": "Optag stemme",
+	"Redirecting you to OpenWebUI Community": "Omdirigerer dig til OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referer til dig selv som \"Bruger\" (f.eks. \"Bruger lærer spansk\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Afvist, når den ikke burde have været det",
+	"Regenerate": "Regenerer",
+	"Release Notes": "Udgivelsesnoter",
+	"Relevance": "",
+	"Remove": "Fjern",
+	"Remove Model": "Fjern model",
+	"Rename": "Omdøb",
+	"Reorder Models": "",
+	"Repeat Last N": "Gentag sidste N",
+	"Request Mode": "Forespørgselstilstand",
+	"Reranking Model": "Omarrangeringsmodel",
+	"Reranking model disabled": "Omarrangeringsmodel deaktiveret",
+	"Reranking model set to \"{{reranking_model}}\"": "Omarrangeringsmodel sat til \"{{reranking_model}}\"",
+	"Reset": "Nulstil",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Nulstil uploadmappe",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Svarnotifikationer kan ikke aktiveres, da webstedets tilladelser er blevet nægtet. Besøg dine browserindstillinger for at give den nødvendige adgang.",
+	"Response splitting": "Svaropdeling",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rolle",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Kør",
+	"Running": "Kører",
+	"Save": "Gem",
+	"Save & Create": "Gem og opret",
+	"Save & Update": "Gem og opdater",
+	"Save As Copy": "Gem som kopi",
+	"Save Tag": "Gem tag",
+	"Saved": "Gemt",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Lagring af chatlogs direkte i din browsers lager understøttes ikke længere. Download og slet dine chatlogs ved at klikke på knappen nedenfor. Du kan nemt importere dine chatlogs til backend igennem",
+	"Scroll to bottom when switching between branches": "Rul til bunden, når du skifter mellem grene",
+	"Search": "Søg",
+	"Search a model": "Søg efter en model",
+	"Search Base": "",
+	"Search Chats": "Søg i chats",
+	"Search Collection": "Søg i samling",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Søg i funktioner",
+	"Search Knowledge": "Søg i viden",
+	"Search Models": "Søg i modeller",
+	"Search options": "",
+	"Search Prompts": "Søg i prompts",
+	"Search Result Count": "Antal søgeresultater",
+	"Search the web": "",
+	"Search Tools": "Søg i værktøjer",
+	"SearchApi API Key": "SearchApi API-nøgle",
+	"SearchApi Engine": "SearchApi-engine",
+	"Searched {{count}} sites_one": "Søgte {{count}} websted",
+	"Searched {{count}} sites_other": "Søgte {{count}} websteder",
+	"Searching \"{{searchQuery}}\"": "Søger efter \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Søger i viden efter \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng forespørgsels-URL",
+	"See readme.md for instructions": "Se readme.md for instruktioner",
+	"See what's new": "Se, hvad der er nyt",
+	"Seed": "Seed",
+	"Select a base model": "Vælg en basemodel",
+	"Select a engine": "Vælg en engine",
+	"Select a function": "Vælg en funktion",
+	"Select a group": "",
+	"Select a model": "Vælg en model",
+	"Select a pipeline": "Vælg en pipeline",
+	"Select a pipeline url": "Vælg en pipeline-URL",
+	"Select a tool": "Vælg et værktøj",
+	"Select Engine": "Vælg engine",
+	"Select Knowledge": "Vælg viden",
+	"Select model": "Vælg model",
+	"Select only one model to call": "Vælg kun én model at kalde",
+	"Selected model(s) do not support image inputs": "Valgte model(ler) understøtter ikke billedinput",
+	"Semantic distance to query": "",
+	"Send": "Send",
+	"Send a Message": "Send en besked",
+	"Send message": "Send besked",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Sender `stream_options: { include_usage: true }` i forespørgslen.\nUnderstøttede udbydere vil returnere tokenforbrugsinformation i svaret, når det er indstillet.",
+	"September": "September",
+	"Serper API Key": "Serper API-nøgle",
+	"Serply API Key": "Serply API-nøgle",
+	"Serpstack API Key": "Serpstack API-nøgle",
+	"Server connection verified": "Serverforbindelse bekræftet",
+	"Set as default": "Indstil som standard",
+	"Set CFG Scale": "Indstil CFG-skala",
+	"Set Default Model": "Indstil standardmodel",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Indstil indlejringsmodel (f.eks. {{model}})",
+	"Set Image Size": "Indstil billedstørrelse",
+	"Set reranking model (e.g. {{model}})": "Indstil omarrangeringsmodel (f.eks. {{model}})",
+	"Set Sampler": "Indstil sampler",
+	"Set Scheduler": "Indstil scheduler",
+	"Set Steps": "Indstil trin",
+	"Set Task Model": "Indstil opgavemodel",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Indstil stemme",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Indstillinger",
+	"Settings saved successfully!": "Indstillinger gemt!",
+	"Share": "Del",
+	"Share Chat": "Del chat",
+	"Share to OpenWebUI Community": "Del til OpenWebUI Community",
+	"Show": "Vis",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Vis administratordetaljer i overlay for ventende konto",
+	"Show shortcuts": "Vis genveje",
+	"Show your support!": "Vis din støtte!",
+	"Showcased creativity": "Udstillet kreativitet",
+	"Sign in": "Log ind",
+	"Sign in to {{WEBUI_NAME}}": "Log ind på {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Log ud",
+	"Sign up": "Tilmeld dig",
+	"Sign up to {{WEBUI_NAME}}": "Tilmeld dig {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Logger ind på {{WEBUI_NAME}}",
+	"Source": "Kilde",
+	"Speech Playback Speed": "Talehastighed",
+	"Speech recognition error: {{error}}": "Talegenkendelsesfejl: {{error}}",
+	"Speech-to-Text Engine": "Tale-til-tekst-engine",
+	"Stop": "",
+	"Stop Sequence": "Stopsekvens",
+	"Stream Chat Response": "Stream chatsvar",
+	"STT Model": "STT-model",
+	"STT Settings": "STT-indstillinger",
+	"Subtitle (e.g. about the Roman Empire)": "Undertitel (f.eks. om Romerriget)",
+	"Success": "Succes",
+	"Successfully updated.": "Opdateret.",
+	"Suggested": "Foreslået",
+	"Support": "Support",
+	"Support this plugin:": "Støt dette plugin:",
+	"Sync directory": "Synkroniser mappe",
+	"System": "System",
+	"System Instructions": "",
+	"System Prompt": "Systemprompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Tryk for at afbryde",
+	"Tavily API Key": "Tavily API-nøgle",
+	"Tell us more:": "Fortæl os mere:",
+	"Temperature": "Temperatur",
+	"Template": "Skabelon",
+	"Temporary Chat": "Midlertidig chat",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Tekst-til-tale-engine",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Tak for din feedback!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Udviklerne bag dette plugin er passionerede frivillige fra fællesskabet. Hvis du finder dette plugin nyttigt, kan du overveje at bidrage til dets udvikling.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Den maksimale filstørrelse i MB. Hvis filstørrelsen overstiger denne grænse, uploades filen ikke.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Det maksimale antal filer, der kan bruges på én gang i chatten. Hvis antallet af filer overstiger denne grænse, uploades filerne ikke.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Scoren skal være en værdi mellem 0,0 (0%) og 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Tænker...",
+	"This action cannot be undone. Do you wish to continue?": "Denne handling kan ikke fortrydes. Vil du fortsætte?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dette sikrer, at dine værdifulde samtaler gemmes sikkert i din backend-database. Tak!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dette er en eksperimentel funktion, den fungerer muligvis ikke som forventet og kan ændres når som helst.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Denne indstilling sletter alle eksisterende filer i samlingen og erstatter dem med nyligt uploadede filer.",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Dette vil slette",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Dette vil nulstille vidensbasen og synkronisere alle filer. Vil du fortsætte?",
+	"Thorough explanation": "Grundig forklaring",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika-server-URL påkrævet.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Opdater flere variabelpladser fortløbende ved at trykke på tabulatortasten i chatinput efter hver udskiftning.",
+	"Title": "Titel",
+	"Title (e.g. Tell me a fun fact)": "Titel (f.eks. Fortæl mig en sjov kendsgerning)",
+	"Title Auto-Generation": "Automatisk titelgenerering",
+	"Title cannot be an empty string.": "Titel kan ikke være en tom streng.",
+	"Title Generation Prompt": "Prompt til titelgenerering",
+	"TLS": "",
+	"To access the available model names for downloading,": "For at få adgang til de tilgængelige modelnavne til download,",
+	"To access the GGUF models available for downloading,": "For at få adgang til de GGUF-modeller, der er tilgængelige til download,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "For at få adgang til WebUI skal du kontakte administratoren. Administratorer kan administrere brugerstatus fra administrationspanelet.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "For at vedhæfte vidensbase her skal du først tilføje dem til \"Viden\"-arbejdsområdet.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "For at vælge handlinger her skal du først tilføje dem til \"Funktioner\"-arbejdsområdet.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "For at vælge filtre her skal du først tilføje dem til \"Funktioner\"-arbejdsområdet.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "For at vælge værktøjssæt her skal du først tilføje dem til \"Værktøjer\"-arbejdsområdet.",
+	"Toast notifications for new updates": "",
+	"Today": "I dag",
+	"Toggle settings": "Skift indstillinger",
+	"Toggle sidebar": "Skift sidebjælke",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens, der skal beholdes ved kontekstopdatering (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Værktøj oprettet.",
+	"Tool deleted successfully": "Værktøj slettet.",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Værktøj importeret.",
+	"Tool Name": "",
+	"Tool updated successfully": "Værktøj opdateret.",
+	"Tools": "Værktøjer",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Værktøjer er et funktionkaldssystem med vilkårlig kodeudførelse",
+	"Tools have a function calling system that allows arbitrary code execution": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse",
+	"Tools have a function calling system that allows arbitrary code execution.": "Værktøjer har et funktionkaldssystem, der tillader vilkårlig kodeudførelse.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemer med at få adgang til Ollama?",
+	"TTS Model": "TTS-model",
+	"TTS Settings": "TTS-indstillinger",
+	"TTS Voice": "TTS-stemme",
+	"Type": "Type",
+	"Type Hugging Face Resolve (Download) URL": "Indtast Hugging Face Resolve (Download) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Der opstod et problem med at oprette forbindelse til {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Frigør",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Opdater",
+	"Update and Copy Link": "Opdater og kopier link",
+	"Update for the latest features and improvements.": "Opdater for at få de nyeste funktioner og forbedringer.",
+	"Update password": "Opdater adgangskode",
+	"Updated": "",
+	"Updated at": "Opdateret kl.",
+	"Updated At": "",
+	"Upload": "Upload",
+	"Upload a GGUF model": "Upload en GGUF-model",
+	"Upload directory": "Uploadmappe",
+	"Upload files": "Upload filer",
+	"Upload Files": "Upload filer",
+	"Upload Pipeline": "Upload pipeline",
+	"Upload Progress": "Uploadfremdrift",
+	"URL": "",
+	"URL Mode": "URL-tilstand",
+	"Use '#' in the prompt input to load and include your knowledge.": "Brug '#' i promptinput for at indlæse og inkludere din viden.",
+	"Use Gravatar": "Brug Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Brug initialer",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "bruger",
+	"User": "",
+	"User location successfully retrieved.": "Brugerplacering hentet.",
+	"Username": "",
+	"Users": "Brugere",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Anvend",
+	"Valid time units:": "Gyldige tidsenheder:",
+	"Valves": "Ventiler",
+	"Valves updated": "Ventiler opdateret",
+	"Valves updated successfully": "Ventiler opdateret.",
+	"variable": "variabel",
+	"variable to have them replaced with clipboard content.": "variabel for at få dem erstattet med indholdet af udklipsholderen.",
+	"Version": "Version",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} af {{totalVersions}}",
+	"Visibility": "",
+	"Voice": "Stemme",
+	"Voice Input": "",
+	"Warning": "Advarsel",
+	"Warning:": "Advarsel:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Advarsel: Hvis du opdaterer eller ændrer din indlejringsmodel, skal du importere alle dokumenter igen.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Web Loader-indstillinger",
+	"Web Search": "Websøgning",
+	"Web Search Engine": "Websøgemaskine",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook-URL",
+	"WebUI Settings": "WebUI-indstillinger",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Nyheder i",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (lokal)",
+	"Why?": "",
+	"Widescreen Mode": "Widescreen-tilstand",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Arbejdsområde",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Skriv et promptforslag (f.eks. Hvem er du?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv en opsummering på 50 ord, der opsummerer [emne eller søgeord].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "I går",
+	"You": "Du",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Du kan kun chatte med maksimalt {{maxCount}} fil(er) ad gangen.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan personliggøre dine interaktioner med LLM'er ved at tilføje minder via knappen 'Administrer' nedenfor, hvilket gør dem mere nyttige og skræddersyet til dig.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Du har ingen arkiverede samtaler.",
+	"You have shared this chat": "Du har delt denne chat",
+	"You're a helpful assistant.": "Du er en hjælpsom assistent.",
+	"You're now logged in.": "Du er nu logget ind.",
+	"Your account status is currently pending activation.": "Din kontostatus afventer i øjeblikket aktivering.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Hele dit bidrag går direkte til plugin-udvikleren; Open WebUI tager ikke nogen procentdel. Den valgte finansieringsplatform kan dog have sine egne gebyrer.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Loader-indstillinger"
+}
diff --git a/src/lib/i18n/locales/de-DE/translation.json b/src/lib/i18n/locales/de-DE/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..ac65bce8477f0673d20d01e8d9a1df48f187b8f2
--- /dev/null
+++ b/src/lib/i18n/locales/de-DE/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' oder '-1' für keine Ablaufzeit.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(z. B. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(z. B. `sh webui.sh --api`)",
+	"(latest)": "(neueste)",
+	"{{ models }}": "{{ Modelle }}",
+	"{{user}}'s Chats": "{{user}}s Unterhaltungen",
+	"{{webUIName}} Backend Required": "{{webUIName}}-Backend erforderlich",
+	"*Prompt node ID(s) are required for image generation": "*Prompt-Node-ID(s) sind für die Bildgenerierung erforderlich",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Eine neue Version (v{{LATEST_VERSION}}) ist jetzt verfügbar.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Aufgabenmodelle können Unterhaltungstitel oder Websuchanfragen generieren.",
+	"a user": "ein Benutzer",
+	"About": "Über",
+	"Access": "Zugang",
+	"Access Control": "Zugangskontrolle",
+	"Accessible to all users": "Für alle Benutzer zugänglich",
+	"Account": "Konto",
+	"Account Activation Pending": "Kontoaktivierung ausstehend",
+	"Accurate information": "Präzise Information(en)",
+	"Actions": "Aktionen",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Aktivieren Sie diesen Befehl, indem Sie \"/{{COMMAND}}\" in die Chat-Eingabe eingeben.",
+	"Active Users": "Aktive Benutzer",
+	"Add": "Hinzufügen",
+	"Add a model ID": "Modell-ID hinzufügen",
+	"Add a short description about what this model does": "Fügen Sie eine kurze Beschreibung über dieses Modell hinzu",
+	"Add a tag": "Tag hinzufügen",
+	"Add Arena Model": "Arena-Modell hinzufügen",
+	"Add Connection": "Verbindung hinzufügen",
+	"Add Content": "Inhalt hinzufügen",
+	"Add content here": "Inhalt hier hinzufügen",
+	"Add custom prompt": "Benutzerdefinierten Prompt hinzufügen",
+	"Add Files": "Dateien hinzufügen",
+	"Add Group": "Gruppe hinzufügen",
+	"Add Memory": "Erinnerung hinzufügen",
+	"Add Model": "Modell hinzufügen",
+	"Add Tag": "Tag hinzufügen",
+	"Add Tags": "Tags hinzufügen",
+	"Add text content": "Textinhalt hinzufügen",
+	"Add User": "Benutzer hinzufügen",
+	"Add User Group": "Benutzergruppe hinzufügen",
+	"Adjusting these settings will apply changes universally to all users.": "Das Anpassen dieser Einstellungen wird Änderungen universell auf alle Benutzer anwenden.",
+	"admin": "Administrator",
+	"Admin": "Administrator",
+	"Admin Panel": "Administrationsbereich",
+	"Admin Settings": "Administrationsbereich",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratoren haben jederzeit Zugriff auf alle Werkzeuge. Benutzer können im Arbeitsbereich zugewiesen.",
+	"Advanced Parameters": "Erweiterte Parameter",
+	"Advanced Params": "Erweiterte Parameter",
+	"All chats": "Alle Unterhaltungen",
+	"All Documents": "Alle Dokumente",
+	"All models deleted successfully": "Alle Modelle erfolgreich gelöscht",
+	"Allow Chat Delete": "Löschen von Unterhaltungen erlauben",
+	"Allow Chat Deletion": "Löschen von Unterhaltungen erlauben",
+	"Allow Chat Edit": "Bearbeiten von Unterhaltungen erlauben",
+	"Allow File Upload": "Hochladen von Dateien erlauben",
+	"Allow non-local voices": "Nicht-lokale Stimmen erlauben",
+	"Allow Temporary Chat": "Temporäre Unterhaltungen erlauben",
+	"Allow User Location": "Standort freigeben",
+	"Allow Voice Interruption in Call": "Unterbrechung durch Stimme im Anruf zulassen",
+	"Already have an account?": "Haben Sie bereits einen Account?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternative zu top_p und zielt darauf ab, ein Gleichgewicht zwischen Qualität und Vielfalt zu gewährleisten. Der Parameter p repräsentiert die Mindestwahrscheinlichkeit für ein Token, um berücksichtigt zu werden, relativ zur Wahrscheinlichkeit des wahrscheinlichsten Tokens. Zum Beispiel, bei p=0.05 und das wahrscheinlichste Token hat eine Wahrscheinlichkeit von 0.9, werden Logits mit einem Wert von weniger als 0.045 herausgefiltert. (Standard: 0.0)",
+	"Amazing": "Fantastisch",
+	"an assistant": "ein Assistent",
+	"and": "und",
+	"and {{COUNT}} more": "und {{COUNT}} mehr",
+	"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
+	"API Base URL": "API-Basis-URL",
+	"API Key": "API-Schlüssel",
+	"API Key created.": "API-Schlüssel erstellt.",
+	"API keys": "API-Schlüssel",
+	"Application DN": "Anwendungs-DN",
+	"Application DN Password": "Anwendungs-DN-Passwort",
+	"applies to all users with the \"user\" role": "gilt für alle Benutzer mit der Rolle \"Benutzer\"",
+	"April": "April",
+	"Archive": "Archivieren",
+	"Archive All Chats": "Alle Unterhaltungen archivieren",
+	"Archived Chats": "Archivierte Unterhaltungen",
+	"archived-chat-export": "archivierter-chat-export",
+	"Are you sure you want to unarchive all archived chats?": "Sind Sie sicher, dass Sie alle archivierten Unterhaltungen wiederherstellen möchten?",
+	"Are you sure?": "Sind Sie sicher?",
+	"Arena Models": "Arena-Modelle",
+	"Artifacts": "Artefakte",
+	"Ask a question": "Stellen Sie eine Frage",
+	"Assistant": "Assistent",
+	"Attach file": "Datei anhängen",
+	"Attention to detail": "Aufmerksamkeit für Details",
+	"Attribute for Username": "Attribut für Benutzername",
+	"Audio": "Audio",
+	"August": "August",
+	"Authenticate": "Authentifizieren",
+	"Auto-Copy Response to Clipboard": "Antwort automatisch in die Zwischenablage kopieren",
+	"Auto-playback response": "Antwort automatisch abspielen",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111-API-Authentifizierungszeichenfolge",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111-Basis-URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-Basis-URL ist erforderlich.",
+	"Available list": "Verfügbare Liste",
+	"available!": "Verfügbar!",
+	"Awful": "Schrecklich",
+	"Azure AI Speech": "Azure AI Speech",
+	"Azure Region": "Azure-Region",
+	"Back": "Zurück",
+	"Bad Response": "Schlechte Antwort",
+	"Banners": "Banner",
+	"Base Model (From)": "Basismodell (From)",
+	"Batch Size (num_batch)": "Stapelgröße (num_batch)",
+	"before": "bereits geteilt",
+	"Being lazy": "Faulheit",
+	"Bing Search V7 Endpoint": "Bing Search V7-Endpunkt",
+	"Bing Search V7 Subscription Key": "Bing Search V7-Abonnement-Schlüssel",
+	"Brave Search API Key": "Brave Search API-Schlüssel",
+	"By {{name}}": "Von {{name}}",
+	"Bypass SSL verification for Websites": "SSL-Überprüfung für Webseiten umgehen",
+	"Call": "Anrufen",
+	"Call feature is not supported when using Web STT engine": "Die Anruffunktion wird nicht unterstützt, wenn die Web-STT-Engine verwendet wird.",
+	"Camera": "Kamera",
+	"Cancel": "Abbrechen",
+	"Capabilities": "Fähigkeiten",
+	"Certificate Path": "Zertifikatpfad",
+	"Change Password": "Passwort ändern",
+	"Character": "Zeichen",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Neue Wege beschreiten",
+	"Chat": "Gespräch",
+	"Chat Background Image": "Hintergrundbild des Unterhaltungsfensters",
+	"Chat Bubble UI": "Chat Bubble UI",
+	"Chat Controls": "Chat-Steuerung",
+	"Chat direction": "Textrichtung",
+	"Chat Overview": "Unterhaltungsübersicht",
+	"Chat Permissions": "Unterhaltungsberechtigungen",
+	"Chat Tags Auto-Generation": "Automatische Generierung von Unterhaltungstags",
+	"Chats": "Unterhaltungen",
+	"Check Again": "Erneut überprüfen",
+	"Check for updates": "Nach Updates suchen",
+	"Checking for updates...": "Sucht nach Updates...",
+	"Choose a model before saving...": "Wählen Sie ein Modell, bevor Sie speichern...",
+	"Chunk Overlap": "Blocküberlappung",
+	"Chunk Params": "Blockparameter",
+	"Chunk Size": "Blockgröße",
+	"Ciphers": "Verschlüsselungen",
+	"Citation": "Zitate",
+	"Clear memory": "Alle Erinnerungen entfernen",
+	"click here": "hier klicken",
+	"Click here for filter guides.": "Klicken Sie hier für Filteranleitungen.",
+	"Click here for help.": "Klicken Sie hier für Hilfe.",
+	"Click here to": "Klicken Sie hier, um",
+	"Click here to download user import template file.": "Klicken Sie hier, um die Vorlage für den Benutzerimport herunterzuladen.",
+	"Click here to learn more about faster-whisper and see the available models.": "Klicken Sie hier, um mehr über faster-whisper zu erfahren und die verfügbaren Modelle zu sehen.",
+	"Click here to select": "Klicke Sie zum Auswählen hier",
+	"Click here to select a csv file.": "Klicken Sie zum Auswählen einer CSV-Datei hier.",
+	"Click here to select a py file.": "Klicken Sie zum Auswählen einer py-Datei hier.",
+	"Click here to upload a workflow.json file.": "Klicken sie zum Hochladen einer workflow.json-Datei hier.",
+	"click here.": "hier klicken.",
+	"Click on the user role button to change a user's role.": "Klicken Sie auf die Benutzerrolle, um sie zu ändern.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Schreibberechtigung für die Zwischenablage verweigert. Bitte überprüfen Sie Ihre Browsereinstellungen, um den erforderlichen Zugriff zu erlauben.",
+	"Clone": "Klonen",
+	"Close": "Schließen",
+	"Code execution": "Codeausführung",
+	"Code formatted successfully": "Code erfolgreich formatiert",
+	"Collection": "Kollektion",
+	"Color": "Farbe",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI-Basis-URL",
+	"ComfyUI Base URL is required.": "ComfyUI-Basis-URL wird benötigt.",
+	"ComfyUI Workflow": "ComfyUI-Workflow",
+	"ComfyUI Workflow Nodes": "ComfyUI-Workflow-Knoten",
+	"Command": "Befehl",
+	"Completions": "Vervollständigungen",
+	"Concurrent Requests": "Anzahl gleichzeitiger Anfragen",
+	"Configure": "Konfigurieren",
+	"Configure Models": "",
+	"Confirm": "Bestätigen",
+	"Confirm Password": "Passwort bestätigen",
+	"Confirm your action": "Bestätigen Sie Ihre Aktion.",
+	"Connections": "Verbindungen",
+	"Contact Admin for WebUI Access": "Kontaktieren Sie den Administrator für den Zugriff auf die Weboberfläche",
+	"Content": "Info",
+	"Content Extraction": "Inhaltsextraktion",
+	"Context Length": "Kontextlänge",
+	"Continue Response": "Antwort fortsetzen",
+	"Continue with {{provider}}": "Mit {{provider}} fortfahren",
+	"Continue with Email": "Mit Email fortfahren",
+	"Continue with LDAP": "Mit LDAP fortfahren",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontrollieren Sie, wie Nachrichtentext für TTS-Anfragen aufgeteilt wird. 'Punctuation' teilt in Sätze auf, 'paragraphs' teilt in Absätze auf und 'none' behält die Nachricht als einzelnen String.",
+	"Controls": "Steuerung",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Kontrolliert das Gleichgewicht zwischen Kohärenz und Vielfalt des Ausgabetextes. Ein niedrigerer Wert führt zu fokussierterem und kohärenterem Text. (Standard: 5.0)",
+	"Copied": "Kopiert",
+	"Copied shared chat URL to clipboard!": "Freigabelink in die Zwischenablage kopiert!",
+	"Copied to clipboard": "In die Zwischenablage kopiert",
+	"Copy": "Kopieren",
+	"Copy last code block": "Letzten Codeblock kopieren",
+	"Copy last response": "Letzte Antwort kopieren",
+	"Copy Link": "Link kopieren",
+	"Copy to clipboard": "In die Zwischenablage kopieren",
+	"Copying to clipboard was successful!": "Das Kopieren in die Zwischenablage war erfolgreich!",
+	"Create": "Erstellen",
+	"Create a knowledge base": "Wissensspeicher erstellen",
+	"Create a model": "Modell erstellen",
+	"Create Account": "Konto erstellen",
+	"Create Admin Account": "Administrator-Account erstellen",
+	"Create Group": "Gruppe erstellen",
+	"Create Knowledge": "Wissen erstellen",
+	"Create new key": "Neuen Schlüssel erstellen",
+	"Create new secret key": "Neuen API-Schlüssel erstellen",
+	"Created at": "Erstellt am",
+	"Created At": "Erstellt am",
+	"Created by": "Erstellt von",
+	"CSV Import": "CSV-Import",
+	"Current Model": "Aktuelles Modell",
+	"Current Password": "Aktuelles Passwort",
+	"Custom": "Benutzerdefiniert",
+	"Dark": "Dunkel",
+	"Database": "Datenbank",
+	"December": "Dezember",
+	"Default": "Standard",
+	"Default (Open AI)": "Standard (Open AI)",
+	"Default (SentenceTransformers)": "Standard (SentenceTransformers)",
+	"Default Model": "Standardmodell",
+	"Default model updated": "Standardmodell aktualisiert",
+	"Default Models": "",
+	"Default permissions": "Standardberechtigungen",
+	"Default permissions updated successfully": "Standardberechtigungen erfolgreich aktualisiert",
+	"Default Prompt Suggestions": "Prompt-Vorschläge",
+	"Default to 389 or 636 if TLS is enabled": "Standardmäßig auf 389 oder 636 setzen, wenn TLS aktiviert ist",
+	"Default to ALL": "Standardmäßig auf ALLE setzen",
+	"Default User Role": "Standardbenutzerrolle",
+	"Delete": "Löschen",
+	"Delete a model": "Ein Modell löschen",
+	"Delete All Chats": "Alle Unterhaltungen löschen",
+	"Delete All Models": "Alle Modelle löschen",
+	"Delete chat": "Unterhaltung löschen",
+	"Delete Chat": "Unterhaltung löschen",
+	"Delete chat?": "Unterhaltung löschen?",
+	"Delete folder?": "Ordner löschen?",
+	"Delete function?": "Funktion löschen?",
+	"Delete prompt?": "Prompt löschen?",
+	"delete this link": "diesen Link löschen",
+	"Delete tool?": "Werkzeug löschen?",
+	"Delete User": "Benutzer löschen",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht",
+	"Deleted {{name}}": "{{name}} gelöscht",
+	"Deleted User": "Benutzer gelöscht",
+	"Describe your knowledge base and objectives": "Beschreibe deinen Wissensspeicher und deine Ziele",
+	"Description": "Beschreibung",
+	"Didn't fully follow instructions": "Nicht genau den Answeisungen gefolgt",
+	"Disabled": "Deaktiviert",
+	"Discover a function": "Entdecken Sie weitere Funktionen",
+	"Discover a model": "Entdecken Sie weitere Modelle",
+	"Discover a prompt": "Entdecken Sie weitere Prompts",
+	"Discover a tool": "Entdecken Sie weitere Werkzeuge",
+	"Discover wonders": "Entdecken Sie Wunder",
+	"Discover, download, and explore custom functions": "Entdecken und beziehen Sie benutzerdefinierte Funktionen",
+	"Discover, download, and explore custom prompts": "Entdecken und beziehen Sie benutzerdefinierte Prompts",
+	"Discover, download, and explore custom tools": "Entdecken und beziehen Sie benutzerdefinierte Werkzeuge",
+	"Discover, download, and explore model presets": "Entdecken und beziehen Sie benutzerdefinierte Modellvorlagen",
+	"Dismissible": "ausblendbar",
+	"Display": "Anzeigen",
+	"Display Emoji in Call": "Emojis im Anruf anzeigen",
+	"Display the username instead of You in the Chat": "Soll \"Sie\" durch Ihren Benutzernamen ersetzt werden?",
+	"Displays citations in the response": "Zeigt Zitate in der Antwort an",
+	"Dive into knowledge": "Tauchen Sie in das Wissen ein",
+	"Do not install functions from sources you do not fully trust.": "Installieren Sie keine Funktionen aus Quellen, denen Sie nicht vollständig vertrauen.",
+	"Do not install tools from sources you do not fully trust.": "Installieren Sie keine Werkzeuge aus Quellen, denen Sie nicht vollständig vertrauen.",
+	"Document": "Dokument",
+	"Documentation": "Dokumentation",
+	"Documents": "Dokumente",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "stellt keine externen Verbindungen her, und Ihre Daten bleiben sicher auf Ihrem lokal gehosteten Server.",
+	"Don't have an account?": "Haben Sie noch kein Benutzerkonto?",
+	"don't install random functions from sources you don't trust.": "installieren Sie keine Funktionen aus Quellen, denen Sie nicht vertrauen.",
+	"don't install random tools from sources you don't trust.": "installieren Sie keine Werkzeuge aus Quellen, denen Sie nicht vertrauen.",
+	"Don't like the style": "schlechter Schreibstil",
+	"Done": "Erledigt",
+	"Download": "Exportieren",
+	"Download canceled": "Exportierung abgebrochen",
+	"Download Database": "Datenbank exportieren",
+	"Drag and drop a file to upload or select a file to view": "Ziehen Sie eine Datei zum Hochladen oder wählen Sie eine Datei zum Anzeigen aus",
+	"Draw": "Zeichnen",
+	"Drop any files here to add to the conversation": "Ziehen Sie beliebige Dateien hierher, um sie der Unterhaltung hinzuzufügen",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "z. B. '30s','10m'. Gültige Zeiteinheiten sind 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "z. B. Ein Filter, um Schimpfwörter aus Text zu entfernen",
+	"e.g. My Filter": "z. B. Mein Filter",
+	"e.g. My Tools": "z. B. Meine Werkzeuge",
+	"e.g. my_filter": "z. B. mein_filter",
+	"e.g. my_tools": "z. B. meine_werkzeuge",
+	"e.g. Tools for performing various operations": "z. B. Werkzeuge für verschiedene Operationen",
+	"Edit": "Bearbeiten",
+	"Edit Arena Model": "Arena-Modell bearbeiten",
+	"Edit Connection": "Verbindung bearbeiten",
+	"Edit Default Permissions": "Standardberechtigungen bearbeiten",
+	"Edit Memory": "Erinnerungen bearbeiten",
+	"Edit User": "Benutzer bearbeiten",
+	"Edit User Group": "Benutzergruppe bearbeiten",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-Mail",
+	"Embark on adventures": "Abenteuer erleben",
+	"Embedding Batch Size": "Embedding-Stapelgröße",
+	"Embedding Model": "Embedding-Modell",
+	"Embedding Model Engine": "Embedding-Modell-Engine",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding-Modell auf \"{{embedding_model}}\" gesetzt",
+	"Enable API Key Auth": "API-Schlüssel-Authentifizierung aktivieren",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Community-Freigabe aktivieren",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Aktiviere Memory Locking (mlock), um zu verhindern, dass Modelldaten aus dem RAM ausgelagert werden. Diese Option sperrt die Arbeitsseiten des Modells im RAM, um sicherzustellen, dass sie nicht auf die Festplatte ausgelagert werden. Dies kann die Leistung verbessern, indem Page Faults vermieden und ein schneller Datenzugriff sichergestellt werden.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Aktiviere Memory Mapping (mmap), um Modelldaten zu laden. Diese Option ermöglicht es dem System, den Festplattenspeicher als Erweiterung des RAM zu verwenden, indem Festplattendateien so behandelt werden, als ob sie im RAM wären. Dies kann die Modellleistung verbessern, indem ein schnellerer Datenzugriff ermöglicht wird. Es kann jedoch nicht auf allen Systemen korrekt funktionieren und einen erheblichen Teil des Festplattenspeichers beanspruchen.",
+	"Enable Message Rating": "Nachrichtenbewertung aktivieren",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Mirostat Sampling zur Steuerung der Perplexität aktivieren. (Standard: 0, 0 = Deaktiviert, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Registrierung erlauben",
+	"Enable Web Search": "Websuche aktivieren",
+	"Enabled": "Aktiviert",
+	"Engine": "Engine",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Stellen Sie sicher, dass Ihre CSV-Datei 4 Spalten in dieser Reihenfolge enthält: Name, E-Mail, Passwort, Rolle.",
+	"Enter {{role}} message here": "Geben Sie die {{role}}-Nachricht hier ein",
+	"Enter a detail about yourself for your LLMs to recall": "Geben Sie ein Detail über sich selbst ein, das Ihre Sprachmodelle (LLMs) sich merken sollen",
+	"Enter api auth string (e.g. username:password)": "Geben Sie die API-Authentifizierungszeichenfolge ein (z. B. Benutzername:Passwort)",
+	"Enter Application DN": "Geben Sie die Anwendungs-DN ein",
+	"Enter Application DN Password": "Geben Sie das Anwendungs-DN-Passwort ein",
+	"Enter Bing Search V7 Endpoint": "Geben Sie den Bing Search V7-Endpunkt ein",
+	"Enter Bing Search V7 Subscription Key": "Geben Sie den Bing Search V7-Abonnement-Schlüssel ein",
+	"Enter Brave Search API Key": "Geben Sie den Brave Search API-Schlüssel ein",
+	"Enter certificate path": "Geben Sie den Zertifikatpfad ein",
+	"Enter CFG Scale (e.g. 7.0)": "Geben Sie die CFG-Skala ein (z. B. 7.0)",
+	"Enter Chunk Overlap": "Geben Sie die Blocküberlappung ein",
+	"Enter Chunk Size": "Geben Sie die Blockgröße ein",
+	"Enter description": "Geben Sie eine Beschreibung ein",
+	"Enter Github Raw URL": "Geben Sie die Github Raw-URL ein",
+	"Enter Google PSE API Key": "Geben Sie den Google PSE-API-Schlüssel ein",
+	"Enter Google PSE Engine Id": "Geben Sie die Google PSE-Engine-ID ein",
+	"Enter Image Size (e.g. 512x512)": "Geben Sie die Bildgröße ein (z. B. 512x512)",
+	"Enter Jina API Key": "Geben Sie den Jina-API-Schlüssel ein",
+	"Enter language codes": "Geben Sie die Sprachcodes ein",
+	"Enter Model ID": "Geben Sie die Modell-ID ein",
+	"Enter model tag (e.g. {{modelTag}})": "Geben Sie den Model-Tag ein",
+	"Enter Mojeek Search API Key": "Geben Sie den Mojeek Search API-Schlüssel ein",
+	"Enter Number of Steps (e.g. 50)": "Geben Sie die Anzahl an Schritten ein (z. B. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Geben Sie den Sampler ein (z. B. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Geben Sie den Scheduler ein (z. B. Karras)",
+	"Enter Score": "Punktzahl eingeben",
+	"Enter SearchApi API Key": "Geben Sie den SearchApi-API-Schlüssel ein",
+	"Enter SearchApi Engine": "Geben Sie die SearchApi-Engine ein",
+	"Enter Searxng Query URL": "Geben Sie die Searxng-Abfrage-URL ein",
+	"Enter Seed": "Geben Sie den Seed ein",
+	"Enter Serper API Key": "Geben Sie den Serper-API-Schlüssel ein",
+	"Enter Serply API Key": "Geben Sie den",
+	"Enter Serpstack API Key": "Geben Sie den Serpstack-API-Schlüssel ein",
+	"Enter server host": "Geben Sie den Server-Host ein",
+	"Enter server label": "Geben Sie das Server-Label ein",
+	"Enter server port": "Geben Sie den Server-Port ein",
+	"Enter stop sequence": "Stop-Sequenz eingeben",
+	"Enter system prompt": "Systemprompt eingeben",
+	"Enter Tavily API Key": "Geben Sie den Tavily-API-Schlüssel ein",
+	"Enter Tika Server URL": "Geben Sie die Tika-Server-URL ein",
+	"Enter Top K": "Geben Sie Top K ein",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Geben Sie die URL ein (z. B. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Geben Sie die URL ein (z. B. http://localhost:11434)",
+	"Enter Your Email": "Geben Sie Ihre E-Mail-Adresse ein",
+	"Enter Your Full Name": "Geben Sie Ihren vollständigen Namen ein",
+	"Enter your message": "Geben Sie Ihre Nachricht ein",
+	"Enter Your Password": "Geben Sie Ihr Passwort ein",
+	"Enter Your Role": "Geben Sie Ihre Rolle ein",
+	"Enter Your Username": "Geben Sie Ihren Benutzernamen ein",
+	"Error": "Fehler",
+	"ERROR": "FEHLER",
+	"Evaluations": "Evaluationen",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Beispiel: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Beispiel: ALL",
+	"Example: ou=users,dc=foo,dc=example": "Beispiel: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Beispiel: sAMAccountName or uid or userPrincipalName",
+	"Exclude": "Ausschließen",
+	"Experimental": "Experimentell",
+	"Explore the cosmos": "Erforschen Sie das Universum",
+	"Export": "Exportieren",
+	"Export All Archived Chats": "Alle archivierten Unterhaltungen exportieren",
+	"Export All Chats (All Users)": "Alle Unterhaltungen exportieren (alle Benutzer)",
+	"Export chat (.json)": "Unterhaltung exportieren (.json)",
+	"Export Chats": "Unterhaltungen exportieren",
+	"Export Config to JSON File": "Exportiere Konfiguration als JSON-Datei",
+	"Export Functions": "Funktionen exportieren",
+	"Export Models": "Modelle exportieren",
+	"Export Presets": "Voreinstellungen exportieren",
+	"Export Prompts": "Prompts exportieren",
+	"Export to CSV": "Als CSV exportieren",
+	"Export Tools": "Werkzeuge exportieren",
+	"External Models": "Externe Modelle",
+	"Failed to add file.": "Fehler beim Hinzufügen der Datei.",
+	"Failed to create API Key.": "Fehler beim Erstellen des API-Schlüssels.",
+	"Failed to read clipboard contents": "Fehler beim Abruf der Zwischenablage",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Fehler beim Aktualisieren der Einstellungen",
+	"Failed to upload file.": "Fehler beim Hochladen der Datei.",
+	"February": "Februar",
+	"Feedback History": "Feedback-Verlauf",
+	"Feedbacks": "Feedbacks",
+	"Feel free to add specific details": "Fühlen Sie sich frei, spezifische Details hinzuzufügen",
+	"File": "Datei",
+	"File added successfully.": "Datei erfolgreich hinzugefügt.",
+	"File content updated successfully.": "Dateiinhalt erfolgreich aktualisiert.",
+	"File Mode": "Datei-Modus",
+	"File not found.": "Datei nicht gefunden.",
+	"File removed successfully.": "Datei erfolgreich entfernt.",
+	"File size should not exceed {{maxSize}} MB.": "Datei darf nicht größer als {{maxSize}} MB sein.",
+	"Files": "Dateien",
+	"Filter is now globally disabled": "Filter ist jetzt global deaktiviert",
+	"Filter is now globally enabled": "Filter ist jetzt global aktiviert",
+	"Filters": "Filter",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerabdruck-Spoofing erkannt: Initialen können nicht als Avatar verwendet werden. Standard-Avatar wird verwendet.",
+	"Fluidly stream large external response chunks": "Nahtlose Übertragung großer externer Antwortabschnitte",
+	"Focus chat input": "Chat-Eingabe fokussieren",
+	"Folder deleted successfully": "Ordner erfolgreich gelöscht",
+	"Folder name cannot be empty": "Ordnername darf nicht leer sein",
+	"Folder name cannot be empty.": "Ordnername darf nicht leer sein.",
+	"Folder name updated successfully": "Ordnername erfolgreich aktualisiert",
+	"Followed instructions perfectly": "Anweisungen perfekt befolgt",
+	"Forge new paths": "Neue Wege beschreiten",
+	"Form": "Formular",
+	"Format your variables using brackets like this:": "Formatieren Sie Ihre Variablen mit Klammern, wie hier:",
+	"Frequency Penalty": "Frequenzstrafe",
+	"Function": "Funktion",
+	"Function created successfully": "Funktion erfolgreich erstellt",
+	"Function deleted successfully": "Funktion erfolgreich gelöscht",
+	"Function Description": "Funktionsbeschreibung",
+	"Function ID": "Funktions-ID",
+	"Function is now globally disabled": "Die Funktion ist jetzt global deaktiviert",
+	"Function is now globally enabled": "Die Funktion ist jetzt global aktiviert",
+	"Function Name": "Funktionsname",
+	"Function updated successfully": "Funktion erfolgreich aktualisiert",
+	"Functions": "Funktionen",
+	"Functions allow arbitrary code execution": "Funktionen ermöglichen die Ausführung beliebigen Codes",
+	"Functions allow arbitrary code execution.": "Funktionen ermöglichen die Ausführung beliebigen Codes.",
+	"Functions imported successfully": "Funktionen erfolgreich importiert",
+	"General": "Allgemein",
+	"General Settings": "Allgemeine Einstellungen",
+	"Generate Image": "Bild erzeugen",
+	"Generating search query": "Suchanfrage wird erstellt",
+	"Generation Info": "Generierungsinformationen",
+	"Get started": "Loslegen",
+	"Get started with {{WEBUI_NAME}}": "Loslegen mit {{WEBUI_NAME}}",
+	"Global": "Global",
+	"Good Response": "Gute Antwort",
+	"Google PSE API Key": "Google PSE-API-Schlüssel",
+	"Google PSE Engine Id": "Google PSE-Engine-ID",
+	"Group created successfully": "Gruppe erfolgreich erstellt",
+	"Group deleted successfully": "Gruppe erfolgreich gelöscht",
+	"Group Description": "Gruppenbeschreibung",
+	"Group Name": "Gruppenname",
+	"Group updated successfully": "Gruppe erfolgreich aktualisiert",
+	"Groups": "Gruppen",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Haptisches Feedback",
+	"has no conversations.": "hat keine Unterhaltungen.",
+	"Hello, {{name}}": "Hallo, {{name}}",
+	"Help": "Hilfe",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Helfen Sie uns, die beste Community-Bestenliste zu erstellen, indem Sie Ihren Feedback-Verlauf teilen!",
+	"Hex Color": "Hex-Farbe",
+	"Hex Color - Leave empty for default color": "Hex-Farbe - Leer lassen für Standardfarbe",
+	"Hide": "Verbergen",
+	"Host": "Host",
+	"How can I help you today?": "Wie kann ich Ihnen heute helfen?",
+	"How would you rate this response?": "Wie würden Sie diese Antwort bewerten?",
+	"Hybrid Search": "Hybride Suche",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Ich bestätige, dass ich gelesen habe und die Auswirkungen meiner Aktion verstehe. Mir sind die Risiken bewusst, die mit der Ausführung beliebigen Codes verbunden sind, und ich habe die Vertrauenswürdigkeit der Quelle überprüft.",
+	"ID": "ID",
+	"Ignite curiosity": "Neugier entfachen",
+	"Image Generation (Experimental)": "Bildgenerierung (experimentell)",
+	"Image Generation Engine": "Bildgenerierungs-Engine",
+	"Image Settings": "Bildeinstellungen",
+	"Images": "Bilder",
+	"Import Chats": "Unterhaltungen importieren",
+	"Import Config from JSON File": "Konfiguration aus JSON-Datei importieren",
+	"Import Functions": "Funktionen importieren",
+	"Import Models": "Modelle importieren",
+	"Import Presets": "Voreinstellungen importieren",
+	"Import Prompts": "Prompts importieren",
+	"Import Tools": "Werkzeuge importieren",
+	"Include": "Einschließen",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api-auth` hinzu",
+	"Include `--api` flag when running stable-diffusion-webui": "Fügen Sie beim Ausführen von stable-diffusion-webui die Option `--api` hinzu",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Beeinflusst, wie schnell der Algorithmus auf Feedback aus dem generierten Text reagiert. Eine niedrigere Lernrate führt zu langsameren Anpassungen, während eine höhere Lernrate den Algorithmus reaktionsschneller macht. (Standard: 0.1)",
+	"Info": "Info",
+	"Input commands": "Eingabebefehle",
+	"Install from Github URL": "Installiere von der Github-URL",
+	"Instant Auto-Send After Voice Transcription": "Spracherkennung direkt absenden",
+	"Interface": "Benutzeroberfläche",
+	"Invalid file format.": "Ungültiges Dateiformat.",
+	"Invalid Tag": "Ungültiger Tag",
+	"January": "Januar",
+	"Jina API Key": "Jina-API-Schlüssel",
+	"join our Discord for help.": "Treten Sie unserem Discord bei, um Hilfe zu erhalten.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON-Vorschau",
+	"July": "Juli",
+	"June": "Juni",
+	"JWT Expiration": "JWT-Ablauf",
+	"JWT Token": "JWT-Token",
+	"Keep Alive": "Verbindung aufrechterhalten",
+	"Key": "Schlüssel",
+	"Keyboard shortcuts": "Tastenkombinationen",
+	"Knowledge": "Wissen",
+	"Knowledge Access": "Wissenszugriff",
+	"Knowledge created successfully.": "Wissen erfolgreich erstellt.",
+	"Knowledge deleted successfully.": "Wissen erfolgreich gelöscht.",
+	"Knowledge reset successfully.": "Wissen erfolgreich zurückgesetzt.",
+	"Knowledge updated successfully": "Wissen erfolgreich aktualisiert",
+	"Label": "Label",
+	"Landing Page Mode": "Startseitenmodus",
+	"Language": "Sprache",
+	"Last Active": "Zuletzt aktiv",
+	"Last Modified": "Zuletzt bearbeitet",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP-Server aktualisiert",
+	"Leaderboard": "Bestenliste",
+	"Leave empty for unlimited": "Leer lassen für unbegrenzt",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Leer lassen, um alle Modelle vom \"{{URL}}/api/tags\"-Endpunkt einzuschließen",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Leer lassen, um alle Modelle vom \"{{URL}}/models\"-Endpunkt einzuschließen",
+	"Leave empty to include all models or select specific models": "Leer lassen, um alle Modelle einzuschließen oder spezifische Modelle auszuwählen",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Leer lassen, um den Standardprompt zu verwenden, oder geben Sie einen benutzerdefinierten Prompt ein",
+	"Light": "Hell",
+	"Listening...": "Höre zu...",
+	"LLMs can make mistakes. Verify important information.": "LLMs können Fehler machen. Überprüfe wichtige Informationen.",
+	"Local": "Lokal",
+	"Local Models": "Lokale Modelle",
+	"Lost": "Verloren",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Von der OpenWebUI-Community",
+	"Make sure to enclose them with": "Umschließe Variablen mit",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Stellen Sie sicher, dass sie eine workflow.json-Datei im API-Format von ComfyUI exportieren.",
+	"Manage": "Verwalten",
+	"Manage Arena Models": "Arena-Modelle verwalten",
+	"Manage Ollama": "Ollama verwalten",
+	"Manage Ollama API Connections": "Ollama-API-Verbindungen verwalten",
+	"Manage OpenAI API Connections": "OpenAI-API-Verbindungen verwalten",
+	"Manage Pipelines": "Pipelines verwalten",
+	"March": "März",
+	"Max Tokens (num_predict)": "Maximale Tokenanzahl (num_predict)",
+	"Max Upload Count": "Maximale Anzahl der Uploads",
+	"Max Upload Size": "Maximale Uploadgröße",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es können maximal 3 Modelle gleichzeitig heruntergeladen werden. Bitte versuchen Sie es später erneut.",
+	"May": "Mai",
+	"Memories accessible by LLMs will be shown here.": "Erinnerungen, die für Modelle zugänglich sind, werden hier angezeigt.",
+	"Memory": "Erinnerungen",
+	"Memory added successfully": "Erinnerung erfolgreich hinzugefügt",
+	"Memory cleared successfully": "Erinnerung erfolgreich gelöscht",
+	"Memory deleted successfully": "Erinnerung erfolgreich gelöscht",
+	"Memory updated successfully": "Erinnerung erfolgreich aktualisiert",
+	"Merge Responses": "Antworten zusammenführen",
+	"Message rating should be enabled to use this feature": "Antwortbewertung muss aktiviert sein, um diese Funktion zu verwenden",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Nachrichten, die Sie nach der Erstellung Ihres Links senden, werden nicht geteilt. Nutzer mit der URL können die freigegebene Unterhaltung einsehen.",
+	"Min P": "Min P",
+	"Minimum Score": "Mindestpunktzahl",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm A",
+	"Model": "Modell",
+	"Model '{{modelName}}' has been successfully downloaded.": "Modell '{{modelName}}' wurde erfolgreich heruntergeladen.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modell '{{modelTag}}' befindet sich bereits in der Warteschlange zum Herunterladen.",
+	"Model {{modelId}} not found": "Modell {{modelId}} nicht gefunden",
+	"Model {{modelName}} is not vision capable": "Das Modell {{modelName}} ist nicht für die Bildverarbeitung geeignet",
+	"Model {{name}} is now {{status}}": "Modell {{name}} ist jetzt {{status}}",
+	"Model accepts image inputs": "Modell akzeptiert Bileingaben",
+	"Model created successfully!": "Modell erfolgreich erstellt!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modell-Dateisystempfad erkannt. Modellkurzname ist für das Update erforderlich, Fortsetzung nicht möglich.",
+	"Model Filtering": "Modellfilterung",
+	"Model ID": "Modell-ID",
+	"Model IDs": "Modell-IDs",
+	"Model Name": "Modell-Name",
+	"Model not selected": "Modell nicht ausgewählt",
+	"Model Params": "Modell-Parameter",
+	"Model Permissions": "Modellberechtigungen",
+	"Model updated successfully": "Modell erfolgreich aktualisiert",
+	"Modelfile Content": "Modelfile-Inhalt",
+	"Models": "Modelle",
+	"Models Access": "Modell-Zugriff",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "Mojeek Search API-Schlüssel",
+	"more": "mehr",
+	"More": "Mehr",
+	"Name": "Name",
+	"Name your knowledge base": "Benennen Sie Ihren Wissensspeicher",
+	"New Chat": "Neue Unterhaltung",
+	"New folder": "Neuer Ordner",
+	"New Password": "Neues Passwort",
+	"No content found": "Kein Inhalt gefunden",
+	"No content to speak": "Kein Inhalt zum Vorlesen",
+	"No distance available": "Keine Distanz verfügbar",
+	"No feedbacks found": "Kein Feedback gefunden",
+	"No file selected": "Keine Datei ausgewählt",
+	"No files found.": "Keine Dateien gefunden.",
+	"No groups with access, add a group to grant access": "Keine Gruppen mit Zugriff, fügen Sie eine Gruppe hinzu, um Zugriff zu gewähren",
+	"No HTML, CSS, or JavaScript content found.": "Keine HTML-, CSS- oder JavaScript-Inhalte gefunden.",
+	"No knowledge found": "Kein Wissen gefunden",
+	"No model IDs": "Keine Modell-IDs",
+	"No models found": "Keine Modelle gefunden",
+	"No models selected": "",
+	"No results found": "Keine Ergebnisse gefunden",
+	"No search query generated": "Keine Suchanfrage generiert",
+	"No source available": "Keine Quelle verfügbar",
+	"No users were found.": "Keine Benutzer gefunden.",
+	"No valves to update": "Keine Valves zum Aktualisieren",
+	"None": "Nichts",
+	"Not factually correct": "Nicht sachlich korrekt",
+	"Not helpful": "Nich hilfreich",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Hinweis: Wenn Sie eine Mindestpunktzahl festlegen, werden in der Suche nur Dokumente mit einer Punktzahl größer oder gleich der Mindestpunktzahl zurückgegeben.",
+	"Notes": "Notizen",
+	"Notifications": "Benachrichtigungen",
+	"November": "November",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth-ID",
+	"October": "Oktober",
+	"Off": "Aus",
+	"Okay, Let's Go!": "Okay, los geht's!",
+	"OLED Dark": "OLED-Dunkel",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama-API",
+	"Ollama API disabled": "Ollama-API deaktiviert",
+	"Ollama API settings updated": "Ollama-API-Einstellungen aktualisiert",
+	"Ollama Version": "Ollama-Version",
+	"On": "Ein",
+	"Only alphanumeric characters and hyphens are allowed": "Nur alphanumerische Zeichen und Bindestriche sind erlaubt",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "In der Befehlszeichenfolge sind nur alphanumerische Zeichen und Bindestriche erlaubt.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Nur Sammlungen können bearbeitet werden. Erstellen Sie eine neue Wissensbasis, um Dokumente zu bearbeiten/hinzuzufügen.",
+	"Only select users and groups with permission can access": "Nur ausgewählte Benutzer und Gruppen mit Berechtigung können darauf zugreifen",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppla! Es scheint, dass die URL ungültig ist. Bitte überprüfen Sie diese und versuchen Sie es erneut.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Hoppla! Es werden noch Dateien hochgeladen. Bitte warten Sie, bis der Upload abgeschlossen ist.",
+	"Oops! There was an error in the previous response.": "Hoppla! Es gab einen Fehler in der vorherigen Antwort.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hoppla! Sie verwenden eine nicht unterstützte Methode (nur Frontend). Bitte stellen Sie die WebUI vom Backend bereit.",
+	"Open file": "Datei öffnen",
+	"Open in full screen": "Im Vollbildmodus öffnen",
+	"Open new chat": "Neuen Chat öffnen",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI verwendet intern faster-whisper.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI verwendet SpeechT5 und CMU Arctic-Sprecher-Embeddings.",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Die installierte Open-WebUI-Version (v{{OPEN_WEBUI_VERSION}}) ist niedriger als die erforderliche Version (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI-API",
+	"OpenAI API Config": "OpenAI-API-Konfiguration",
+	"OpenAI API Key is required.": "OpenAI-API-Schlüssel erforderlich.",
+	"OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert",
+	"OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.",
+	"or": "oder",
+	"Organize your users": "Organisieren Sie Ihre Benutzer",
+	"Other": "Andere",
+	"OUTPUT": "AUSGABE",
+	"Output format": "Ausgabeformat",
+	"Overview": "Übersicht",
+	"page": "Seite",
+	"Password": "Passwort",
+	"Paste Large Text as File": "Großen Text als Datei einfügen",
+	"PDF document (.pdf)": "PDF-Dokument (.pdf)",
+	"PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)",
+	"pending": "ausstehend",
+	"Permission denied when accessing media devices": "Zugriff auf Mediengeräte verweigert",
+	"Permission denied when accessing microphone": "Zugriff auf das Mikrofon verweigert",
+	"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
+	"Permissions": "Berechtigungen",
+	"Personalization": "Personalisierung",
+	"Pin": "Anheften",
+	"Pinned": "Angeheftet",
+	"Pioneer insights": "Bahnbrechende Erkenntnisse",
+	"Pipeline deleted successfully": "Pipeline erfolgreich gelöscht",
+	"Pipeline downloaded successfully": "Pipeline erfolgreich heruntergeladen",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Pipelines nicht erkannt",
+	"Pipelines Valves": "Pipeline Valves",
+	"Plain text (.txt)": "Nur Text (.txt)",
+	"Playground": "Testumgebung",
+	"Please carefully review the following warnings:": "Bitte überprüfen Sie die folgenden Warnungen sorgfältig:",
+	"Please enter a prompt": "Bitte geben Sie einen Prompt ein",
+	"Please fill in all fields.": "Bitte füllen Sie alle Felder aus.",
+	"Please select a model first.": "",
+	"Please select a reason": "Bitte wählen Sie einen Grund aus",
+	"Port": "Port",
+	"Positive attitude": "Positive Einstellung",
+	"Prefix ID": "Präfix-ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Prefix-ID wird verwendet, um Konflikte mit anderen Verbindungen zu vermeiden, indem ein Präfix zu den Modell-IDs hinzugefügt wird - leer lassen, um zu deaktivieren",
+	"Previous 30 days": "Vorherige 30 Tage",
+	"Previous 7 days": "Vorherige 7 Tage",
+	"Profile Image": "Profilbild",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (z. B. \"Erzähle mir eine interessante Tatsache über das Römische Reich\")",
+	"Prompt Content": "Prompt-Inhalt",
+	"Prompt created successfully": "Prompt erfolgreich erstellt",
+	"Prompt suggestions": "Prompt-Vorschläge",
+	"Prompt updated successfully": "Prompt erfolgreich aktualisiert",
+	"Prompts": "Prompts",
+	"Prompts Access": "Prompt-Zugriff",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" von Ollama.com beziehen",
+	"Pull a model from Ollama.com": "Modell von Ollama.com beziehen",
+	"Query Generation Prompt": "Abfragegenerierungsprompt",
+	"Query Params": "Abfrageparameter",
+	"RAG Template": "RAG-Vorlage",
+	"Rating": "Bewertung",
+	"Re-rank models by topic similarity": "Modelle nach thematischer Ähnlichkeit neu ordnen",
+	"Read Aloud": "Vorlesen",
+	"Record voice": "Stimme aufnehmen",
+	"Redirecting you to OpenWebUI Community": "Sie werden zur OpenWebUI-Community weitergeleitet",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Reduziert die Wahrscheinlichkeit, Unsinn zu generieren. Ein höherer Wert (z.B. 100) liefert vielfältigere Antworten, während ein niedrigerer Wert (z.B. 10) konservativer ist. (Standard: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Beziehen Sie sich auf sich selbst als \"Benutzer\" (z. B. \"Benutzer lernt Spanisch\")",
+	"References from": "Referenzen aus",
+	"Refused when it shouldn't have": "Abgelehnt, obwohl es nicht hätte abgelehnt werden sollen",
+	"Regenerate": "Neu generieren",
+	"Release Notes": "Veröffentlichungshinweise",
+	"Relevance": "Relevanz",
+	"Remove": "Entfernen",
+	"Remove Model": "Modell entfernen",
+	"Rename": "Umbenennen",
+	"Reorder Models": "",
+	"Repeat Last N": "Wiederhole die letzten N",
+	"Request Mode": "Anforderungsmodus",
+	"Reranking Model": "Reranking-Modell",
+	"Reranking model disabled": "Reranking-Modell deaktiviert",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking-Modell \"{{reranking_model}}\" fesgelegt",
+	"Reset": "Zurücksetzen",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Upload-Verzeichnis zurücksetzen",
+	"Reset Vector Storage/Knowledge": "Vektorspeicher/Wissen zurücksetzen",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Benachrichtigungen können nicht aktiviert werden, da die Website-Berechtigungen abgelehnt wurden. Bitte besuchen Sie Ihre Browser-Einstellungen, um den erforderlichen Zugriff zu gewähren.",
+	"Response splitting": "Antwortaufteilung",
+	"Result": "Ergebnis",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Rich-Text-Eingabe für Unterhaltungen",
+	"RK": "RK",
+	"Role": "Rolle",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Ausführen",
+	"Running": "Läuft",
+	"Save": "Speichern",
+	"Save & Create": "Erstellen",
+	"Save & Update": "Aktualisieren",
+	"Save As Copy": "Als Kopie speichern",
+	"Save Tag": "Tag speichern",
+	"Saved": "Gespeichert",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Das direkte Speichern von Unterhaltungen im Browser-Speicher wird nicht mehr unterstützt. Bitte nehmen Sie einen Moment Zeit, um Ihre Unterhaltungen zu exportieren und zu löschen, indem Sie auf die Schaltfläche unten klicken. Keine Sorge, Sie können Ihre Unterhaltungen problemlos über das Backend wieder importieren.",
+	"Scroll to bottom when switching between branches": "Beim Wechsel zwischen Branches nach unten scrollen",
+	"Search": "Suchen",
+	"Search a model": "Modell suchen",
+	"Search Base": "Suchbasis",
+	"Search Chats": "Unterhaltungen durchsuchen...",
+	"Search Collection": "Sammlung durchsuchen",
+	"Search Filters": "Suchfilter",
+	"search for tags": "nach Tags suchen",
+	"Search Functions": "Funktionen durchsuchen...",
+	"Search Knowledge": "Wissen durchsuchen",
+	"Search Models": "Modelle durchsuchen...",
+	"Search options": "Suchoptionen",
+	"Search Prompts": "Prompts durchsuchen...",
+	"Search Result Count": "Anzahl der Suchergebnisse",
+	"Search the web": "Im Web suchen",
+	"Search Tools": "Werkzeuge durchsuchen...",
+	"SearchApi API Key": "SearchApi-API-Schlüssel",
+	"SearchApi Engine": "SearchApi-Engine",
+	"Searched {{count}} sites_one": "{{count}} Seite durchsucht",
+	"Searched {{count}} sites_other": "{{count}} Seiten durchsucht",
+	"Searching \"{{searchQuery}}\"": "Suche nach \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Suche im Wissen nach \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng-Abfrage-URL",
+	"See readme.md for instructions": "Anleitung in readme.md anzeigen",
+	"See what's new": "Entdecken Sie die Neuigkeiten",
+	"Seed": "Seed",
+	"Select a base model": "Wählen Sie ein Basismodell",
+	"Select a engine": "Wählen Sie eine Engine",
+	"Select a function": "Wählen Sie eine Funktion",
+	"Select a group": "Wählen Sie eine Gruppe",
+	"Select a model": "Wählen Sie ein Modell",
+	"Select a pipeline": "Wählen Sie eine Pipeline",
+	"Select a pipeline url": "Wählen Sie eine Pipeline-URL",
+	"Select a tool": "Wählen Sie ein Werkzeug",
+	"Select Engine": "Engine auswählen",
+	"Select Knowledge": "Wissensdatenbank auswählen",
+	"Select model": "Modell auswählen",
+	"Select only one model to call": "Wählen Sie nur ein Modell zum Anrufen aus",
+	"Selected model(s) do not support image inputs": "Ihre ausgewählten Modelle unterstützen keine Bildeingaben",
+	"Semantic distance to query": "Semantische Distanz zur Abfrage",
+	"Send": "Senden",
+	"Send a Message": "Eine Nachricht senden",
+	"Send message": "Nachricht senden",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Sendet `stream_options: { include_usage: true }` in der Anfrage.\nUnterstützte Anbieter geben Token-Nutzungsinformationen in der Antwort zurück, wenn dies festgelegt ist.",
+	"September": "September",
+	"Serper API Key": "Serper-API-Schlüssel",
+	"Serply API Key": "Serply-API-Schlüssel",
+	"Serpstack API Key": "Serpstack-API-Schlüssel",
+	"Server connection verified": "Serververbindung überprüft",
+	"Set as default": "Als Standard festlegen",
+	"Set CFG Scale": "CFG-Skala festlegen",
+	"Set Default Model": "Standardmodell festlegen",
+	"Set embedding model": "Einbettungsmodell festlegen",
+	"Set embedding model (e.g. {{model}})": "Einbettungsmodell festlegen (z. B. {{model}})",
+	"Set Image Size": "Bildgröße festlegen",
+	"Set reranking model (e.g. {{model}})": "Rerankingmodell festlegen (z. B. {{model}})",
+	"Set Sampler": "Sampler festlegen",
+	"Set Scheduler": "Scheduler festlegen",
+	"Set Steps": "Schrittgröße festlegen",
+	"Set Task Model": "Aufgabenmodell festlegen",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Legt die Anzahl der für die Berechnung verwendeten GPU-Geräte fest. Diese Option steuert, wie viele GPU-Geräte (falls verfügbar) zur Verarbeitung eingehender Anfragen verwendet werden. Eine Erhöhung dieses Wertes kann die Leistung für Modelle, die für GPU-Beschleunigung optimiert sind, erheblich verbessern, kann jedoch auch mehr Strom und GPU-Ressourcen verbrauchen.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Legt die Anzahl der für die Berechnung verwendeten GPU-Geräte fest. Diese Option steuert, wie viele GPU-Geräte (falls verfügbar) zur Verarbeitung eingehender Anfragen verwendet werden. Eine Erhöhung dieses Wertes kann die Leistung für Modelle, die für GPU-Beschleunigung optimiert sind, erheblich verbessern, kann jedoch auch mehr Strom und GPU-Ressourcen verbrauchen.",
+	"Set Voice": "Stimme festlegen",
+	"Set whisper model": "Whisper-Modell festlegen",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Legt fest, wie weit das Modell zurückblicken soll, um Wiederholungen zu verhindern. (Standard: 64, 0 = deaktiviert, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Legt fest, wie stark Wiederholungen bestraft werden sollen. Ein höherer Wert (z.B. 1.5) bestraft Wiederholungen stärker, während ein niedrigerer Wert (z.B. 0.9) nachsichtiger ist. (Standard: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Legt den Zufallszahlengenerator-Seed für die Generierung fest. Wenn dieser auf eine bestimmte Zahl gesetzt wird, erzeugt das Modell denselben Text für denselben Prompt. (Standard: zufällig)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Legt die Größe des Kontextfensters fest, das zur Generierung des nächsten Tokens verwendet wird. (Standard: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Legt die zu verwendenden Stoppsequenzen fest. Wenn dieses Muster erkannt wird, stoppt das LLM die Textgenerierung und gibt zurück. Mehrere Stoppmuster können festgelegt werden, indem mehrere separate Stopp-Parameter in einer Modelldatei angegeben werden.",
+	"Settings": "Einstellungen",
+	"Settings saved successfully!": "Einstellungen erfolgreich gespeichert!",
+	"Share": "Teilen",
+	"Share Chat": "Unterhaltung teilen",
+	"Share to OpenWebUI Community": "Mit OpenWebUI Community teilen",
+	"Show": "Anzeigen",
+	"Show \"What's New\" modal on login": "\"Was gibt's Neues\"-Modal beim Anmelden anzeigen",
+	"Show Admin Details in Account Pending Overlay": "Admin-Details im Account-Pending-Overlay anzeigen",
+	"Show shortcuts": "Verknüpfungen anzeigen",
+	"Show your support!": "Zeigen Sie Ihre Unterstützung!",
+	"Showcased creativity": "Kreativität gezeigt",
+	"Sign in": "Anmelden",
+	"Sign in to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} anmelden",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Bei {{WEBUI_NAME}} mit LDAP anmelden",
+	"Sign Out": "Abmelden",
+	"Sign up": "Registrieren",
+	"Sign up to {{WEBUI_NAME}}": "Bei {{WEBUI_NAME}} registrieren",
+	"Signing in to {{WEBUI_NAME}}": "Wird bei {{WEBUI_NAME}} angemeldet",
+	"Source": "Quelle",
+	"Speech Playback Speed": "Sprachwiedergabegeschwindigkeit",
+	"Speech recognition error: {{error}}": "Spracherkennungsfehler: {{error}}",
+	"Speech-to-Text Engine": "Sprache-zu-Text-Engine",
+	"Stop": "Stop",
+	"Stop Sequence": "Stop-Sequenz",
+	"Stream Chat Response": "Unterhaltungsantwort streamen",
+	"STT Model": "STT-Modell",
+	"STT Settings": "STT-Einstellungen",
+	"Subtitle (e.g. about the Roman Empire)": "Untertitel (z. B. über das Römische Reich)",
+	"Success": "Erfolg",
+	"Successfully updated.": "Erfolgreich aktualisiert.",
+	"Suggested": "Vorgeschlagen",
+	"Support": "Unterstützung",
+	"Support this plugin:": "Unterstützen Sie dieses Plugin:",
+	"Sync directory": "Verzeichnis synchronisieren",
+	"System": "System",
+	"System Instructions": "Systemanweisungen",
+	"System Prompt": "System-Prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Prompt für Tag-Generierung",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail-Free Sampling wird verwendet, um den Einfluss weniger wahrscheinlicher Tokens auf die Ausgabe zu reduzieren. Ein höherer Wert (z.B. 2.0) reduziert den Einfluss stärker, während ein Wert von 1.0 diese Einstellung deaktiviert. (Standard: 1)",
+	"Tap to interrupt": "Zum Unterbrechen tippen",
+	"Tavily API Key": "Tavily-API-Schlüssel",
+	"Tell us more:": "Erzähl uns mehr",
+	"Temperature": "Temperatur",
+	"Template": "Vorlage",
+	"Temporary Chat": "Temporäre Unterhaltung",
+	"Text Splitter": "Text-Splitter",
+	"Text-to-Speech Engine": "Text-zu-Sprache-Engine",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Danke für Ihr Feedback!",
+	"The Application Account DN you bind with for search": "Der Anwendungs-Konto-DN, mit dem Sie für die Suche binden",
+	"The base to search for users": "Die Basis, in der nach Benutzern gesucht wird",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Die Batch-Größe bestimmt, wie viele Textanfragen gleichzeitig verarbeitet werden. Eine größere Batch-Größe kann die Leistung und Geschwindigkeit des Modells erhöhen, erfordert jedoch auch mehr Speicher. (Standard: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Die Entwickler hinter diesem Plugin sind leidenschaftliche Freiwillige aus der Community. Wenn Sie dieses Plugin hilfreich finden, erwägen Sie bitte, zu seiner Entwicklung beizutragen.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Die Bewertungs-Bestenliste basiert auf dem Elo-Bewertungssystem und wird in Echtzeit aktualisiert.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "Das LDAP-Attribut, das dem Benutzernamen zugeordnet ist, den Benutzer zum Anmelden verwenden.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Die Bestenliste befindet sich derzeit in der Beta-Phase, und es ist möglich, dass wir die Bewertungsberechnungen anpassen, während wir den Algorithmus verfeinern.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Die maximale Dateigröße in MB. Wenn die Dateigröße dieses Limit überschreitet, wird die Datei nicht hochgeladen.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Die maximale Anzahl von Dateien, die gleichzeitig in der Unterhaltung verwendet werden können. Wenn die Anzahl der Dateien dieses Limit überschreitet, werden die Dateien nicht hochgeladen.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Die Punktzahl sollte ein Wert zwischen 0,0 (0 %) und 1,0 (100 %) sein.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Die Temperatur des Modells. Eine Erhöhung der Temperatur führt dazu, dass das Modell kreativer antwortet. (Standard: 0,8)",
+	"Theme": "Design",
+	"Thinking...": "Denke nach...",
+	"This action cannot be undone. Do you wish to continue?": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie fortfahren?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dies stellt sicher, dass Ihre wertvollen Unterhaltungen sicher in Ihrer Backend-Datenbank gespeichert werden. Vielen Dank!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dies ist eine experimentelle Funktion, sie funktioniert möglicherweise nicht wie erwartet und kann jederzeit geändert werden.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Diese Option steuert, wie viele Tokens beim Aktualisieren des Kontexts beibehalten werden. Wenn sie beispielsweise auf 2 gesetzt ist, werden die letzten 2 Tokens des Gesprächskontexts beibehalten. Das Beibehalten des Kontexts kann helfen, die Kontinuität eines Gesprächs aufrechtzuerhalten, kann jedoch die Fähigkeit verringern, auf neue Themen zu reagieren. (Standard: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Diese Option legt die maximale Anzahl von Tokens fest, die das Modell in seiner Antwort generieren kann. Eine Erhöhung dieses Limits ermöglicht es dem Modell, längere Antworten zu geben, kann jedoch auch die Wahrscheinlichkeit erhöhen, dass unhilfreicher oder irrelevanter Inhalt generiert wird. (Standard: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Diese Option löscht alle vorhandenen Dateien in der Sammlung und ersetzt sie durch neu hochgeladene Dateien.",
+	"This response was generated by \"{{model}}\"": "Diese Antwort wurde von \"{{model}}\" generiert",
+	"This will delete": "Dies löscht",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dies löscht <strong>{{NAME}}</strong> und <strong>alle Inhalte</strong>.",
+	"This will delete all models including custom models": "Dies wird alle Modelle einschließlich benutzerdefinierter Modelle löschen",
+	"This will delete all models including custom models and cannot be undone.": "Dies wird alle Modelle einschließlich benutzerdefinierter Modelle löschen und kann nicht rückgängig gemacht werden.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Dadurch wird die Wissensdatenbank zurückgesetzt und alle Dateien synchronisiert. Möchten Sie fortfahren?",
+	"Thorough explanation": "Ausführliche Erklärung",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika-Server-URL erforderlich.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tipp: Aktualisieren Sie mehrere Variablenfelder nacheinander, indem Sie nach jedem Ersetzen die Tabulatortaste im Eingabefeld der Unterhaltung drücken.",
+	"Title": "Titel",
+	"Title (e.g. Tell me a fun fact)": "Titel (z. B. Erzähl mir einen lustigen Fakt)",
+	"Title Auto-Generation": "Unterhaltungstitel automatisch generieren",
+	"Title cannot be an empty string.": "Titel darf nicht leer sein.",
+	"Title Generation Prompt": "Prompt für Titelgenerierung",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Um auf die verfügbaren Modellnamen zuzugreifen,",
+	"To access the GGUF models available for downloading,": "Um auf die verfügbaren GGUF-Modelle zuzugreifen,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Um auf das WebUI zugreifen zu können, wenden Sie sich bitte an einen Administrator. Administratoren können den Benutzerstatus über das Admin-Panel verwalten.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Um Wissensdatenbanken hier anzuhängen, fügen Sie sie zunächst dem Arbeitsbereich \"Wissen\" hinzu.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Um Ihre Privatsphäre zu schützen, werden nur Bewertungen, Modell-IDs, Tags und Metadaten aus Ihrem Feedback geteilt – Ihre Unterhaltungen bleiben privat und werden nicht einbezogen.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Um Aktionen auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich „Funktionen“ hinzu.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Um Filter auszuwählen, fügen Sie diese zunächst dem Arbeitsbereich „Funktionen“ hinzu.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Um Toolkits auszuwählen, fügen Sie sie zunächst dem Arbeitsbereich „Werkzeuge“ hinzu.",
+	"Toast notifications for new updates": "Toast-Benachrichtigungen für neue Updates",
+	"Today": "Heute",
+	"Toggle settings": "Einstellungen umschalten",
+	"Toggle sidebar": "Seitenleiste umschalten",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Beizubehaltende Tokens bei Kontextaktualisierung (num_keep)",
+	"Too verbose": "Zu ausführlich",
+	"Tool created successfully": "Werkzeug erfolgreich erstellt",
+	"Tool deleted successfully": "Werkzeug erfolgreich gelöscht",
+	"Tool Description": "Werkzeugbeschreibung",
+	"Tool ID": "Werkzeug-ID",
+	"Tool imported successfully": "Werkzeug erfolgreich importiert",
+	"Tool Name": "Werkzeugname",
+	"Tool updated successfully": "Werkzeug erfolgreich aktualisiert",
+	"Tools": "Werkzeuge",
+	"Tools Access": "Werkzeugzugriff",
+	"Tools are a function calling system with arbitrary code execution": "Wekzeuge sind ein Funktionssystem mit beliebiger Codeausführung",
+	"Tools have a function calling system that allows arbitrary code execution": "Werkezuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht",
+	"Tools have a function calling system that allows arbitrary code execution.": "Werkzeuge verfügen über ein Funktionssystem, das die Ausführung beliebigen Codes ermöglicht.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformers",
+	"Trouble accessing Ollama?": "Probleme beim Zugriff auf Ollama?",
+	"TTS Model": "TTS-Modell",
+	"TTS Settings": "TTS-Einstellungen",
+	"TTS Voice": "TTS-Stimme",
+	"Type": "Art",
+	"Type Hugging Face Resolve (Download) URL": "Geben Sie die Hugging Face Resolve-URL ein",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ups! Es gab ein Problem bei der Verbindung mit {{provider}}.",
+	"UI": "Oberfläche",
+	"Unarchive All": "Alle wiederherstellen",
+	"Unarchive All Archived Chats": "Alle archivierten Unterhaltungen wiederherstellen",
+	"Unarchive Chat": "Unterhaltung wiederherstellen",
+	"Unlock mysteries": "Geheimnisse entsperren",
+	"Unpin": "Lösen",
+	"Unravel secrets": "Geheimnisse lüften",
+	"Untagged": "Ungetaggt",
+	"Update": "Aktualisieren",
+	"Update and Copy Link": "Aktualisieren und Link kopieren",
+	"Update for the latest features and improvements.": "Aktualisieren Sie für die neuesten Funktionen und Verbesserungen.",
+	"Update password": "Passwort aktualisieren",
+	"Updated": "Aktualisiert",
+	"Updated at": "Aktualisiert am",
+	"Updated At": "Aktualisiert am",
+	"Upload": "Hochladen",
+	"Upload a GGUF model": "GGUF-Model hochladen",
+	"Upload directory": "Upload-Verzeichnis",
+	"Upload files": "Dateien hochladen",
+	"Upload Files": "Datei(en) hochladen",
+	"Upload Pipeline": "Pipeline hochladen",
+	"Upload Progress": "Hochladefortschritt",
+	"URL": "URL",
+	"URL Mode": "URL-Modus",
+	"Use '#' in the prompt input to load and include your knowledge.": "Nutzen Sie '#' in der Prompt-Eingabe, um Ihr Wissen zu laden und einzuschließen.",
+	"Use Gravatar": "Gravatar verwenden",
+	"Use groups to group your users and assign permissions.": "Nutzen Sie Gruppen, um Ihre Benutzer zu gruppieren und Berechtigungen zuzuweisen.",
+	"Use Initials": "Initialen verwenden",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "Benutzer",
+	"User": "Benutzer",
+	"User location successfully retrieved.": "Benutzerstandort erfolgreich ermittelt.",
+	"Username": "Benutzername",
+	"Users": "Benutzer",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Verwendung des Standard-Arena-Modells mit allen Modellen. Klicken Sie auf die Plus-Schaltfläche, um benutzerdefinierte Modelle hinzuzufügen.",
+	"Utilize": "Verwende",
+	"Valid time units:": "Gültige Zeiteinheiten:",
+	"Valves": "Valves",
+	"Valves updated": "Valves aktualisiert",
+	"Valves updated successfully": "Valves erfolgreich aktualisiert",
+	"variable": "Variable",
+	"variable to have them replaced with clipboard content.": "Variable, um den Inhalt der Zwischenablage beim Nutzen des Prompts zu ersetzen.",
+	"Version": "Version",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} von {{totalVersions}}",
+	"Visibility": "Sichtbarkeit",
+	"Voice": "Stimme",
+	"Voice Input": "Spracheingabe",
+	"Warning": "Warnung",
+	"Warning:": "Warnung:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Warnung: Wenn Sie dies aktivieren, können Benutzer beliebigen Code auf dem Server hochladen.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Warnung: Wenn Sie das Einbettungsmodell aktualisieren oder ändern, müssen Sie alle Dokumente erneut importieren.",
+	"Web": "Web",
+	"Web API": "Web-API",
+	"Web Loader Settings": "Web Loader Einstellungen",
+	"Web Search": "Websuche",
+	"Web Search Engine": "Suchmaschine",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI-Einstellungen",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI wird Anfragen an \"{{url}}/api/chat\" senden",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI wird Anfragen an \"{{url}}/chat/completions\" senden",
+	"What are you trying to achieve?": "Was versuchen Sie zu erreichen?",
+	"What are you working on?": "Woran arbeiten Sie?",
+	"What’s New in": "Neuigkeiten von",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Wenn aktiviert, antwortet das Modell in Echtzeit auf jede Chat-Nachricht und generiert eine Antwort, sobald der Benutzer eine Nachricht sendet. Dieser Modus ist nützlich für Live-Chat-Anwendungen, kann jedoch die Leistung auf langsamerer Hardware beeinträchtigen.",
+	"wherever you are": "wo immer Sie sind",
+	"Whisper (Local)": "Whisper (lokal)",
+	"Why?": "Warum?",
+	"Widescreen Mode": "Breitbildmodus",
+	"Won": "Gewonnen",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funktioniert zusammen mit top-k. Ein höherer Wert (z.B. 0,95) führt zu vielfältigerem Text, während ein niedrigerer Wert (z.B. 0,5) fokussierteren und konservativeren Text erzeugt. (Standard: 0,9)",
+	"Workspace": "Arbeitsbereich",
+	"Workspace Permissions": "Arbeitsbereichsberechtigungen",
+	"Write a prompt suggestion (e.g. Who are you?)": "Schreiben Sie einen Promptvorschlag (z. B. Wer sind Sie?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.",
+	"Write something...": "Schreiben Sie etwas...",
+	"Write your model template content here": "Schreiben Sie hier Ihren Modellvorlageninhalt",
+	"Yesterday": "Gestern",
+	"You": "Sie",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Sie können nur mit maximal {{maxCount}} Datei(en) gleichzeitig chatten.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Personalisieren Sie Interaktionen mit LLMs, indem Sie über die Schaltfläche \"Verwalten\" Erinnerungen hinzufügen.",
+	"You cannot upload an empty file.": "Sie können keine leere Datei hochladen.",
+	"You do not have permission to upload files.": "Sie haben keine Berechtigung zum Hochladen von Dateien.",
+	"You have no archived conversations.": "Du hast keine archivierten Unterhaltungen.",
+	"You have shared this chat": "Sie haben diese Unterhaltung geteilt",
+	"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.",
+	"You're now logged in.": "Sie sind jetzt eingeloggt.",
+	"Your account status is currently pending activation.": "Ihr Kontostatus ist derzeit ausstehend und wartet auf Aktivierung.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Ihr gesamter Beitrag geht direkt an den Plugin-Entwickler; Open WebUI behält keinen Prozentsatz ein. Die gewählte Finanzierungsplattform kann jedoch eigene Gebühren haben.",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube-Ladeeinstellungen"
+}
diff --git a/src/lib/i18n/locales/dg-DG/translation.json b/src/lib/i18n/locales/dg-DG/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..4bb7613a8bc56a4c8c8b3360902343bf8ce80def
--- /dev/null
+++ b/src/lib/i18n/locales/dg-DG/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' for no expire. Much permanent, very wow.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(such e.g. `sh webui.sh --api`)",
+	"(latest)": "(much latest)",
+	"{{ models }}": "",
+	"{{user}}'s Chats": "",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend Much Required",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "",
+	"a user": "such user",
+	"About": "Much About",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Account",
+	"Account Activation Pending": "",
+	"Accurate information": "",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "",
+	"Add a tag": "Add such tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "",
+	"Add Files": "Add Files",
+	"Add Group": "",
+	"Add Memory": "",
+	"Add Model": "",
+	"Add Tag": "",
+	"Add Tags": "",
+	"Add text content": "",
+	"Add User": "",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Adjusting these settings will apply changes to all users. Such universal, very wow.",
+	"admin": "admin",
+	"Admin": "",
+	"Admin Panel": "Admin Panel",
+	"Admin Settings": "Admin Settings",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Advanced Parameters",
+	"Advanced Params": "",
+	"All chats": "",
+	"All Documents": "",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Allow Delete Chats",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Such account exists?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "such assistant",
+	"and": "and",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "",
+	"API Base URL": "API Base URL",
+	"API Key": "API Key",
+	"API Key created.": "",
+	"API keys": "",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "",
+	"Archive": "",
+	"Archive All Chats": "",
+	"Archived Chats": "",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Such certainty?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Attach file",
+	"Attention to detail": "",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Copy Bark Auto Bark",
+	"Auto-playback response": "Auto-playback response",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Base URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Base URL is required.",
+	"Available list": "",
+	"available!": "available! So excite!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Back",
+	"Bad Response": "",
+	"Banners": "",
+	"Base Model (From)": "",
+	"Batch Size (num_batch)": "",
+	"before": "",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Cancel",
+	"Capabilities": "",
+	"Certificate Path": "",
+	"Change Password": "Change Password",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "",
+	"Chat Controls": "",
+	"Chat direction": "",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chats",
+	"Check Again": "Check Again",
+	"Check for updates": "Check for updates",
+	"Checking for updates...": "Checking for updates... Such anticipation...",
+	"Choose a model before saving...": "Choose model before saving... Wow choose first.",
+	"Chunk Overlap": "Chunk Overlap",
+	"Chunk Params": "Chunk Params",
+	"Chunk Size": "Chunk Size",
+	"Ciphers": "",
+	"Citation": "",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Click for help. Much assist.",
+	"Click here to": "",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Click to select",
+	"Click here to select a csv file.": "",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "click here. Such click.",
+	"Click on the user role button to change a user's role.": "Click user role button to change role.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "",
+	"Close": "Close",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Collection",
+	"Color": "",
+	"ComfyUI": "",
+	"ComfyUI Base URL": "",
+	"ComfyUI Base URL is required.": "",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Command",
+	"Completions": "",
+	"Concurrent Requests": "",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Confirm Password",
+	"Confirm your action": "",
+	"Connections": "Connections",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Content",
+	"Content Extraction": "",
+	"Context Length": "Context Length",
+	"Continue Response": "",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "",
+	"Copied to clipboard": "",
+	"Copy": "",
+	"Copy last code block": "Copy last code block",
+	"Copy last response": "Copy last response",
+	"Copy Link": "",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Copying to clipboard was success! Very success!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "",
+	"Create Account": "Create Account",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "",
+	"Create new secret key": "",
+	"Created at": "Created at",
+	"Created At": "",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Current Model",
+	"Current Password": "Current Password",
+	"Custom": "Custom",
+	"Dark": "Dark",
+	"Database": "Database",
+	"December": "",
+	"Default": "Default",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "",
+	"Default Model": "",
+	"Default model updated": "Default model much updated",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Default Prompt Suggestions",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Default User Role",
+	"Delete": "",
+	"Delete a model": "Delete a model",
+	"Delete All Chats": "",
+	"Delete All Models": "",
+	"Delete chat": "Delete chat",
+	"Delete Chat": "",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "",
+	"Delete tool?": "",
+	"Delete User": "",
+	"Deleted {{deleteModelTag}}": "Deleted {{deleteModelTag}}",
+	"Deleted {{name}}": "",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Description",
+	"Didn't fully follow instructions": "",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "",
+	"Discover a prompt": "Discover a prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Discover, download, and explore custom prompts",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Discover, download, and explore model presets",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Display username instead of You in Chat",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Document",
+	"Documentation": "",
+	"Documents": "Documents",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "does not connect external, data stays safe locally.",
+	"Don't have an account?": "No account? Much sad.",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "",
+	"Done": "",
+	"Download": "",
+	"Download canceled": "",
+	"Download Database": "Download Database",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Drop files here to add to conversation",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "e.g. '30s','10m'. Much time units are 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Edit Wowser",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Enable New Bark Ups",
+	"Enable Web Search": "",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
+	"Enter {{role}} message here": "Enter {{role}} bork here",
+	"Enter a detail about yourself for your LLMs to recall": "",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Enter Overlap of Chunks",
+	"Enter Chunk Size": "Enter Size of Chunk",
+	"Enter description": "",
+	"Enter Github Raw URL": "",
+	"Enter Google PSE API Key": "",
+	"Enter Google PSE Engine Id": "",
+	"Enter Image Size (e.g. 512x512)": "Enter Size of Wow (e.g. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Enter model doge tag (e.g. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "",
+	"Enter Seed": "",
+	"Enter Serper API Key": "",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Enter stop bark",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Enter Top Wow",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Enter URL (e.g. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "",
+	"Enter Your Email": "Enter Your Dogemail",
+	"Enter Your Full Name": "Enter Your Full Wow",
+	"Enter your message": "",
+	"Enter Your Password": "Enter Your Barkword",
+	"Enter Your Role": "",
+	"Enter Your Username": "",
+	"Error": "",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Much Experiment",
+	"Explore the cosmos": "",
+	"Export": "",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Export All Chats (All Doggos)",
+	"Export chat (.json)": "",
+	"Export Chats": "Export Barks",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "",
+	"Export Presets": "",
+	"Export Prompts": "Export Promptos",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "",
+	"Failed to read clipboard contents": "Failed to read clipboard borks",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Bark Mode",
+	"File not found.": "Bark not found.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerprint dogeing: Unable to use initials as avatar. Defaulting to default doge image.",
+	"Fluidly stream large external response chunks": "Fluidly wow big chunks",
+	"Focus chat input": "Focus chat bork",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Woweral",
+	"General Settings": "General Doge Settings",
+	"Generate Image": "",
+	"Generating search query": "",
+	"Generation Info": "",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "",
+	"Google PSE API Key": "",
+	"Google PSE Engine Id": "",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "",
+	"Haptic Feedback": "",
+	"has no conversations.": "",
+	"Hello, {{name}}": "Much helo, {{name}}",
+	"Help": "",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Hide",
+	"Host": "",
+	"How can I help you today?": "How can I halp u today?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Image Wow (Much Experiment)",
+	"Image Generation Engine": "Image Engine",
+	"Image Settings": "Settings for Wowmage",
+	"Images": "Wowmages",
+	"Import Chats": "Import Barks",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "",
+	"Import Presets": "",
+	"Import Prompts": "Import Promptos",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Include `--api` flag when running stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "",
+	"Input commands": "Input commands",
+	"Install from Github URL": "",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Interface",
+	"Invalid file format.": "",
+	"Invalid Tag": "",
+	"January": "",
+	"Jina API Key": "",
+	"join our Discord for help.": "join our Discord for help.",
+	"JSON": "JSON",
+	"JSON Preview": "",
+	"July": "",
+	"June": "",
+	"JWT Expiration": "JWT Expire",
+	"JWT Token": "JWT Borken",
+	"Keep Alive": "Keep Wow",
+	"Key": "",
+	"Keyboard shortcuts": "Keyboard Barkcuts",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Doge Speak",
+	"Last Active": "",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Light",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLMs can make borks. Verify important info.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "",
+	"Made by OpenWebUI Community": "Made by OpenWebUI Community",
+	"Make sure to enclose them with": "Make sure to enclose them with",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "",
+	"March": "",
+	"Max Tokens (num_predict)": "",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum of 3 models can be downloaded simultaneously. Please try again later.",
+	"May": "",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
+	"Min P": "",
+	"Minimum Score": "",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' has been successfully downloaded.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' is already in queue for downloading.",
+	"Model {{modelId}} not found": "Model {{modelId}} not found",
+	"Model {{modelName}} is not vision capable": "",
+	"Model {{name}} is now {{status}}": "",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model filesystem bark detected. Model shortname is required for update, cannot continue.",
+	"Model Filtering": "",
+	"Model ID": "",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model not selected",
+	"Model Params": "",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Modelfile Content",
+	"Models": "Wowdels",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "",
+	"Name": "Name",
+	"Name your knowledge base": "",
+	"New Chat": "New Bark",
+	"New folder": "",
+	"New Password": "New Barkword",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "",
+	"No search query generated": "",
+	"No source available": "No source available",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "",
+	"Not factually correct": "",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
+	"Notes": "",
+	"Notifications": "Notifications",
+	"November": "",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "",
+	"Off": "Off",
+	"Okay, Let's Go!": "Okay, Let's Go!",
+	"OLED Dark": "OLED Dark",
+	"Ollama": "",
+	"Ollama API": "",
+	"Ollama API disabled": "",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama Version",
+	"On": "On",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Only wow characters and hyphens are allowed in the bork string.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Looks like the URL is invalid. Please double-check and try again.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Open new bark",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "",
+	"OpenAI API Key is required.": "OpenAI Bark Key is required.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "",
+	"or": "or",
+	"Organize your users": "",
+	"Other": "",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Barkword",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "",
+	"PDF Extract Images (OCR)": "PDF Extract Wowmages (OCR)",
+	"pending": "pending",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalization",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "Plain text (.txt)",
+	"Playground": "Playground",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "",
+	"Previous 7 days": "",
+	"Profile Image": "",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
+	"Prompt Content": "Prompt Content",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Prompt wowgestions",
+	"Prompt updated successfully": "",
+	"Prompts": "Promptos",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "Pull a wowdel from Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Query Bark",
+	"RAG Template": "RAG Template",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "",
+	"Record voice": "Record Bark",
+	"Redirecting you to OpenWebUI Community": "Redirecting you to OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "",
+	"Regenerate": "",
+	"Release Notes": "Release Borks",
+	"Relevance": "",
+	"Remove": "",
+	"Remove Model": "",
+	"Rename": "",
+	"Reorder Models": "",
+	"Repeat Last N": "Repeat Last N",
+	"Request Mode": "Request Bark",
+	"Reranking Model": "",
+	"Reranking model disabled": "",
+	"Reranking model set to \"{{reranking_model}}\"": "",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Role",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "",
+	"Run": "",
+	"Running": "",
+	"Save": "Save much wow",
+	"Save & Create": "Save & Create much create",
+	"Save & Update": "Save & Update much update",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Saving chat logs in browser storage not support anymore. Pls download and delete your chat logs by click button below. Much easy re-import to backend through",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Search very search",
+	"Search a model": "",
+	"Search Base": "",
+	"Search Chats": "",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "",
+	"Search options": "",
+	"Search Prompts": "Search Prompts much wow",
+	"Search Result Count": "",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "",
+	"Searched {{count}} sites_few": "",
+	"Searched {{count}} sites_many": "",
+	"Searched {{count}} sites_other": "",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "",
+	"See readme.md for instructions": "See readme.md for instructions wow",
+	"See what's new": "See what's new so amaze",
+	"Seed": "Seed very plant",
+	"Select a base model": "",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Select a model much choice",
+	"Select a pipeline": "",
+	"Select a pipeline url": "",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Select model much choice",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "",
+	"Semantic distance to query": "",
+	"Send": "",
+	"Send a Message": "Send a Message much message",
+	"Send message": "Send message very send",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "",
+	"Serper API Key": "",
+	"Serply API Key": "",
+	"Serpstack API Key": "",
+	"Server connection verified": "Server connection verified much secure",
+	"Set as default": "Set as default very default",
+	"Set CFG Scale": "",
+	"Set Default Model": "Set Default Model much model",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "",
+	"Set Image Size": "Set Image Size very size",
+	"Set reranking model (e.g. {{model}})": "",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Set Steps so many steps",
+	"Set Task Model": "",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Set Voice so speak",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Settings much settings",
+	"Settings saved successfully!": "Settings saved successfully! Very success!",
+	"Share": "",
+	"Share Chat": "",
+	"Share to OpenWebUI Community": "Share to OpenWebUI Community much community",
+	"Show": "Show much show",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Show shortcuts much shortcut",
+	"Show your support!": "",
+	"Showcased creativity": "",
+	"Sign in": "Sign in very sign",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Sign Out much logout",
+	"Sign up": "Sign up much join",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Source",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Speech recognition error: {{error}} so error",
+	"Speech-to-Text Engine": "Speech-to-Text Engine much speak",
+	"Stop": "",
+	"Stop Sequence": "Stop Sequence much stop",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT Settings very settings",
+	"Subtitle (e.g. about the Roman Empire)": "",
+	"Success": "Success very success",
+	"Successfully updated.": "Successfully updated. Very updated.",
+	"Suggested": "",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "System very system",
+	"System Instructions": "",
+	"System Prompt": "System Prompt much prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "",
+	"Temperature": "Temperature very temp",
+	"Template": "Template much template",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Text-to-Speech Engine much speak",
+	"Tfs Z": "Tfs Z much Z",
+	"Thanks for your feedback!": "",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Theme much theme",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "This ensures that your valuable conversations are securely saved to your backend database. Thank you! Much secure!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement. Much tip!",
+	"Title": "Title very title",
+	"Title (e.g. Tell me a fun fact)": "",
+	"Title Auto-Generation": "Title Auto-Generation much auto-gen",
+	"Title cannot be an empty string.": "",
+	"Title Generation Prompt": "Title Generation Prompt very prompt",
+	"TLS": "",
+	"To access the available model names for downloading,": "To access the available model names for downloading, much access",
+	"To access the GGUF models available for downloading,": "To access the GGUF models available for downloading, much access",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "",
+	"Toggle settings": "Toggle settings much toggle",
+	"Toggle sidebar": "Toggle sidebar much toggle",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K very top",
+	"Top P": "Top P very top",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Trouble accessing Ollama? Much trouble?",
+	"TTS Model": "",
+	"TTS Settings": "TTS Settings much settings",
+	"TTS Voice": "",
+	"Type": "",
+	"Type Hugging Face Resolve (Download) URL": "Type Hugging Face Resolve (Download) URL much download",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! There was an issue connecting to {{provider}}. Much uh-oh!",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Update password much change",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Upload a GGUF model very upload",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "",
+	"Upload Pipeline": "",
+	"Upload Progress": "Upload Progress much progress",
+	"URL": "",
+	"URL Mode": "URL Mode much mode",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Use Gravatar much avatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Use Initials much initial",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "user much user",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Users much users",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Utilize very use",
+	"Valid time units:": "Valid time units: much time",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "variable very variable",
+	"variable to have them replaced with clipboard content.": "variable to have them replaced with clipboard content. Very replace.",
+	"Version": "Version much version",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
+	"Web": "Web very web",
+	"Web API": "",
+	"Web Loader Settings": "",
+	"Web Search": "",
+	"Web Search Engine": "",
+	"Web Search Query Generation": "",
+	"Webhook URL": "",
+	"WebUI Settings": "WebUI Settings much settings",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "What’s New in much new",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Write a prompt suggestion (e.g. Who are you?) much suggest",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword]. Much summarize.",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "",
+	"You": "",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "",
+	"You have shared this chat": "",
+	"You're a helpful assistant.": "You're a helpful assistant. Much helpful.",
+	"You're now logged in.": "You're now logged in. Much logged.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "",
+	"Youtube Loader Settings": ""
+}
diff --git a/src/lib/i18n/locales/el-GR/translation.json b/src/lib/i18n/locales/el-GR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..69e44b5cd1c5be433ac3ba0eedab4f5aaf91aadf
--- /dev/null
+++ b/src/lib/i18n/locales/el-GR/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(π.χ. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(π.χ. `sh webui.sh --api`)",
+	"(latest)": "(τελευταίο)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Συνομιλίες του {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Απαιτείται Backend",
+	"*Prompt node ID(s) are required for image generation": "*Τα αναγνωριστικά κόμβου Prompt απαιτούνται για τη δημιουργία εικόνων",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Μια νέα έκδοση (v{{LATEST_VERSION}}) είναι τώρα διαθέσιμη.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Ένα μοντέλο εργασίας χρησιμοποιείται κατά την εκτέλεση εργασιών όπως η δημιουργία τίτλων για συνομιλίες και αναζητήσεις στο διαδίκτυο",
+	"a user": "ένας χρήστης",
+	"About": "Σχετικά",
+	"Access": "Πρόσβαση",
+	"Access Control": "Έλεγχος Πρόσβασης",
+	"Accessible to all users": "Προσβάσιμο σε όλους τους χρήστες",
+	"Account": "Λογαριασμός",
+	"Account Activation Pending": "Ενεργοποίηση Λογαριασμού Εκκρεμεί",
+	"Accurate information": "Ακριβείς πληροφορίες",
+	"Actions": "Ενέργειες",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Ενεργοποιήστε αυτή την εντολή πληκτρολογώντας \"/{{COMMAND}}\" στο πεδίο συνομιλίας.",
+	"Active Users": "Ενεργοί Χρήστες",
+	"Add": "Προσθήκη",
+	"Add a model ID": "Προσθήκη αναγνωριστικού μοντέλου",
+	"Add a short description about what this model does": "Προσθήκη σύντομης περιγραφής για το τι κάνει αυτό το μοντέλο",
+	"Add a tag": "Προσθήκη ετικέτας",
+	"Add Arena Model": "Προσθήκη Μοντέλου Arena",
+	"Add Connection": "Προσθήκη Σύνδεσης",
+	"Add Content": "Προσθήκη Περιεχομένου",
+	"Add content here": "Προσθέστε περιεχόμενο εδώ",
+	"Add custom prompt": "Προσθήκη προσαρμοσμένης προτροπής",
+	"Add Files": "Προσθήκη Αρχείων",
+	"Add Group": "Προσθήκη Ομάδας",
+	"Add Memory": "Προσθήκη Μνήμης",
+	"Add Model": "Προσθήκη Μοντέλου",
+	"Add Tag": "Προσθήκη Ετικέτας",
+	"Add Tags": "Προσθήκη Ετικετών",
+	"Add text content": "Προσθήκη κειμένου",
+	"Add User": "Προσθήκη Χρήστη",
+	"Add User Group": "Προσθήκη Ομάδας Χρηστών",
+	"Adjusting these settings will apply changes universally to all users.": "Η ρύθμιση αυτών των παραμέτρων θα εφαρμόσει τις αλλαγές καθολικά σε όλους τους χρήστες.",
+	"admin": "διαχειριστής",
+	"Admin": "Διαχειριστής",
+	"Admin Panel": "Πίνακας Διαχειριστή",
+	"Admin Settings": "Ρυθμίσεις Διαχειριστή",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Οι διαχειριστές έχουν πρόσβαση σε όλα τα εργαλεία ανά πάσα στιγμή· οι χρήστες χρειάζονται εργαλεία ανά μοντέλο στον χώρο εργασίας.",
+	"Advanced Parameters": "Προηγμένοι Παράμετροι",
+	"Advanced Params": "Προηγμένα Παράμετροι",
+	"All chats": "Όλες οι συνομιλίες",
+	"All Documents": "Όλα τα Έγγραφα",
+	"All models deleted successfully": "Όλα τα μοντέλα διαγράφηκαν με επιτυχία",
+	"Allow Chat Delete": "Επιτρέπεται η διαγραφή συνομιλίας",
+	"Allow Chat Deletion": "Επιτρέπεται η Διαγραφή Συνομιλίας",
+	"Allow Chat Edit": "Επιτρέπεται η Επεξεργασία Συνομιλίας",
+	"Allow File Upload": "Επιτρέπεται η Αποστολή Αρχείων",
+	"Allow non-local voices": "Επιτρέπονται μη τοπικές φωνές",
+	"Allow Temporary Chat": "Επιτρέπεται η Προσωρινή Συνομιλία",
+	"Allow User Location": "Επιτρέπεται η Τοποθεσία Χρήστη",
+	"Allow Voice Interruption in Call": "Επιτρέπεται η Παύση Φωνής στην Κλήση",
+	"Already have an account?": "Έχετε ήδη λογαριασμό;",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Εναλλακτικό στο top_p, και στοχεύει στη διασφάλιση μιας ισορροπίας μεταξύ ποιότητας και ποικιλίας. Η παράμετρος p αντιπροσωπεύει την ελάχιστη πιθανότητα για ένα token να θεωρηθεί, σε σχέση με την πιθανότητα του πιο πιθανού token. Για παράδειγμα, με p=0.05 και το πιο πιθανό token να έχει πιθανότητα 0.9, τα logits με τιμή μικρότερη από 0.045 φιλτράρονται. (Προεπιλογή: 0.0)",
+	"Amazing": "Καταπληκτικό",
+	"an assistant": "ένας βοηθός",
+	"and": "και",
+	"and {{COUNT}} more": "και {{COUNT}} ακόμα",
+	"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
+	"API Base URL": "API Βασικό URL",
+	"API Key": "Κλειδί API",
+	"API Key created.": "Το κλειδί API δημιουργήθηκε.",
+	"API keys": "κλειδιά API",
+	"Application DN": "DN Εφαρμογής",
+	"Application DN Password": "Κωδικός DN Εφαρμογής",
+	"applies to all users with the \"user\" role": "εφαρμόζεται σε όλους τους χρήστες με το ρόλο \"user\"",
+	"April": "Απρίλιος",
+	"Archive": "Αρχείο",
+	"Archive All Chats": "Αρχειοθέτηση Όλων των Συνομιλιών",
+	"Archived Chats": "Αρχειοθετημένες Συνομιλίες",
+	"archived-chat-export": "εξαγωγή-αρχείου-συνομιλίας",
+	"Are you sure you want to unarchive all archived chats?": "Είστε σίγουροι ότι θέλετε να απο-αρχειοθετήσετε όλες τις αρχειοθετημένες συνομιλίες;",
+	"Are you sure?": "Είστε σίγουροι;",
+	"Arena Models": "Μοντέλα Arena",
+	"Artifacts": "Αρχεία",
+	"Ask a question": "Ρωτήστε μια ερώτηση",
+	"Assistant": "Βοηθός",
+	"Attach file": "Συνημμένο αρχείο",
+	"Attention to detail": "Προσοχή στη λεπτομέρεια",
+	"Attribute for Username": "Ιδιότητα για Όνομα Χρήστη",
+	"Audio": "Ήχος",
+	"August": "Αύγουστος",
+	"Authenticate": "Επαλήθευση",
+	"Auto-Copy Response to Clipboard": "Αυτόματη Αντιγραφή Απάντησης στο Πρόχειρο",
+	"Auto-playback response": "Αυτόματη αναπαραγωγή της απάντησης",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "Σειρά Επαλήθευσης API AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "Βασικό URL AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Απαιτείται το Βασικό URL AUTOMATIC1111.",
+	"Available list": "Διαθέσιμη λίστα",
+	"available!": "διαθέσιμο!",
+	"Awful": "Ασχημο",
+	"Azure AI Speech": "Ομιλία Azure AI",
+	"Azure Region": "Περιοχή Azure",
+	"Back": "Πίσω",
+	"Bad Response": "Κακή Απάντηση",
+	"Banners": "Προβολές",
+	"Base Model (From)": "Βασικό Μοντέλο (Από)",
+	"Batch Size (num_batch)": "Μέγεθος Παρτίδας (num_batch)",
+	"before": "πριν",
+	"Being lazy": "Τρώλακας",
+	"Bing Search V7 Endpoint": "Τέλος Bing Search V7",
+	"Bing Search V7 Subscription Key": "Κλειδί Συνδρομής Bing Search V7",
+	"Brave Search API Key": "Κλειδί API Brave Search",
+	"By {{name}}": "Από {{name}}",
+	"Bypass SSL verification for Websites": "Παράκαμψη επαλήθευσης SSL για Ιστότοπους",
+	"Call": "Κλήση",
+	"Call feature is not supported when using Web STT engine": "Η λειτουργία κλήσης δεν υποστηρίζεται όταν χρησιμοποιείται η μηχανή Web STT",
+	"Camera": "Κάμερα",
+	"Cancel": "Ακύρωση",
+	"Capabilities": "Δυνατότητες",
+	"Certificate Path": "Διαδρομή Πιστοποιητικού",
+	"Change Password": "Αλλαγή Κωδικού",
+	"Character": "Χαρακτήρας",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Σχεδιάστε νέους ορίζοντες",
+	"Chat": "Συνομιλία",
+	"Chat Background Image": "Εικόνα Φόντου Συνομιλίας",
+	"Chat Bubble UI": "Διεπαφή Φούσκας Συνομιλίας",
+	"Chat Controls": "Έλεγχοι Συνομιλίας",
+	"Chat direction": "Κατεύθυνση Συνομιλίας",
+	"Chat Overview": "Επισκόπηση Συνομιλίας",
+	"Chat Permissions": "Δικαιώματα Συνομιλίας",
+	"Chat Tags Auto-Generation": "Αυτόματη Γενιά Ετικετών Συνομιλίας",
+	"Chats": "Συνομιλίες",
+	"Check Again": "Ελέγξτε ξανά",
+	"Check for updates": "Έλεγχος για ενημερώσεις",
+	"Checking for updates...": "Ελέγχεται για ενημερώσεις...",
+	"Choose a model before saving...": "Επιλέξτε ένα μοντέλο πριν αποθηκεύσετε...",
+	"Chunk Overlap": "Επικάλυψη Τμημάτων",
+	"Chunk Params": "Παράμετροι Τμημάτων",
+	"Chunk Size": "Μέγεθος Τμημάτων",
+	"Ciphers": "Κρυπτογραφήσεις",
+	"Citation": "Παράθεση",
+	"Clear memory": "Καθαρισμός μνήμης",
+	"click here": "κλικ εδώ",
+	"Click here for filter guides.": "Κάντε κλικ εδώ για οδηγούς φίλτρων.",
+	"Click here for help.": "Κάντε κλικ εδώ για βοήθεια.",
+	"Click here to": "Κάντε κλικ εδώ για να",
+	"Click here to download user import template file.": "Κάντε κλικ εδώ για να κατεβάσετε το αρχείο προτύπου εισαγωγής χρήστη.",
+	"Click here to learn more about faster-whisper and see the available models.": "Κάντε κλικ εδώ για να μάθετε περισσότερα σχετικά με το faster-whisper και να δείτε τα διαθέσιμα μοντέλα.",
+	"Click here to select": "Κάντε κλικ εδώ για επιλογή",
+	"Click here to select a csv file.": "Κάντε κλικ εδώ για να επιλέξετε ένα αρχείο csv.",
+	"Click here to select a py file.": "Κάντε κλικ εδώ για να επιλέξετε ένα αρχείο py.",
+	"Click here to upload a workflow.json file.": "Κάντε κλικ εδώ για να ανεβάσετε ένα αρχείο workflow.json.",
+	"click here.": "κλικ εδώ.",
+	"Click on the user role button to change a user's role.": "Κάντε κλικ στο κουμπί ρόλου χρήστη για να αλλάξετε το ρόλο ενός χρήστη.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Άρνηση δικαιώματος εγγραφής στο πρόχειρο. Παρακαλώ ελέγξτε τις ρυθμίσεις του περιηγητή σας για να δώσετε την απαραίτητη πρόσβαση.",
+	"Clone": "Κλώνος",
+	"Close": "Κλείσιμο",
+	"Code execution": "Εκτέλεση κώδικα",
+	"Code formatted successfully": "Ο κώδικας μορφοποιήθηκε επιτυχώς",
+	"Collection": "Συλλογή",
+	"Color": "Χρώμα",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "Βασικό URL ComfyUI",
+	"ComfyUI Base URL is required.": "Απαιτείται το Βασικό URL ComfyUI.",
+	"ComfyUI Workflow": "Ροές Εργασίας ComfyUI",
+	"ComfyUI Workflow Nodes": "Κόμβοι Ροής Εργασίας ComfyUI",
+	"Command": "Εντολή",
+	"Completions": "Ολοκληρώσεις",
+	"Concurrent Requests": "Ταυτόχρονες Αιτήσεις",
+	"Configure": "Διαμόρφωση",
+	"Configure Models": "Διαμόρφωση Μοντέλων",
+	"Confirm": "Επιβεβαίωση",
+	"Confirm Password": "Επιβεβαίωση Κωδικού",
+	"Confirm your action": "Επιβεβαιώστε την ενέργειά σας",
+	"Connections": "Συνδέσεις",
+	"Contact Admin for WebUI Access": "Επικοινωνήστε με τον Διαχειριστή για Πρόσβαση στο WebUI",
+	"Content": "Περιεχόμενο",
+	"Content Extraction": "Εξαγωγή Περιεχομένου",
+	"Context Length": "Μήκος Πλαισίου",
+	"Continue Response": "Συνέχεια Απάντησης",
+	"Continue with {{provider}}": "Συνέχεια με {{provider}}",
+	"Continue with Email": "Συνέχεια με Email",
+	"Continue with LDAP": "Συνέχεια με LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Έλεγχος πώς διαχωρίζεται το κείμενο του μηνύματος για αιτήματα TTS. Το 'Στίξη' διαχωρίζει σε προτάσεις, οι 'παραγράφοι' σε παραγράφους, και το 'κανένα' κρατά το μήνυμα ως μια αλυσίδα.",
+	"Controls": "Έλεγχοι",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Διαχειρίζεται την ισορροπία μεταξύ συνεκτικότητας και ποικιλίας της εξόδου. Μια χαμηλότερη τιμή θα έχει ως αποτέλεσμα πιο εστιασμένο και συνεκτικό κείμενο. (Προεπιλογή: 5.0)",
+	"Copied": "Αντιγράφηκε",
+	"Copied shared chat URL to clipboard!": "Αντιγράφηκε το URL της κοινόχρηστης συνομιλίας στο πρόχειρο!",
+	"Copied to clipboard": "Αντιγράφηκε στο πρόχειρο",
+	"Copy": "Αντιγραφή",
+	"Copy last code block": "Αντιγραφή τελευταίου μπλοκ κώδικα",
+	"Copy last response": "Αντιγραφή τελευταίας απάντησης",
+	"Copy Link": "Αντιγραφή Συνδέσμου",
+	"Copy to clipboard": "Αντιγραφή στο πρόχειρο",
+	"Copying to clipboard was successful!": "Η αντιγραφή στο πρόχειρο ήταν επιτυχής!",
+	"Create": "Δημιουργία",
+	"Create a knowledge base": "Δημιουργία βάσης γνώσης",
+	"Create a model": "Δημιουργία μοντέλου",
+	"Create Account": "Δημιουργία Λογαριασμού",
+	"Create Admin Account": "Δημιουργία Λογαριασμού Διαχειριστή",
+	"Create Group": "Δημιουργία Ομάδας",
+	"Create Knowledge": "Δημιουργία Γνώσης",
+	"Create new key": "Δημιουργία νέου κλειδιού",
+	"Create new secret key": "Δημιουργία νέου μυστικού κλειδιού",
+	"Created at": "Δημιουργήθηκε στις",
+	"Created At": "Δημιουργήθηκε στις",
+	"Created by": "Δημιουργήθηκε από",
+	"CSV Import": "Εισαγωγή CSV",
+	"Current Model": "Τρέχον Μοντέλο",
+	"Current Password": "Τρέχων Κωδικός",
+	"Custom": "Προσαρμοσμένο",
+	"Dark": "Σκούρο",
+	"Database": "Βάση Δεδομένων",
+	"December": "Δεκέμβριος",
+	"Default": "Προεπιλογή",
+	"Default (Open AI)": "Προεπιλογή (Open AI)",
+	"Default (SentenceTransformers)": "Προεπιλογή (SentenceTransformers)",
+	"Default Model": "Προεπιλεγμένο Μοντέλο",
+	"Default model updated": "Το προεπιλεγμένο μοντέλο ενημερώθηκε",
+	"Default Models": "Προεπιλεγμένα Μοντέλα",
+	"Default permissions": "Προεπιλεγμένα δικαιώματα",
+	"Default permissions updated successfully": "Τα προεπιλεγμένα δικαιώματα ενημερώθηκαν με επιτυχία",
+	"Default Prompt Suggestions": "Προεπιλεγμένες Προτάσεις Προτροπής",
+	"Default to 389 or 636 if TLS is enabled": "Προεπιλογή στο 389 ή 636 εάν είναι ενεργοποιημένο το TLS",
+	"Default to ALL": "Προεπιλογή σε ΟΛΑ",
+	"Default User Role": "Προεπιλεγμένος Ρόλος Χρήστη",
+	"Delete": "Διαγραφή",
+	"Delete a model": "Διαγραφή ενός μοντέλου",
+	"Delete All Chats": "Διαγραφή Όλων των Συνομιλιών",
+	"Delete All Models": "Διαγραφή Όλων των Μοντέλων",
+	"Delete chat": "Διαγραφή συνομιλίας",
+	"Delete Chat": "Διαγραφή Συνομιλίας",
+	"Delete chat?": "Διαγραφή συνομιλίας;",
+	"Delete folder?": "Διαγραφή φακέλου;",
+	"Delete function?": "Διαγραφή λειτουργίας;",
+	"Delete prompt?": "Διαγραφή προτροπής;",
+	"delete this link": "διαγραφή αυτού του συνδέσμου",
+	"Delete tool?": "Διαγραφή εργαλείου;",
+	"Delete User": "Διαγραφή Χρήστη",
+	"Deleted {{deleteModelTag}}": "Διαγράφηκε το {{deleteModelTag}}",
+	"Deleted {{name}}": "Διαγράφηκε το {{name}}",
+	"Deleted User": "Διαγράφηκε ο Χρήστης",
+	"Describe your knowledge base and objectives": "Περιγράψτε τη βάση γνώσης και τους στόχους σας",
+	"Description": "Περιγραφή",
+	"Didn't fully follow instructions": "Δεν ακολούθησε πλήρως τις οδηγίες",
+	"Disabled": "Απενεργοποιημένο",
+	"Discover a function": "Ανακάλυψη λειτουργίας",
+	"Discover a model": "Ανακάλυψη μοντέλου",
+	"Discover a prompt": "Ανακάλυψη προτροπής",
+	"Discover a tool": "Ανακάλυψη εργαλείου",
+	"Discover wonders": "Ανακάλυψη θαυμάτων",
+	"Discover, download, and explore custom functions": "Ανακαλύψτε, κατεβάστε και εξερευνήστε προσαρμοσμένες λειτουργίες",
+	"Discover, download, and explore custom prompts": "Ανακαλύψτε, κατεβάστε και εξερευνήστε προσαρμοσμένες προτροπές",
+	"Discover, download, and explore custom tools": "Ανακαλύψτε, κατεβάστε και εξερευνήστε προσαρμοσμένα εργαλεία",
+	"Discover, download, and explore model presets": "Ανακαλύψτε, κατεβάστε και εξερευνήστε προκαθορισμένα μοντέλα",
+	"Dismissible": "Αποκλειστέο",
+	"Display": "Εμφάνιση",
+	"Display Emoji in Call": "Εμφάνιση Emoji στην Κλήση",
+	"Display the username instead of You in the Chat": "Εμφάνιση του ονόματος χρήστη αντί του Εσάς στη Συνομιλία",
+	"Displays citations in the response": "Εμφανίζει αναφορές στην απάντηση",
+	"Dive into knowledge": "Βυθιστείτε στη γνώση",
+	"Do not install functions from sources you do not fully trust.": "Μην εγκαθιστάτε λειτουργίες από πηγές που δεν εμπιστεύεστε πλήρως.",
+	"Do not install tools from sources you do not fully trust.": "Μην εγκαθιστάτε εργαλεία από πηγές που δεν εμπιστεύεστε πλήρως.",
+	"Document": "Έγγραφο",
+	"Documentation": "Τεκμηρίωση",
+	"Documents": "Έγγραφα",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "δεν κάνει καμία εξωτερική σύνδεση, και τα δεδομένα σας παραμένουν ασφαλή στον τοπικά φιλοξενούμενο διακομιστή σας.",
+	"Don't have an account?": "Δεν έχετε λογαριασμό;",
+	"don't install random functions from sources you don't trust.": "μην εγκαθιστάτε τυχαίες λειτουργίες από πηγές που δεν εμπιστεύεστε.",
+	"don't install random tools from sources you don't trust.": "μην εγκαθιστάτε τυχαία εργαλεία από πηγές που δεν εμπιστεύεστε.",
+	"Don't like the style": "Δεν σας αρέσει το στυλ",
+	"Done": "Έτοιμο",
+	"Download": "Λήψη",
+	"Download canceled": "Η λήψη ακυρώθηκε",
+	"Download Database": "Λήψη Βάσης Δεδομένων",
+	"Drag and drop a file to upload or select a file to view": "Σύρετε και αφήστε ένα αρχείο για να το ανεβάσετε ή επιλέξτε ένα αρχείο για να το δείτε",
+	"Draw": "Σχεδίαση",
+	"Drop any files here to add to the conversation": "Αφήστε οποιαδήποτε αρχεία εδώ για να προστεθούν στη συνομιλία",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "π.χ. '30s','10m'. Οι έγκυρες μονάδες χρόνου είναι 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "π.χ. Ένα φίλτρο για να αφαιρέσετε βρισιές από το κείμενο",
+	"e.g. My Filter": "π.χ. Το Φίλτρου Μου",
+	"e.g. My Tools": "π.χ. Τα Εργαλεία Μου",
+	"e.g. my_filter": "π.χ. my_filter",
+	"e.g. my_tools": "π.χ. my_tools",
+	"e.g. Tools for performing various operations": "π.χ. Εργαλεία για την εκτέλεση διάφορων λειτουργιών",
+	"Edit": "Επεξεργασία",
+	"Edit Arena Model": "Επεξεργασία Μοντέλου Arena",
+	"Edit Connection": "Επεξεργασία Σύνδεσης",
+	"Edit Default Permissions": "Επεξεργασία Προεπιλεγμένων Δικαιωμάτων",
+	"Edit Memory": "Επεξεργασία Μνήμης",
+	"Edit User": "Επεξεργασία Χρήστη",
+	"Edit User Group": "Επεξεργασία Ομάδας Χρηστών",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Email",
+	"Embark on adventures": "Ξεκινήστε περιπέτειες",
+	"Embedding Batch Size": "Μέγεθος Παρτίδας Ενσωμάτωσης",
+	"Embedding Model": "Μοντέλο Ενσωμάτωσης",
+	"Embedding Model Engine": "Μηχανή Μοντέλου Ενσωμάτωσης",
+	"Embedding model set to \"{{embedding_model}}\"": "Το μοντέλο ενσωμάτωσης έχει οριστεί σε \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Ενεργοποίηση Επαλήθευσης Κλειδιού API",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Ενεργοποίηση Κοινοτικής Κοινής Χρήσης",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Ενεργοποίηση Κλείδωσης Μνήμης (mlock) για την αποτροπή της ανταλλαγής δεδομένων του μοντέλου από τη μνήμη RAM. Αυτή η επιλογή κλειδώνει το σύνολο εργασίας των σελίδων του μοντέλου στη μνήμη RAM, διασφαλίζοντας ότι δεν θα ανταλλαχθούν στο δίσκο. Αυτό μπορεί να βοηθήσει στη διατήρηση της απόδοσης αποφεύγοντας σφάλματα σελίδων και διασφαλίζοντας γρήγορη πρόσβαση στα δεδομένα.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Ενεργοποίηση Χαρτογράφησης Μνήμης (mmap) για φόρτωση δεδομένων μοντέλου. Αυτή η επιλογή επιτρέπει στο σύστημα να χρησιμοποιεί αποθήκευση δίσκου ως επέκταση της μνήμης RAM, αντιμετωπίζοντας αρχεία δίσκου σαν να ήταν στη μνήμη RAM. Αυτό μπορεί να βελτιώσει την απόδοση του μοντέλου επιτρέποντας γρηγορότερη πρόσβαση στα δεδομένα. Ωστόσο, μπορεί να μην λειτουργεί σωστά με όλα τα συστήματα και να καταναλώνει σημαντικό χώρο στο δίσκο.",
+	"Enable Message Rating": "Ενεργοποίηση Αξιολόγησης Μηνυμάτων",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Ενεργοποίηση δειγματοληψίας Mirostat για έλεγχο της περιπλοκότητας. (Προεπιλογή: 0, 0 = Απενεργοποιημένο, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Ενεργοποίηση Νέων Εγγραφών",
+	"Enable Web Search": "Ενεργοποίηση Αναζήτησης στο Διαδίκτυο",
+	"Enabled": "Ενεργοποιημένο",
+	"Engine": "Μηχανή",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Βεβαιωθείτε ότι το αρχείο CSV σας περιλαμβάνει 4 στήλες με αυτή τη σειρά: Όνομα, Email, Κωδικός, Ρόλος.",
+	"Enter {{role}} message here": "Εισάγετε το μήνυμα {{role}} εδώ",
+	"Enter a detail about yourself for your LLMs to recall": "Εισάγετε μια λεπτομέρεια για τον εαυτό σας ώστε τα LLMs να την ανακαλούν",
+	"Enter api auth string (e.g. username:password)": "Εισάγετε τη σειρά επαλήθευσης api (π.χ. username:password)",
+	"Enter Application DN": "Εισάγετε DN Εφαρμογής",
+	"Enter Application DN Password": "Εισάγετε Κωδικό DN Εφαρμογής",
+	"Enter Bing Search V7 Endpoint": "Εισάγετε το Τέλος Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Εισάγετε το Κλειδί Συνδρομής Bing Search V7",
+	"Enter Brave Search API Key": "Εισάγετε το Κλειδί API Brave Search",
+	"Enter certificate path": "Εισάγετε τη διαδρομή πιστοποιητικού",
+	"Enter CFG Scale (e.g. 7.0)": "Εισάγετε το CFG Scale (π.χ. 7.0)",
+	"Enter Chunk Overlap": "Εισάγετε την Επικάλυψη Τμημάτων",
+	"Enter Chunk Size": "Εισάγετε το Μέγεθος Τμημάτων",
+	"Enter description": "Εισάγετε την περιγραφή",
+	"Enter Github Raw URL": "Εισάγετε το Github Raw URL",
+	"Enter Google PSE API Key": "Εισάγετε το Κλειδί API Google PSE",
+	"Enter Google PSE Engine Id": "Εισάγετε το Αναγνωριστικό Μηχανής Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Εισάγετε το Μέγεθος Εικόνας (π.χ. 512x512)",
+	"Enter Jina API Key": "Εισάγετε το Κλειδί API Jina",
+	"Enter language codes": "Εισάγετε κωδικούς γλώσσας",
+	"Enter Model ID": "Εισάγετε το ID Μοντέλου",
+	"Enter model tag (e.g. {{modelTag}})": "Εισάγετε την ετικέτα μοντέλου (π.χ. {{modelTag}})",
+	"Enter Mojeek Search API Key": "Εισάγετε το Κλειδί API Mojeek Search",
+	"Enter Number of Steps (e.g. 50)": "Εισάγετε τον Αριθμό Βημάτων (π.χ. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Εισάγετε τον Sampler (π.χ. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Εισάγετε τον Scheduler (π.χ. Karras)",
+	"Enter Score": "Εισάγετε το Score",
+	"Enter SearchApi API Key": "Εισάγετε το Κλειδί API SearchApi",
+	"Enter SearchApi Engine": "Εισάγετε τη Μηχανή SearchApi",
+	"Enter Searxng Query URL": "Εισάγετε το URL Ερώτησης Searxng",
+	"Enter Seed": "Εισάγετε το Seed",
+	"Enter Serper API Key": "Εισάγετε το Κλειδί API Serper",
+	"Enter Serply API Key": "Εισάγετε το Κλειδί API Serply",
+	"Enter Serpstack API Key": "Εισάγετε το Κλειδί API Serpstack",
+	"Enter server host": "Εισάγετε τον διακομιστή host",
+	"Enter server label": "Εισάγετε την ετικέτα διακομιστή",
+	"Enter server port": "Εισάγετε την θύρα διακομιστή",
+	"Enter stop sequence": "Εισάγετε τη σειρά παύσης",
+	"Enter system prompt": "Εισάγετε την προτροπή συστήματος",
+	"Enter Tavily API Key": "Εισάγετε το Κλειδί API Tavily",
+	"Enter Tika Server URL": "Εισάγετε το URL διακομιστή Tika",
+	"Enter Top K": "Εισάγετε το Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Εισάγετε το URL (π.χ. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Εισάγετε το URL (π.χ. http://localhost:11434)",
+	"Enter Your Email": "Εισάγετε το Email σας",
+	"Enter Your Full Name": "Εισάγετε το Πλήρες Όνομά σας",
+	"Enter your message": "Εισάγετε το μήνυμά σας",
+	"Enter Your Password": "Εισάγετε τον Κωδικό σας",
+	"Enter Your Role": "Εισάγετε τον Ρόλο σας",
+	"Enter Your Username": "Εισάγετε το Όνομα Χρήστη σας",
+	"Error": "Σφάλμα",
+	"ERROR": "ΣΦΑΛΜΑ",
+	"Evaluations": "Αξιολογήσεις",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Παράδειγμα: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Παράδειγμα: ALL",
+	"Example: ou=users,dc=foo,dc=example": "Παράδειγμα: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Παράδειγμα: sAMAccountName ή uid ή userPrincipalName",
+	"Exclude": "Εξαίρεση",
+	"Experimental": "Πειραματικό",
+	"Explore the cosmos": "Εξερευνήστε το σύμπαν",
+	"Export": "Εξαγωγή",
+	"Export All Archived Chats": "Εξαγωγή Όλων των Αρχειοθετημένων Συνομιλιών",
+	"Export All Chats (All Users)": "Εξαγωγή Όλων των Συνομιλιών (Όλοι οι Χρήστες)",
+	"Export chat (.json)": "Εξαγωγή συνομιλίας (.json)",
+	"Export Chats": "Εξαγωγή Συνομιλιών",
+	"Export Config to JSON File": "Εξαγωγή Διαμόρφωσης σε Αρχείο JSON",
+	"Export Functions": "Εξαγωγή Λειτουργιών",
+	"Export Models": "Εξαγωγή Μοντέλων",
+	"Export Presets": "Εξαγωγή Προκαθορισμένων",
+	"Export Prompts": "Εξαγωγή Προτροπών",
+	"Export to CSV": "Εξαγωγή σε CSV",
+	"Export Tools": "Εξαγωγή Εργαλείων",
+	"External Models": "Εξωτερικά Μοντέλα",
+	"Failed to add file.": "Αποτυχία προσθήκης αρχείου.",
+	"Failed to create API Key.": "Αποτυχία δημιουργίας Κλειδιού API.",
+	"Failed to read clipboard contents": "Αποτυχία ανάγνωσης περιεχομένων πρόχειρου",
+	"Failed to save models configuration": "Αποτυχία αποθήκευσης ρυθμίσεων μοντέλων",
+	"Failed to update settings": "Αποτυχία ενημέρωσης ρυθμίσεων",
+	"Failed to upload file.": "Αποτυχία ανεβάσματος αρχείου.",
+	"February": "Φεβρουάριος",
+	"Feedback History": "Ιστορικό Ανατροφοδότησης",
+	"Feedbacks": "Ανατροφοδοτήσεις",
+	"Feel free to add specific details": "Νιώστε ελεύθεροι να προσθέσετε συγκεκριμένες λεπτομέρειες",
+	"File": "Αρχείο",
+	"File added successfully.": "Το αρχείο προστέθηκε με επιτυχία.",
+	"File content updated successfully.": "Το περιεχόμενο του αρχείου ενημερώθηκε με επιτυχία.",
+	"File Mode": "Λειτουργία Αρχείου",
+	"File not found.": "Αρχείο δεν βρέθηκε.",
+	"File removed successfully.": "Το αρχείο αφαιρέθηκε με επιτυχία.",
+	"File size should not exceed {{maxSize}} MB.": "Το μέγεθος του αρχείου δεν πρέπει να υπερβαίνει τα {{maxSize}} MB.",
+	"Files": "Αρχεία",
+	"Filter is now globally disabled": "Το φίλτρο είναι τώρα καθολικά απενεργοποιημένο",
+	"Filter is now globally enabled": "Το φίλτρο είναι τώρα καθολικά ενεργοποιημένο",
+	"Filters": "Φίλτρα",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Εντοπίστηκε spoofing δακτυλικού αποτυπώματος: Αδυναμία χρήσης αρχικών ως avatar. Χρήση της προεπιλεγμένης εικόνας προφίλ.",
+	"Fluidly stream large external response chunks": "Ροή μεγάλων εξωτερικών τμημάτων απάντησης ομαλά",
+	"Focus chat input": "Εστίαση στο πεδίο συνομιλίας",
+	"Folder deleted successfully": "Ο φάκελος διαγράφηκε με επιτυχία",
+	"Folder name cannot be empty": "Το όνομα του φακέλου δεν μπορεί να είναι κενό",
+	"Folder name cannot be empty.": "Το όνομα του φακέλου δεν μπορεί να είναι κενό.",
+	"Folder name updated successfully": "Το όνομα του φακέλου ενημερώθηκε με επιτυχία",
+	"Followed instructions perfectly": "Ακολούθησε τις οδηγίες τέλεια",
+	"Forge new paths": "Δημιουργήστε νέες διαδρομές",
+	"Form": "Φόρμα",
+	"Format your variables using brackets like this:": "Μορφοποιήστε τις μεταβλητές σας χρησιμοποιώντας αγκύλες όπως αυτό:",
+	"Frequency Penalty": "Ποινή Συχνότητας",
+	"Function": "Λειτουργία",
+	"Function created successfully": "Η λειτουργία δημιουργήθηκε με επιτυχία",
+	"Function deleted successfully": "Η λειτουργία διαγράφηκε με επιτυχία",
+	"Function Description": "Περιγραφή Λειτουργίας",
+	"Function ID": "ID Λειτουργίας",
+	"Function is now globally disabled": "Η λειτουργία είναι τώρα καθολικά απενεργοποιημένη",
+	"Function is now globally enabled": "Η λειτουργία είναι τώρα καθολικά ενεργοποιημένη",
+	"Function Name": "Όνομα Λειτουργίας",
+	"Function updated successfully": "Η λειτουργία ενημερώθηκε με επιτυχία",
+	"Functions": "Λειτουργίες",
+	"Functions allow arbitrary code execution": "Οι λειτουργίες επιτρέπουν την εκτέλεση αυθαίρετου κώδικα",
+	"Functions allow arbitrary code execution.": "Οι λειτουργίες επιτρέπουν την εκτέλεση αυθαίρετου κώδικα.",
+	"Functions imported successfully": "Οι λειτουργίες εισήχθησαν με επιτυχία",
+	"General": "Γενικά",
+	"General Settings": "Γενικές Ρυθμίσεις",
+	"Generate Image": "Δημιουργία Εικόνας",
+	"Generating search query": "Γενιά αναζήτησης ερώτησης",
+	"Generation Info": "Πληροφορίες Γενιάς",
+	"Get started": "Ξεκινήστε",
+	"Get started with {{WEBUI_NAME}}": "Ξεκινήστε με {{WEBUI_NAME}}",
+	"Global": "Καθολικό",
+	"Good Response": "Καλή Απάντηση",
+	"Google PSE API Key": "Κλειδί API Google PSE",
+	"Google PSE Engine Id": "Αναγνωριστικό Μηχανής Google PSE",
+	"Group created successfully": "Η ομάδα δημιουργήθηκε με επιτυχία",
+	"Group deleted successfully": "Η ομάδα διαγράφηκε με επιτυχία",
+	"Group Description": "Περιγραφή Ομάδας",
+	"Group Name": "Όνομα Ομάδας",
+	"Group updated successfully": "Η ομάδα ενημερώθηκε με επιτυχία",
+	"Groups": "Ομάδες",
+	"h:mm a": "h:mm π.μ./μ.μ.",
+	"Haptic Feedback": "Ανατροφοδότηση Haptic",
+	"has no conversations.": "δεν έχει συνομιλίες.",
+	"Hello, {{name}}": "Γειά σου, {{name}}",
+	"Help": "Βοήθεια",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Βοηθήστε μας να δημιουργήσουμε την καλύτερη κατάταξη κοινότητας μοιράζοντας το ιστορικό ανατροφοδότησής σας!",
+	"Hex Color": "Χρώμα Hex",
+	"Hex Color - Leave empty for default color": "Χρώμα Hex - Αφήστε κενό για προεπιλεγμένο χρώμα",
+	"Hide": "Απόκρυψη",
+	"Host": "Διακομιστής",
+	"How can I help you today?": "Πώς μπορώ να σας βοηθήσω σήμερα;",
+	"How would you rate this response?": "Πώς θα βαθμολογούσατε αυτή την απάντηση;",
+	"Hybrid Search": "Υβριδική Αναζήτηση",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Αναγνωρίζω ότι έχω διαβάσει και κατανοώ τις συνέπειες της ενέργειάς μου. Γνωρίζω τους κινδύνους που σχετίζονται με την εκτέλεση αυθαίρετου κώδικα και έχω επαληθεύσει την αξιοπιστία της πηγής.",
+	"ID": "ID",
+	"Ignite curiosity": "Ξύπνημα της περιέργειας",
+	"Image Generation (Experimental)": "Δημιουργία Εικόνας (Πειραματικό)",
+	"Image Generation Engine": "Μηχανή Δημιουργίας Εικόνας",
+	"Image Settings": "Ρυθμίσεις Εικόνας",
+	"Images": "Εικόνες",
+	"Import Chats": "Εισαγωγή Συνομιλιών",
+	"Import Config from JSON File": "Εισαγωγή Διαμόρφωσης από Αρχείο JSON",
+	"Import Functions": "Εισαγωγή Λειτουργιών",
+	"Import Models": "Εισαγωγή Μοντέλων",
+	"Import Presets": "Εισαγωγή Προκαθορισμένων",
+	"Import Prompts": "Εισαγωγή Προτροπών",
+	"Import Tools": "Εισαγωγή Εργαλείων",
+	"Include": "Συμπερίληψη",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Συμπεριλάβετε το flag `--api-auth` όταν τρέχετε το stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Συμπεριλάβετε το flag `--api` όταν τρέχετε το stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Επηρεάζει πόσο γρήγορα ανταποκρίνεται ο αλγόριθμος στην ανατροφοδότηση από το παραγόμενο κείμενο. Μια χαμηλότερη ταχύτητα μάθησης θα έχει ως αποτέλεσμα πιο αργές προσαρμογές, ενώ μια υψηλότερη ταχύτητα μάθησης θα κάνει τον αλγόριθμο πιο ανταποκρινόμενο. (Προεπιλογή: 0.1)",
+	"Info": "Πληροφορίες",
+	"Input commands": "Εισαγωγή εντολών",
+	"Install from Github URL": "Εγκατάσταση από URL Github",
+	"Instant Auto-Send After Voice Transcription": "Άμεση Αυτόματη Αποστολή μετά τη μεταγραφή φωνής",
+	"Interface": "Διεπαφή",
+	"Invalid file format.": "Μη έγκυρη μορφή αρχείου.",
+	"Invalid Tag": "Μη έγκυρη Ετικέτα",
+	"January": "Ιανουάριος",
+	"Jina API Key": "Κλειδί API Jina",
+	"join our Discord for help.": "συμμετέχετε στο Discord μας για βοήθεια.",
+	"JSON": "JSON",
+	"JSON Preview": "Προεπισκόπηση JSON",
+	"July": "Ιούλιος",
+	"June": "Ιούνιος",
+	"JWT Expiration": "Λήξη JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Διατήρηση Ζωντανής Σύνδεσης",
+	"Key": "Κλειδί",
+	"Keyboard shortcuts": "Συντομεύσεις Πληκτρολογίου",
+	"Knowledge": "Γνώση",
+	"Knowledge Access": "Πρόσβαση στη Γνώση",
+	"Knowledge created successfully.": "Η γνώση δημιουργήθηκε με επιτυχία.",
+	"Knowledge deleted successfully.": "Η γνώση διαγράφηκε με επιτυχία.",
+	"Knowledge reset successfully.": "Η γνώση επαναφέρθηκε με επιτυχία.",
+	"Knowledge updated successfully": "Η γνώση ενημερώθηκε με επιτυχία",
+	"Label": "Ετικέτα",
+	"Landing Page Mode": "Λειτουργία Σελίδας Άφιξης",
+	"Language": "Γλώσσα",
+	"Last Active": "Τελευταία Ενεργή",
+	"Last Modified": "Τελευταία Τροποποίηση",
+	"LDAP": "LDAP",
+	"LDAP server updated": "Ο διακομιστής LDAP ενημερώθηκε",
+	"Leaderboard": "Κατάταξη",
+	"Leave empty for unlimited": "Αφήστε κενό για απεριόριστο",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Αφήστε κενό για να συμπεριλάβετε όλα τα μοντέλα από το endpoint \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Αφήστε κενό για να συμπεριλάβετε όλα τα μοντέλα από το endpoint \"{{URL}}/models\"",
+	"Leave empty to include all models or select specific models": "Αφήστε κενό για να χρησιμοποιήσετε όλα τα μοντέλα ή επιλέξτε συγκεκριμένα μοντέλα",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Αφήστε κενό για να χρησιμοποιήσετε την προεπιλεγμένη προτροπή, ή εισάγετε μια προσαρμοσμένη προτροπή",
+	"Light": "Φως",
+	"Listening...": "Ακούγεται...",
+	"LLMs can make mistakes. Verify important information.": "Τα LLM μπορούν να κάνουν λάθη. Επαληθεύστε σημαντικές πληροφορίες.",
+	"Local": "Τοπικό",
+	"Local Models": "Τοπικά Μοντέλα",
+	"Lost": "Χαμένος",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Δημιουργήθηκε από την Κοινότητα OpenWebUI",
+	"Make sure to enclose them with": "Βεβαιωθείτε ότι τα περικλείετε με",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Βεβαιωθείτε ότι εξάγετε ένα αρχείο workflow.json ως μορφή API από το ComfyUI.",
+	"Manage": "Διαχείριση",
+	"Manage Arena Models": "Διαχείριση Μοντέλων Arena",
+	"Manage Ollama": "Διαχείριση Ollama",
+	"Manage Ollama API Connections": "Διαχείριση Συνδέσεων API Ollama",
+	"Manage OpenAI API Connections": "Διαχείριση Συνδέσεων API OpenAI",
+	"Manage Pipelines": "Διαχείριση Καναλιών",
+	"March": "Μάρτιος",
+	"Max Tokens (num_predict)": "Μέγιστος Αριθμός Tokens (num_predict)",
+	"Max Upload Count": "Μέγιστος Αριθμός Ανεβάσματος",
+	"Max Upload Size": "Μέγιστο Μέγεθος Αρχείου",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Μέγιστο των 3 μοντέλων μπορούν να κατεβούν ταυτόχρονα. Παρακαλώ δοκιμάστε ξανά αργότερα.",
+	"May": "Μάιος",
+	"Memories accessible by LLMs will be shown here.": "Οι αναμνήσεις προσβάσιμες από τα LLMs θα εμφανιστούν εδώ.",
+	"Memory": "Μνήμη",
+	"Memory added successfully": "Η μνήμη προστέθηκε με επιτυχία",
+	"Memory cleared successfully": "Η μνήμη καθαρίστηκε με επιτυχία",
+	"Memory deleted successfully": "Η μνήμη διαγράφηκε με επιτυχία",
+	"Memory updated successfully": "Η μνήμη ενημερώθηκε με επιτυχία",
+	"Merge Responses": "Συγχώνευση Απαντήσεων",
+	"Message rating should be enabled to use this feature": "Η αξιολόγηση μηνυμάτων πρέπει να είναι ενεργοποιημένη για να χρησιμοποιήσετε αυτή τη λειτουργία",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Τα μηνύματα που στέλνετε μετά τη δημιουργία του συνδέσμου σας δεν θα κοινοποιηθούν. Οι χρήστες με το URL θα μπορούν να δουν τη συνομιλία που μοιραστήκατε.",
+	"Min P": "Min P",
+	"Minimum Score": "Ελάχιστο Score",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Μοντέλο",
+	"Model '{{modelName}}' has been successfully downloaded.": "Το μοντέλο '{{modelName}}' κατεβάστηκε με επιτυχία.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Το μοντέλο '{{modelTag}}' βρίσκεται ήδη στην ουρά για λήψη.",
+	"Model {{modelId}} not found": "Το μοντέλο {{modelId}} δεν βρέθηκε",
+	"Model {{modelName}} is not vision capable": "Το μοντέλο {{modelName}} δεν έχει δυνατότητα όρασης",
+	"Model {{name}} is now {{status}}": "Το μοντέλο {{name}} είναι τώρα {{status}}",
+	"Model accepts image inputs": "Το μοντέλο δέχεται είσοδο εικόνας",
+	"Model created successfully!": "Το μοντέλο δημιουργήθηκε με επιτυχία!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Ανιχνεύθηκε διαδρομή αρχείου μοντέλου. Το σύντομο όνομα μοντέλου απαιτείται για ενημέρωση, δεν μπορεί να συνεχιστεί.",
+	"Model Filtering": "Φιλτράρισμα Μοντέλων",
+	"Model ID": "ID Μοντέλου",
+	"Model IDs": "IDs Μοντέλων",
+	"Model Name": "Όνομα Μοντέλου",
+	"Model not selected": "Το μοντέλο δεν έχει επιλεγεί",
+	"Model Params": "Παράμετροι Μοντέλου",
+	"Model Permissions": "Δικαιώματα Μοντέλου",
+	"Model updated successfully": "Το μοντέλο ενημερώθηκε με επιτυχία",
+	"Modelfile Content": "Περιεχόμενο Αρχείου Μοντέλου",
+	"Models": "Μοντέλα",
+	"Models Access": "Πρόσβαση Μοντέλων",
+	"Models configuration saved successfully": "Η διαμόρφωση των μοντέλων αποθηκεύτηκε με επιτυχία",
+	"Mojeek Search API Key": "Κλειδί API Mojeek Search",
+	"more": "περισσότερα",
+	"More": "Περισσότερα",
+	"Name": "Όνομα",
+	"Name your knowledge base": "Ονομάστε τη βάση γνώσης σας",
+	"New Chat": "Νέα Συνομιλία",
+	"New folder": "Νέος φάκελος",
+	"New Password": "Νέος Κωδικός",
+	"No content found": "Δεν βρέθηκε περιεχόμενο",
+	"No content to speak": "Δεν υπάρχει περιεχόμενο για ανάγνωση",
+	"No distance available": "Δεν υπάρχει διαθέσιμη απόσταση",
+	"No feedbacks found": "Δεν βρέθηκαν ανατροφοδοτήσεις",
+	"No file selected": "Δεν έχει επιλεγεί αρχείο",
+	"No files found.": "Δεν βρέθηκαν αρχεία.",
+	"No groups with access, add a group to grant access": "Δεν υπάρχουν ομάδες με πρόσβαση, προσθέστε μια ομάδα για να χορηγήσετε πρόσβαση",
+	"No HTML, CSS, or JavaScript content found.": "Δεν βρέθηκε περιεχόμενο HTML, CSS ή JavaScript.",
+	"No knowledge found": "Δεν βρέθηκε γνώση",
+	"No model IDs": "Δεν υπάρχουν IDs μοντέλων",
+	"No models found": "Δεν βρέθηκαν μοντέλα",
+	"No models selected": "Δεν έχουν επιλεγεί μοντέλα",
+	"No results found": "Δεν βρέθηκαν αποτελέσματα",
+	"No search query generated": "Δεν δημιουργήθηκε ερώτηση αναζήτησης",
+	"No source available": "Δεν υπάρχει διαθέσιμη πηγή",
+	"No users were found.": "Δεν βρέθηκαν χρήστες.",
+	"No valves to update": "Δεν υπάρχουν βαλβίδες για ενημέρωση",
+	"None": "Κανένα",
+	"Not factually correct": "Δεν είναι γεγονότα",
+	"Not helpful": "Δεν είναι χρήσιμο",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Σημείωση: Αν ορίσετε ένα ελάχιστο score, η αναζήτηση θα επιστρέψει μόνο έγγραφα με score μεγαλύτερο ή ίσο με το ελάχιστο score.",
+	"Notes": "Σημειώσεις",
+	"Notifications": "Ειδοποιήσεις",
+	"November": "Νοέμβριος",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Οκτώβριος",
+	"Off": "Off",
+	"Okay, Let's Go!": "Εντάξει, Πάμε!",
+	"OLED Dark": "Σκούρο OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama απενεργοποιημένο",
+	"Ollama API settings updated": "Οι ρυθμίσεις API Ollama ενημερώθηκαν",
+	"Ollama Version": "Έκδοση Ollama",
+	"On": "On",
+	"Only alphanumeric characters and hyphens are allowed": "Επιτρέπονται μόνο αλφαριθμητικοί χαρακτήρες και παύλες",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Επιτρέπονται μόνο αλφαριθμητικοί χαρακτήρες και παύλες στο string της εντολής.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Μόνο συλλογές μπορούν να επεξεργαστούν, δημιουργήστε μια νέα βάση γνώσης για επεξεργασία/προσθήκη εγγράφων.",
+	"Only select users and groups with permission can access": "Μόνο επιλεγμένοι χρήστες και ομάδες με άδεια μπορούν να έχουν πρόσβαση",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ωχ! Φαίνεται ότι το URL είναι μη έγκυρο. Παρακαλώ ελέγξτε ξανά και δοκιμάστε.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Ωχ! Υπάρχουν αρχεία που εξακολουθούν να ανεβαίνουν. Παρακαλώ περιμένετε να ολοκληρωθεί η μεταφόρτωση.",
+	"Oops! There was an error in the previous response.": "Ωχ! Υπήρξε σφάλμα στην προηγούμενη απάντηση.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ωχ! Χρησιμοποιείτε μια μη υποστηριζόμενη μέθοδο (μόνο frontend). Παρακαλώ σερβίρετε το WebUI από το backend.",
+	"Open file": "Άνοιγμα αρχείου",
+	"Open in full screen": "Άνοιγμα σε πλήρη οθόνη",
+	"Open new chat": "Άνοιγμα νέας συνομιλίας",
+	"Open WebUI uses faster-whisper internally.": "Το Open WebUI χρησιμοποιεί το faster-whisper εσωτερικά.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Το Open WebUI χρησιμοποιεί τα SpeechT5 και CMU Arctic embeddings ομιλητών.",
+	"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 API": "API OpenAI",
+	"OpenAI API Config": "Διαμόρφωση API OpenAI",
+	"OpenAI API Key is required.": "Απαιτείται το Κλειδί API OpenAI.",
+	"OpenAI API settings updated": "Οι ρυθμίσεις API OpenAI ενημερώθηκαν",
+	"OpenAI URL/Key required.": "Απαιτείται URL/Kλειδί OpenAI.",
+	"or": "ή",
+	"Organize your users": "Οργανώστε τους χρήστες σας",
+	"Other": "Άλλο",
+	"OUTPUT": "ΕΞΟΔΟΣ",
+	"Output format": "Μορφή εξόδου",
+	"Overview": "Επισκόπηση",
+	"page": "σελίδα",
+	"Password": "Κωδικός",
+	"Paste Large Text as File": "Επικόλληση Μεγάλου Κειμένου ως Αρχείο",
+	"PDF document (.pdf)": "Έγγραφο PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Εξαγωγή Εικόνων PDF (OCR)",
+	"pending": "εκκρεμεί",
+	"Permission denied when accessing media devices": "Άρνηση δικαιώματος κατά την πρόσβαση σε μέσα συσκευές",
+	"Permission denied when accessing microphone": "Άρνηση δικαιώματος κατά την πρόσβαση σε μικρόφωνο",
+	"Permission denied when accessing microphone: {{error}}": "Άρνηση δικαιώματος κατά την πρόσβαση σε μικρόφωνο: {{error}}",
+	"Permissions": "Δικαιώματα",
+	"Personalization": "Προσωποποίηση",
+	"Pin": "Καρφίτσωμα",
+	"Pinned": "Καρφιτσωμένο",
+	"Pioneer insights": "Συμβουλές πρωτοπόρων",
+	"Pipeline deleted successfully": "Η συνάρτηση διαγράφηκε με επιτυχία",
+	"Pipeline downloaded successfully": "Η συνάρτηση κατεβλήθηκε με επιτυχία",
+	"Pipelines": "Συναρτήσεις",
+	"Pipelines Not Detected": "Συναρτήσεις Δεν Εντοπίστηκαν",
+	"Pipelines Valves": "Βαλβίδες Συναρτήσεων",
+	"Plain text (.txt)": "Απλό κείμενο (.txt)",
+	"Playground": "Γήπεδο παιχνιδιών",
+	"Please carefully review the following warnings:": "Παρακαλώ αναθεωρήστε προσεκτικά τις ακόλουθες προειδοποιήσεις:",
+	"Please enter a prompt": "Παρακαλώ εισάγετε μια προτροπή",
+	"Please fill in all fields.": "Παρακαλώ συμπληρώστε όλα τα πεδία.",
+	"Please select a model first.": "",
+	"Please select a reason": "Παρακαλώ επιλέξτε έναν λόγο",
+	"Port": "Θύρα",
+	"Positive attitude": "Θετική στάση",
+	"Prefix ID": "ID Προθέματος",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Το ID Προθέματος χρησιμοποιείται για να αποφεύγονται συγκρούσεις με άλλες συνδέσεις προσθέτοντας ένα πρόθεμα στα IDs των μοντέλων - αφήστε κενό για απενεργοποίηση",
+	"Previous 30 days": "Προηγούμενες 30 ημέρες",
+	"Previous 7 days": "Προηγούμενες 7 ημέρες",
+	"Profile Image": "Εικόνα Προφίλ",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Προτροπή (π.χ. Πες μου ένα διασκεδαστικό γεγονός για την Ρωμαϊκή Αυτοκρατορία)",
+	"Prompt Content": "Περιεχόμενο Προτροπής",
+	"Prompt created successfully": "Η προτροπή δημιουργήθηκε με επιτυχία",
+	"Prompt suggestions": "Προτάσεις Προτροπής",
+	"Prompt updated successfully": "Η προτροπή ενημερώθηκε με επιτυχία",
+	"Prompts": "Προτροπές",
+	"Prompts Access": "Πρόσβαση Προτροπών",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Τραβήξτε \"{{searchValue}}\" από το Ollama.com",
+	"Pull a model from Ollama.com": "Τραβήξτε ένα μοντέλο από το Ollama.com",
+	"Query Generation Prompt": "Προτροπή Δημιουργίας Ερωτήσεων",
+	"Query Params": "Παράμετροι Ερωτήσεων",
+	"RAG Template": "Πρότυπο RAG",
+	"Rating": "Βαθμολογία",
+	"Re-rank models by topic similarity": "Επανατάξη μοντέλων κατά ομοιότητα θέματος",
+	"Read Aloud": "Ανάγνωση Φωναχτά",
+	"Record voice": "Εγγραφή φωνής",
+	"Redirecting you to OpenWebUI Community": "Μετακατεύθυνση στην Κοινότητα OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Μειώνει την πιθανότητα δημιουργίας ανοησιών. Μια υψηλότερη τιμή (π.χ. 100) θα δώσει πιο ποικίλες απαντήσεις, ενώ μια χαμηλότερη τιμή (π.χ. 10) θα δημιουργήσει πιο συντηρητικές απαντήσεις. (Προεπιλογή: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Αναφέρεστε στον εαυτό σας ως \"User\" (π.χ., \"User μαθαίνει Ισπανικά\")",
+	"References from": "Αναφορές από",
+	"Refused when it shouldn't have": "Αρνήθηκε όταν δεν έπρεπε",
+	"Regenerate": "Αναγεννήστε",
+	"Release Notes": "Σημειώσεις Έκδοσης",
+	"Relevance": "Σχετικότητα",
+	"Remove": "Αφαίρεση",
+	"Remove Model": "Αφαίρεση Μοντέλου",
+	"Rename": "Μετονομασία",
+	"Reorder Models": "Επαναταξινόμηση Μοντέλων",
+	"Repeat Last N": "Επανάληψη Τελευταίων N",
+	"Request Mode": "Λειτουργία Αιτήματος",
+	"Reranking Model": "Μοντέλο Επαναταξινόμησης",
+	"Reranking model disabled": "Το μοντέλο επαναταξινόμησης απενεργοποιήθηκε",
+	"Reranking model set to \"{{reranking_model}}\"": "Το μοντέλο επαναταξινόμησης ορίστηκε σε \"{{reranking_model}}\"",
+	"Reset": "Επαναφορά",
+	"Reset All Models": "Επαναφορά Όλων των Μοντέλων",
+	"Reset Upload Directory": "Επαναφορά Καταλόγου Ανεβάσματος",
+	"Reset Vector Storage/Knowledge": "Επαναφορά Αποθήκευσης Διανυσμάτων/Γνώσης",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Οι ειδοποιήσεις απάντησης δεν μπορούν να ενεργοποιηθούν καθώς οι άδειες του ιστότοπου έχουν αρνηθεί. Παρακαλώ επισκεφτείτε τις ρυθμίσεις του περιηγητή σας για να δώσετε την απαραίτητη πρόσβαση.",
+	"Response splitting": "Διαχωρισμός απάντησης",
+	"Result": "Αποτέλεσμα",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Πλούσιο Εισαγωγή Κειμένου για Συνομιλία",
+	"RK": "RK",
+	"Role": "Ρόλος",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Εκτέλεση",
+	"Running": "Εκτέλεση",
+	"Save": "Αποθήκευση",
+	"Save & Create": "Αποθήκευση & Δημιουργία",
+	"Save & Update": "Αποθήκευση & Ενημέρωση",
+	"Save As Copy": "Αποθήκευση ως Αντιγραφή",
+	"Save Tag": "Αποθήκευση Ετικέτας",
+	"Saved": "Αποθηκευμένο",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Η αποθήκευση των αρχείων συνομιλίας απευθείας στη μνήμη αποθήκευσης του προγράμματος περιήγησής σας δεν υποστηρίζεται πλέον. Παρακαλώ αφιερώστε λίγο χρόνο να κατεβάσετε και να διαγράψετε τα αρχεία συνομιλίας σας κάνοντας κλικ στο κουμπί παρακάτω. Μην ανησυχείτε, μπορείτε εύκολα να επαναφέρετε τα αρχεία συνομιλιών σας στο backend μέσω",
+	"Scroll to bottom when switching between branches": "Κύλιση προς τα κάτω όταν αλλάζετε μεταξύ κλάδων",
+	"Search": "Αναζήτηση",
+	"Search a model": "Αναζήτηση μοντέλου",
+	"Search Base": "Βάση Αναζήτησης",
+	"Search Chats": "Αναζήτηση Συνομιλιών",
+	"Search Collection": "Αναζήτηση Συλλογής",
+	"Search Filters": "Φίλτρα Αναζήτησης",
+	"search for tags": "αναζήτηση για ετικέτες",
+	"Search Functions": "Αναζήτηση Λειτουργιών",
+	"Search Knowledge": "Αναζήτηση Γνώσης",
+	"Search Models": "Αναζήτηση Μοντέλων",
+	"Search options": "Επιλογές Αναζήτησης",
+	"Search Prompts": "Αναζήτηση Προτροπών",
+	"Search Result Count": "Αριθμός Αποτελεσμάτων Αναζήτησης",
+	"Search the web": "Αναζήτηση στο διαδίκτυο",
+	"Search Tools": "Αναζήτηση Εργαλείων",
+	"SearchApi API Key": "Κλειδί API SearchApi",
+	"SearchApi Engine": "Μηχανή SearchApi",
+	"Searched {{count}} sites_one": "Αναζήτησε {{count}} site",
+	"Searched {{count}} sites_other": "Αναζήτησε {{count}} sites",
+	"Searching \"{{searchQuery}}\"": "Αναζήτηση \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Αναζήτηση Γνώσης για \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL Ερώτησης Searxng",
+	"See readme.md for instructions": "Δείτε readme.md για οδηγίες",
+	"See what's new": "Δείτε τι νέο υπάρχει",
+	"Seed": "Seed",
+	"Select a base model": "Επιλέξτε ένα βασικό μοντέλο",
+	"Select a engine": "Επιλέξτε μια μηχανή",
+	"Select a function": "Επιλέξτε μια λειτουργία",
+	"Select a group": "Επιλέξτε μια ομάδα",
+	"Select a model": "Επιλέξτε ένα μοντέλο",
+	"Select a pipeline": "Επιλέξτε ένα pipeline",
+	"Select a pipeline url": "Επιλέξτε ένα pipeline url",
+	"Select a tool": "Επιλέξτε ένα εργαλείο",
+	"Select Engine": "Επιλέξτε Μηχανή",
+	"Select Knowledge": "Επιλέξτε Γνώση",
+	"Select model": "Επιλέξτε μοντέλο",
+	"Select only one model to call": "Επιλέξτε μόνο ένα μοντέλο για κλήση",
+	"Selected model(s) do not support image inputs": "Τα επιλεγμένα μοντέλα δεν υποστηρίζουν είσοδο εικόνων",
+	"Semantic distance to query": "Σημαντική απόσταση προς την ερώτηση",
+	"Send": "Αποστολή",
+	"Send a Message": "Αποστολή Μηνύματος",
+	"Send message": "Αποστολή μηνύματος",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Στέλνει `stream_options: { include_usage: true }` στο αίτημα.\nΟι υποστηριζόμενοι πάροχοι θα επιστρέψουν πληροφορίες χρήσης token στην απάντηση όταν ρυθμιστεί.",
+	"September": "Σεπτέμβριος",
+	"Serper API Key": "Κλειδί API Serper",
+	"Serply API Key": "Κλειδί API Serply",
+	"Serpstack API Key": "Κλειδί API Serpstack",
+	"Server connection verified": "Η σύνδεση διακομιστή επαληθεύθηκε",
+	"Set as default": "Ορισμός ως προεπιλογή",
+	"Set CFG Scale": "Ορισμός CFG Scale",
+	"Set Default Model": "Ορισμός Προεπιλεγμένου Μοντέλου",
+	"Set embedding model": "Ορισμός μοντέλου ενσωμάτωσης",
+	"Set embedding model (e.g. {{model}})": "Ορισμός μοντέλου ενσωμάτωσης (π.χ. {{model}})",
+	"Set Image Size": "Ορισμός Μεγέθους Εικόνας",
+	"Set reranking model (e.g. {{model}})": "Ορισμός μοντέλου επαναταξινόμησης (π.χ. {{model}})",
+	"Set Sampler": "Ορισμός Sampler",
+	"Set Scheduler": "Ορισμός Scheduler",
+	"Set Steps": "Ορισμός Βημάτων",
+	"Set Task Model": "Ορισμός Μοντέλου Εργασίας",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Ορισμός του αριθμού των συσκευών GPU που χρησιμοποιούνται για υπολογισμούς. Αυτή η επιλογή ελέγχει πόσες συσκευές GPU (αν υπάρχουν) χρησιμοποιούνται για την επεξεργασία των εισερχόμενων αιτημάτων. Η αύξηση αυτής της τιμής μπορεί να βελτιώσει σημαντικά την απόδοση για μοντέλα που είναι βελτιστοποιημένα για επιτάχυνση GPU αλλά μπορεί επίσης να καταναλώσει περισσότερη ενέργεια και πόρους GPU.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Ορισμός του αριθμού των νημάτων εργασίας που χρησιμοποιούνται για υπολογισμούς. Αυτή η επιλογή ελέγχει πόσα νήματα χρησιμοποιούνται για την επεξεργασία των εισερχόμενων αιτημάτων ταυτόχρονα. Η αύξηση αυτής της τιμής μπορεί να βελτιώσει την απόδοση σε εργασίες υψηλής συγχρονισμένης φόρτωσης αλλά μπορεί επίσης να καταναλώσει περισσότερους πόρους CPU.",
+	"Set Voice": "Ορισμός Φωνής",
+	"Set whisper model": "Ορισμός μοντέλου whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Ορίζει πόσο πίσω θα κοιτάξει το μοντέλο για να αποτρέψει την επανάληψη. (Προεπιλογή: 64, 0 = απενεργοποιημένο, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Ορίζει πόσο δυνατά θα τιμωρηθούν οι επαναλήψεις. Μια υψηλότερη τιμή (π.χ., 1.5) θα τιμωρήσει τις επαναλήψεις πιο έντονα, ενώ μια χαμηλότερη τιμή (π.χ., 0.9) θα είναι πιο ευέλικτη. (Προεπιλογή: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Ορίζει τον τυχαίο σπόρο αριθμού που θα χρησιμοποιηθεί για τη δημιουργία. Ορισμός αυτού σε έναν συγκεκριμένο αριθμό θα κάνει το μοντέλο να δημιουργεί το ίδιο κείμενο για την ίδια προτροπή. (Προεπιλογή: τυχαίο)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Ορίζει το μέγεθος του παραθύρου πλαισίου που χρησιμοποιείται για τη δημιουργία του επόμενου token. (Προεπιλογή: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Ορίζει τις σειρές παύσης που θα χρησιμοποιηθούν. Όταν εντοπιστεί αυτό το μοτίβο, το LLM θα σταματήσει να δημιουργεί κείμενο και θα επιστρέψει. Πολλαπλά μοτίβα παύσης μπορούν να οριστούν καθορίζοντας πολλαπλές ξεχωριστές παραμέτρους παύσης σε ένα αρχείο μοντέλου.",
+	"Settings": "Ρυθμίσεις",
+	"Settings saved successfully!": "Οι Ρυθμίσεις αποθηκεύτηκαν με επιτυχία!",
+	"Share": "Κοινή Χρήση",
+	"Share Chat": "Κοινή Χρήση Συνομιλίας",
+	"Share to OpenWebUI Community": "Κοινή Χρήση στην Κοινότητα OpenWebUI",
+	"Show": "Εμφάνιση",
+	"Show \"What's New\" modal on login": "Εμφάνιση του παράθυρου \"Τι νέο υπάρχει\" κατά την είσοδο",
+	"Show Admin Details in Account Pending Overlay": "Εμφάνιση Λεπτομερειών Διαχειριστή στο Υπέρθεση Εκκρεμής Λογαριασμού",
+	"Show shortcuts": "Εμφάνιση συντομεύσεων",
+	"Show your support!": "Δείξτε την υποστήριξή σας!",
+	"Showcased creativity": "Εμφανιζόμενη δημιουργικότητα",
+	"Sign in": "Σύνδεση",
+	"Sign in to {{WEBUI_NAME}}": "Σύνδεση στο {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Σύνδεση στο {{WEBUI_NAME}} με LDAP",
+	"Sign Out": "Αποσύνδεση",
+	"Sign up": "Εγγραφή",
+	"Sign up to {{WEBUI_NAME}}": "Εγγραφή στο {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Σύνδεση στο {{WEBUI_NAME}}",
+	"Source": "Πηγή",
+	"Speech Playback Speed": "Ταχύτητα Αναπαραγωγής Ομιλίας",
+	"Speech recognition error: {{error}}": "Σφάλμα αναγνώρισης ομιλίας: {{error}}",
+	"Speech-to-Text Engine": "Μηχανή Speech-to-Text",
+	"Stop": "Σταμάτημα",
+	"Stop Sequence": "Σειρά Παύσης",
+	"Stream Chat Response": "Συνομιλία Ροής Απάντησης",
+	"STT Model": "Μοντέλο STT",
+	"STT Settings": "Ρυθμίσεις STT",
+	"Subtitle (e.g. about the Roman Empire)": "Υπότιτλος (π.χ. για την Ρωμαϊκή Αυτοκρατορία)",
+	"Success": "Επιτυχία",
+	"Successfully updated.": "Επιτυχώς ενημερώθηκε.",
+	"Suggested": "Προτεινόμενο",
+	"Support": "Υποστήριξη",
+	"Support this plugin:": "Υποστήριξη αυτού του plugin:",
+	"Sync directory": "Συγχρονισμός καταλόγου",
+	"System": "Σύστημα",
+	"System Instructions": "Οδηγίες Συστήματος",
+	"System Prompt": "Προτροπή Συστήματος",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Προτροπή Γενιάς Ετικετών",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Η δειγματοληψία Tail free χρησιμοποιείται για να μειώσει την επίδραση των λιγότερο πιθανών tokens από την έξοδο. Μια υψηλότερη τιμή (π.χ., 2.0) θα μειώσει την επίδραση περισσότερο, ενώ μια τιμή 1.0 απενεργοποιεί αυτή τη ρύθμιση. (προεπιλογή: 1)",
+	"Tap to interrupt": "Πατήστε για παύση",
+	"Tavily API Key": "Κλειδί API Tavily",
+	"Tell us more:": "Πείτε μας περισσότερα:",
+	"Temperature": "Temperature",
+	"Template": "Πρότυπο",
+	"Temporary Chat": "Προσωρινή Συνομιλία",
+	"Text Splitter": "Διαχωριστής Κειμένου",
+	"Text-to-Speech Engine": "Μηχανή Text-to-Speech",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Ευχαριστούμε για την ανατροφοδότησή σας!",
+	"The Application Account DN you bind with for search": "Το DN του Λογαριασμού Εφαρμογής που συνδέετε για αναζήτηση",
+	"The base to search for users": "Η βάση για αναζήτηση χρηστών",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Το μέγεθος παρτίδας καθορίζει πόσες αιτήσεις κειμένου επεξεργάζονται μαζί ταυτόχρονα. Ένα μεγαλύτερο μέγεθος παρτίδας μπορεί να αυξήσει την απόδοση και την ταχύτητα του μοντέλου, αλλά απαιτεί επίσης περισσότερη μνήμη. (Προεπιλογή: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Οι προγραμματιστές πίσω από αυτό το plugin είναι παθιασμένοι εθελοντές από την κοινότητα. Αν βρείτε αυτό το plugin χρήσιμο, παρακαλώ σκεφτείτε να συνεισφέρετε στην ανάπτυξή του.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Η κατάταξη αξιολόγησης βασίζεται στο σύστημα βαθμολόγησης Elo και ενημερώνεται σε πραγματικό χρόνο.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "Το χαρακτηριστικό LDAP που αντιστοιχεί στο όνομα χρήστη που χρησιμοποιούν οι χρήστες για να συνδεθούν.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Η κατάταξη είναι αυτή τη στιγμή σε beta, και ενδέχεται να προσαρμόσουμε τους υπολογισμούς βαθμολογίας καθώς βελτιώνουμε τον αλγόριθμο.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Το μέγιστο μέγεθος αρχείου σε MB. Αν το μέγεθος του αρχείου υπερβαίνει αυτό το όριο, το αρχείο δεν θα ανεβεί.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Ο μέγιστος αριθμός αρχείων που μπορούν να χρησιμοποιηθούν ταυτόχρονα στη συνομιλία. Αν ο αριθμός των αρχείων υπερβαίνει αυτό το όριο, τα αρχεία δεν θα ανεβούν.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Η βαθμολογία θα πρέπει να είναι μια τιμή μεταξύ 0.0 (0%) και 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Η θερμοκρασία του μοντέλου. Η αύξηση της θερμοκρασίας θα κάνει το μοντέλο να απαντά πιο δημιουργικά. (Προεπιλογή: 0.8)",
+	"Theme": "Θέμα",
+	"Thinking...": "Σκέφτομαι...",
+	"This action cannot be undone. Do you wish to continue?": "Αυτή η ενέργεια δεν μπορεί να αναιρεθεί. Θέλετε να συνεχίσετε;",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Αυτό διασφαλίζει ότι οι πολύτιμες συνομιλίες σας αποθηκεύονται με ασφάλεια στη βάση δεδομένων backend σας. Ευχαριστούμε!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Αυτή είναι μια πειραματική λειτουργία, μπορεί να μην λειτουργεί όπως αναμένεται και υπόκειται σε αλλαγές οποιαδήποτε στιγμή.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Αυτή η επιλογή ελέγχει πόσα tokens διατηρούνται κατά την ανανέωση του πλαισίου. Για παράδειγμα, αν οριστεί σε 2, τα τελευταία 2 tokens του πλαισίου συνομιλίας θα διατηρηθούν. Η διατήρηση του πλαισίου μπορεί να βοηθήσει στη διατήρηση της συνέχειας μιας συνομιλίας, αλλά μπορεί να μειώσει την ικανότητα ανταπόκρισης σε νέα θέματα. (Προεπιλογή: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Αυτή η επιλογή ορίζει τον μέγιστο αριθμό tokens που μπορεί να δημιουργήσει το μοντέλο στην απάντησή του. Η αύξηση αυτού του ορίου επιτρέπει στο μοντέλο να παρέχει μεγαλύτερες απαντήσεις, αλλά μπορεί επίσης να αυξήσει την πιθανότητα δημιουργίας αχρήσιμου ή άσχετου περιεχομένου. (Προεπιλογή: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Αυτή η επιλογή θα διαγράψει όλα τα υπάρχοντα αρχεία στη συλλογή και θα τα αντικαταστήσει με νέα ανεβασμένα αρχεία.",
+	"This response was generated by \"{{model}}\"": "Αυτή η απάντηση δημιουργήθηκε από \"{{model}}\"",
+	"This will delete": "Αυτό θα διαγράψει",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Αυτό θα διαγράψει το <strong>{{NAME}}</strong> και <strong>όλο το περιεχόμενό του</strong>.",
+	"This will delete all models including custom models": "Αυτό θα διαγράψει όλα τα μοντέλα, συμπεριλαμβανομένων των προσαρμοσμένων μοντέλων",
+	"This will delete all models including custom models and cannot be undone.": "Αυτό θα διαγράψει όλα τα μοντέλα, συμπεριλαμβανομένων των προσαρμοσμένων μοντέλων και δεν μπορεί να αναιρεθεί.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Αυτό θα επαναφέρει τη βάση γνώσης και θα συγχρονίσει όλα τα αρχεία. Θέλετε να συνεχίσετε;",
+	"Thorough explanation": "Λεπτομερής εξήγηση",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Απαιτείται το URL διακομιστή Tika.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Συμβουλή: Ενημερώστε πολλαπλές θέσεις μεταβλητών διαδοχικά πατώντας το πλήκτρο tab στο πεδίο συνομιλίας μετά από κάθε αντικατάσταση.",
+	"Title": "Τίτλος",
+	"Title (e.g. Tell me a fun fact)": "Τίτλος (π.χ. Πες μου ένα διασκεδαστικό γεγονός)",
+	"Title Auto-Generation": "Αυτόματη Γενιά Τίτλων",
+	"Title cannot be an empty string.": "Ο τίτλος δεν μπορεί να είναι κενή συμβολοσειρά.",
+	"Title Generation Prompt": "Προτροπή Δημιουργίας Τίτλου",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Για να αποκτήσετε πρόσβαση στα διαθέσιμα ονόματα μοντέλων για λήψη,",
+	"To access the GGUF models available for downloading,": "Για να αποκτήσετε πρόσβαση στα μοντέλα GGUF διαθέσιμα για λήψη,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Για να αποκτήσετε πρόσβαση στο WebUI, παρακαλώ επικοινωνήστε με τον διαχειριστή. Οι διαχειριστές μπορούν να διαχειριστούν τις καταστάσεις των χρηστών από τον Πίνακα Διαχειριστή.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Για να επισυνάψετε τη βάση γνώσης εδώ, προσθέστε τα πρώτα στο χώρο εργασίας \"Knowledge\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Για να προστατεύσετε την ιδιωτικότητά σας, μόνο οι βαθμολογίες, τα IDs μοντέλων, οι ετικέτες και τα μεταδεδομένα μοιράζονται από την ανατροφοδότησή σας—τα αρχεία συνομιλιών σας παραμένουν ιδιωτικά και δεν περιλαμβάνονται.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Για να επιλέξετε ενέργειες εδώ, προσθέστε τις πρώτα στο χώρο εργασίας \"Functions\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Για να επιλέξετε φίλτρα εδώ, προσθέστε τα πρώτα στο χώρο εργασίας \"Functions\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Για να επιλέξετε toolkits εδώ, προσθέστε τα πρώτα στο χώρο εργασίας \"Tools\".",
+	"Toast notifications for new updates": "Ειδοποιήσεις Toast για νέες ενημερώσεις",
+	"Today": "Σήμερα",
+	"Toggle settings": "Εναλλαγή ρυθμίσεων",
+	"Toggle sidebar": "Εναλλαγή πλαϊνού μενού",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens To Keep On Context Refresh (num_keep)",
+	"Too verbose": "Πολύ λεπτομερές",
+	"Tool created successfully": "Το εργαλείο δημιουργήθηκε με επιτυχία",
+	"Tool deleted successfully": "Το εργαλείο διαγράφηκε με επιτυχία",
+	"Tool Description": "Περιγραφή Εργαλείου",
+	"Tool ID": "ID Εργαλείου",
+	"Tool imported successfully": "Το εργαλείο εισήχθη με επιτυχία",
+	"Tool Name": "Όνομα Εργαλείου",
+	"Tool updated successfully": "Το εργαλείο ενημερώθηκε με επιτυχία",
+	"Tools": "Εργαλεία",
+	"Tools Access": "Πρόσβαση Εργαλείων",
+	"Tools are a function calling system with arbitrary code execution": "Τα εργαλεία είναι ένα σύστημα κλήσης λειτουργιών με αυθαίρετη εκτέλεση κώδικα",
+	"Tools have a function calling system that allows arbitrary code execution": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα",
+	"Tools have a function calling system that allows arbitrary code execution.": "Τα εργαλεία διαθέτουν ένα σύστημα κλήσης λειτουργιών που επιτρέπει την αυθαίρετη εκτέλεση κώδικα.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformers",
+	"Trouble accessing Ollama?": "Προβλήματα πρόσβασης στο Ollama?",
+	"TTS Model": "Μοντέλο TTS",
+	"TTS Settings": "Ρυθμίσεις TTS",
+	"TTS Voice": "Φωνή TTS",
+	"Type": "Τύπος",
+	"Type Hugging Face Resolve (Download) URL": "Τύπος URL Ανάλυσης Hugging Face Resolve (Λήψη)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ωχ! Υπήρξε πρόβλημα στη σύνδεση με το {{provider}}.",
+	"UI": "Διεπαφή Χρήστη (UI)",
+	"Unarchive All": "Απο-αρχειοθέτηση Όλων",
+	"Unarchive All Archived Chats": "Απο-αρχειοθέτηση Όλων των Αρχειοθετημένων Συνομιλιών",
+	"Unarchive Chat": "Απο-αρχειοθέτηση Συνομιλίας",
+	"Unlock mysteries": "Ξεκλείδωμα μυστηρίων",
+	"Unpin": "Αφαίρεση καρφίτσματος",
+	"Unravel secrets": "Ξετυλίξτε μυστικά",
+	"Untagged": "Χωρίς Ετικέτες",
+	"Update": "Ενημέρωση",
+	"Update and Copy Link": "Ενημέρωση και Αντιγραφή Συνδέσμου",
+	"Update for the latest features and improvements.": "Ενημέρωση για τις τελευταίες λειτουργίες και βελτιώσεις.",
+	"Update password": "Ενημέρωση κωδικού",
+	"Updated": "Ενημερώθηκε",
+	"Updated at": "Ενημερώθηκε στις",
+	"Updated At": "Ενημερώθηκε στις",
+	"Upload": "Ανέβασμα",
+	"Upload a GGUF model": "Ανέβασμα μοντέλου GGUF",
+	"Upload directory": "Κατάλογος ανεβάσματος",
+	"Upload files": "Ανέβασμα αρχείων",
+	"Upload Files": "Ανέβασμα Αρχείων",
+	"Upload Pipeline": "Ανέβασμα Pipeline",
+	"Upload Progress": "Πρόοδος Ανεβάσματος",
+	"URL": "URL",
+	"URL Mode": "Λειτουργία URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Χρησιμοποιήστε '#' στην είσοδο προτροπής για φόρτωση και συμπερίληψη της γνώσης σας.",
+	"Use Gravatar": "Χρησιμοποιήστε Gravatar",
+	"Use groups to group your users and assign permissions.": "Χρησιμοποιήστε ομάδες για να ομαδοποιήσετε τους χρήστες σας και να αναθέσετε δικαιώματα.",
+	"Use Initials": "Χρησιμοποιήστε Αρχικά",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "user",
+	"User": "Χρήστης",
+	"User location successfully retrieved.": "Η τοποθεσία του χρήστη ανακτήθηκε με επιτυχία.",
+	"Username": "Όνομα Χρήστη",
+	"Users": "Χρήστες",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Χρησιμοποιώντας το προεπιλεγμένο μοντέλο arena με όλα τα μοντέλα. Κάντε κλικ στο κουμπί συν για να προσθέσετε προσαρμοσμένα μοντέλα.",
+	"Utilize": "Αξιοποίηση",
+	"Valid time units:": "Έγκυρες μονάδες χρόνου:",
+	"Valves": "Βαλβίδες",
+	"Valves updated": "Οι βαλβίδες ενημερώθηκαν",
+	"Valves updated successfully": "Οι βαλβίδες ενημερώθηκαν με επιτυχία",
+	"variable": "μεταβλητή",
+	"variable to have them replaced with clipboard content.": "μεταβλητή να αντικατασταθούν με το περιεχόμενο του πρόχειρου.",
+	"Version": "Έκδοση",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Έκδοση {{selectedVersion}} από {{totalVersions}}",
+	"Visibility": "Ορατότητα",
+	"Voice": "Φωνή",
+	"Voice Input": "Εισαγωγή Φωνής",
+	"Warning": "Προειδοποίηση",
+	"Warning:": "Προειδοποίηση:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Προειδοποίηση: Η ενεργοποίηση αυτού θα επιτρέψει στους χρήστες να ανεβάσουν αυθαίρετο κώδικα στον διακομιστή.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Προειδοποίηση: Αν ενημερώσετε ή αλλάξετε το μοντέλο ενσωμάτωσής σας, θα χρειαστεί να επαναφέρετε όλα τα έγγραφα.",
+	"Web": "Διαδίκτυο",
+	"Web API": "Web API",
+	"Web Loader Settings": "Ρυθμίσεις Φόρτωσης Web",
+	"Web Search": "Αναζήτηση στο Διαδίκτυο",
+	"Web Search Engine": "Μηχανή Αναζήτησης στο Διαδίκτυο",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL Webhook",
+	"WebUI Settings": "Ρυθμίσεις WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "Το WebUI θα κάνει αιτήματα στο \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "Το WebUI θα κάνει αιτήματα στο \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Τι προσπαθείτε να πετύχετε?",
+	"What are you working on?": "Τι εργάζεστε;",
+	"What’s New in": "Τι νέο υπάρχει στο",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Όταν ενεργοποιηθεί, το μοντέλο θα ανταποκρίνεται σε κάθε μήνυμα συνομιλίας σε πραγματικό χρόνο, δημιουργώντας μια απάντηση μόλις ο χρήστης στείλει ένα μήνυμα. Αυτή η λειτουργία είναι χρήσιμη για εφαρμογές ζωντανής συνομιλίας, αλλά μπορεί να επηρεάσει την απόδοση σε πιο αργό υλικό.",
+	"wherever you are": "οπουδήποτε βρίσκεστε",
+	"Whisper (Local)": "Whisper (Τοπικό)",
+	"Why?": "Γιατί?",
+	"Widescreen Mode": "Λειτουργία Οθόνης Ευρείας",
+	"Won": "Κέρδισε",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Συνεργάζεται μαζί με top-k. Μια υψηλότερη τιμή (π.χ., 0.95) θα οδηγήσει σε πιο ποικίλο κείμενο, ενώ μια χαμηλότερη τιμή (π.χ., 0.5) θα δημιουργήσει πιο εστιασμένο και συντηρητικό κείμενο. (Προεπιλογή: 0.9)",
+	"Workspace": "Χώρος Εργασίας",
+	"Workspace Permissions": "Δικαιώματα Χώρου Εργασίας",
+	"Write a prompt suggestion (e.g. Who are you?)": "Γράψτε μια πρόταση προτροπής (π.χ. Ποιος είσαι;)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Γράψτε μια περίληψη σε 50 λέξεις που συνοψίζει [θέμα ή λέξη-κλειδί].",
+	"Write something...": "Γράψτε κάτι...",
+	"Write your model template content here": "Γράψτε το περιεχόμενο του προτύπου μοντέλου σας εδώ",
+	"Yesterday": "Εχθές",
+	"You": "Εσείς",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Μπορείτε να συνομιλήσετε μόνο με μέγιστο αριθμό {{maxCount}} αρχείου(-ων) ταυτόχρονα.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Μπορείτε να προσωποποιήσετε τις αλληλεπιδράσεις σας με τα LLMs προσθέτοντας αναμνήσεις μέσω του κουμπιού 'Διαχείριση' παρακάτω, κάνοντάς τα πιο χρήσιμα και προσαρμοσμένα σε εσάς.",
+	"You cannot upload an empty file.": "Δεν μπορείτε να ανεβάσετε ένα κενό αρχείο.",
+	"You do not have permission to upload files.": "Δεν έχετε άδεια να ανεβάσετε αρχεία.",
+	"You have no archived conversations.": "Δεν έχετε αρχειοθετημένες συνομιλίες.",
+	"You have shared this chat": "Έχετε μοιραστεί αυτή τη συνομιλία",
+	"You're a helpful assistant.": "Είστε ένας βοηθητικός βοηθός.",
+	"You're now logged in.": "Τώρα είστε συνδεδεμένοι.",
+	"Your account status is currently pending activation.": "Η κατάσταση του λογαριασμού σας είναι αυτή τη στιγμή σε εκκρεμότητα ενεργοποίησης.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Η ολόκληρη η συνεισφορά σας θα πάει απευθείας στον προγραμματιστή του plugin· το Open WebUI δεν παίρνει κανένα ποσοστό. Ωστόσο, η επιλεγμένη πλατφόρμα χρηματοδότησης μπορεί να έχει τα δικά της τέλη.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Ρυθμίσεις Φόρτωσης Youtube"
+}
diff --git a/src/lib/i18n/locales/en-GB/translation.json b/src/lib/i18n/locales/en-GB/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..99575141a355d7de670082371a1667b3e022599f
--- /dev/null
+++ b/src/lib/i18n/locales/en-GB/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "",
+	"(latest)": "",
+	"{{ models }}": "",
+	"{{user}}'s Chats": "",
+	"{{webUIName}} Backend Required": "",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "",
+	"a user": "",
+	"About": "",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "",
+	"Account Activation Pending": "",
+	"Accurate information": "",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "",
+	"Add a tag": "",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "",
+	"Add Files": "",
+	"Add Group": "",
+	"Add Memory": "",
+	"Add Model": "",
+	"Add Tag": "",
+	"Add Tags": "",
+	"Add text content": "",
+	"Add User": "",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "",
+	"admin": "",
+	"Admin": "",
+	"Admin Panel": "",
+	"Admin Settings": "",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "",
+	"Advanced Params": "",
+	"All chats": "",
+	"All Documents": "",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "",
+	"and": "",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "",
+	"API Base URL": "",
+	"API Key": "",
+	"API Key created.": "",
+	"API keys": "",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "",
+	"Archive": "",
+	"Archive All Chats": "",
+	"Archived Chats": "",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "",
+	"Attention to detail": "",
+	"Attribute for Username": "",
+	"Audio": "",
+	"August": "",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "",
+	"Auto-playback response": "",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "",
+	"AUTOMATIC1111 Base URL is required.": "",
+	"Available list": "",
+	"available!": "",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "",
+	"Bad Response": "",
+	"Banners": "",
+	"Base Model (From)": "",
+	"Batch Size (num_batch)": "",
+	"before": "",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "",
+	"Capabilities": "",
+	"Certificate Path": "",
+	"Change Password": "",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "",
+	"Chat Controls": "",
+	"Chat direction": "",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "",
+	"Check Again": "",
+	"Check for updates": "",
+	"Checking for updates...": "",
+	"Choose a model before saving...": "",
+	"Chunk Overlap": "",
+	"Chunk Params": "",
+	"Chunk Size": "",
+	"Ciphers": "",
+	"Citation": "",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "",
+	"Click here to": "",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "",
+	"Click here to select a csv file.": "",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "",
+	"Click on the user role button to change a user's role.": "",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "",
+	"Close": "",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "",
+	"Color": "",
+	"ComfyUI": "",
+	"ComfyUI Base URL": "",
+	"ComfyUI Base URL is required.": "",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "",
+	"Completions": "",
+	"Concurrent Requests": "",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "",
+	"Confirm your action": "",
+	"Connections": "",
+	"Contact Admin for WebUI Access": "",
+	"Content": "",
+	"Content Extraction": "",
+	"Context Length": "",
+	"Continue Response": "",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "",
+	"Copied to clipboard": "",
+	"Copy": "",
+	"Copy last code block": "",
+	"Copy last response": "",
+	"Copy Link": "",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "",
+	"Create Account": "",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "",
+	"Create new secret key": "",
+	"Created at": "",
+	"Created At": "",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "",
+	"Current Password": "",
+	"Custom": "",
+	"Dark": "",
+	"Database": "",
+	"December": "",
+	"Default": "",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "",
+	"Default Model": "",
+	"Default model updated": "",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "",
+	"Delete": "",
+	"Delete a model": "",
+	"Delete All Chats": "",
+	"Delete All Models": "",
+	"Delete chat": "",
+	"Delete Chat": "",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "",
+	"Delete tool?": "",
+	"Delete User": "",
+	"Deleted {{deleteModelTag}}": "",
+	"Deleted {{name}}": "",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "",
+	"Didn't fully follow instructions": "",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "",
+	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "",
+	"Documentation": "",
+	"Documents": "",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "",
+	"Don't have an account?": "",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "",
+	"Done": "",
+	"Download": "",
+	"Download canceled": "",
+	"Download Database": "",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "",
+	"Enable Web Search": "",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
+	"Enter {{role}} message here": "",
+	"Enter a detail about yourself for your LLMs to recall": "",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "",
+	"Enter Chunk Size": "",
+	"Enter description": "",
+	"Enter Github Raw URL": "",
+	"Enter Google PSE API Key": "",
+	"Enter Google PSE Engine Id": "",
+	"Enter Image Size (e.g. 512x512)": "",
+	"Enter Jina API Key": "",
+	"Enter language codes": "",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "",
+	"Enter Seed": "",
+	"Enter Serper API Key": "",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "",
+	"Enter URL (e.g. http://localhost:11434)": "",
+	"Enter Your Email": "",
+	"Enter Your Full Name": "",
+	"Enter your message": "",
+	"Enter Your Password": "",
+	"Enter Your Role": "",
+	"Enter Your Username": "",
+	"Error": "",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "",
+	"Explore the cosmos": "",
+	"Export": "",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "",
+	"Export chat (.json)": "",
+	"Export Chats": "",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "",
+	"Export Presets": "",
+	"Export Prompts": "",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "",
+	"Failed to read clipboard contents": "",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "",
+	"File not found.": "",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Fluidly stream large external response chunks": "",
+	"Focus chat input": "",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "",
+	"General Settings": "",
+	"Generate Image": "",
+	"Generating search query": "",
+	"Generation Info": "",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "",
+	"Google PSE API Key": "",
+	"Google PSE Engine Id": "",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "",
+	"Haptic Feedback": "",
+	"has no conversations.": "",
+	"Hello, {{name}}": "",
+	"Help": "",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "",
+	"Host": "",
+	"How can I help you today?": "",
+	"How would you rate this response?": "",
+	"Hybrid Search": "",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "",
+	"Image Generation Engine": "",
+	"Image Settings": "",
+	"Images": "",
+	"Import Chats": "",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "",
+	"Import Presets": "",
+	"Import Prompts": "",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "",
+	"Input commands": "",
+	"Install from Github URL": "",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "",
+	"Invalid file format.": "",
+	"Invalid Tag": "",
+	"January": "",
+	"Jina API Key": "",
+	"join our Discord for help.": "",
+	"JSON": "",
+	"JSON Preview": "",
+	"July": "",
+	"June": "",
+	"JWT Expiration": "",
+	"JWT Token": "",
+	"Keep Alive": "",
+	"Key": "",
+	"Keyboard shortcuts": "",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "",
+	"Last Active": "",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "",
+	"Made by OpenWebUI Community": "",
+	"Make sure to enclose them with": "",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "",
+	"March": "",
+	"Max Tokens (num_predict)": "",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
+	"May": "",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
+	"Min P": "",
+	"Minimum Score": "",
+	"Mirostat": "",
+	"Mirostat Eta": "",
+	"Mirostat Tau": "",
+	"MMMM DD, YYYY": "",
+	"MMMM DD, YYYY HH:mm": "",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "",
+	"Model '{{modelTag}}' is already in queue for downloading.": "",
+	"Model {{modelId}} not found": "",
+	"Model {{modelName}} is not vision capable": "",
+	"Model {{name}} is now {{status}}": "",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
+	"Model Filtering": "",
+	"Model ID": "",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "",
+	"Model Params": "",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "",
+	"Models": "",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "",
+	"Name": "",
+	"Name your knowledge base": "",
+	"New Chat": "",
+	"New folder": "",
+	"New Password": "",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "",
+	"No search query generated": "",
+	"No source available": "",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "",
+	"Not factually correct": "",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
+	"Notes": "",
+	"Notifications": "",
+	"November": "",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "",
+	"Off": "",
+	"Okay, Let's Go!": "",
+	"OLED Dark": "",
+	"Ollama": "",
+	"Ollama API": "",
+	"Ollama API disabled": "",
+	"Ollama API settings updated": "",
+	"Ollama Version": "",
+	"On": "",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "",
+	"OpenAI API": "",
+	"OpenAI API Config": "",
+	"OpenAI API Key is required.": "",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "",
+	"or": "",
+	"Organize your users": "",
+	"Other": "",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "",
+	"PDF Extract Images (OCR)": "",
+	"pending": "",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "",
+	"Permissions": "",
+	"Personalization": "",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "",
+	"Playground": "",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "",
+	"Previous 7 days": "",
+	"Profile Image": "",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
+	"Prompt Content": "",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "",
+	"Prompt updated successfully": "",
+	"Prompts": "",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "",
+	"Query Generation Prompt": "",
+	"Query Params": "",
+	"RAG Template": "",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "",
+	"Record voice": "",
+	"Redirecting you to OpenWebUI Community": "",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "",
+	"Regenerate": "",
+	"Release Notes": "",
+	"Relevance": "",
+	"Remove": "",
+	"Remove Model": "",
+	"Rename": "",
+	"Reorder Models": "",
+	"Repeat Last N": "",
+	"Request Mode": "",
+	"Reranking Model": "",
+	"Reranking model disabled": "",
+	"Reranking model set to \"{{reranking_model}}\"": "",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "",
+	"Rosé Pine": "",
+	"Rosé Pine Dawn": "",
+	"RTL": "",
+	"Run": "",
+	"Running": "",
+	"Save": "",
+	"Save & Create": "",
+	"Save & Update": "",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "",
+	"Search a model": "",
+	"Search Base": "",
+	"Search Chats": "",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "",
+	"Search options": "",
+	"Search Prompts": "",
+	"Search Result Count": "",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "",
+	"Searched {{count}} sites_other": "",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "",
+	"See readme.md for instructions": "",
+	"See what's new": "",
+	"Seed": "",
+	"Select a base model": "",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "",
+	"Select a pipeline": "",
+	"Select a pipeline url": "",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "",
+	"Semantic distance to query": "",
+	"Send": "",
+	"Send a Message": "",
+	"Send message": "",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "",
+	"Serper API Key": "",
+	"Serply API Key": "",
+	"Serpstack API Key": "",
+	"Server connection verified": "",
+	"Set as default": "",
+	"Set CFG Scale": "",
+	"Set Default Model": "",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "",
+	"Set Image Size": "",
+	"Set reranking model (e.g. {{model}})": "",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "",
+	"Set Task Model": "",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "",
+	"Settings saved successfully!": "",
+	"Share": "",
+	"Share Chat": "",
+	"Share to OpenWebUI Community": "",
+	"Show": "",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "",
+	"Show your support!": "",
+	"Showcased creativity": "",
+	"Sign in": "",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "",
+	"Sign up": "",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "",
+	"Speech-to-Text Engine": "",
+	"Stop": "",
+	"Stop Sequence": "",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "",
+	"Subtitle (e.g. about the Roman Empire)": "",
+	"Success": "",
+	"Successfully updated.": "",
+	"Suggested": "",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "",
+	"System Instructions": "",
+	"System Prompt": "",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "",
+	"Temperature": "",
+	"Template": "",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "",
+	"Tfs Z": "",
+	"Thanks for your feedback!": "",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
+	"Title": "",
+	"Title (e.g. Tell me a fun fact)": "",
+	"Title Auto-Generation": "",
+	"Title cannot be an empty string.": "",
+	"Title Generation Prompt": "",
+	"TLS": "",
+	"To access the available model names for downloading,": "",
+	"To access the GGUF models available for downloading,": "",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "",
+	"Toggle settings": "",
+	"Toggle sidebar": "",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "",
+	"Top P": "",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "",
+	"TTS Model": "",
+	"TTS Settings": "",
+	"TTS Voice": "",
+	"Type": "",
+	"Type Hugging Face Resolve (Download) URL": "",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "",
+	"Update for the latest features and improvements.": "",
+	"Update password": "",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "",
+	"Upload Pipeline": "",
+	"Upload Progress": "",
+	"URL": "",
+	"URL Mode": "",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "",
+	"Valid time units:": "",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "",
+	"variable to have them replaced with clipboard content.": "",
+	"Version": "",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
+	"Web": "",
+	"Web API": "",
+	"Web Loader Settings": "",
+	"Web Search": "",
+	"Web Search Engine": "",
+	"Web Search Query Generation": "",
+	"Webhook URL": "",
+	"WebUI Settings": "",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "",
+	"You": "",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "",
+	"You have shared this chat": "",
+	"You're a helpful assistant.": "",
+	"You're now logged in.": "",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "",
+	"Youtube Loader Settings": ""
+}
diff --git a/src/lib/i18n/locales/en-US/translation.json b/src/lib/i18n/locales/en-US/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..99575141a355d7de670082371a1667b3e022599f
--- /dev/null
+++ b/src/lib/i18n/locales/en-US/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "",
+	"(latest)": "",
+	"{{ models }}": "",
+	"{{user}}'s Chats": "",
+	"{{webUIName}} Backend Required": "",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "",
+	"a user": "",
+	"About": "",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "",
+	"Account Activation Pending": "",
+	"Accurate information": "",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "",
+	"Add a tag": "",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "",
+	"Add Files": "",
+	"Add Group": "",
+	"Add Memory": "",
+	"Add Model": "",
+	"Add Tag": "",
+	"Add Tags": "",
+	"Add text content": "",
+	"Add User": "",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "",
+	"admin": "",
+	"Admin": "",
+	"Admin Panel": "",
+	"Admin Settings": "",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "",
+	"Advanced Params": "",
+	"All chats": "",
+	"All Documents": "",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "",
+	"and": "",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "",
+	"API Base URL": "",
+	"API Key": "",
+	"API Key created.": "",
+	"API keys": "",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "",
+	"Archive": "",
+	"Archive All Chats": "",
+	"Archived Chats": "",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "",
+	"Attention to detail": "",
+	"Attribute for Username": "",
+	"Audio": "",
+	"August": "",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "",
+	"Auto-playback response": "",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "",
+	"AUTOMATIC1111 Base URL is required.": "",
+	"Available list": "",
+	"available!": "",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "",
+	"Bad Response": "",
+	"Banners": "",
+	"Base Model (From)": "",
+	"Batch Size (num_batch)": "",
+	"before": "",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "",
+	"Capabilities": "",
+	"Certificate Path": "",
+	"Change Password": "",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "",
+	"Chat Controls": "",
+	"Chat direction": "",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "",
+	"Check Again": "",
+	"Check for updates": "",
+	"Checking for updates...": "",
+	"Choose a model before saving...": "",
+	"Chunk Overlap": "",
+	"Chunk Params": "",
+	"Chunk Size": "",
+	"Ciphers": "",
+	"Citation": "",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "",
+	"Click here to": "",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "",
+	"Click here to select a csv file.": "",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "",
+	"Click on the user role button to change a user's role.": "",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "",
+	"Close": "",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "",
+	"Color": "",
+	"ComfyUI": "",
+	"ComfyUI Base URL": "",
+	"ComfyUI Base URL is required.": "",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "",
+	"Completions": "",
+	"Concurrent Requests": "",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "",
+	"Confirm your action": "",
+	"Connections": "",
+	"Contact Admin for WebUI Access": "",
+	"Content": "",
+	"Content Extraction": "",
+	"Context Length": "",
+	"Continue Response": "",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "",
+	"Copied to clipboard": "",
+	"Copy": "",
+	"Copy last code block": "",
+	"Copy last response": "",
+	"Copy Link": "",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "",
+	"Create Account": "",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "",
+	"Create new secret key": "",
+	"Created at": "",
+	"Created At": "",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "",
+	"Current Password": "",
+	"Custom": "",
+	"Dark": "",
+	"Database": "",
+	"December": "",
+	"Default": "",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "",
+	"Default Model": "",
+	"Default model updated": "",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "",
+	"Delete": "",
+	"Delete a model": "",
+	"Delete All Chats": "",
+	"Delete All Models": "",
+	"Delete chat": "",
+	"Delete Chat": "",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "",
+	"Delete tool?": "",
+	"Delete User": "",
+	"Deleted {{deleteModelTag}}": "",
+	"Deleted {{name}}": "",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "",
+	"Didn't fully follow instructions": "",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "",
+	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "",
+	"Documentation": "",
+	"Documents": "",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "",
+	"Don't have an account?": "",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "",
+	"Done": "",
+	"Download": "",
+	"Download canceled": "",
+	"Download Database": "",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "",
+	"Enable Web Search": "",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
+	"Enter {{role}} message here": "",
+	"Enter a detail about yourself for your LLMs to recall": "",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "",
+	"Enter Chunk Size": "",
+	"Enter description": "",
+	"Enter Github Raw URL": "",
+	"Enter Google PSE API Key": "",
+	"Enter Google PSE Engine Id": "",
+	"Enter Image Size (e.g. 512x512)": "",
+	"Enter Jina API Key": "",
+	"Enter language codes": "",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "",
+	"Enter Seed": "",
+	"Enter Serper API Key": "",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "",
+	"Enter URL (e.g. http://localhost:11434)": "",
+	"Enter Your Email": "",
+	"Enter Your Full Name": "",
+	"Enter your message": "",
+	"Enter Your Password": "",
+	"Enter Your Role": "",
+	"Enter Your Username": "",
+	"Error": "",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "",
+	"Explore the cosmos": "",
+	"Export": "",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "",
+	"Export chat (.json)": "",
+	"Export Chats": "",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "",
+	"Export Presets": "",
+	"Export Prompts": "",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "",
+	"Failed to read clipboard contents": "",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "",
+	"File not found.": "",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Fluidly stream large external response chunks": "",
+	"Focus chat input": "",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "",
+	"General Settings": "",
+	"Generate Image": "",
+	"Generating search query": "",
+	"Generation Info": "",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "",
+	"Google PSE API Key": "",
+	"Google PSE Engine Id": "",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "",
+	"Haptic Feedback": "",
+	"has no conversations.": "",
+	"Hello, {{name}}": "",
+	"Help": "",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "",
+	"Host": "",
+	"How can I help you today?": "",
+	"How would you rate this response?": "",
+	"Hybrid Search": "",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "",
+	"Image Generation Engine": "",
+	"Image Settings": "",
+	"Images": "",
+	"Import Chats": "",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "",
+	"Import Presets": "",
+	"Import Prompts": "",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "",
+	"Input commands": "",
+	"Install from Github URL": "",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "",
+	"Invalid file format.": "",
+	"Invalid Tag": "",
+	"January": "",
+	"Jina API Key": "",
+	"join our Discord for help.": "",
+	"JSON": "",
+	"JSON Preview": "",
+	"July": "",
+	"June": "",
+	"JWT Expiration": "",
+	"JWT Token": "",
+	"Keep Alive": "",
+	"Key": "",
+	"Keyboard shortcuts": "",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "",
+	"Last Active": "",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "",
+	"Made by OpenWebUI Community": "",
+	"Make sure to enclose them with": "",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "",
+	"March": "",
+	"Max Tokens (num_predict)": "",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
+	"May": "",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
+	"Min P": "",
+	"Minimum Score": "",
+	"Mirostat": "",
+	"Mirostat Eta": "",
+	"Mirostat Tau": "",
+	"MMMM DD, YYYY": "",
+	"MMMM DD, YYYY HH:mm": "",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "",
+	"Model '{{modelTag}}' is already in queue for downloading.": "",
+	"Model {{modelId}} not found": "",
+	"Model {{modelName}} is not vision capable": "",
+	"Model {{name}} is now {{status}}": "",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
+	"Model Filtering": "",
+	"Model ID": "",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "",
+	"Model Params": "",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "",
+	"Models": "",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "",
+	"Name": "",
+	"Name your knowledge base": "",
+	"New Chat": "",
+	"New folder": "",
+	"New Password": "",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "",
+	"No search query generated": "",
+	"No source available": "",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "",
+	"Not factually correct": "",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
+	"Notes": "",
+	"Notifications": "",
+	"November": "",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "",
+	"Off": "",
+	"Okay, Let's Go!": "",
+	"OLED Dark": "",
+	"Ollama": "",
+	"Ollama API": "",
+	"Ollama API disabled": "",
+	"Ollama API settings updated": "",
+	"Ollama Version": "",
+	"On": "",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "",
+	"OpenAI API": "",
+	"OpenAI API Config": "",
+	"OpenAI API Key is required.": "",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "",
+	"or": "",
+	"Organize your users": "",
+	"Other": "",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "",
+	"PDF Extract Images (OCR)": "",
+	"pending": "",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "",
+	"Permissions": "",
+	"Personalization": "",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "",
+	"Playground": "",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "",
+	"Previous 7 days": "",
+	"Profile Image": "",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
+	"Prompt Content": "",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "",
+	"Prompt updated successfully": "",
+	"Prompts": "",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "",
+	"Query Generation Prompt": "",
+	"Query Params": "",
+	"RAG Template": "",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "",
+	"Record voice": "",
+	"Redirecting you to OpenWebUI Community": "",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "",
+	"Regenerate": "",
+	"Release Notes": "",
+	"Relevance": "",
+	"Remove": "",
+	"Remove Model": "",
+	"Rename": "",
+	"Reorder Models": "",
+	"Repeat Last N": "",
+	"Request Mode": "",
+	"Reranking Model": "",
+	"Reranking model disabled": "",
+	"Reranking model set to \"{{reranking_model}}\"": "",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "",
+	"Rosé Pine": "",
+	"Rosé Pine Dawn": "",
+	"RTL": "",
+	"Run": "",
+	"Running": "",
+	"Save": "",
+	"Save & Create": "",
+	"Save & Update": "",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "",
+	"Search a model": "",
+	"Search Base": "",
+	"Search Chats": "",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "",
+	"Search options": "",
+	"Search Prompts": "",
+	"Search Result Count": "",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "",
+	"Searched {{count}} sites_other": "",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "",
+	"See readme.md for instructions": "",
+	"See what's new": "",
+	"Seed": "",
+	"Select a base model": "",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "",
+	"Select a pipeline": "",
+	"Select a pipeline url": "",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "",
+	"Semantic distance to query": "",
+	"Send": "",
+	"Send a Message": "",
+	"Send message": "",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "",
+	"Serper API Key": "",
+	"Serply API Key": "",
+	"Serpstack API Key": "",
+	"Server connection verified": "",
+	"Set as default": "",
+	"Set CFG Scale": "",
+	"Set Default Model": "",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "",
+	"Set Image Size": "",
+	"Set reranking model (e.g. {{model}})": "",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "",
+	"Set Task Model": "",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "",
+	"Settings saved successfully!": "",
+	"Share": "",
+	"Share Chat": "",
+	"Share to OpenWebUI Community": "",
+	"Show": "",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "",
+	"Show your support!": "",
+	"Showcased creativity": "",
+	"Sign in": "",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "",
+	"Sign up": "",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "",
+	"Speech-to-Text Engine": "",
+	"Stop": "",
+	"Stop Sequence": "",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "",
+	"Subtitle (e.g. about the Roman Empire)": "",
+	"Success": "",
+	"Successfully updated.": "",
+	"Suggested": "",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "",
+	"System Instructions": "",
+	"System Prompt": "",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "",
+	"Temperature": "",
+	"Template": "",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "",
+	"Tfs Z": "",
+	"Thanks for your feedback!": "",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
+	"Title": "",
+	"Title (e.g. Tell me a fun fact)": "",
+	"Title Auto-Generation": "",
+	"Title cannot be an empty string.": "",
+	"Title Generation Prompt": "",
+	"TLS": "",
+	"To access the available model names for downloading,": "",
+	"To access the GGUF models available for downloading,": "",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "",
+	"Toggle settings": "",
+	"Toggle sidebar": "",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "",
+	"Top P": "",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "",
+	"TTS Model": "",
+	"TTS Settings": "",
+	"TTS Voice": "",
+	"Type": "",
+	"Type Hugging Face Resolve (Download) URL": "",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "",
+	"Update for the latest features and improvements.": "",
+	"Update password": "",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "",
+	"Upload Pipeline": "",
+	"Upload Progress": "",
+	"URL": "",
+	"URL Mode": "",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "",
+	"Valid time units:": "",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "",
+	"variable to have them replaced with clipboard content.": "",
+	"Version": "",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
+	"Web": "",
+	"Web API": "",
+	"Web Loader Settings": "",
+	"Web Search": "",
+	"Web Search Engine": "",
+	"Web Search Query Generation": "",
+	"Webhook URL": "",
+	"WebUI Settings": "",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "",
+	"You": "",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "",
+	"You have shared this chat": "",
+	"You're a helpful assistant.": "",
+	"You're now logged in.": "",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "",
+	"Youtube Loader Settings": ""
+}
diff --git a/src/lib/i18n/locales/es-ES/translation.json b/src/lib/i18n/locales/es-ES/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..4f5ea446814d68819b586a676826c49318efe0a6
--- /dev/null
+++ b/src/lib/i18n/locales/es-ES/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' para evitar expiración.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(p.ej. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(p.ej. `sh webui.sh --api`)",
+	"(latest)": "(latest)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Chats de {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Servidor Requerido",
+	"*Prompt node ID(s) are required for image generation": "Los ID de nodo son requeridos para la generación de imágenes",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un modelo de tareas se utiliza cuando se realizan tareas como la generación de títulos para chats y consultas de búsqueda web",
+	"a user": "un usuario",
+	"About": "Sobre nosotros",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Cuenta",
+	"Account Activation Pending": "Activación de cuenta pendiente",
+	"Accurate information": "Información precisa",
+	"Actions": "Acciones",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Usuarios activos",
+	"Add": "Agregar",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Agregue una breve descripción sobre lo que hace este modelo",
+	"Add a tag": "Agregar una etiqueta",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "Agregar Contenido",
+	"Add content here": "Agrege contenido aquí",
+	"Add custom prompt": "Agregar un prompt personalizado",
+	"Add Files": "Agregar Archivos",
+	"Add Group": "",
+	"Add Memory": "Agregar Memoria",
+	"Add Model": "Agregar Modelo",
+	"Add Tag": "Agregar etiqueta",
+	"Add Tags": "agregar etiquetas",
+	"Add text content": "Añade contenido de texto",
+	"Add User": "Agregar Usuario",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ajustar estas opciones aplicará los cambios universalmente a todos los usuarios.",
+	"admin": "admin",
+	"Admin": "Admin",
+	"Admin Panel": "Panel de Administración",
+	"Admin Settings": "Configuración de Administrador",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Admins tienen acceso a todas las herramientas en todo momento; los usuarios necesitan herramientas asignadas por modelo en el espacio de trabajo.",
+	"Advanced Parameters": "Parámetros Avanzados",
+	"Advanced Params": "Parámetros avanzados",
+	"All chats": "",
+	"All Documents": "Todos los Documentos",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Permitir Borrar Chats",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Permitir voces no locales",
+	"Allow Temporary Chat": "Permitir Chat Temporal",
+	"Allow User Location": "Permitir Ubicación del Usuario",
+	"Allow Voice Interruption in Call": "Permitir interrupción de voz en llamada",
+	"Already have an account?": "¿Ya tienes una cuenta?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "un asistente",
+	"and": "y",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "y crear un nuevo enlace compartido.",
+	"API Base URL": "Dirección URL de la API",
+	"API Key": "Clave de la API ",
+	"API Key created.": "Clave de la API creada.",
+	"API keys": "Claves de la API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Abril",
+	"Archive": "Archivar",
+	"Archive All Chats": "Archivar todos los chats",
+	"Archived Chats": "Chats archivados",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "¿Está seguro?",
+	"Arena Models": "",
+	"Artifacts": "Artefactos",
+	"Ask a question": "Haz una pregunta",
+	"Assistant": "",
+	"Attach file": "Adjuntar archivo",
+	"Attention to detail": "Detalle preciso",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Agosto",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Copiar respuesta automáticamente al portapapeles",
+	"Auto-playback response": "Respuesta de reproducción automática",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "Cadena de autenticación de API",
+	"AUTOMATIC1111 Base URL": "Dirección URL de AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "La dirección URL de AUTOMATIC1111 es requerida.",
+	"Available list": "Lista disponible",
+	"available!": "¡disponible!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "Región de Azure",
+	"Back": "Volver",
+	"Bad Response": "Respuesta incorrecta",
+	"Banners": "Banners",
+	"Base Model (From)": "Modelo base (desde)",
+	"Batch Size (num_batch)": "Tamaño del Batch (num_batch)",
+	"before": "antes",
+	"Being lazy": "Ser perezoso",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Clave de API de Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Desactivar la verificación SSL para sitios web",
+	"Call": "Llamada",
+	"Call feature is not supported when using Web STT engine": "La funcionalidad de llamada no puede usarse junto con el motor de STT Web",
+	"Camera": "Cámara",
+	"Cancel": "Cancelar",
+	"Capabilities": "Capacidades",
+	"Certificate Path": "",
+	"Change Password": "Cambia la Contraseña",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "Imágen de fondo del Chat",
+	"Chat Bubble UI": "Burbuja de chat UI",
+	"Chat Controls": "Controles de chat",
+	"Chat direction": "Dirección del Chat",
+	"Chat Overview": "Vista general del chat",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chats",
+	"Check Again": "Verifica de nuevo",
+	"Check for updates": "Verificar actualizaciones",
+	"Checking for updates...": "Verificando actualizaciones...",
+	"Choose a model before saving...": "Escoge un modelo antes de guardar los cambios...",
+	"Chunk Overlap": "Superposición de fragmentos",
+	"Chunk Params": "Parámetros de fragmentos",
+	"Chunk Size": "Tamaño de fragmentos",
+	"Ciphers": "",
+	"Citation": "Cita",
+	"Clear memory": "Liberar memoria",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Presiona aquí para obtener ayuda.",
+	"Click here to": "Presiona aquí para",
+	"Click here to download user import template file.": "Presiona aquí para descargar el archivo de plantilla de importación de usuario.",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Presiona aquí para seleccionar",
+	"Click here to select a csv file.": "Presiona aquí para seleccionar un archivo csv.",
+	"Click here to select a py file.": "Presiona aquí para seleccionar un archivo py.",
+	"Click here to upload a workflow.json file.": "Presiona aquí para subir un archivo workflow.json.",
+	"click here.": "Presiona aquí.",
+	"Click on the user role button to change a user's role.": "Presiona en el botón de roles del usuario para cambiar su rol.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisos de escritura del portapapeles denegados. Por favor, comprueba las configuraciones de tu navegador para otorgar el acceso necesario.",
+	"Clone": "Clonar",
+	"Close": "Cerrar",
+	"Code execution": "",
+	"Code formatted successfully": "Se ha formateado correctamente el código.",
+	"Collection": "Colección",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL es requerido.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "Nodos para ComfyUI Workflow",
+	"Command": "Comando",
+	"Completions": "",
+	"Concurrent Requests": "Solicitudes simultáneas",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Confirmar",
+	"Confirm Password": "Confirmar Contraseña",
+	"Confirm your action": "Confirma tu acción",
+	"Connections": "Conexiones",
+	"Contact Admin for WebUI Access": "Contacta el administrador para obtener acceso al WebUI",
+	"Content": "Contenido",
+	"Content Extraction": "Extracción de contenido",
+	"Context Length": "Longitud del contexto",
+	"Continue Response": "Continuar Respuesta",
+	"Continue with {{provider}}": "Continuar con {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "Controles",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Copiado",
+	"Copied shared chat URL to clipboard!": "¡URL de chat compartido copiado al portapapeles!",
+	"Copied to clipboard": "Copiado al portapapeles",
+	"Copy": "Copiar",
+	"Copy last code block": "Copia el último bloque de código",
+	"Copy last response": "Copia la última respuesta",
+	"Copy Link": "Copiar enlace",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "¡La copia al portapapeles se ha realizado correctamente!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Crear un modelo",
+	"Create Account": "Crear una cuenta",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "Crear Conocimiento",
+	"Create new key": "Crear una nueva clave",
+	"Create new secret key": "Crear una nueva clave secreta",
+	"Created at": "Creado en",
+	"Created At": "Creado en",
+	"Created by": "Creado por",
+	"CSV Import": "Importa un CSV",
+	"Current Model": "Modelo Actual",
+	"Current Password": "Contraseña Actual",
+	"Custom": "Personalizado",
+	"Dark": "Oscuro",
+	"Database": "Base de datos",
+	"December": "Diciembre",
+	"Default": "Por defecto",
+	"Default (Open AI)": "Predeterminado (Open AI)",
+	"Default (SentenceTransformers)": "Predeterminado (SentenceTransformers)",
+	"Default Model": "Modelo predeterminado",
+	"Default model updated": "El modelo por defecto ha sido actualizado",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Sugerencias de mensajes por defecto",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Rol por defecto para usuarios",
+	"Delete": "Borrar",
+	"Delete a model": "Borra un modelo",
+	"Delete All Chats": "Eliminar todos los chats",
+	"Delete All Models": "",
+	"Delete chat": "Borrar chat",
+	"Delete Chat": "Borrar Chat",
+	"Delete chat?": "Borrar el chat?",
+	"Delete folder?": "",
+	"Delete function?": "Borrar la función?",
+	"Delete prompt?": "Borrar el prompt?",
+	"delete this link": "Borrar este enlace",
+	"Delete tool?": "Borrar la herramienta",
+	"Delete User": "Borrar Usuario",
+	"Deleted {{deleteModelTag}}": "Se borró {{deleteModelTag}}",
+	"Deleted {{name}}": "Eliminado {{nombre}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Descripción",
+	"Didn't fully follow instructions": "No siguió las instrucciones",
+	"Disabled": "Desactivado",
+	"Discover a function": "Descubre una función",
+	"Discover a model": "Descubrir un modelo",
+	"Discover a prompt": "Descubre un Prompt",
+	"Discover a tool": "Descubre una herramienta",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Descubre, descarga y explora funciones personalizadas",
+	"Discover, download, and explore custom prompts": "Descubre, descarga, y explora Prompts personalizados",
+	"Discover, download, and explore custom tools": "Descubre, descarga y explora herramientas personalizadas",
+	"Discover, download, and explore model presets": "Descubre, descarga y explora ajustes preestablecidos de modelos",
+	"Dismissible": "Desestimable",
+	"Display": "",
+	"Display Emoji in Call": "Muestra Emoji en llamada",
+	"Display the username instead of You in the Chat": "Mostrar el nombre de usuario en lugar de Usted en el chat",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "No instale funciones desde fuentes que no confíe totalmente.",
+	"Do not install tools from sources you do not fully trust.": "No instale herramientas desde fuentes que no confíe totalmente.",
+	"Document": "Documento",
+	"Documentation": "Documentación",
+	"Documents": "Documentos",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "no realiza ninguna conexión externa y sus datos permanecen seguros en su servidor alojado localmente.",
+	"Don't have an account?": "¿No tienes una cuenta?",
+	"don't install random functions from sources you don't trust.": "no instale funciones aleatorias desde fuentes que no confíe.",
+	"don't install random tools from sources you don't trust.": "no instale herramientas aleatorias desde fuentes que no confíe.",
+	"Don't like the style": "No te gusta el estilo?",
+	"Done": "Hecho",
+	"Download": "Descargar",
+	"Download canceled": "Descarga cancelada",
+	"Download Database": "Descarga la Base de Datos",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Suelta cualquier archivo aquí para agregarlo a la conversación",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p.ej. '30s','10m'. Unidades válidas de tiempo son 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Editar",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Editar Memoria",
+	"Edit User": "Editar Usuario",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Tamaño de Embedding",
+	"Embedding Model": "Modelo de Embedding",
+	"Embedding Model Engine": "Motor de Modelo de Embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Modelo de Embedding configurado a \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Habilitar el uso compartido de la comunidad",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Habilitar la calificación de los mensajes",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Habilitar Nuevos Registros",
+	"Enable Web Search": "Habilitar la búsqueda web",
+	"Enabled": "Activado",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Asegúrese de que su archivo CSV incluya 4 columnas en este orden: Nombre, Correo Electrónico, Contraseña, Rol.",
+	"Enter {{role}} message here": "Ingrese el mensaje {{role}} aquí",
+	"Enter a detail about yourself for your LLMs to recall": "Ingrese un detalle sobre usted para que sus LLMs recuerden",
+	"Enter api auth string (e.g. username:password)": "Ingrese la cadena de autorización de api (p.ej., nombre:contraseña)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Ingresa la clave de API de Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "Ingresa la escala de CFG (p.ej., 7.0)",
+	"Enter Chunk Overlap": "Ingresar superposición de fragmentos",
+	"Enter Chunk Size": "Ingrese el tamaño del fragmento",
+	"Enter description": "",
+	"Enter Github Raw URL": "Ingresa la URL sin procesar de Github",
+	"Enter Google PSE API Key": "Ingrese la clave API de Google PSE",
+	"Enter Google PSE Engine Id": "Introduzca el ID del motor PSE de Google",
+	"Enter Image Size (e.g. 512x512)": "Ingrese el tamaño de la imagen (p.ej. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Ingrese códigos de idioma",
+	"Enter Model ID": "Ingresa el ID del modelo",
+	"Enter model tag (e.g. {{modelTag}})": "Ingrese la etiqueta del modelo (p.ej. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Ingrese el número de pasos (p.ej., 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Ingrese el sampler (p.ej., Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Ingrese el planificador (p.ej., Karras)",
+	"Enter Score": "Ingrese la puntuación",
+	"Enter SearchApi API Key": "Ingrese la Clave API de SearchApi",
+	"Enter SearchApi Engine": "Ingrese el motor de SearchApi",
+	"Enter Searxng Query URL": "Introduzca la URL de consulta de Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Ingrese la clave API de Serper",
+	"Enter Serply API Key": "Ingrese la clave API de Serply",
+	"Enter Serpstack API Key": "Ingrese la clave API de Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Ingrese la secuencia de parada",
+	"Enter system prompt": "Ingrese el prompt del sistema",
+	"Enter Tavily API Key": "Ingrese la clave API de Tavily",
+	"Enter Tika Server URL": "Ingrese la URL del servidor Tika",
+	"Enter Top K": "Ingrese el Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Ingrese la URL (p.ej., http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Ingrese la URL (p.ej., http://localhost:11434)",
+	"Enter Your Email": "Ingrese su correo electrónico",
+	"Enter Your Full Name": "Ingrese su nombre completo",
+	"Enter your message": "Ingrese su mensaje",
+	"Enter Your Password": "Ingrese su contraseña",
+	"Enter Your Role": "Ingrese su rol",
+	"Enter Your Username": "",
+	"Error": "Error",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Experimental",
+	"Explore the cosmos": "",
+	"Export": "Exportar",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exportar todos los chats (Todos los usuarios)",
+	"Export chat (.json)": "Exportar chat (.json)",
+	"Export Chats": "Exportar Chats",
+	"Export Config to JSON File": "",
+	"Export Functions": "Exportar Funciones",
+	"Export Models": "Exportar Modelos",
+	"Export Presets": "",
+	"Export Prompts": "Exportar Prompts",
+	"Export to CSV": "",
+	"Export Tools": "Exportar Herramientas",
+	"External Models": "Modelos Externos",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "No se pudo crear la clave API.",
+	"Failed to read clipboard contents": "No se pudo leer el contenido del portapapeles",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Falla al actualizar los ajustes",
+	"Failed to upload file.": "Falla al subir el archivo.",
+	"February": "Febrero",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Libre de agregar detalles específicos",
+	"File": "Archivo",
+	"File added successfully.": "Archivo agregado correctamente.",
+	"File content updated successfully.": "Contenido del archivo actualizado correctamente.",
+	"File Mode": "Modo de archivo",
+	"File not found.": "Archivo no encontrado.",
+	"File removed successfully.": "Archivo eliminado correctamente.",
+	"File size should not exceed {{maxSize}} MB.": "Tamaño del archivo no debe exceder {{maxSize}} MB.",
+	"Files": "Archivos",
+	"Filter is now globally disabled": "El filtro ahora está desactivado globalmente",
+	"Filter is now globally enabled": "El filtro ahora está habilitado globalmente",
+	"Filters": "Filtros",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Se detectó suplantación de huellas: No se pueden usar las iniciales como avatar. Por defecto se utiliza la imagen de perfil predeterminada.",
+	"Fluidly stream large external response chunks": "Transmita con fluidez grandes fragmentos de respuesta externa",
+	"Focus chat input": "Enfoca la entrada del chat",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Siguió las instrucciones perfectamente",
+	"Forge new paths": "",
+	"Form": "De",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Penalización de frecuencia",
+	"Function": "",
+	"Function created successfully": "Función creada exitosamente",
+	"Function deleted successfully": "Función borrada exitosamente",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "La función ahora está desactivada globalmente",
+	"Function is now globally enabled": "La función está habilitada globalmente",
+	"Function Name": "",
+	"Function updated successfully": "Función actualizada exitosamente",
+	"Functions": "Funciones",
+	"Functions allow arbitrary code execution": "Funciones habilitan la ejecución de código arbitrario",
+	"Functions allow arbitrary code execution.": "Funciones habilitan la ejecución de código arbitrario.",
+	"Functions imported successfully": "Funciones importadas exitosamente",
+	"General": "General",
+	"General Settings": "Opciones Generales",
+	"Generate Image": "Generar imagen",
+	"Generating search query": "Generación de consultas de búsqueda",
+	"Generation Info": "Información de Generación",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Global",
+	"Good Response": "Buena Respuesta",
+	"Google PSE API Key": "Clave API de Google PSE",
+	"Google PSE Engine Id": "ID del motor PSE de Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Retroalimentación háptica",
+	"has no conversations.": "no tiene conversaciones.",
+	"Hello, {{name}}": "Hola, {{name}}",
+	"Help": "Ayuda",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Esconder",
+	"Host": "",
+	"How can I help you today?": "¿Cómo puedo ayudarte hoy?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Búsqueda Híbrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Aseguro que he leído y entiendo las implicaciones de mi acción. Estoy consciente de los riesgos asociados con la ejecución de código arbitrario y he verificado la confianza de la fuente.",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generación de imágenes (experimental)",
+	"Image Generation Engine": "Motor de generación de imágenes",
+	"Image Settings": "Ajustes de la Imágen",
+	"Images": "Imágenes",
+	"Import Chats": "Importar chats",
+	"Import Config from JSON File": "",
+	"Import Functions": "Importar Funciones",
+	"Import Models": "Importar modelos",
+	"Import Presets": "",
+	"Import Prompts": "Importar Prompts",
+	"Import Tools": "Importar Herramientas",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Incluir el indicador `--api-auth` al ejecutar stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Incluir el indicador `--api` al ejecutar stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Información",
+	"Input commands": "Ingresar comandos",
+	"Install from Github URL": "Instalar desde la URL de Github",
+	"Instant Auto-Send After Voice Transcription": "Auto-Enviar Después de la Transcripción de Voz",
+	"Interface": "Interfaz",
+	"Invalid file format.": "",
+	"Invalid Tag": "Etiqueta Inválida",
+	"January": "Enero",
+	"Jina API Key": "",
+	"join our Discord for help.": "Únase a nuestro Discord para obtener ayuda.",
+	"JSON": "JSON",
+	"JSON Preview": "Vista previa de JSON",
+	"July": "Julio",
+	"June": "Junio",
+	"JWT Expiration": "Expiración del JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Mantener Vivo",
+	"Key": "",
+	"Keyboard shortcuts": "Atajos de teclado",
+	"Knowledge": "Conocimiento",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Conocimiento creado exitosamente.",
+	"Knowledge deleted successfully.": "Conocimiento eliminado exitosamente.",
+	"Knowledge reset successfully.": "Conocimiento restablecido exitosamente.",
+	"Knowledge updated successfully": "Conocimiento actualizado exitosamente.",
+	"Label": "",
+	"Landing Page Mode": "Modo de Página de Inicio",
+	"Language": "Lenguaje",
+	"Last Active": "Última Actividad",
+	"Last Modified": "Modificado por última vez",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "Deje vacío para ilimitado",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Deje vacío para usar el propmt predeterminado, o ingrese un propmt personalizado",
+	"Light": "Claro",
+	"Listening...": "Escuchando...",
+	"LLMs can make mistakes. Verify important information.": "Los LLM pueden cometer errores. Verifica la información importante.",
+	"Local": "",
+	"Local Models": "Modelos locales",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Hecho por la comunidad de OpenWebUI",
+	"Make sure to enclose them with": "Asegúrese de adjuntarlos con",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Asegúrese de exportar un archivo workflow.json en formato API desde ComfyUI.",
+	"Manage": "Gestionar",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Administrar Pipelines",
+	"March": "Marzo",
+	"Max Tokens (num_predict)": "Máximo de fichas (num_predict)",
+	"Max Upload Count": "Cantidad máxima de cargas",
+	"Max Upload Size": "Tamaño máximo de Cargas",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Se pueden descargar un máximo de 3 modelos simultáneamente. Por favor, inténtelo de nuevo más tarde.",
+	"May": "Mayo",
+	"Memories accessible by LLMs will be shown here.": "Las memorias accesibles por los LLMs se mostrarán aquí.",
+	"Memory": "Memoria",
+	"Memory added successfully": "Memoria añadida correctamente",
+	"Memory cleared successfully": "Memoria liberada correctamente",
+	"Memory deleted successfully": "Memoria borrada correctamente",
+	"Memory updated successfully": "Memoria actualizada correctamente",
+	"Merge Responses": "Fusionar Respuestas",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Los mensajes que envíe después de crear su enlace no se compartirán. Los usuarios con el enlace podrán ver el chat compartido.",
+	"Min P": "",
+	"Minimum Score": "Puntuación mínima",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "El modelo '{{modelName}}' se ha descargado correctamente.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "El modelo '{{modelTag}}' ya está en cola para descargar.",
+	"Model {{modelId}} not found": "El modelo {{modelId}} no fue encontrado",
+	"Model {{modelName}} is not vision capable": "El modelo {{modelName}} no es capaz de ver",
+	"Model {{name}} is now {{status}}": "El modelo {{name}} ahora es {{status}}",
+	"Model accepts image inputs": "El modelo acepta entradas de imagenes",
+	"Model created successfully!": "Modelo creado correctamente!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Se detectó la ruta del sistema de archivos del modelo. Se requiere el nombre corto del modelo para la actualización, no se puede continuar.",
+	"Model Filtering": "",
+	"Model ID": "ID del modelo",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modelo no seleccionado",
+	"Model Params": "Parámetros del modelo",
+	"Model Permissions": "",
+	"Model updated successfully": "Modelo actualizado correctamente",
+	"Modelfile Content": "Contenido del Modelfile",
+	"Models": "Modelos",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Más",
+	"Name": "Nombre",
+	"Name your knowledge base": "",
+	"New Chat": "Nuevo Chat",
+	"New folder": "",
+	"New Password": "Nueva Contraseña",
+	"No content found": "",
+	"No content to speak": "No hay contenido para hablar",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Ningún archivo fué seleccionado",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "No se encontró contenido HTML, CSS, o JavaScript.",
+	"No knowledge found": "No se encontró ningún conocimiento",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "No se han encontrado resultados",
+	"No search query generated": "No se ha generado ninguna consulta de búsqueda",
+	"No source available": "No hay fuente disponible",
+	"No users were found.": "",
+	"No valves to update": "No valves para actualizar",
+	"None": "Ninguno",
+	"Not factually correct": "No es correcto en todos los aspectos",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Si estableces una puntuación mínima, la búsqueda sólo devolverá documentos con una puntuación mayor o igual a la puntuación mínima.",
+	"Notes": "",
+	"Notifications": "Notificaciones",
+	"November": "Noviembre",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Octubre",
+	"Off": "Desactivado",
+	"Okay, Let's Go!": "Bien, ¡Vamos!",
+	"OLED Dark": "OLED oscuro",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API de Ollama deshabilitada",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versión de Ollama",
+	"On": "Activado",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Sólo se permiten caracteres alfanuméricos y guiones en la cadena de comando.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Solo se pueden editar las colecciones, crear una nueva base de conocimientos para editar / añadir documentos",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "¡Ups! Parece que la URL no es válida. Vuelva a verificar e inténtelo nuevamente.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "¡Ups! Estás utilizando un método no compatible (solo frontend). Por favor ejecute la WebUI desde el backend.",
+	"Open file": "Abrir archivo",
+	"Open in full screen": "Abrir en pantalla completa",
+	"Open new chat": "Abrir nuevo chat",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versión de Open WebUI (v{{OPEN_WEBUI_VERSION}}) es inferior a la versión requerida (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API Config",
+	"OpenAI API Key is required.": "La Clave de la API de OpenAI es requerida.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Clave de OpenAI es requerida.",
+	"or": "o",
+	"Organize your users": "",
+	"Other": "Otro",
+	"OUTPUT": "",
+	"Output format": "Formato de salida",
+	"Overview": "Vista general",
+	"page": "página",
+	"Password": "Contraseña",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF document (.pdf)",
+	"PDF Extract Images (OCR)": "Extraer imágenes de PDF (OCR)",
+	"pending": "pendiente",
+	"Permission denied when accessing media devices": "Permiso denegado al acceder a los dispositivos",
+	"Permission denied when accessing microphone": "Permiso denegado al acceder a la micrófono",
+	"Permission denied when accessing microphone: {{error}}": "Permiso denegado al acceder al micrófono: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalización",
+	"Pin": "Fijar",
+	"Pinned": "Fijado",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline borrada exitosamente",
+	"Pipeline downloaded successfully": "Pipeline descargada exitosamente",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Pipeline No Detectada",
+	"Pipelines Valves": "Tuberías Válvulas",
+	"Plain text (.txt)": "Texto plano (.txt)",
+	"Playground": "Patio de juegos",
+	"Please carefully review the following warnings:": "Por favor revise con cuidado los siguientes avisos:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "Por favor llene todos los campos.",
+	"Please select a model first.": "",
+	"Please select a reason": "Por favor seleccione una razón",
+	"Port": "",
+	"Positive attitude": "Actitud positiva",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Últimos 30 días",
+	"Previous 7 days": "Últimos 7 días",
+	"Profile Image": "Imagen de perfil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (por ejemplo, cuéntame una cosa divertida sobre el Imperio Romano)",
+	"Prompt Content": "Contenido del Prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Sugerencias de Prompts",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompts",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Extraer \"{{searchValue}}\" de Ollama.com",
+	"Pull a model from Ollama.com": "Obtener un modelo de Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parámetros de consulta",
+	"RAG Template": "Plantilla de RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Leer al oído",
+	"Record voice": "Grabar voz",
+	"Redirecting you to OpenWebUI Community": "Redireccionándote a la comunidad OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referirse a usted mismo como \"Usuario\" (por ejemplo, \"El usuario está aprendiendo Español\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Rechazado cuando no debería",
+	"Regenerate": "Regenerar",
+	"Release Notes": "Notas de la versión",
+	"Relevance": "",
+	"Remove": "Eliminar",
+	"Remove Model": "Eliminar modelo",
+	"Rename": "Renombrar",
+	"Reorder Models": "",
+	"Repeat Last N": "Repetir las últimas N",
+	"Request Mode": "Modo de petición",
+	"Reranking Model": "Modelo de reranking",
+	"Reranking model disabled": "Modelo de reranking deshabilitado",
+	"Reranking model set to \"{{reranking_model}}\"": "Modelo de reranking establecido en \"{{reranking_model}}\"",
+	"Reset": "Reiniciar",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Reiniciar Directorio de carga",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Las notificaciones de respuesta no pueden activarse debido a que los permisos del sitio web han sido denegados. Por favor, visite las configuraciones de su navegador para otorgar el acceso necesario.",
+	"Response splitting": "División de respuestas",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rol",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Ejecutar",
+	"Running": "Ejecutando",
+	"Save": "Guardar",
+	"Save & Create": "Guardar y Crear",
+	"Save & Update": "Guardar y Actualizar",
+	"Save As Copy": "Guardar como copia",
+	"Save Tag": "Guardar etiqueta",
+	"Saved": "Guardado",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Ya no se admite guardar registros de chat directamente en el almacenamiento de su navegador. Tómese un momento para descargar y eliminar sus registros de chat haciendo clic en el botón a continuación. No te preocupes, puedes volver a importar fácilmente tus registros de chat al backend a través de",
+	"Scroll to bottom when switching between branches": "Moverse a la parte inferior cuando se cambia entre ramas",
+	"Search": "Buscar",
+	"Search a model": "Buscar un modelo",
+	"Search Base": "",
+	"Search Chats": "Chats de búsqueda",
+	"Search Collection": "Buscar Colección",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Buscar Funciones",
+	"Search Knowledge": "Buscar Conocimiento",
+	"Search Models": "Buscar Modelos",
+	"Search options": "",
+	"Search Prompts": "Buscar Prompts",
+	"Search Result Count": "Recuento de resultados de búsqueda",
+	"Search the web": "",
+	"Search Tools": "Búsqueda de herramientas",
+	"SearchApi API Key": "Clave API de SearchApi",
+	"SearchApi Engine": "Motor de SearchApi",
+	"Searched {{count}} sites_one": "Buscado {{count}} sites_one",
+	"Searched {{count}} sites_many": "Buscado {{count}} sites_many",
+	"Searched {{count}} sites_other": "Buscó {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Buscando \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Buscando Conocimiento para \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng URL de consulta",
+	"See readme.md for instructions": "Vea el readme.md para instrucciones",
+	"See what's new": "Ver las novedades",
+	"Seed": "Seed",
+	"Select a base model": "Seleccionar un modelo base",
+	"Select a engine": "Busca un motor",
+	"Select a function": "Busca una función",
+	"Select a group": "",
+	"Select a model": "Selecciona un modelo",
+	"Select a pipeline": "Selección de una Pipeline",
+	"Select a pipeline url": "Selección de una dirección URL de Pipeline",
+	"Select a tool": "Busca una herramienta",
+	"Select Engine": "Selecciona Motor",
+	"Select Knowledge": "Selecciona Conocimiento",
+	"Select model": "Selecciona un modelo",
+	"Select only one model to call": "Selecciona sólo un modelo para llamar",
+	"Selected model(s) do not support image inputs": "Los modelos seleccionados no admiten entradas de imagen",
+	"Semantic distance to query": "",
+	"Send": "Enviar",
+	"Send a Message": "Enviar un Mensaje",
+	"Send message": "Enviar Mensaje",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Envia `stream_options: { include_usage: true }` en la solicitud.\nLos proveedores admitidos devolverán información de uso del token en la respuesta cuando se establezca.",
+	"September": "Septiembre",
+	"Serper API Key": "Clave API de Serper",
+	"Serply API Key": "Clave API de Serply",
+	"Serpstack API Key": "Clave API de Serpstack",
+	"Server connection verified": "Conexión del servidor verificada",
+	"Set as default": "Establecer por defecto",
+	"Set CFG Scale": "Establecer la escala CFG",
+	"Set Default Model": "Establecer modelo predeterminado",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Establecer modelo de embedding (ej. {{model}})",
+	"Set Image Size": "Establecer tamaño de imagen",
+	"Set reranking model (e.g. {{model}})": "Establecer modelo de reranking (ej. {{model}})",
+	"Set Sampler": "Establecer Sampler",
+	"Set Scheduler": "Establecer Programador",
+	"Set Steps": "Establecer Pasos",
+	"Set Task Model": "Establecer modelo de tarea",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Establecer la voz",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Configuración",
+	"Settings saved successfully!": "¡Configuración guardada con éxito!",
+	"Share": "Compartir",
+	"Share Chat": "Compartir Chat",
+	"Share to OpenWebUI Community": "Compartir con la comunidad OpenWebUI",
+	"Show": "Mostrar",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Mostrar detalles de administración en la capa de espera de la cuenta",
+	"Show shortcuts": "Mostrar atajos",
+	"Show your support!": "¡Muestra tu apoyo!",
+	"Showcased creativity": "Creatividad mostrada",
+	"Sign in": "Iniciar sesión",
+	"Sign in to {{WEBUI_NAME}}": "Iniciar sesión en {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Cerrar sesión",
+	"Sign up": "Crear una cuenta",
+	"Sign up to {{WEBUI_NAME}}": "Crear una cuenta en {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Iniciando sesión en {{WEBUI_NAME}}",
+	"Source": "Fuente",
+	"Speech Playback Speed": "Velocidad de reproducción de voz",
+	"Speech recognition error: {{error}}": "Error de reconocimiento de voz: {{error}}",
+	"Speech-to-Text Engine": "Motor de voz a texto",
+	"Stop": "",
+	"Stop Sequence": "Detener secuencia",
+	"Stream Chat Response": "",
+	"STT Model": "Modelo STT",
+	"STT Settings": "Configuraciones de STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtítulo (por ejemplo, sobre el Imperio Romano)",
+	"Success": "Éxito",
+	"Successfully updated.": "Actualizado exitosamente.",
+	"Suggested": "Sugerido",
+	"Support": "Soporte",
+	"Support this plugin:": "Brinda soporte a este plugin:",
+	"Sync directory": "Sincroniza directorio",
+	"System": "Sistema",
+	"System Instructions": "",
+	"System Prompt": "Prompt del sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Toca para interrumpir",
+	"Tavily API Key": "Clave API de Tavily",
+	"Tell us more:": "Dinos más:",
+	"Temperature": "Temperatura",
+	"Template": "Plantilla",
+	"Temporary Chat": "Chat temporal",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Motor de texto a voz",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "¡Gracias por tu retroalimentación!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Los desarrolladores de este plugin son apasionados voluntarios de la comunidad. Si encuentras este plugin útil, por favor considere contribuir a su desarrollo.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "El tamaño máximo del archivo en MB. Si el tamaño del archivo supera este límite, el archivo no se subirá.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "El número máximo de archivos que se pueden utilizar a la vez en chat. Si este límite es superado, los archivos no se subirán.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "El puntaje debe ser un valor entre 0.0 (0%) y 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Pensando...",
+	"This action cannot be undone. Do you wish to continue?": "Esta acción no se puede deshacer. ¿Desea continuar?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Esto garantiza que sus valiosas conversaciones se guarden de forma segura en su base de datos en el backend. ¡Gracias!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Esta es una característica experimental que puede no funcionar como se esperaba y está sujeto a cambios en cualquier momento.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": " Esta opción eliminará todos los archivos existentes en la colección y los reemplazará con nuevos archivos subidos.",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Esto eliminará",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Esto reseteará la base de conocimientos y sincronizará todos los archivos. ¿Desea continuar?",
+	"Thorough explanation": "Explicación exhaustiva",
+	"Tika": "Tika",
+	"Tika Server URL required.": "URL del servidor de Tika",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consejo: Actualice múltiples variables consecutivamente presionando la tecla tab en la entrada del chat después de cada reemplazo.",
+	"Title": "Título",
+	"Title (e.g. Tell me a fun fact)": "Título (por ejemplo, cuéntame una curiosidad)",
+	"Title Auto-Generation": "Generación automática de títulos",
+	"Title cannot be an empty string.": "El título no puede ser una cadena vacía.",
+	"Title Generation Prompt": "Prompt de generación de título",
+	"TLS": "",
+	"To access the available model names for downloading,": "Para acceder a los nombres de modelos disponibles para descargar,",
+	"To access the GGUF models available for downloading,": "Para acceder a los modelos GGUF disponibles para descargar,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acceder al interfaz de usuario web, por favor contacte al administrador. Los administradores pueden administrar los estados de los usuarios desde el panel de administración.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Para adjuntar la base de conocimientos aquí, agreguelas al área de trabajo \"Conocimiento\" primero.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Para seleccionar acciones aquí, agreguelas al área de trabajo \"Funciones\" primero.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Para seleccionar filtros aquí, agreguelos al área de trabajo \"Funciones\" primero.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Para seleccionar herramientas aquí, agreguelas al área de trabajo \"Herramientas\" primero.",
+	"Toast notifications for new updates": "",
+	"Today": "Hoy",
+	"Toggle settings": "Alternar configuración",
+	"Toggle sidebar": "Alternar barra lateral",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens a mantener en el contexto de actualización (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Herramienta creada con éxito",
+	"Tool deleted successfully": "Herramienta eliminada con éxito",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Herramienta importada con éxito",
+	"Tool Name": "",
+	"Tool updated successfully": "Herramienta actualizada con éxito",
+	"Tools": "Herramientas",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Las herramientas son un sistema de llamada de funciones con código arbitrario",
+	"Tools have a function calling system that allows arbitrary code execution": "Las herramientas tienen un sistema de llamadas de funciones que permite la ejecución de código arbitrario",
+	"Tools have a function calling system that allows arbitrary code execution.": "Las herramientas tienen un sistema de llamada de funciones que permite la ejecución de código arbitrario.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "¿Problemas para acceder a Ollama?",
+	"TTS Model": "Modelo TTS",
+	"TTS Settings": "Configuración de TTS",
+	"TTS Voice": "Voz del TTS",
+	"Type": "Tipo",
+	"Type Hugging Face Resolve (Download) URL": "Escriba la URL (Descarga) de Hugging Face Resolve",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "¡Uh oh! Hubo un problema al conectarse a {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Desanclar",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Actualizar",
+	"Update and Copy Link": "Actualizar y copiar enlace",
+	"Update for the latest features and improvements.": "Actualize para las últimas características e mejoras.",
+	"Update password": "Actualizar contraseña",
+	"Updated": "",
+	"Updated at": "Actualizado en",
+	"Updated At": "",
+	"Upload": "Subir",
+	"Upload a GGUF model": "Subir un modelo GGUF",
+	"Upload directory": "Directorio de carga",
+	"Upload files": "Subir archivos",
+	"Upload Files": "Subir archivos",
+	"Upload Pipeline": "Subir Pipeline",
+	"Upload Progress": "Progreso de carga",
+	"URL": "",
+	"URL Mode": "Modo de URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Utilize '#' en el prompt para cargar y incluir su conocimiento.",
+	"Use Gravatar": "Usar Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Usar Iniciales",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "usuario",
+	"User": "",
+	"User location successfully retrieved.": "Localización del usuario recuperada con éxito.",
+	"Username": "",
+	"Users": "Usuarios",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Utilizar",
+	"Valid time units:": "Unidades válidas de tiempo:",
+	"Valves": "Valves",
+	"Valves updated": "Valves actualizados",
+	"Valves updated successfully": "Valves actualizados con éxito",
+	"variable": "variable",
+	"variable to have them replaced with clipboard content.": "variable para reemplazarlos con el contenido del portapapeles.",
+	"Version": "Versión",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Versión {{selectedVersion}} de {{totalVersions}}",
+	"Visibility": "",
+	"Voice": "Voz",
+	"Voice Input": "",
+	"Warning": "Advertencia",
+	"Warning:": "Advertencia:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Advertencia: Si actualiza o cambia su modelo de inserción, necesitará volver a importar todos los documentos.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Web Loader Settings",
+	"Web Search": "Búsqueda en la Web",
+	"Web Search Engine": "Motor de búsqueda web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "Configuración del WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Novedades en",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (Local)",
+	"Why?": "",
+	"Widescreen Mode": "Modo de pantalla ancha",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Espacio de trabajo",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escribe una sugerencia para un prompt (por ejemplo, ¿quién eres?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Escribe un resumen en 50 palabras que resuma [tema o palabra clave].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Ayer",
+	"You": "Usted",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Puede personalizar sus interacciones con LLMs añadiendo memorias a través del botón 'Gestionar' debajo, haciendo que sean más útiles y personalizados para usted.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "No tiene conversaciones archivadas.",
+	"You have shared this chat": "Usted ha compartido esta conversación",
+	"You're a helpful assistant.": "Usted es un asistente útil.",
+	"You're now logged in.": "Usted ahora está conectado.",
+	"Your account status is currently pending activation.": "El estado de su cuenta actualmente se encuentra pendiente de activación.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Su contribución completa irá directamente a el desarrollador del plugin; Open WebUI no toma ningun porcentaje. Sin embargo, la plataforma de financiación elegida podría tener sus propias tarifas.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Configuración del cargador de Youtube"
+}
diff --git a/src/lib/i18n/locales/eu-ES/translation.json b/src/lib/i18n/locales/eu-ES/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..73fb56a40065161236dd5be6041afc61f98f3cd6
--- /dev/null
+++ b/src/lib/i18n/locales/eu-ES/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' edo '-1' iraungitzerik ez izateko.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(adib. `sh webui.sh --api --api-auth erabiltzaile_pasahitza`)",
+	"(e.g. `sh webui.sh --api`)": "(adib. `sh webui.sh --api`)",
+	"(latest)": "(azkena)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}}-ren Txatak",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend-a Beharrezkoa",
+	"*Prompt node ID(s) are required for image generation": "Prompt nodoaren IDa(k) beharrezkoak dira irudiak sortzeko",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Bertsio berri bat (v{{LATEST_VERSION}}) eskuragarri dago orain.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Ataza eredua erabiltzen da txatentzako izenburuak eta web bilaketa kontsultak sortzeko bezalako atazak egitean",
+	"a user": "erabiltzaile bat",
+	"About": "Honi buruz",
+	"Access": "Sarbidea",
+	"Access Control": "Sarbide Kontrola",
+	"Accessible to all users": "Erabiltzaile guztientzat eskuragarri",
+	"Account": "Kontua",
+	"Account Activation Pending": "Kontuaren Aktibazioa Zain",
+	"Accurate information": "Informazio zehatza",
+	"Actions": "Ekintzak",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "\"/{{COMMAND}}\" idatziz aktibatu komando hau txataren sarreran.",
+	"Active Users": "Erabiltzaile Aktiboak",
+	"Add": "Gehitu",
+	"Add a model ID": "Gehitu eredu ID bat",
+	"Add a short description about what this model does": "Gehitu eredu honek egiten duenaren deskribapen labur bat",
+	"Add a tag": "Gehitu etiketa bat",
+	"Add Arena Model": "Gehitu Arena Eredua",
+	"Add Connection": "Gehitu Konexioa",
+	"Add Content": "Gehitu Edukia",
+	"Add content here": "Gehitu edukia hemen",
+	"Add custom prompt": "Gehitu prompt pertsonalizatua",
+	"Add Files": "Gehitu Fitxategiak",
+	"Add Group": "Gehitu Taldea",
+	"Add Memory": "Gehitu Memoria",
+	"Add Model": "Gehitu Eredua",
+	"Add Tag": "Gehitu Etiketa",
+	"Add Tags": "Gehitu Etiketak",
+	"Add text content": "Gehitu testu edukia",
+	"Add User": "Gehitu Erabiltzailea",
+	"Add User Group": "Gehitu Erabiltzaile Taldea",
+	"Adjusting these settings will apply changes universally to all users.": "Ezarpen hauek aldatzeak aldaketak erabiltzaile guztiei aplikatuko dizkie.",
+	"admin": "administratzailea",
+	"Admin": "Administratzailea",
+	"Admin Panel": "Administrazio Panela",
+	"Admin Settings": "Administrazio Ezarpenak",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratzaileek tresna guztietarako sarbidea dute beti; erabiltzaileek lan-eremuan eredu bakoitzeko esleituak behar dituzte tresnak.",
+	"Advanced Parameters": "Parametro Aurreratuak",
+	"Advanced Params": "Parametro Aurreratuak",
+	"All chats": "Txat guztiak",
+	"All Documents": "Dokumentu Guztiak",
+	"All models deleted successfully": "Eredu guztiak ongi ezabatu dira",
+	"Allow Chat Delete": "Baimendu Txata Ezabatzea",
+	"Allow Chat Deletion": "Baimendu Txata Ezabatzea",
+	"Allow Chat Edit": "Baimendu Txata Editatzea",
+	"Allow File Upload": "Baimendu Fitxategiak Igotzea",
+	"Allow non-local voices": "Baimendu urruneko ahotsak",
+	"Allow Temporary Chat": "Baimendu Behin-behineko Txata",
+	"Allow User Location": "Baimendu Erabiltzailearen Kokapena",
+	"Allow Voice Interruption in Call": "Baimendu Ahots Etena Deietan",
+	"Already have an account?": "Baduzu kontu bat?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "top_p-ren alternatiba, kalitate eta aniztasunaren arteko oreka bermatzea du helburu. p parametroak token bat kontuan hartzeko gutxieneko probabilitatea adierazten du, token probableenaren probabilitatearen arabera. Adibidez, p=0.05 balioarekin eta token probableenaren probabilitatea 0.9 denean, 0.045 baino balio txikiagoko logit-ak baztertzen dira. (Lehenetsia: 0.0)",
+	"Amazing": "Harrigarria",
+	"an assistant": "laguntzaile bat",
+	"and": "eta",
+	"and {{COUNT}} more": "eta {{COUNT}} gehiago",
+	"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
+	"API Base URL": "API Oinarri URLa",
+	"API Key": "API Gakoa",
+	"API Key created.": "API Gakoa sortu da.",
+	"API keys": "API gakoak",
+	"Application DN": "Aplikazioaren DN",
+	"Application DN Password": "Aplikazioaren DN Pasahitza",
+	"applies to all users with the \"user\" role": "\"erabiltzaile\" rola duten erabiltzaile guztiei aplikatzen zaie",
+	"April": "Apirila",
+	"Archive": "Artxibatu",
+	"Archive All Chats": "Artxibatu Txat Guztiak",
+	"Archived Chats": "Artxibatutako Txatak",
+	"archived-chat-export": "artxibatutako-txat-esportazioa",
+	"Are you sure you want to unarchive all archived chats?": "Ziur zaude artxibatutako txat guztiak desartxibatu nahi dituzula?",
+	"Are you sure?": "Ziur zaude?",
+	"Arena Models": "Arena Ereduak",
+	"Artifacts": "Artefaktuak",
+	"Ask a question": "Egin galdera bat",
+	"Assistant": "Laguntzailea",
+	"Attach file": "Erantsi fitxategia",
+	"Attention to detail": "Xehetasunei arreta",
+	"Attribute for Username": "Erabiltzaile-izenerako atributua",
+	"Audio": "Audioa",
+	"August": "Abuztua",
+	"Authenticate": "Autentifikatu",
+	"Auto-Copy Response to Clipboard": "Automatikoki Kopiatu Erantzuna Arbelera",
+	"Auto-playback response": "Automatikoki erreproduzitu erantzuna",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Autentifikazio Katea",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Oinarri URLa",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Oinarri URLa beharrezkoa da.",
+	"Available list": "Zerrenda erabilgarria",
+	"available!": "eskuragarri!",
+	"Awful": "Penagarria",
+	"Azure AI Speech": "Azure AI Hizketa",
+	"Azure Region": "Azure Eskualdea",
+	"Back": "Atzera",
+	"Bad Response": "Erantzun Txarra",
+	"Banners": "Bannerrak",
+	"Base Model (From)": "Oinarrizko Eredua (Nondik)",
+	"Batch Size (num_batch)": "Batch Tamaina (num_batch)",
+	"before": "aurretik",
+	"Being lazy": "Alferra izatea",
+	"Bing Search V7 Endpoint": "Bing Bilaketa V7 Endpointua",
+	"Bing Search V7 Subscription Key": "Bing Bilaketa V7 Harpidetza Gakoa",
+	"Brave Search API Key": "Brave Bilaketa API Gakoa",
+	"By {{name}}": "{{name}}-k",
+	"Bypass SSL verification for Websites": "Saihestu SSL egiaztapena Webguneentzat",
+	"Call": "Deia",
+	"Call feature is not supported when using Web STT engine": "Dei funtzioa ez da onartzen Web STT motorra erabiltzean",
+	"Camera": "Kamera",
+	"Cancel": "Utzi",
+	"Capabilities": "Gaitasunak",
+	"Certificate Path": "Ziurtagiriaren Bidea",
+	"Change Password": "Aldatu Pasahitza",
+	"Character": "Karakterea",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Esploratu muga berriak",
+	"Chat": "Txata",
+	"Chat Background Image": "Txataren Atzeko Irudia",
+	"Chat Bubble UI": "Txat Burbuilen Interfazea",
+	"Chat Controls": "Txat Kontrolak",
+	"Chat direction": "Txataren norabidea",
+	"Chat Overview": "Txataren Laburpena",
+	"Chat Permissions": "Txat Baimenak",
+	"Chat Tags Auto-Generation": "Txat Etiketen Auto-Sorkuntza",
+	"Chats": "Txatak",
+	"Check Again": "Egiaztatu Berriro",
+	"Check for updates": "Bilatu eguneraketak",
+	"Checking for updates...": "Eguneraketak bilatzen...",
+	"Choose a model before saving...": "Aukeratu eredu bat gorde aurretik...",
+	"Chunk Overlap": "Zatien Gainjartzea",
+	"Chunk Params": "Zatien Parametroak",
+	"Chunk Size": "Zati Tamaina",
+	"Ciphers": "Zifratuak",
+	"Citation": "Aipamena",
+	"Clear memory": "Garbitu memoria",
+	"click here": "klikatu hemen",
+	"Click here for filter guides.": "Klikatu hemen iragazkien gidak ikusteko.",
+	"Click here for help.": "Klikatu hemen laguntzarako.",
+	"Click here to": "Klikatu hemen",
+	"Click here to download user import template file.": "Klikatu hemen erabiltzaileen inportazio txantiloia deskargatzeko.",
+	"Click here to learn more about faster-whisper and see the available models.": "Klikatu hemen faster-whisper-i buruz gehiago ikasteko eta eredu erabilgarriak ikusteko.",
+	"Click here to select": "Klikatu hemen hautatzeko",
+	"Click here to select a csv file.": "Klikatu hemen csv fitxategi bat hautatzeko.",
+	"Click here to select a py file.": "Klikatu hemen py fitxategi bat hautatzeko.",
+	"Click here to upload a workflow.json file.": "Klikatu hemen workflow.json fitxategia igotzeko.",
+	"click here.": "klikatu hemen.",
+	"Click on the user role button to change a user's role.": "Klikatu erabiltzaile rolaren botoian erabiltzaile baten rola aldatzeko.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Arbelerako idazteko baimena ukatua. Mesedez, egiaztatu zure nabigatzailearen ezarpenak beharrezko sarbidea emateko.",
+	"Clone": "Klonatu",
+	"Close": "Itxi",
+	"Code execution": "Kodearen exekuzioa",
+	"Code formatted successfully": "Kodea ongi formateatu da",
+	"Collection": "Bilduma",
+	"Color": "Kolorea",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Oinarri URLa",
+	"ComfyUI Base URL is required.": "ComfyUI Oinarri URLa beharrezkoa da.",
+	"ComfyUI Workflow": "ComfyUI Lan-fluxua",
+	"ComfyUI Workflow Nodes": "ComfyUI Lan-fluxu Nodoak",
+	"Command": "Komandoa",
+	"Completions": "Osatzeak",
+	"Concurrent Requests": "Eskari Konkurrenteak",
+	"Configure": "Konfiguratu",
+	"Configure Models": "Konfiguratu Ereduak",
+	"Confirm": "Berretsi",
+	"Confirm Password": "Berretsi Pasahitza",
+	"Confirm your action": "Berretsi zure ekintza",
+	"Connections": "Konexioak",
+	"Contact Admin for WebUI Access": "Jarri harremanetan Administratzailearekin WebUI Sarbiderako",
+	"Content": "Edukia",
+	"Content Extraction": "Eduki Erauzketa",
+	"Context Length": "Testuinguru Luzera",
+	"Continue Response": "Jarraitu Erantzuna",
+	"Continue with {{provider}}": "Jarraitu {{provider}}-rekin",
+	"Continue with Email": "Jarraitu Posta Elektronikoarekin",
+	"Continue with LDAP": "Jarraitu LDAP-rekin",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontrolatu nola banatzen den mezuaren testua TTS eskaeretarako. 'Puntuazioa'-k esaldietan banatzen du, 'paragrafoak'-k paragrafoetan, eta 'bat ere ez'-ek mezua kate bakar gisa mantentzen du.",
+	"Controls": "Kontrolak",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Irteeraren koherentzia eta aniztasunaren arteko oreka kontrolatzen du. Balio txikiagoak testu zentratuagoa eta koherenteagoa emango du. (Lehenetsia: 5.0)",
+	"Copied": "Kopiatuta",
+	"Copied shared chat URL to clipboard!": "Partekatutako txataren URLa arbelera kopiatu da!",
+	"Copied to clipboard": "Arbelera kopiatuta",
+	"Copy": "Kopiatu",
+	"Copy last code block": "Kopiatu azken kode blokea",
+	"Copy last response": "Kopiatu azken erantzuna",
+	"Copy Link": "Kopiatu Esteka",
+	"Copy to clipboard": "Kopiatu arbelera",
+	"Copying to clipboard was successful!": "Arbelera kopiatzea arrakastatsua izan da!",
+	"Create": "Sortu",
+	"Create a knowledge base": "Sortu ezagutza-base bat",
+	"Create a model": "Sortu eredu bat",
+	"Create Account": "Sortu Kontua",
+	"Create Admin Account": "Sortu Administratzaile Kontua",
+	"Create Group": "Sortu Taldea",
+	"Create Knowledge": "Sortu Ezagutza",
+	"Create new key": "Sortu gako berria",
+	"Create new secret key": "Sortu gako sekretu berria",
+	"Created at": "Sortze data",
+	"Created At": "Sortze Data",
+	"Created by": "Sortzailea",
+	"CSV Import": "CSV Inportazioa",
+	"Current Model": "Uneko Eredua",
+	"Current Password": "Uneko Pasahitza",
+	"Custom": "Pertsonalizatua",
+	"Dark": "Iluna",
+	"Database": "Datu-basea",
+	"December": "Abendua",
+	"Default": "Lehenetsia",
+	"Default (Open AI)": "Lehenetsia (Open AI)",
+	"Default (SentenceTransformers)": "Lehenetsia (SentenceTransformers)",
+	"Default Model": "Eredu Lehenetsia",
+	"Default model updated": "Eredu lehenetsia eguneratu da",
+	"Default Models": "Eredu Lehenetsiak",
+	"Default permissions": "Baimen lehenetsiak",
+	"Default permissions updated successfully": "Baimen lehenetsiak ongi eguneratu dira",
+	"Default Prompt Suggestions": "Prompt Iradokizun Lehenetsiak",
+	"Default to 389 or 636 if TLS is enabled": "Lehenetsi 389 edo 636 TLS gaituta badago",
+	"Default to ALL": "Lehenetsi GUZTIAK",
+	"Default User Role": "Erabiltzaile Rol Lehenetsia",
+	"Delete": "Ezabatu",
+	"Delete a model": "Ezabatu eredu bat",
+	"Delete All Chats": "Ezabatu Txat Guztiak",
+	"Delete All Models": "Ezabatu Eredu Guztiak",
+	"Delete chat": "Ezabatu txata",
+	"Delete Chat": "Ezabatu Txata",
+	"Delete chat?": "Ezabatu txata?",
+	"Delete folder?": "Ezabatu karpeta?",
+	"Delete function?": "Ezabatu funtzioa?",
+	"Delete prompt?": "Ezabatu prompta?",
+	"delete this link": "ezabatu esteka hau",
+	"Delete tool?": "Ezabatu tresna?",
+	"Delete User": "Ezabatu Erabiltzailea",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} ezabatu da",
+	"Deleted {{name}}": "{{name}} ezabatu da",
+	"Deleted User": "Ezabatutako Erabiltzailea",
+	"Describe your knowledge base and objectives": "Deskribatu zure ezagutza-basea eta helburuak",
+	"Description": "Deskribapena",
+	"Didn't fully follow instructions": "Ez ditu jarraibideak guztiz jarraitu",
+	"Disabled": "Desgaituta",
+	"Discover a function": "Aurkitu funtzio bat",
+	"Discover a model": "Aurkitu eredu bat",
+	"Discover a prompt": "Aurkitu prompt bat",
+	"Discover a tool": "Aurkitu tresna bat",
+	"Discover wonders": "Aurkitu mirariak",
+	"Discover, download, and explore custom functions": "Aurkitu, deskargatu eta esploratu funtzio pertsonalizatuak",
+	"Discover, download, and explore custom prompts": "Aurkitu, deskargatu eta esploratu prompt pertsonalizatuak",
+	"Discover, download, and explore custom tools": "Aurkitu, deskargatu eta esploratu tresna pertsonalizatuak",
+	"Discover, download, and explore model presets": "Aurkitu, deskargatu eta esploratu ereduen aurrezarpenak",
+	"Dismissible": "Baztergarria",
+	"Display": "Bistaratu",
+	"Display Emoji in Call": "Bistaratu Emojiak Deietan",
+	"Display the username instead of You in the Chat": "Erakutsi erabiltzaile-izena Zu-ren ordez Txatean",
+	"Displays citations in the response": "Erakutsi aipamenak erantzunean",
+	"Dive into knowledge": "Murgildu ezagutzan",
+	"Do not install functions from sources you do not fully trust.": "Ez instalatu guztiz fidagarriak ez diren iturrietatik datozen funtzioak.",
+	"Do not install tools from sources you do not fully trust.": "Ez instalatu guztiz fidagarriak ez diren iturrietatik datozen tresnak.",
+	"Document": "Dokumentua",
+	"Documentation": "Dokumentazioa",
+	"Documents": "Dokumentuak",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ez du kanpo konexiorik egiten, eta zure datuak modu seguruan mantentzen dira zure zerbitzari lokalean.",
+	"Don't have an account?": "Ez duzu konturik?",
+	"don't install random functions from sources you don't trust.": "ez instalatu fidagarriak ez diren iturrietatik datozen ausazko funtzioak.",
+	"don't install random tools from sources you don't trust.": "ez instalatu fidagarriak ez diren iturrietatik datozen ausazko tresnak.",
+	"Don't like the style": "Ez zaizu estiloa gustatzen?",
+	"Done": "Eginda",
+	"Download": "Deskargatu",
+	"Download canceled": "Deskarga bertan behera utzi da",
+	"Download Database": "Deskargatu Datu-basea",
+	"Drag and drop a file to upload or select a file to view": "Arrastatu eta jaregin fitxategi bat igotzeko edo hautatu fitxategi bat ikusteko",
+	"Draw": "Marraztu",
+	"Drop any files here to add to the conversation": "Jaregin edozein fitxategi hemen elkarrizketara gehitzeko",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "adib. '30s','10m'. Denbora unitate baliodunak dira 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "adib. Testutik lizunkeriak kentzeko iragazki bat",
+	"e.g. My Filter": "adib. Nire Iragazkia",
+	"e.g. My Tools": "adib. Nire Tresnak",
+	"e.g. my_filter": "adib. nire_iragazkia",
+	"e.g. my_tools": "adib. nire_tresnak",
+	"e.g. Tools for performing various operations": "adib. Hainbat eragiketa egiteko tresnak",
+	"Edit": "Editatu",
+	"Edit Arena Model": "Editatu Arena Eredua",
+	"Edit Connection": "Editatu Konexioa",
+	"Edit Default Permissions": "Editatu Baimen Lehenetsiak",
+	"Edit Memory": "Editatu Memoria",
+	"Edit User": "Editatu Erabiltzailea",
+	"Edit User Group": "Editatu Erabiltzaile Taldea",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Posta elektronikoa",
+	"Embark on adventures": "Hasi abenturak",
+	"Embedding Batch Size": "Embedding Batch Tamaina",
+	"Embedding Model": "Embedding Eredua",
+	"Embedding Model Engine": "Embedding Eredu Motorea",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding eredua \"{{embedding_model}}\"-ra ezarri da",
+	"Enable API Key Auth": "Gaitu API Gako Autentikazioa",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Gaitu Komunitatearen Partekatzea",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Gaitu Memoria Blokeatzea (mlock) ereduaren datuak RAM memoriatik kanpo ez trukatzeko. Aukera honek ereduaren lan-orri multzoa RAMean blokatzen du, diskora ez direla trukatuko ziurtatuz. Honek errendimendua mantentzen lagun dezake, orri-hutsegiteak saihestuz eta datuen sarbide azkarra bermatuz.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Gaitu Memoria Mapaketa (mmap) ereduaren datuak kargatzeko. Aukera honek sistemari disko-biltegiratzea RAM memoriaren luzapen gisa erabiltzea ahalbidetzen dio, diskoko fitxategiak RAMean baleude bezala tratatuz. Honek ereduaren errendimendua hobe dezake, datuen sarbide azkarragoa ahalbidetuz. Hala ere, baliteke sistema guztietan behar bezala ez funtzionatzea eta disko-espazio handia kontsumitu dezake.",
+	"Enable Message Rating": "Gaitu Mezuen Balorazioa",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Gaitu Mirostat laginketa nahasmena kontrolatzeko. (Lehenetsia: 0, 0 = Desgaituta, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Gaitu Izena Emate Berriak",
+	"Enable Web Search": "Gaitu Web Bilaketa",
+	"Enabled": "Gaituta",
+	"Engine": "Motorea",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Ziurtatu zure CSV fitxategiak 4 zutabe dituela ordena honetan: Izena, Posta elektronikoa, Pasahitza, Rola.",
+	"Enter {{role}} message here": "Sartu {{role}} mezua hemen",
+	"Enter a detail about yourself for your LLMs to recall": "Sartu zure buruari buruzko xehetasun bat LLMek gogoratzeko",
+	"Enter api auth string (e.g. username:password)": "Sartu api autentifikazio katea (adib. erabiltzailea:pasahitza)",
+	"Enter Application DN": "Sartu Aplikazioaren DN",
+	"Enter Application DN Password": "Sartu Aplikazioaren DN Pasahitza",
+	"Enter Bing Search V7 Endpoint": "Sartu Bing Bilaketa V7 Endpointua",
+	"Enter Bing Search V7 Subscription Key": "Sartu Bing Bilaketa V7 Harpidetza Gakoa",
+	"Enter Brave Search API Key": "Sartu Brave Bilaketa API Gakoa",
+	"Enter certificate path": "Sartu ziurtagiriaren bidea",
+	"Enter CFG Scale (e.g. 7.0)": "Sartu CFG Eskala (adib. 7.0)",
+	"Enter Chunk Overlap": "Sartu Zatien Gainjartzea (chunk overlap)",
+	"Enter Chunk Size": "Sartu Zati Tamaina",
+	"Enter description": "Sartu deskribapena",
+	"Enter Github Raw URL": "Sartu Github Raw URLa",
+	"Enter Google PSE API Key": "Sartu Google PSE API Gakoa",
+	"Enter Google PSE Engine Id": "Sartu Google PSE Motor IDa",
+	"Enter Image Size (e.g. 512x512)": "Sartu Irudi Tamaina (adib. 512x512)",
+	"Enter Jina API Key": "Sartu Jina API Gakoa",
+	"Enter language codes": "Sartu hizkuntza kodeak",
+	"Enter Model ID": "Sartu Eredu IDa",
+	"Enter model tag (e.g. {{modelTag}})": "Sartu eredu etiketa (adib. {{modelTag}})",
+	"Enter Mojeek Search API Key": "Sartu Mojeek Bilaketa API Gakoa",
+	"Enter Number of Steps (e.g. 50)": "Sartu Urrats Kopurua (adib. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Sartu Sampler-a (adib. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Sartu Planifikatzailea (adib. Karras)",
+	"Enter Score": "Sartu Puntuazioa",
+	"Enter SearchApi API Key": "Sartu SearchApi API Gakoa",
+	"Enter SearchApi Engine": "Sartu SearchApi Motorea",
+	"Enter Searxng Query URL": "Sartu Searxng Kontsulta URLa",
+	"Enter Seed": "Sartu Hazia",
+	"Enter Serper API Key": "Sartu Serper API Gakoa",
+	"Enter Serply API Key": "Sartu Serply API Gakoa",
+	"Enter Serpstack API Key": "Sartu Serpstack API Gakoa",
+	"Enter server host": "Sartu zerbitzariaren ostalaria",
+	"Enter server label": "Sartu zerbitzariaren etiketa",
+	"Enter server port": "Sartu zerbitzariaren portua",
+	"Enter stop sequence": "Sartu gelditze sekuentzia",
+	"Enter system prompt": "Sartu sistema prompta",
+	"Enter Tavily API Key": "Sartu Tavily API Gakoa",
+	"Enter Tika Server URL": "Sartu Tika Zerbitzari URLa",
+	"Enter Top K": "Sartu Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Sartu URLa (adib. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Sartu URLa (adib. http://localhost:11434)",
+	"Enter Your Email": "Sartu Zure Posta Elektronikoa",
+	"Enter Your Full Name": "Sartu Zure Izen-abizenak",
+	"Enter your message": "Sartu zure mezua",
+	"Enter Your Password": "Sartu Zure Pasahitza",
+	"Enter Your Role": "Sartu Zure Rola",
+	"Enter Your Username": "Sartu Zure Erabiltzaile-izena",
+	"Error": "Errorea",
+	"ERROR": "ERROREA",
+	"Evaluations": "Ebaluazioak",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Adibidea: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Adibidea: GUZTIAK",
+	"Example: ou=users,dc=foo,dc=example": "Adibidea: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Adibidea: sAMAccountName edo uid edo userPrincipalName",
+	"Exclude": "Baztertu",
+	"Experimental": "Esperimentala",
+	"Explore the cosmos": "Esploratu kosmosa",
+	"Export": "Esportatu",
+	"Export All Archived Chats": "Esportatu Artxibatutako Txat Guztiak",
+	"Export All Chats (All Users)": "Esportatu Txat Guztiak (Erabiltzaile Guztiak)",
+	"Export chat (.json)": "Esportatu txata (.json)",
+	"Export Chats": "Esportatu Txatak",
+	"Export Config to JSON File": "Esportatu Konfigurazioa JSON Fitxategira",
+	"Export Functions": "Esportatu Funtzioak",
+	"Export Models": "Esportatu Ereduak",
+	"Export Presets": "Esportatu Aurrezarpenak",
+	"Export Prompts": "Esportatu Promptak",
+	"Export to CSV": "Esportatu CSVra",
+	"Export Tools": "Esportatu Tresnak",
+	"External Models": "Kanpoko Ereduak",
+	"Failed to add file.": "Huts egin du fitxategia gehitzean.",
+	"Failed to create API Key.": "Huts egin du API Gakoa sortzean.",
+	"Failed to read clipboard contents": "Huts egin du arbelaren edukia irakurtzean",
+	"Failed to save models configuration": "Huts egin du ereduen konfigurazioa gordetzean",
+	"Failed to update settings": "Huts egin du ezarpenak eguneratzean",
+	"Failed to upload file.": "Huts egin du fitxategia igotzean.",
+	"February": "Otsaila",
+	"Feedback History": "Feedbacken Historia",
+	"Feedbacks": "Feedbackak",
+	"Feel free to add specific details": "Gehitu xehetasun zehatzak nahi izanez gero",
+	"File": "Fitxategia",
+	"File added successfully.": "Fitxategia ongi gehitu da.",
+	"File content updated successfully.": "Fitxategiaren edukia ongi eguneratu da.",
+	"File Mode": "Fitxategi Modua",
+	"File not found.": "Ez da fitxategia aurkitu.",
+	"File removed successfully.": "Fitxategia ongi ezabatu da.",
+	"File size should not exceed {{maxSize}} MB.": "Fitxategiaren tamainak ez luke {{maxSize}} MB gainditu behar.",
+	"Files": "Fitxategiak",
+	"Filter is now globally disabled": "Iragazkia orain globalki desgaituta dago",
+	"Filter is now globally enabled": "Iragazkia orain globalki gaituta dago",
+	"Filters": "Iragazkiak",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Hatz-marka faltsutzea detektatu da: Ezin dira inizialak avatar gisa erabili. Profil irudi lehenetsia erabiliko da.",
+	"Fluidly stream large external response chunks": "Modu jariagarrian transmititu kanpoko erantzun zati handiak",
+	"Focus chat input": "Fokuratu txataren sarrera",
+	"Folder deleted successfully": "Karpeta ongi ezabatu da",
+	"Folder name cannot be empty": "Karpetaren izena ezin da hutsik egon",
+	"Folder name cannot be empty.": "Karpetaren izena ezin da hutsik egon.",
+	"Folder name updated successfully": "Karpetaren izena ongi eguneratu da",
+	"Followed instructions perfectly": "Jarraibideak perfektuki jarraitu ditu",
+	"Forge new paths": "Sortu bide berriak",
+	"Form": "Inprimakia",
+	"Format your variables using brackets like this:": "Formateatu zure aldagaiak kortxeteak erabiliz honela:",
+	"Frequency Penalty": "Maiztasun Zigorra",
+	"Function": "Funtzioa",
+	"Function created successfully": "Funtzioa ongi sortu da",
+	"Function deleted successfully": "Funtzioa ongi ezabatu da",
+	"Function Description": "Funtzioaren Deskribapena",
+	"Function ID": "Funtzio IDa",
+	"Function is now globally disabled": "Funtzioa orain globalki desgaituta dago",
+	"Function is now globally enabled": "Funtzioa orain globalki gaituta dago",
+	"Function Name": "Funtzioaren Izena",
+	"Function updated successfully": "Funtzioa ongi eguneratu da",
+	"Functions": "Funtzioak",
+	"Functions allow arbitrary code execution": "Funtzioek kode arbitrarioa exekutatzea ahalbidetzen dute",
+	"Functions allow arbitrary code execution.": "Funtzioek kode arbitrarioa exekutatzea ahalbidetzen dute.",
+	"Functions imported successfully": "Funtzioak ongi inportatu dira",
+	"General": "Orokorra",
+	"General Settings": "Ezarpen Orokorrak",
+	"Generate Image": "Sortu Irudia",
+	"Generating search query": "Bilaketa kontsulta sortzen",
+	"Generation Info": "Sorkuntzaren Informazioa",
+	"Get started": "Hasi",
+	"Get started with {{WEBUI_NAME}}": "Hasi {{WEBUI_NAME}}-rekin",
+	"Global": "Globala",
+	"Good Response": "Erantzun Ona",
+	"Google PSE API Key": "Google PSE API Gakoa",
+	"Google PSE Engine Id": "Google PSE Motor IDa",
+	"Group created successfully": "Taldea ongi sortu da",
+	"Group deleted successfully": "Taldea ongi ezabatu da",
+	"Group Description": "Taldearen Deskribapena",
+	"Group Name": "Taldearen Izena",
+	"Group updated successfully": "Taldea ongi eguneratu da",
+	"Groups": "Taldeak",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Feedback Haptikoa",
+	"has no conversations.": "ez du elkarrizketarik.",
+	"Hello, {{name}}": "Kaixo, {{name}}",
+	"Help": "Laguntza",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Lagundu komunitatearen sailkapen onena sortzen zure feedback historia partekatuz!",
+	"Hex Color": "Hex Kolorea",
+	"Hex Color - Leave empty for default color": "Hex Kolorea - Utzi hutsik kolore lehenetsia erabiltzeko",
+	"Hide": "Ezkutatu",
+	"Host": "Ostalaria",
+	"How can I help you today?": "Zertan lagun zaitzaket gaur?",
+	"How would you rate this response?": "Nola baloratuko zenuke erantzun hau?",
+	"Hybrid Search": "Bilaketa Hibridoa",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Onartzen dut irakurri dudala eta nire ekintzaren ondorioak ulertzen ditudala. Kode arbitrarioa exekutatzearekin lotutako arriskuez jabetzen naiz eta iturriaren fidagarritasuna egiaztatu dut.",
+	"ID": "IDa",
+	"Ignite curiosity": "Piztu jakin-mina",
+	"Image Generation (Experimental)": "Irudi Sorkuntza (Esperimentala)",
+	"Image Generation Engine": "Irudi Sorkuntza Motorea",
+	"Image Settings": "Irudi Ezarpenak",
+	"Images": "Irudiak",
+	"Import Chats": "Inportatu Txatak",
+	"Import Config from JSON File": "Inportatu Konfigurazioa JSON Fitxategitik",
+	"Import Functions": "Inportatu Funtzioak",
+	"Import Models": "Inportatu Ereduak",
+	"Import Presets": "Inportatu Aurrezarpenak",
+	"Import Prompts": "Inportatu Promptak",
+	"Import Tools": "Inportatu Tresnak",
+	"Include": "Sartu",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Sartu `--api-auth` bandera stable-diffusion-webui exekutatzean",
+	"Include `--api` flag when running stable-diffusion-webui": "Sartu `--api` bandera stable-diffusion-webui exekutatzean",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Algoritmoak sortutako testutik jasotako feedbackari erantzuteko abiadura zehazten du. Ikasketa-tasa baxuago batek doikuntza motelagoak eragingo ditu, eta ikasketa-tasa altuago batek algoritmoaren erantzuna bizkorragoa egingo du. (Lehenetsia: 0.1)",
+	"Info": "Informazioa",
+	"Input commands": "Sartu komandoak",
+	"Install from Github URL": "Instalatu Github URLtik",
+	"Instant Auto-Send After Voice Transcription": "Bidalketa Automatiko Berehalakoa Ahots Transkripzioaren Ondoren",
+	"Interface": "Interfazea",
+	"Invalid file format.": "Fitxategi formatu baliogabea.",
+	"Invalid Tag": "Etiketa Baliogabea",
+	"January": "Urtarrila",
+	"Jina API Key": "Jina API Gakoa",
+	"join our Discord for help.": "batu gure Discord-era laguntzarako.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON Aurrebista",
+	"July": "Uztaila",
+	"June": "Ekaina",
+	"JWT Expiration": "JWT Iraungitzea",
+	"JWT Token": "JWT Tokena",
+	"Keep Alive": "Mantendu Aktibo",
+	"Key": "Gakoa",
+	"Keyboard shortcuts": "Teklatuko lasterbideak",
+	"Knowledge": "Ezagutza",
+	"Knowledge Access": "Ezagutzarako Sarbidea",
+	"Knowledge created successfully.": "Ezagutza ongi sortu da.",
+	"Knowledge deleted successfully.": "Ezagutza ongi ezabatu da.",
+	"Knowledge reset successfully.": "Ezagutza ongi berrezarri da.",
+	"Knowledge updated successfully": "Ezagutza ongi eguneratu da.",
+	"Label": "Etiketa",
+	"Landing Page Mode": "Hasiera Orriaren Modua",
+	"Language": "Hizkuntza",
+	"Last Active": "Azken Aktibitatea",
+	"Last Modified": "Azken Aldaketa",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP zerbitzaria eguneratu da",
+	"Leaderboard": "Sailkapena",
+	"Leave empty for unlimited": "Utzi hutsik mugarik ez jartzeko",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Utzi hutsik \"{{URL}}/api/tags\" endpointuko eredu guztiak sartzeko",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Utzi hutsik \"{{URL}}/models\" endpointuko eredu guztiak sartzeko",
+	"Leave empty to include all models or select specific models": "Utzi hutsik eredu guztiak sartzeko edo hautatu eredu zehatzak",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Utzi hutsik prompt lehenetsia erabiltzeko, edo sartu prompt pertsonalizatu bat",
+	"Light": "Argia",
+	"Listening...": "Entzuten...",
+	"LLMs can make mistakes. Verify important information.": "LLMek akatsak egin ditzakete. Egiaztatu informazio garrantzitsua.",
+	"Local": "Lokala",
+	"Local Models": "Modelo lokalak",
+	"Lost": "Galduta",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "OpenWebUI Komunitateak egina",
+	"Make sure to enclose them with": "Ziurtatu hauek gehitzen dituzula",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Ziurtatu workflow.json fitxategia API formatu gisa esportatzen duzula ComfyUI-tik.",
+	"Manage": "Kudeatu",
+	"Manage Arena Models": "Kudeatu Arena Modeloak",
+	"Manage Ollama": "Kudeatu Ollama",
+	"Manage Ollama API Connections": "Kudeatu Ollama API Konexioak",
+	"Manage OpenAI API Connections": "Kudeatu OpenAI API Konexioak",
+	"Manage Pipelines": "Kudeatu Pipeline-ak",
+	"March": "Martxoa",
+	"Max Tokens (num_predict)": "Token maximoak (num_predict)",
+	"Max Upload Count": "Karga kopuru maximoa",
+	"Max Upload Size": "Karga tamaina maximoa",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Gehienez 3 modelo deskarga daitezke aldi berean. Saiatu berriro geroago.",
+	"May": "Maiatza",
+	"Memories accessible by LLMs will be shown here.": "LLMek atzitu ditzaketen memoriak hemen erakutsiko dira.",
+	"Memory": "Memoria",
+	"Memory added successfully": "Memoria ongi gehitu da",
+	"Memory cleared successfully": "Memoria ongi garbitu da",
+	"Memory deleted successfully": "Memoria ongi ezabatu da",
+	"Memory updated successfully": "Memoria ongi eguneratu da",
+	"Merge Responses": "Batu erantzunak",
+	"Message rating should be enabled to use this feature": "Mezuen balorazioa gaitu behar da funtzionalitate hau erabiltzeko",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Esteka sortu ondoren bidaltzen dituzun mezuak ez dira partekatuko. URLa duten erabiltzaileek partekatutako txata ikusi ahal izango dute.",
+	"Min P": "Min P",
+	"Minimum Score": "Puntuazio minimoa",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "YYYY-ko MMMM-ren DD",
+	"MMMM DD, YYYY HH:mm": "YYYY-ko MMMM-ren DD HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "YYYY-ko MMMM-ren DD hh:mm:ss A",
+	"Model": "Modeloa",
+	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modeloa ongi deskargatu da.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' modeloa dagoeneko deskarga ilaran dago.",
+	"Model {{modelId}} not found": "{{modelId}} modeloa ez da aurkitu",
+	"Model {{modelName}} is not vision capable": "{{modelName}} modeloak ez du ikusmen gaitasunik",
+	"Model {{name}} is now {{status}}": "{{name}} modeloa orain {{status}} dago",
+	"Model accepts image inputs": "Modeloak irudi sarrerak onartzen ditu",
+	"Model created successfully!": "Modeloa ongi sortu da!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modeloaren fitxategi sistemaren bidea detektatu da. Modeloaren izen laburra behar da eguneratzeko, ezin da jarraitu.",
+	"Model Filtering": "Modelo iragazketa",
+	"Model ID": "Modelo ID",
+	"Model IDs": "Modelo IDak",
+	"Model Name": "Modeloaren izena",
+	"Model not selected": "Ez da modelorik hautatu",
+	"Model Params": "Modelo parametroak",
+	"Model Permissions": "Modelo baimenak",
+	"Model updated successfully": "Modeloa ongi eguneratu da",
+	"Modelfile Content": "Modelfile edukia",
+	"Models": "Modeloak",
+	"Models Access": "Modeloen sarbidea",
+	"Models configuration saved successfully": "Modeloen konfigurazioa ongi gorde da",
+	"Mojeek Search API Key": "Mojeek bilaketa API gakoa",
+	"more": "gehiago",
+	"More": "Gehiago",
+	"Name": "Izena",
+	"Name your knowledge base": "Izendatu zure ezagutza-basea",
+	"New Chat": "Txat berria",
+	"New folder": "Karpeta berria",
+	"New Password": "Pasahitz berria",
+	"No content found": "Ez da edukirik aurkitu",
+	"No content to speak": "Ez dago hitz egiteko edukirik",
+	"No distance available": "Ez dago distantziarik eskuragarri",
+	"No feedbacks found": "Ez da iritzirik aurkitu",
+	"No file selected": "Ez da fitxategirik hautatu",
+	"No files found.": "Ez da fitxategirik aurkitu.",
+	"No groups with access, add a group to grant access": "Ez dago sarbidea duen talderik, gehitu talde bat sarbidea emateko",
+	"No HTML, CSS, or JavaScript content found.": "Ez da HTML, CSS, edo JavaScript edukirik aurkitu.",
+	"No knowledge found": "Ez da ezagutzarik aurkitu",
+	"No model IDs": "Ez dago modelo IDrik",
+	"No models found": "Ez da modelorik aurkitu",
+	"No models selected": "Ez da modelorik hautatu",
+	"No results found": "Ez da emaitzarik aurkitu",
+	"No search query generated": "Ez da bilaketa kontsultarik sortu",
+	"No source available": "Ez dago iturririk eskuragarri",
+	"No users were found.": "Ez da erabiltzailerik aurkitu.",
+	"No valves to update": "Ez dago balbularik eguneratzeko",
+	"None": "Bat ere ez",
+	"Not factually correct": "Ez da faktikoki zuzena",
+	"Not helpful": "Ez da lagungarria",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Oharra: Gutxieneko puntuazio bat ezartzen baduzu, bilaketak gutxieneko puntuazioa baino handiagoa edo berdina duten dokumentuak soilik itzuliko ditu.",
+	"Notes": "Oharrak",
+	"Notifications": "Jakinarazpenak",
+	"November": "Azaroa",
+	"num_gpu (Ollama)": "GPU kopurua (Ollama)",
+	"num_thread (Ollama)": "Hari kopurua (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Urria",
+	"Off": "Itzalita",
+	"Okay, Let's Go!": "Ados, Goazen!",
+	"OLED Dark": "OLED iluna",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API desgaituta",
+	"Ollama API settings updated": "Ollama API ezarpenak eguneratu dira",
+	"Ollama Version": "Ollama bertsioa",
+	"On": "Piztuta",
+	"Only alphanumeric characters and hyphens are allowed": "Karaktere alfanumerikoak eta marratxoak soilik onartzen dira",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Karaktere alfanumerikoak eta marratxoak soilik onartzen dira komando katean.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Bildumak soilik edita daitezke, sortu ezagutza-base berri bat dokumentuak editatzeko/gehitzeko.",
+	"Only select users and groups with permission can access": "Baimena duten erabiltzaile eta talde hautatuek soilik sar daitezke",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! URLa ez da baliozkoa. Mesedez, egiaztatu eta saiatu berriro.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Ui! Oraindik fitxategiak kargatzen ari dira. Mesedez, itxaron karga amaitu arte.",
+	"Oops! There was an error in the previous response.": "Ui! Errore bat egon da aurreko erantzunean.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ui! Onartzen ez den metodo bat erabiltzen ari zara (frontend soilik). Mesedez, zerbitzatu WebUI-a backendetik.",
+	"Open file": "Ireki fitxategia",
+	"Open in full screen": "Ireki pantaila osoan",
+	"Open new chat": "Ireki txat berria",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI-k faster-whisper erabiltzen du barnean.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI-k SpeechT5 eta CMU Arctic hiztun txertaketak erabiltzen ditu.",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open WebUI bertsioa (v{{OPEN_WEBUI_VERSION}}) beharrezko bertsioa (v{{REQUIRED_VERSION}}) baino baxuagoa da",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API konfigurazioa",
+	"OpenAI API Key is required.": "OpenAI API gakoa beharrezkoa da.",
+	"OpenAI API settings updated": "OpenAI API ezarpenak eguneratu dira",
+	"OpenAI URL/Key required.": "OpenAI URL/Gakoa beharrezkoa da.",
+	"or": "edo",
+	"Organize your users": "Antolatu zure erabiltzaileak",
+	"Other": "Bestelakoa",
+	"OUTPUT": "IRTEERA",
+	"Output format": "Irteera formatua",
+	"Overview": "Ikuspegi orokorra",
+	"page": "orria",
+	"Password": "Pasahitza",
+	"Paste Large Text as File": "Itsatsi testu luzea fitxategi gisa",
+	"PDF document (.pdf)": "PDF dokumentua (.pdf)",
+	"PDF Extract Images (OCR)": "PDF irudiak erauzi (OCR)",
+	"pending": "zain",
+	"Permission denied when accessing media devices": "Baimena ukatu da multimedia gailuak atzitzean",
+	"Permission denied when accessing microphone": "Baimena ukatu da mikrofonoa atzitzean",
+	"Permission denied when accessing microphone: {{error}}": "Baimena ukatu da mikrofonoa atzitzean: {{error}}",
+	"Permissions": "Baimenak",
+	"Personalization": "Pertsonalizazioa",
+	"Pin": "Ainguratu",
+	"Pinned": "Ainguratuta",
+	"Pioneer insights": "Ikuspegi aitzindariak",
+	"Pipeline deleted successfully": "Pipeline-a ongi ezabatu da",
+	"Pipeline downloaded successfully": "Pipeline-a ongi deskargatu da",
+	"Pipelines": "Pipeline-ak",
+	"Pipelines Not Detected": "Ez da Pipeline-rik detektatu",
+	"Pipelines Valves": "Pipeline balbulak",
+	"Plain text (.txt)": "Testu laua (.txt)",
+	"Playground": "Jolaslekua",
+	"Please carefully review the following warnings:": "Mesedez, berrikusi arretaz hurrengo oharrak:",
+	"Please enter a prompt": "Mesedez, sartu prompt bat",
+	"Please fill in all fields.": "Mesedez, bete eremu guztiak.",
+	"Please select a model first.": "",
+	"Please select a reason": "Mesedez, hautatu arrazoi bat",
+	"Port": "Ataka",
+	"Positive attitude": "Jarrera positiboa",
+	"Prefix ID": "Aurrizki ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Aurrizki IDa erabiltzen da beste konexioekin gatazkak saihesteko modelo IDei aurrizki bat gehituz - utzi hutsik desgaitzeko",
+	"Previous 30 days": "Aurreko 30 egunak",
+	"Previous 7 days": "Aurreko 7 egunak",
+	"Profile Image": "Profil irudia",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt-a (adib. Kontatu datu dibertigarri bat Erromatar Inperioari buruz)",
+	"Prompt Content": "Prompt edukia",
+	"Prompt created successfully": "Prompt-a ongi sortu da",
+	"Prompt suggestions": "Prompt iradokizunak",
+	"Prompt updated successfully": "Prompt-a ongi eguneratu da",
+	"Prompts": "Prompt-ak",
+	"Prompts Access": "Prompt sarbidea",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ekarri \"{{searchValue}}\" Ollama.com-etik",
+	"Pull a model from Ollama.com": "Ekarri modelo bat Ollama.com-etik",
+	"Query Generation Prompt": "Kontsulta sortzeko prompt-a",
+	"Query Params": "Kontsulta parametroak",
+	"RAG Template": "RAG txantiloia",
+	"Rating": "Balorazioa",
+	"Re-rank models by topic similarity": "Berrantolatu modeloak gai antzekotasunaren arabera",
+	"Read Aloud": "Irakurri ozen",
+	"Record voice": "Grabatu ahotsa",
+	"Redirecting you to OpenWebUI Community": "OpenWebUI Komunitatera berbideratzen",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Zentzugabekeriak sortzeko probabilitatea murrizten du. Balio altuago batek (adib. 100) erantzun anitzagoak emango ditu, balio baxuago batek (adib. 10) kontserbadoreagoa izango den bitartean. (Lehenetsia: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Egin erreferentzia zure buruari \"Erabiltzaile\" gisa (adib., \"Erabiltzailea gaztelania ikasten ari da\")",
+	"References from": "Erreferentziak hemendik",
+	"Refused when it shouldn't have": "Ukatu duenean ukatu behar ez zuenean",
+	"Regenerate": "Bersortu",
+	"Release Notes": "Bertsio oharrak",
+	"Relevance": "Garrantzia",
+	"Remove": "Kendu",
+	"Remove Model": "Kendu modeloa",
+	"Rename": "Berrizendatu",
+	"Reorder Models": "Berrantolatu modeloak",
+	"Repeat Last N": "Errepikatu azken N",
+	"Request Mode": "Eskaera modua",
+	"Reranking Model": "Berrantolatze modeloa",
+	"Reranking model disabled": "Berrantolatze modeloa desgaituta",
+	"Reranking model set to \"{{reranking_model}}\"": "Berrantolatze modeloa \"{{reranking_model}}\"-era ezarrita",
+	"Reset": "Berrezarri",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Berrezarri karga direktorioa",
+	"Reset Vector Storage/Knowledge": "Berrezarri bektore biltegia/ezagutza",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Erantzunen jakinarazpenak ezin dira aktibatu webgunearen baimenak ukatu direlako. Mesedez, bisitatu zure nabigatzailearen ezarpenak beharrezko sarbidea emateko.",
+	"Response splitting": "Erantzun banaketa",
+	"Result": "Emaitza",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Testu aberastuko sarrera txaterako",
+	"RK": "RK",
+	"Role": "Rola",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Exekutatu",
+	"Running": "Exekutatzen",
+	"Save": "Gorde",
+	"Save & Create": "Gorde eta sortu",
+	"Save & Update": "Gorde eta eguneratu",
+	"Save As Copy": "Gorde kopia gisa",
+	"Save Tag": "Gorde etiketa",
+	"Saved": "Gordeta",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Txat erregistroak zuzenean zure nabigatzailearen biltegian gordetzea ez da jadanik onartzen. Mesedez, hartu une bat zure txat erregistroak deskargatu eta ezabatzeko beheko botoia sakatuz. Ez kezkatu, zure txat erregistroak erraz inportatu ditzakezu berriro backendera honen bidez",
+	"Scroll to bottom when switching between branches": "Joan behera adarren artean aldatzean",
+	"Search": "Bilatu",
+	"Search a model": "Bilatu modelo bat",
+	"Search Base": "Bilaketa oinarria",
+	"Search Chats": "Bilatu txatak",
+	"Search Collection": "Bilatu bilduma",
+	"Search Filters": "Bilaketa iragazkiak",
+	"search for tags": "bilatu etiketak",
+	"Search Functions": "Bilatu funtzioak",
+	"Search Knowledge": "Bilatu ezagutza",
+	"Search Models": "Bilatu modeloak",
+	"Search options": "Bilaketa aukerak",
+	"Search Prompts": "Bilatu prompt-ak",
+	"Search Result Count": "Bilaketa emaitzen kopurua",
+	"Search the web": "Bilatu sarean",
+	"Search Tools": "Bilaketa tresnak",
+	"SearchApi API Key": "SearchApi API gakoa",
+	"SearchApi Engine": "SearchApi motorra",
+	"Searched {{count}} sites_one": "Gune {{count}} bilatuta",
+	"Searched {{count}} sites_other": "{{count}} gune bilatuta",
+	"Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" bilatzen",
+	"Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\"rentzako ezagutza bilatzen",
+	"Searxng Query URL": "Searxng kontsulta URLa",
+	"See readme.md for instructions": "Ikusi readme.md argibideetarako",
+	"See what's new": "Ikusi berritasunak",
+	"Seed": "Hazia",
+	"Select a base model": "Hautatu oinarrizko modeloa",
+	"Select a engine": "Hautatu motor bat",
+	"Select a function": "Hautatu funtzio bat",
+	"Select a group": "Hautatu talde bat",
+	"Select a model": "Hautatu modelo bat",
+	"Select a pipeline": "Hautatu pipeline bat",
+	"Select a pipeline url": "Hautatu pipeline url bat",
+	"Select a tool": "Hautatu tresna bat",
+	"Select Engine": "Hautatu motorra",
+	"Select Knowledge": "Hautatu ezagutza",
+	"Select model": "Hautatu modeloa",
+	"Select only one model to call": "Hautatu modelo bakarra deitzeko",
+	"Selected model(s) do not support image inputs": "Hautatutako modelo(e)k ez dute irudi sarrerarik onartzen",
+	"Semantic distance to query": "Kontsultarako distantzia semantikoa",
+	"Send": "Bidali",
+	"Send a Message": "Bidali mezu bat",
+	"Send message": "Bidali mezua",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Bidaltzen du `stream_options: { include_usage: true }` eskaeran.\nOnartutako hornitzaileek token erabileraren informazioa itzuliko dute erantzunean ezarrita dagoenean.",
+	"September": "Iraila",
+	"Serper API Key": "Serper API gakoa",
+	"Serply API Key": "Serply API gakoa",
+	"Serpstack API Key": "Serpstack API gakoa",
+	"Server connection verified": "Zerbitzari konexioa egiaztatuta",
+	"Set as default": "Ezarri lehenetsi gisa",
+	"Set CFG Scale": "Ezarri CFG eskala",
+	"Set Default Model": "Ezarri lehenetsi modeloa",
+	"Set embedding model": "Ezarri txertatze modeloa",
+	"Set embedding model (e.g. {{model}})": "Ezarri txertatze modeloa (adib. {{model}})",
+	"Set Image Size": "Ezarri irudi tamaina",
+	"Set reranking model (e.g. {{model}})": "Ezarri berrantolatze modeloa (adib. {{model}})",
+	"Set Sampler": "Ezarri laginketa",
+	"Set Scheduler": "Ezarri antolatzailea",
+	"Set Steps": "Ezarri urratsak",
+	"Set Task Model": "Ezarri ataza modeloa",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Ezarri kalkulurako erabilitako GPU gailuen kopurua. Aukera honek kontrolatzen du zenbat GPU gailu (eskuragarri badaude) erabiltzen diren sarrerako eskaerak prozesatzeko. Balio hau handitzeak errendimendua nabarmen hobetu dezake GPU azeleraziorako optimizatutako modeloentzat, baina energia eta GPU baliabide gehiago kontsumitu ditzake.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Ezarri kalkulurako erabilitako langile harien kopurua. Aukera honek kontrolatzen du zenbat hari erabiltzen diren sarrerako eskaerak aldi berean prozesatzeko. Balio hau handitzeak errendimendua hobetu dezake konkurrentzia altuko lan-kargetan, baina CPU baliabide gehiago kontsumitu ditzake.",
+	"Set Voice": "Ezarri ahotsa",
+	"Set whisper model": "Ezarri whisper modeloa",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Ezartzen du modeloak zenbat atzera begiratu behar duen errepikapenak saihesteko. (Lehenetsia: 64, 0 = desgaituta, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Ezartzen du zenbateraino zigortu behar diren errepikapenak. Balio altuago batek (adib., 1.5) errepikapenak gogorrago zigortuko ditu, balio baxuago batek (adib., 0.9) malguagoa izango den bitartean. (Lehenetsia: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Ezartzen du sorkuntzarako erabiliko den ausazko zenbakien hazia. Hau zenbaki zehatz batera ezartzeak modeloak testu bera sortzea eragingo du prompt bererako. (Lehenetsia: ausazkoa)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Ezartzen du hurrengo tokena sortzeko erabilitako testuinguru leihoaren tamaina. (Lehenetsia: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Ezartzen ditu erabiliko diren gelditzeko sekuentziak. Patroi hau aurkitzen denean, LLMak testua sortzeari utziko dio eta itzuli egingo da. Gelditzeko patroi anitz ezar daitezke modelfile batean gelditzeko parametro anitz zehaztuz.",
+	"Settings": "Ezarpenak",
+	"Settings saved successfully!": "Ezarpenak ongi gorde dira!",
+	"Share": "Partekatu",
+	"Share Chat": "Partekatu txata",
+	"Share to OpenWebUI Community": "Partekatu OpenWebUI komunitatearekin",
+	"Show": "Erakutsi",
+	"Show \"What's New\" modal on login": "Erakutsi \"Berritasunak\" modala saioa hastean",
+	"Show Admin Details in Account Pending Overlay": "Erakutsi administratzaile xehetasunak kontu zain geruzan",
+	"Show shortcuts": "Erakutsi lasterbideak",
+	"Show your support!": "Erakutsi zure babesa!",
+	"Showcased creativity": "Erakutsitako sormena",
+	"Sign in": "Hasi saioa",
+	"Sign in to {{WEBUI_NAME}}": "Hasi saioa {{WEBUI_NAME}}-n",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Hasi saioa {{WEBUI_NAME}}-n LDAP bidez",
+	"Sign Out": "Amaitu saioa",
+	"Sign up": "Erregistratu",
+	"Sign up to {{WEBUI_NAME}}": "Erregistratu {{WEBUI_NAME}}-n",
+	"Signing in to {{WEBUI_NAME}}": "{{WEBUI_NAME}}-n saioa hasten",
+	"Source": "Iturria",
+	"Speech Playback Speed": "Ahots erreprodukzio abiadura",
+	"Speech recognition error: {{error}}": "Ahots ezagutze errorea: {{error}}",
+	"Speech-to-Text Engine": "Ahotsetik-testura motorra",
+	"Stop": "Gelditu",
+	"Stop Sequence": "Gelditzeko sekuentzia",
+	"Stream Chat Response": "Transmititu txat erantzuna",
+	"STT Model": "STT modeloa",
+	"STT Settings": "STT ezarpenak",
+	"Subtitle (e.g. about the Roman Empire)": "Azpititulua (adib. Erromatar Inperioari buruz)",
+	"Success": "Arrakasta",
+	"Successfully updated.": "Ongi eguneratu da.",
+	"Suggested": "Iradokitua",
+	"Support": "Laguntza",
+	"Support this plugin:": "Lagundu plugin hau:",
+	"Sync directory": "Sinkronizatu direktorioa",
+	"System": "Sistema",
+	"System Instructions": "Sistema jarraibideak",
+	"System Prompt": "Sistema prompta",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Etiketa sortzeko prompta",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Isats-libre laginketa erabiltzen da irteran probabilitate txikiagoko tokenen eragina murrizteko. Balio altuago batek (adib., 2.0) eragina gehiago murriztuko du, 1.0 balioak ezarpen hau desgaitzen duen bitartean. (lehenetsia: 1)",
+	"Tap to interrupt": "Ukitu eteteko",
+	"Tavily API Key": "Tavily API gakoa",
+	"Tell us more:": "Kontatu gehiago:",
+	"Temperature": "Tenperatura",
+	"Template": "Txantiloia",
+	"Temporary Chat": "Behin-behineko txata",
+	"Text Splitter": "Testu banatzailea",
+	"Text-to-Speech Engine": "Testutik-ahotsera motorra",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Eskerrik asko zure iritzia emateagatik!",
+	"The Application Account DN you bind with for search": "Bilaketarako lotzen duzun aplikazio kontuaren DN-a",
+	"The base to search for users": "Erabiltzaileak bilatzeko oinarria",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Sorta tamainak zehazten du zenbat testu eskaera prozesatzen diren batera aldi berean. Sorta tamaina handiago batek modeloaren errendimendua eta abiadura handitu ditzake, baina memoria gehiago behar du. (Lehenetsia: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Plugin honen atzean dauden garatzaileak komunitateko boluntario sutsuak dira. Plugin hau baliagarria iruditzen bazaizu, mesedez kontuan hartu bere garapenean laguntzea.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Ebaluazio sailkapena Elo sailkapen sisteman oinarritzen da eta denbora errealean eguneratzen da.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "Erabiltzaileek saioa hasteko erabiltzen duten erabiltzaile-izenarekin mapeatzen den LDAP atributua.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Sailkapena beta fasean dago, eta balorazioen kalkuluak doitu ditzakegu algoritmoa fintzen dugun heinean.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Fitxategiaren gehienezko tamaina MB-tan. Fitxategiaren tamainak muga hau gainditzen badu, fitxategia ez da kargatuko.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Txatean aldi berean erabili daitezkeen fitxategien gehienezko kopurua. Fitxategi kopuruak muga hau gainditzen badu, fitxategiak ez dira kargatuko.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Puntuazioa 0.0 (0%) eta 1.0 (100%) arteko balio bat izan behar da.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Modeloaren tenperatura. Tenperatura handitzeak modeloaren erantzunak sortzaileagoak izatea eragingo du. (Lehenetsia: 0.8)",
+	"Theme": "Gaia",
+	"Thinking...": "Pentsatzen...",
+	"This action cannot be undone. Do you wish to continue?": "Ekintza hau ezin da desegin. Jarraitu nahi duzu?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Honek zure elkarrizketa baliotsuak modu seguruan zure backend datu-basean gordeko direla ziurtatzen du. Eskerrik asko!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Hau funtzionalitate esperimental bat da, baliteke espero bezala ez funtzionatzea eta edozein unetan aldaketak izatea.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Aukera honek kontrolatzen du zenbat token mantentzen diren testuingurua freskatzean. Adibidez, 2-ra ezarrita badago, elkarrizketaren testuinguruko azken 2 tokenak mantenduko dira. Testuingurua mantentzeak elkarrizketaren jarraitutasuna mantentzen lagun dezake, baina gai berriei erantzuteko gaitasuna murriztu dezake. (Lehenetsia: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Aukera honek ereduak bere erantzunean sor dezakeen token kopuru maximoa ezartzen du. Muga hau handitzeak ereduari erantzun luzeagoak emateko aukera ematen dio, baina eduki ez-erabilgarri edo ez-egokia sortzeko probabilitatea ere handitu dezake. (Lehenetsia: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Aukera honek bilduman dauden fitxategi guztiak ezabatuko ditu eta berriki kargatutako fitxategiekin ordezkatuko ditu.",
+	"This response was generated by \"{{model}}\"": "Erantzun hau \"{{model}}\" modeloak sortu du",
+	"This will delete": "Honek ezabatuko du",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Honek <strong>{{NAME}}</strong> eta <strong>bere eduki guztiak</strong> ezabatuko ditu.",
+	"This will delete all models including custom models": "Honek modelo guztiak ezabatuko ditu, modelo pertsonalizatuak barne",
+	"This will delete all models including custom models and cannot be undone.": "Honek modelo guztiak ezabatuko ditu, modelo pertsonalizatuak barne, eta ezin da desegin.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Honek ezagutza-basea berrezarri eta fitxategi guztiak sinkronizatuko ditu. Jarraitu nahi duzu?",
+	"Thorough explanation": "Azalpen sakona",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika zerbitzariaren URLa beharrezkoa da.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Aholkua: Eguneratu aldagai slot anitz jarraian txateko sarreran tabuladore tekla sakatuz ordezpen bakoitzaren ondoren.",
+	"Title": "Izenburua",
+	"Title (e.g. Tell me a fun fact)": "Izenburua (adib. Kontatu datu dibertigarri bat)",
+	"Title Auto-Generation": "Izenburuen sorrera automatikoa",
+	"Title cannot be an empty string.": "Izenburua ezin da kate hutsa izan.",
+	"Title Generation Prompt": "Izenburua sortzeko prompta",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Deskargatzeko eskuragarri dauden modelo izenak atzitzeko,",
+	"To access the GGUF models available for downloading,": "Deskargatzeko eskuragarri dauden GGUF modeloak atzitzeko,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "WebUI-a atzitzeko, mesedez jarri harremanetan administratzailearekin. Administratzaileek erabiltzaileen egoerak kudeatu ditzakete Admin Paneletik.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Ezagutza-basea hemen eransteko, gehitu ezazu lehenik \"Ezagutza\" lan-eremura.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Zure pribatutasuna babesteko, zure feedbacketik bakarrik partekatzen dira balorazioak, modelo IDak, etiketak eta metadatuak—zure txat erregistroak pribatuak dira eta ez dira sartzen.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Ekintzak hemen hautatzeko, gehitu itzazu lehenik \"Funtzioak\" lan-eremura.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Iragazkiak hemen hautatzeko, gehitu itzazu lehenik \"Funtzioak\" lan-eremura.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Tresna-multzoak hemen hautatzeko, gehitu itzazu lehenik \"Tresnak\" lan-eremura.",
+	"Toast notifications for new updates": "Toast jakinarazpenak eguneraketa berrientzat",
+	"Today": "Gaur",
+	"Toggle settings": "Aldatu ezarpenak",
+	"Toggle sidebar": "Aldatu alboko barra",
+	"Token": "Tokena",
+	"Tokens To Keep On Context Refresh (num_keep)": "Testuinguru freskatzean mantendu beharreko tokenak (num_keep)",
+	"Too verbose": "Luzeegia",
+	"Tool created successfully": "Tresna ongi sortu da",
+	"Tool deleted successfully": "Tresna ongi ezabatu da",
+	"Tool Description": "Tresnaren deskribapena",
+	"Tool ID": "Tresna ID",
+	"Tool imported successfully": "Tresna ongi inportatu da",
+	"Tool Name": "Tresnaren izena",
+	"Tool updated successfully": "Tresna ongi eguneratu da",
+	"Tools": "Tresnak",
+	"Tools Access": "Tresnen sarbidea",
+	"Tools are a function calling system with arbitrary code execution": "Tresnak kode arbitrarioa exekutatzeko funtzio deitzeko sistema bat dira",
+	"Tools have a function calling system that allows arbitrary code execution": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute",
+	"Tools have a function calling system that allows arbitrary code execution.": "Tresnek kode arbitrarioa exekutatzeko aukera ematen duen funtzio deitzeko sistema dute.",
+	"Top K": "Goiko K",
+	"Top P": "Goiko P",
+	"Transformers": "Transformatzaileak",
+	"Trouble accessing Ollama?": "Arazoak Ollama atzitzeko?",
+	"TTS Model": "TTS modeloa",
+	"TTS Settings": "TTS ezarpenak",
+	"TTS Voice": "TTS ahotsa",
+	"Type": "Mota",
+	"Type Hugging Face Resolve (Download) URL": "Idatzi Hugging Face Resolve (Deskarga) URLa",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ui! Arazoa egon da {{provider}}-era konektatzean.",
+	"UI": "Erabiltzaile interfazea",
+	"Unarchive All": "Desartxibatu guztiak",
+	"Unarchive All Archived Chats": "Desartxibatu artxibatutako txat guztiak",
+	"Unarchive Chat": "Desartxibatu txata",
+	"Unlock mysteries": "Askatu misterioak",
+	"Unpin": "Kendu aingura",
+	"Unravel secrets": "Askatu sekretuak",
+	"Untagged": "Etiketatu gabea",
+	"Update": "Eguneratu",
+	"Update and Copy Link": "Eguneratu eta kopiatu esteka",
+	"Update for the latest features and improvements.": "Eguneratu azken ezaugarri eta hobekuntzak izateko.",
+	"Update password": "Eguneratu pasahitza",
+	"Updated": "Eguneratuta",
+	"Updated at": "Noiz eguneratuta",
+	"Updated At": "Noiz eguneratuta",
+	"Upload": "Kargatu",
+	"Upload a GGUF model": "Kargatu GGUF modelo bat",
+	"Upload directory": "Karga direktorioa",
+	"Upload files": "Kargatu fitxategiak",
+	"Upload Files": "Kargatu fitxategiak",
+	"Upload Pipeline": "Kargatu Pipeline-a",
+	"Upload Progress": "Kargaren aurrerapena",
+	"URL": "URLa",
+	"URL Mode": "URL modua",
+	"Use '#' in the prompt input to load and include your knowledge.": "Erabili '#' prompt sarreran zure ezagutza kargatu eta sartzeko.",
+	"Use Gravatar": "Erabili Gravatar",
+	"Use groups to group your users and assign permissions.": "Erabili taldeak zure erabiltzaileak taldekatu eta baimenak esleitzeko.",
+	"Use Initials": "Erabili inizialak",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "erabiltzailea",
+	"User": "Erabiltzailea",
+	"User location successfully retrieved.": "Erabiltzailearen kokapena ongi berreskuratu da.",
+	"Username": "Erabiltzaile-izena",
+	"Users": "Erabiltzaileak",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Arena modelo lehenetsia erabiltzen modelo guztiekin. Egin klik plus botoian modelo pertsonalizatuak gehitzeko.",
+	"Utilize": "Erabili",
+	"Valid time units:": "Denbora unitate baliozkoak:",
+	"Valves": "Balbulak",
+	"Valves updated": "Balbulak eguneratuta",
+	"Valves updated successfully": "Balbulak ongi eguneratu dira",
+	"variable": "aldagaia",
+	"variable to have them replaced with clipboard content.": "aldagaia arbeleko edukiarekin ordezkatzeko.",
+	"Version": "Bertsioa",
+	"Version {{selectedVersion}} of {{totalVersions}}": "{{totalVersions}}-tik {{selectedVersion}}. bertsioa",
+	"Visibility": "Ikusgarritasuna",
+	"Voice": "Ahotsa",
+	"Voice Input": "Ahots sarrera",
+	"Warning": "Abisua",
+	"Warning:": "Abisua:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Abisua: Hau gaitzeak erabiltzaileei zerbitzarian kode arbitrarioa kargatzea ahalbidetuko die.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Abisua: Zure txertatze modeloa eguneratu edo aldatzen baduzu, dokumentu guztiak berriz inportatu beharko dituzu.",
+	"Web": "Weba",
+	"Web API": "Web APIa",
+	"Web Loader Settings": "Web kargatzailearen ezarpenak",
+	"Web Search": "Web bilaketa",
+	"Web Search Engine": "Web bilaketa motorra",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URLa",
+	"WebUI Settings": "WebUI ezarpenak",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI-k eskaerak egingo ditu \"{{url}}/api/chat\"-era",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI-k eskaerak egingo ditu \"{{url}}/chat/completions\"-era",
+	"What are you trying to achieve?": "Zer lortu nahi duzu?",
+	"What are you working on?": "Zertan ari zara lanean?",
+	"What’s New in": "Zer berri honetan:",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Gaituta dagoenean, modeloak txat mezu bakoitzari denbora errealean erantzungo dio, erantzun bat sortuz erabiltzaileak mezua bidaltzen duen bezain laster. Modu hau erabilgarria da zuzeneko txat aplikazioetarako, baina errendimenduan eragina izan dezake hardware motelagoan.",
+	"wherever you are": "zauden tokian zaudela",
+	"Whisper (Local)": "Whisper (Lokala)",
+	"Why?": "Zergatik?",
+	"Widescreen Mode": "Pantaila zabaleko modua",
+	"Won": "Irabazi du",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Top-k-rekin batera lan egiten du. Balio altuago batek (adib., 0.95) testu anitzagoa sortuko du, balio baxuago batek (adib., 0.5) testu fokatu eta kontserbadoreagoa sortuko duen bitartean. (Lehenetsia: 0.9)",
+	"Workspace": "Lan-eremua",
+	"Workspace Permissions": "Lan-eremuaren baimenak",
+	"Write a prompt suggestion (e.g. Who are you?)": "Idatzi prompt iradokizun bat (adib. Nor zara zu?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Idatzi 50 hitzeko laburpen bat [gaia edo gako-hitza] laburbiltzen duena.",
+	"Write something...": "Idatzi zerbait...",
+	"Write your model template content here": "Idatzi hemen zure modelo txantiloi edukia",
+	"Yesterday": "Atzo",
+	"You": "Zu",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Gehienez {{maxCount}} fitxategirekin txateatu dezakezu aldi berean.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "LLMekin dituzun interakzioak pertsonalizatu ditzakezu memoriak gehituz beheko 'Kudeatu' botoiaren bidez, lagungarriagoak eta zuretzat egokituagoak eginez.",
+	"You cannot upload an empty file.": "Ezin duzu fitxategi huts bat kargatu.",
+	"You do not have permission to upload files.": "Ez duzu fitxategiak kargatzeko baimenik.",
+	"You have no archived conversations.": "Ez duzu artxibatutako elkarrizketarik.",
+	"You have shared this chat": "Txat hau partekatu duzu",
+	"You're a helpful assistant.": "Laguntzaile baliagarri bat zara.",
+	"You're now logged in.": "Orain saioa hasita duzu.",
+	"Your account status is currently pending activation.": "Zure kontuaren egoera aktibazio zain dago.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Zure ekarpen osoa zuzenean plugin garatzaileari joango zaio; Open WebUI-k ez du ehunekorik hartzen. Hala ere, aukeratutako finantzaketa plataformak bere komisioak izan ditzake.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube kargatzailearen ezarpenak"
+}
diff --git a/src/lib/i18n/locales/fa-IR/translation.json b/src/lib/i18n/locales/fa-IR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..526e7e00c5632dc0e78b21450f334e9c70e97a45
--- /dev/null
+++ b/src/lib/i18n/locales/fa-IR/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '-1' برای غیر فعال کردن انقضا.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
+	"(latest)": "(آخرین)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}} گفتگوهای",
+	"{{webUIName}} Backend Required": "بکند {{webUIName}} نیاز است.",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "یک مدل وظیفه هنگام انجام وظایف مانند تولید عناوین برای چت ها و نمایش های جستجوی وب استفاده می شود.",
+	"a user": "یک کاربر",
+	"About": "درباره",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "حساب کاربری",
+	"Account Activation Pending": "فعال\u200cسازی حساب در حال انتظار",
+	"Accurate information": "اطلاعات دقیق",
+	"Actions": "کنش\u200cها",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "کاربران فعال",
+	"Add": "اضافه کردن",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "افزودن توضیحات کوتاه در مورد انچه که این مدل انجام می دهد",
+	"Add a tag": "افزودن یک برچسب",
+	"Add Arena Model": "افزودن مدل Arena",
+	"Add Connection": "",
+	"Add Content": "افزودن محتوا",
+	"Add content here": "محتوا را اینجا اضافه کنید",
+	"Add custom prompt": "افزودن یک درخواست سفارشی",
+	"Add Files": "افزودن فایل\u200cها",
+	"Add Group": "",
+	"Add Memory": "افزودن حافظه",
+	"Add Model": "افزودن مدل",
+	"Add Tag": "افزودن برچسب",
+	"Add Tags": "افزودن برچسب\u200cها",
+	"Add text content": "افزودن محتوای متنی",
+	"Add User": "افزودن کاربر",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "با تنظیم این تنظیمات، تغییرات به طور کلی برای همه کاربران اعمال می\u200cشود.",
+	"admin": "مدیر",
+	"Admin": "مدیر",
+	"Admin Panel": "پنل مدیریت",
+	"Admin Settings": "تنظیمات مدیریت",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "پارامترهای پیشرفته",
+	"Advanced Params": "پارام\u200cهای پیشرفته",
+	"All chats": "همهٔ گفتگوها",
+	"All Documents": "همهٔ سند\u200cها",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "اجازهٔ حذف گفتگو",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "اجازهٔ گفتگوی موقتی",
+	"Allow User Location": "اجازهٔ موقعیت مکانی کاربر",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "از قبل حساب کاربری دارید؟",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "یک دستیار",
+	"and": "و",
+	"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
+	"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
+	"API Base URL": "نشانی پایهٔ API",
+	"API Key": "کلید API",
+	"API Key created.": "کلید API ساخته شد.",
+	"API keys": "کلیدهای API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "آوریل",
+	"Archive": "بایگانی",
+	"Archive All Chats": "بایگانی همه گفتگوها",
+	"Archived Chats": "گفتگوهای بایگانی\u200cشده",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "مطمئنید؟",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "سوالی بپرسید",
+	"Assistant": "دستیار",
+	"Attach file": "پیوست پرونده",
+	"Attention to detail": "دقیق",
+	"Attribute for Username": "",
+	"Audio": "صدا",
+	"August": "آگوست",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "کپی خودکار پاسخ به کلیپ بورد",
+	"Auto-playback response": "پخش خودکار پاسخ",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "پایه URL AUTOMATIC1111 ",
+	"AUTOMATIC1111 Base URL is required.": "به URL پایه AUTOMATIC1111 مورد نیاز است.",
+	"Available list": "فهرست دردسترس",
+	"available!": "در دسترس!",
+	"Awful": "",
+	"Azure AI Speech": "سخنگوی هوش\u200cمصنوعی Azure",
+	"Azure Region": "منطقهٔ Azure",
+	"Back": "بازگشت",
+	"Bad Response": "پاسخ خوب نیست",
+	"Banners": "بنر",
+	"Base Model (From)": "مدل پایه (از)",
+	"Batch Size (num_batch)": "",
+	"before": "قبل",
+	"Being lazy": "حالت سازنده",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "کلید API جستجوی شجاع",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "عبور از تأیید SSL برای وب سایت ها",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "دوربین",
+	"Cancel": "لغو",
+	"Capabilities": "قابلیت",
+	"Certificate Path": "",
+	"Change Password": "تغییر رمز عبور",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "گفتگو",
+	"Chat Background Image": "تصویر پس\u200cزمینهٔ گفتگو",
+	"Chat Bubble UI": "رابط کاربری حبابی گفتگو",
+	"Chat Controls": "کنترل\u200cهای گفتگو",
+	"Chat direction": "جهت\u200cگفتگو",
+	"Chat Overview": "نمای کلی گفتگو",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "تولید خودکار برچسب\u200cهای گفتگو",
+	"Chats": "گفتگو\u200cها",
+	"Check Again": "بررسی دوباره",
+	"Check for updates": "بررسی به\u200cروزرسانی",
+	"Checking for updates...": "در حال بررسی برای به\u200cروزرسانی..",
+	"Choose a model before saving...": "قبل از ذخیره یک مدل را انتخاب کنید...",
+	"Chunk Overlap": "همپوشانی تکه",
+	"Chunk Params": "پارامترهای تکه",
+	"Chunk Size": "اندازه تکه",
+	"Ciphers": "",
+	"Citation": "استناد",
+	"Clear memory": "پاک کردن حافظه",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "برای کمک اینجا را کلیک کنید.",
+	"Click here to": "برای کمک اینجا را کلیک کنید.",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "برای انتخاب اینجا کلیک کنید",
+	"Click here to select a csv file.": "برای انتخاب یک فایل csv اینجا را کلیک کنید.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "اینجا کلیک کنید.",
+	"Click on the user role button to change a user's role.": "برای تغییر نقش کاربر، روی دکمه نقش کاربر کلیک کنید.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "کلون",
+	"Close": "بسته",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "مجموعه",
+	"Color": "",
+	"ComfyUI": "کومیوآی",
+	"ComfyUI Base URL": "URL پایه کومیوآی",
+	"ComfyUI Base URL is required.": "URL پایه کومیوآی الزامی است.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "دستور",
+	"Completions": "",
+	"Concurrent Requests": "درخواست های همزمان",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "تایید",
+	"Confirm Password": "تایید رمز عبور",
+	"Confirm your action": "",
+	"Connections": "ارتباطات",
+	"Contact Admin for WebUI Access": "برای دسترسی به WebUI با مدیر تماس بگیرید",
+	"Content": "محتوا",
+	"Content Extraction": "استخراج محتوا",
+	"Context Length": "طول زمینه",
+	"Continue Response": "ادامه پاسخ",
+	"Continue with {{provider}}": "با {{provider}} ادامه دهید",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "کنترل\u200cها",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "کپی شد",
+	"Copied shared chat URL to clipboard!": "URL چت به کلیپ بورد کپی شد!",
+	"Copied to clipboard": "به بریده\u200cدان کپی\u200cشد",
+	"Copy": "کپی",
+	"Copy last code block": "کپی آخرین بلوک کد",
+	"Copy last response": "کپی آخرین پاسخ",
+	"Copy Link": "کپی لینک",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "کپی کردن در کلیپ بورد با موفقیت انجام شد!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "ایجاد یک مدل",
+	"Create Account": "ساخت حساب کاربری",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "ساخت کلید جدید",
+	"Create new secret key": "ساخت کلید gehez جدید",
+	"Created at": "ایجاد شده در",
+	"Created At": "ایجاد شده در",
+	"Created by": "ایجاد شده توسط",
+	"CSV Import": "درون\u200cریزی CSV",
+	"Current Model": "مدل فعلی",
+	"Current Password": "رمز عبور فعلی",
+	"Custom": "دلخواه",
+	"Dark": "تیره",
+	"Database": "پایگاه داده",
+	"December": "دسامبر",
+	"Default": "پیشفرض",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "پیشفرض (SentenceTransformers)",
+	"Default Model": "مدل پیشفرض",
+	"Default model updated": "مدل پیشفرض به\u200cروزرسانی شد",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "پیشنهادات پرامپت پیش فرض",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "نقش کاربر پیش فرض",
+	"Delete": "حذف",
+	"Delete a model": "حذف یک مدل",
+	"Delete All Chats": "حذف همه گفتگوها",
+	"Delete All Models": "",
+	"Delete chat": "حذف گپ",
+	"Delete Chat": "حذف گپ",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "حذف این لینک",
+	"Delete tool?": "",
+	"Delete User": "حذف کاربر",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} پاک شد",
+	"Deleted {{name}}": "حذف شده {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "توضیحات",
+	"Didn't fully follow instructions": "نمی تواند دستورالعمل را کامل پیگیری کند",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "کشف یک مدل",
+	"Discover a prompt": "یک اعلان را کشف کنید",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "پرامپت\u200cهای سفارشی را کشف، دانلود و کاوش کنید",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "پیش تنظیمات مدل را کشف، دانلود و کاوش کنید",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "نمایش نام کاربری به جای «شما» در چت",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "سند",
+	"Documentation": "",
+	"Documents": "اسناد",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "هیچ اتصال خارجی ایجاد نمی کند و داده های شما به طور ایمن در سرور میزبان محلی شما باقی می ماند.",
+	"Don't have an account?": "حساب کاربری ندارید؟",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "نظری ندارید؟",
+	"Done": "",
+	"Download": "دانلود کن",
+	"Download canceled": "دانلود لغو شد",
+	"Download Database": "دانلود پایگاه داده",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "هر فایلی را اینجا رها کنید تا به مکالمه اضافه شود",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "به طور مثال '30s','10m'. واحد\u200cهای زمانی معتبر 's', 'm', 'h' هستند.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "ویرایش",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "ویرایش کاربر",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "ایمیل",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "مدل پیدائش",
+	"Embedding Model Engine": "محرک مدل پیدائش",
+	"Embedding model set to \"{{embedding_model}}\"": "مدل پیدائش را به \"{{embedding_model}}\" تنظیم کنید",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "فعالسازی اشتراک انجمن",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "فعال کردن ثبت نام\u200cهای جدید",
+	"Enable Web Search": "فعالسازی جستجوی وب",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "اطمینان حاصل کنید که فایل CSV شما شامل چهار ستون در این ترتیب است: نام، ایمیل، رمز عبور، نقش.",
+	"Enter {{role}} message here": "پیام {{role}} را اینجا وارد کنید",
+	"Enter a detail about yourself for your LLMs to recall": "برای ذخیره سازی اطلاعات خود، یک توضیح کوتاه درباره خود را وارد کنید",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "کلید API جستجوی شجاع را وارد کنید",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "مقدار Chunk Overlap را وارد کنید",
+	"Enter Chunk Size": "مقدار Chunk Size را وارد کنید",
+	"Enter description": "",
+	"Enter Github Raw URL": "ادرس Github Raw را وارد کنید",
+	"Enter Google PSE API Key": "کلید API گوگل PSE را وارد کنید",
+	"Enter Google PSE Engine Id": "شناسه موتور PSE گوگل را وارد کنید",
+	"Enter Image Size (e.g. 512x512)": "اندازه تصویر را وارد کنید (مثال: 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "کد زبان را وارد کنید",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "تگ مدل را وارد کنید (مثلا {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "تعداد گام ها را وارد کنید (مثال: 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "امتیاز را وارد کنید",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "نشانی وب پرسوجوی Searxng را وارد کنید",
+	"Enter Seed": "",
+	"Enter Serper API Key": "کلید API Serper را وارد کنید",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "کلید API Serpstack را وارد کنید",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "توالی توقف را وارد کنید",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "مقدار Top K را وارد کنید",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "مقدار URL را وارد کنید (مثال http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "مقدار URL را وارد کنید (مثال http://localhost:11434)",
+	"Enter Your Email": "ایمیل خود را وارد کنید",
+	"Enter Your Full Name": "نام کامل خود را وارد کنید",
+	"Enter your message": "",
+	"Enter Your Password": "رمز عبور خود را وارد کنید",
+	"Enter Your Role": "نقش خود را وارد کنید",
+	"Enter Your Username": "",
+	"Error": "خطا",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "آزمایشی",
+	"Explore the cosmos": "",
+	"Export": "برون\u200cریزی",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "برون\u200cریزی همه گفتگو\u200cها (همه کاربران)",
+	"Export chat (.json)": "برون\u200cریزی گفتگو (json)",
+	"Export Chats": "برون\u200cریزی گفتگوها",
+	"Export Config to JSON File": "برون\u200cریزی پیکربندی به پروندهٔ JSON",
+	"Export Functions": "برون\u200cریزی توابع",
+	"Export Models": "برون\u200cریزی مدل\u200cها",
+	"Export Presets": "",
+	"Export Prompts": "برون\u200cریزی پرامپت\u200cها",
+	"Export to CSV": "",
+	"Export Tools": "برون\u200cریزی ابزارها",
+	"External Models": "مدل\u200cهای بیرونی",
+	"Failed to add file.": "خطا در افزودن پرونده",
+	"Failed to create API Key.": "ایجاد کلید API با خطا مواجه شد.",
+	"Failed to read clipboard contents": "خواندن محتوای کلیپ بورد ناموفق بود",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "خطا در به\u200cروزرسانی تنظیمات",
+	"Failed to upload file.": "خطا در بارگذاری پرونده",
+	"February": "فوریه",
+	"Feedback History": "تاریخچهٔ بازخورد",
+	"Feedbacks": "",
+	"Feel free to add specific details": "اگر به دلخواه، معلومات خاصی اضافه کنید",
+	"File": "پرونده",
+	"File added successfully.": "پرونده با موفقیت افزوده شد.",
+	"File content updated successfully.": "محتوای پرونده با موفقیت به\u200cروز شد.",
+	"File Mode": "حالت پرونده",
+	"File not found.": "پرونده یافت نشد.",
+	"File removed successfully.": "پرونده با موفقیت حذف شد.",
+	"File size should not exceed {{maxSize}} MB.": "حجم پرونده نبایستی از {{maxSize}} MB بیشتر باشد.",
+	"Files": "پرونده\u200cها",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "فانگ سرفیس شناسایی شد: نمی توان از نمایه شما به عنوان آواتار استفاده کرد. پیش فرض به عکس پروفایل پیش فرض برگشت داده شد.",
+	"Fluidly stream large external response chunks": "تکه های پاسخ خارجی بزرگ را به صورت سیال پخش کنید",
+	"Focus chat input": "فوکوس کردن ورودی گپ",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "دستورالعمل ها را کاملا دنبال کرد",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "مجازات فرکانس",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "درون\u200cریزی توابع با موفقیت انجام شد",
+	"General": "عمومی",
+	"General Settings": "تنظیمات عمومی",
+	"Generate Image": "",
+	"Generating search query": "در حال تولید پرسوجوی جستجو",
+	"Generation Info": "اطلاعات تولید",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "پاسخ خوب",
+	"Google PSE API Key": "گوگل PSE API کلید",
+	"Google PSE Engine Id": "شناسه موتور PSE گوگل",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "ندارد.",
+	"Hello, {{name}}": "سلام، {{name}}",
+	"Help": "کمک",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "پنهان\u200cسازی",
+	"Host": "",
+	"How can I help you today?": "امروز چطور می توانم کمک تان کنم؟",
+	"How would you rate this response?": "",
+	"Hybrid Search": "جستجوی همزمان",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "تولید تصویر (آزمایشی)",
+	"Image Generation Engine": "موتور تولید تصویر",
+	"Image Settings": "تنظیمات تصویر",
+	"Images": "تصاویر",
+	"Import Chats": "درون\u200cریزی گفتگوها",
+	"Import Config from JSON File": "درون\u200cریزی از پروندهٔ JSON",
+	"Import Functions": "درون\u200cریزی توابع",
+	"Import Models": "درون\u200cریزی مدل\u200cها",
+	"Import Presets": "",
+	"Import Prompts": "درون\u200cریزی پرامپت\u200cها",
+	"Import Tools": "درون\u200cریزی ابزارها",
+	"Include": "شامل",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "فلگ `--api` را هنکام اجرای stable-diffusion-webui استفاده کنید.",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "اطلاعات",
+	"Input commands": "ورودی دستورات",
+	"Install from Github URL": "نصب از ادرس Github",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "رابط",
+	"Invalid file format.": "",
+	"Invalid Tag": "تگ نامعتبر",
+	"January": "ژانویه",
+	"Jina API Key": "",
+	"join our Discord for help.": "برای کمک به دیسکورد ما بپیوندید.",
+	"JSON": "JSON",
+	"JSON Preview": "پیش نمایش JSON",
+	"July": "ژوئن",
+	"June": "جولای",
+	"JWT Expiration": "JWT انقضای",
+	"JWT Token": "JWT توکن",
+	"Keep Alive": "Keep Alive",
+	"Key": "",
+	"Keyboard shortcuts": "میانبرهای صفحه کلید",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "زبان",
+	"Last Active": "آخرین فعال",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "روشن",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "مدل\u200cهای زبانی بزرگ می\u200cتوانند اشتباه کنند. اطلاعات مهم را راستی\u200cآزمایی کنید.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "ساخته شده توسط OpenWebUI Community",
+	"Make sure to enclose them with": "مطمئن شوید که آنها را با این محصور کنید:",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "مدیریت خطوط لوله",
+	"March": "مارچ",
+	"Max Tokens (num_predict)": "توکنهای بیشینه (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "حداکثر 3 مدل را می توان به طور همزمان دانلود کرد. لطفاً بعداً دوباره امتحان کنید.",
+	"May": "ماهی",
+	"Memories accessible by LLMs will be shown here.": "حافظه های دسترسی به LLMs در اینجا نمایش داده می شوند.",
+	"Memory": "حافظه",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "پیام های شما بعد از ایجاد لینک شما به اشتراک نمی گردد. کاربران با لینک URL می توانند چت اشتراک را مشاهده کنند.",
+	"Min P": "",
+	"Minimum Score": "نماد کمینه",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "مدل '{{modelName}}' با موفقیت دانلود شد.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "مدل '{{modelTag}}' در حال حاضر در صف برای دانلود است.",
+	"Model {{modelId}} not found": "مدل {{modelId}} یافت نشد",
+	"Model {{modelName}} is not vision capable": "مدل {{modelName}} قادر به بینایی نیست",
+	"Model {{name}} is now {{status}}": "مدل {{name}} در حال حاضر {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "مسیر فایل سیستم مدل یافت شد. برای بروزرسانی نیاز است نام کوتاه مدل وجود داشته باشد.",
+	"Model Filtering": "",
+	"Model ID": "شناسه مدل",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "مدل انتخاب نشده",
+	"Model Params": "مدل پارامز",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "محتویات فایل مدل",
+	"Models": "مدل\u200cها",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "بیشتر",
+	"Name": "نام",
+	"Name your knowledge base": "",
+	"New Chat": "گپ جدید",
+	"New folder": "",
+	"New Password": "رمز عبور جدید",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "نتیجه\u200cای یافت نشد",
+	"No search query generated": "پرسوجوی جستجویی ایجاد نشده است",
+	"No source available": "منبعی در دسترس نیست",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "هیچ کدام",
+	"Not factually correct": "اشتباهی فکری نیست",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "توجه: اگر حداقل نمره را تعیین کنید، جستجو تنها اسنادی را با نمره بیشتر یا برابر با حداقل نمره باز می گرداند.",
+	"Notes": "",
+	"Notifications": "اعلان",
+	"November": "نوامبر",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (اولاما)",
+	"OAuth ID": "",
+	"October": "اکتبر",
+	"Off": "خاموش",
+	"Okay, Let's Go!": "باشه، بزن بریم!",
+	"OLED Dark": "OLED تیره",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API Ollama غیرفعال شد",
+	"Ollama API settings updated": "",
+	"Ollama Version": "نسخه اولاما",
+	"On": "روشن",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "فقط کاراکترهای الفبایی و خط فاصله در رشته فرمان مجاز هستند.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "اوه! به نظر می رسد URL نامعتبر است. لطفاً دوباره بررسی کنید و دوباره امتحان کنید.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "اوه! شما از یک روش پشتیبانی نشده (فقط frontend) استفاده می کنید. لطفاً WebUI را از بکند اجرا کنید.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "باز کردن گپ جدید",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API Config",
+	"OpenAI API Key is required.": "مقدار کلید OpenAI API مورد نیاز است.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Key OpenAI مورد نیاز است.",
+	"or": "یا",
+	"Organize your users": "",
+	"Other": "دیگر",
+	"OUTPUT": "خروجی",
+	"Output format": "قالب خروجی",
+	"Overview": "نمای کلی",
+	"page": "صفحه",
+	"Password": "رمز عبور",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF سند (.pdf)",
+	"PDF Extract Images (OCR)": "استخراج تصاویر از PDF (OCR)",
+	"pending": "در انتظار",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}",
+	"Permissions": "",
+	"Personalization": "شخصی سازی",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "خط لوله",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "شیرالات خطوط لوله",
+	"Plain text (.txt)": "متن ساده (.txt)",
+	"Playground": "زمین بازی",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "نظرات مثبت",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 روز قبل",
+	"Previous 7 days": "7 روز قبل",
+	"Profile Image": "تصویر پروفایل",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "پیشنهاد (برای مثال: به من بگوید چیزی که برای من یک کاربرد داره درباره ایران)",
+	"Prompt Content": "محتویات پرامپت",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "پیشنهادات پرامپت",
+	"Prompt updated successfully": "",
+	"Prompts": "پرامپت\u200cها",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "بازگرداندن \"{{searchValue}}\" از Ollama.com",
+	"Pull a model from Ollama.com": "دریافت یک مدل از Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "پارامترهای پرس و جو",
+	"RAG Template": "RAG الگوی",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "خواندن به صورت صوتی",
+	"Record voice": "ضبط صدا",
+	"Redirecting you to OpenWebUI Community": "در حال هدایت به OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "رد شده زمانی که باید نباشد",
+	"Regenerate": "ری\u200cسازی",
+	"Release Notes": "یادداشت\u200cهای انتشار",
+	"Relevance": "ارتباط",
+	"Remove": "حذف",
+	"Remove Model": "حذف مدل",
+	"Rename": "تغییر نام",
+	"Reorder Models": "",
+	"Repeat Last N": "Repeat Last N",
+	"Request Mode": "حالت درخواست",
+	"Reranking Model": "مدل ری\u200cشناسی مجدد غیرفعال است",
+	"Reranking model disabled": "مدل ری\u200cشناسی مجدد غیرفعال است",
+	"Reranking model set to \"{{reranking_model}}\"": "مدل ری\u200cشناسی مجدد به \"{{reranking_model}}\" تنظیم شده است",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "نتیجه",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "نقش",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "اجرا",
+	"Running": "",
+	"Save": "ذخیره",
+	"Save & Create": "ذخیره و ایجاد",
+	"Save & Update": "ذخیره و به\u200cروزرسانی",
+	"Save As Copy": "ذخیره به صویت رونوشت",
+	"Save Tag": "ذخیرهٔ برچسب",
+	"Saved": "ذخیره شد",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ذخیره گزارش\u200cهای چت مستقیماً در حافظه مرورگر شما دیگر پشتیبانی نمی\u200cشود. لطفاً با کلیک بر روی دکمه زیر، چند لحظه برای دانلود و حذف گزارش های چت خود وقت بگذارید. نگران نباشید، شما به راحتی می توانید گزارش های چت خود را از طریق بکند دوباره وارد کنید",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "جستجو",
+	"Search a model": "جستجوی یک مدل",
+	"Search Base": "",
+	"Search Chats": "جستجو گفتگوها",
+	"Search Collection": "جستجوی مجموعه\u200cها",
+	"Search Filters": "",
+	"search for tags": "جستجو برای برچسب\u200cها",
+	"Search Functions": "جستجوی توابع",
+	"Search Knowledge": "جستجوی دانش",
+	"Search Models": "جستجوی مدل\u200cها",
+	"Search options": "",
+	"Search Prompts": "جستجوی پرامپت\u200cها",
+	"Search Result Count": "تعداد نتایج جستجو",
+	"Search the web": "",
+	"Search Tools": "ابزارهای جستجو",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "جستجو {{count}} sites_one",
+	"Searched {{count}} sites_other": "جستجو {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "جستجوی «{{searchQuery}}»",
+	"Searching Knowledge for \"{{searchQuery}}\"": "جستجوی دانش برای «{{searchQuery}}»",
+	"Searxng Query URL": "نشانی وب جستجوی Searxng",
+	"See readme.md for instructions": "برای مشاهده دستورالعمل\u200cها به readme.md مراجعه کنید",
+	"See what's new": "ببینید موارد جدید چه بوده",
+	"Seed": "",
+	"Select a base model": "انتخاب یک مدل پایه",
+	"Select a engine": "انتخاب یک موتور",
+	"Select a function": "انتخاب یک تابع",
+	"Select a group": "",
+	"Select a model": "انتخاب یک مدل",
+	"Select a pipeline": "انتخاب یک خط لوله",
+	"Select a pipeline url": "یک ادرس خط لوله را انتخاب کنید",
+	"Select a tool": "انتخاب یک ابقزار",
+	"Select Engine": "انتخاب موتور",
+	"Select Knowledge": "انتخاب دانش",
+	"Select model": "انتخاب یک مدل",
+	"Select only one model to call": "تنها یک مدل را برای صدا زدن انتخاب کنید",
+	"Selected model(s) do not support image inputs": "مدل) های (انتخاب شده ورودیهای تصویر را پشتیبانی نمیکند",
+	"Semantic distance to query": "",
+	"Send": "ارسال",
+	"Send a Message": "ارسال یک پیام",
+	"Send message": "ارسال پیام",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "سپتامبر",
+	"Serper API Key": "کلید API Serper",
+	"Serply API Key": "",
+	"Serpstack API Key": "کلید API Serpstack",
+	"Server connection verified": "اتصال سرور تأیید شد",
+	"Set as default": "تنظیم به عنوان پیشفرض",
+	"Set CFG Scale": "",
+	"Set Default Model": "تنظیم مدل پیش فرض",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "تنظیم مدل پیچشی (برای مثال {{model}})",
+	"Set Image Size": "تنظیم اندازه تصویر",
+	"Set reranking model (e.g. {{model}})": "تنظیم مدل ری\u200cراینگ (برای مثال {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "تنظیم گام\u200cها",
+	"Set Task Model": "تنظیم مدل تکلیف",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "تنظیم صدا",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "تنظیمات",
+	"Settings saved successfully!": "تنظیمات با موفقیت ذخیره شد!",
+	"Share": "اشتراک\u200cگذاری",
+	"Share Chat": "اشتراک\u200cگذاری چت",
+	"Share to OpenWebUI Community": "اشتراک گذاری با OpenWebUI Community",
+	"Show": "نمایش",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "نمایش میانبرها",
+	"Show your support!": "",
+	"Showcased creativity": "ایده\u200cآفرینی",
+	"Sign in": "ورود",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "خروج",
+	"Sign up": "ثبت نام",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "منبع",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "خطای تشخیص گفتار: {{error}}",
+	"Speech-to-Text Engine": "موتور گفتار به متن",
+	"Stop": "توقف",
+	"Stop Sequence": "توقف توالی",
+	"Stream Chat Response": "",
+	"STT Model": "مدل تبدیل صدا به متن",
+	"STT Settings": "تنظیمات تبدیل صدا به متن",
+	"Subtitle (e.g. about the Roman Empire)": "زیرنویس (برای مثال: درباره رمانی)",
+	"Success": "موفقیت",
+	"Successfully updated.": "با موفقیت به\u200cروز شد",
+	"Suggested": "پیشنهادی",
+	"Support": "حمایت",
+	"Support this plugin:": "حمایت از این افزونه",
+	"Sync directory": "هم\u200cگام\u200cسازی پوشه",
+	"System": "سیستم",
+	"System Instructions": "",
+	"System Prompt": "پرامپت سیستم",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "بیشتر بگویید:",
+	"Temperature": "دما",
+	"Template": "الگو",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "موتور تبدیل متن به گفتار",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "با تشکر از بازخورد شما!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "امتیاز باید یک مقدار بین 0.0 (0%) و 1.0 (100%) باشد.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "پوسته",
+	"Thinking...": "در حال فکر...",
+	"This action cannot be undone. Do you wish to continue?": "این اقدام قابل بازگردانی نیست. برای ادامه اطمینان دارید؟",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "این تضمین می کند که مکالمات ارزشمند شما به طور ایمن در پایگاه داده بکند ذخیره می شود. تشکر!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "توضیح کامل",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "با فشردن کلید Tab در ورودی چت پس از هر بار تعویض، چندین متغیر را به صورت متوالی به روزرسانی کنید.",
+	"Title": "عنوان",
+	"Title (e.g. Tell me a fun fact)": "عنوان (برای مثال: به من بگوید چیزی که دوست دارید)",
+	"Title Auto-Generation": "تولید خودکار عنوان",
+	"Title cannot be an empty string.": "عنوان نمی تواند یک رشته خالی باشد.",
+	"Title Generation Prompt": "پرامپت تولید عنوان",
+	"TLS": "",
+	"To access the available model names for downloading,": "برای دسترسی به نام مدل های موجود برای دانلود،",
+	"To access the GGUF models available for downloading,": "برای دسترسی به مدل\u200cهای GGUF موجود برای دانلود،",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "امروز",
+	"Toggle settings": "نمایش/عدم نمایش تنظیمات",
+	"Toggle sidebar": "نمایش/عدم نمایش نوار کناری",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "در دسترسی به اولاما مشکل دارید؟",
+	"TTS Model": "",
+	"TTS Settings": "تنظیمات TTS",
+	"TTS Voice": "",
+	"Type": "نوع",
+	"Type Hugging Face Resolve (Download) URL": "مقدار URL دانلود (Resolve) Hugging Face را وارد کنید",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "اوه اوه! مشکلی در اتصال به {{provider}} وجود داشت.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "به\u200cروزرسانی",
+	"Update and Copy Link": "به روزرسانی و کپی لینک",
+	"Update for the latest features and improvements.": "",
+	"Update password": "به روزرسانی رمزعبور",
+	"Updated": "بارگذاری شد",
+	"Updated at": "بارگذاری در",
+	"Updated At": "بارگذاری در",
+	"Upload": "بارگذاری",
+	"Upload a GGUF model": "آپلود یک مدل GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "بارگذاری پروندهها",
+	"Upload Pipeline": "",
+	"Upload Progress": "پیشرفت آپلود",
+	"URL": "",
+	"URL Mode": "حالت URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "استفاده از گراواتار",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "استفاده از سرواژه",
+	"use_mlock (Ollama)": "use_mlock (اولاما)",
+	"use_mmap (Ollama)": "use_mmap (اولاما)",
+	"user": "کاربر",
+	"User": "کاربر",
+	"User location successfully retrieved.": "موقعیت مکانی کاربر با موفقیت دریافت شد.",
+	"Username": "",
+	"Users": "کاربران",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "در حال استفاده از مدل آرنا با همهٔ مدل\u200cهای دیگر به طور پیش\u200cفرض. برای افزودن مدل\u200cهای سفارشی، روی دکمه به\u200cعلاوه کلیک کنید.",
+	"Utilize": "استفاده کنید",
+	"Valid time units:": "واحدهای زمانی معتبر:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "متغیر",
+	"variable to have them replaced with clipboard content.": "متغیر برای جایگزینی آنها با محتوای بریده\u200cدان.",
+	"Version": "نسخه",
+	"Version {{selectedVersion}} of {{totalVersions}}": "نسخهٔ {{selectedVersion}} از {{totalVersions}}",
+	"Visibility": "",
+	"Voice": "صوت",
+	"Voice Input": "ورودی صوتی",
+	"Warning": "هشدار",
+	"Warning:": "هشدار",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "هشدار: اگر شما به روز کنید یا تغییر دهید مدل شما، باید تمام سند ها را مجددا وارد کنید.",
+	"Web": "وب",
+	"Web API": "",
+	"Web Loader Settings": "تنظیمات لودر وب",
+	"Web Search": "جستجوی وب",
+	"Web Search Engine": "موتور جستجوی وب",
+	"Web Search Query Generation": "",
+	"Webhook URL": "نشانی وب\u200cهوک",
+	"WebUI Settings": "تنظیمات WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "موارد جدید در",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "حالت صفحهٔ عریض",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "محیط کار",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "یک پیشنهاد پرامپت بنویسید (مثلاً شما کی هستید؟)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "خلاصه ای در 50 کلمه بنویسید که [موضوع یا کلمه کلیدی] را خلاصه کند.",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "دیروز",
+	"You": "شما",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "شما در هر زمان نهایتا می\u200cتوانید با {{maxCount}} پرونده گفتگو کنید.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "شما هیچ گفتگوی ذخیره شده ندارید.",
+	"You have shared this chat": "شما این گفتگو را به اشتراک گذاشته اید",
+	"You're a helpful assistant.": "تو یک دستیار سودمند هستی.",
+	"You're now logged in.": "شما اکنون وارد شده\u200cاید.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "یوتیوب",
+	"Youtube Loader Settings": "تنظیمات لودر یوتیوب"
+}
diff --git a/src/lib/i18n/locales/fi-FI/translation.json b/src/lib/i18n/locales/fi-FI/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..4ca87dc903349129653fa81a9209196b03fb81bf
--- /dev/null
+++ b/src/lib/i18n/locales/fi-FI/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' tai '-1' jottei vanhene.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(esim. `sh webui.sh --api`)",
+	"(latest)": "(uusin)",
+	"{{ models }}": "{{ mallit }}",
+	"{{user}}'s Chats": "{{user}}:n keskustelut",
+	"{{webUIName}} Backend Required": "{{webUIName}} backend vaaditaan",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Tehtävämallia käytetään tehtävien suorittamiseen, kuten otsikoiden luomiseen keskusteluille ja verkkohakukyselyille",
+	"a user": "käyttäjä",
+	"About": "Tietoja",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Tili",
+	"Account Activation Pending": "",
+	"Accurate information": "Tarkkaa tietoa",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "Lisää",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Lisää lyhyt kuvaus siitä, mitä tämä malli tekee",
+	"Add a tag": "Lisää tagi",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Lisää mukautettu kehote",
+	"Add Files": "Lisää tiedostoja",
+	"Add Group": "",
+	"Add Memory": "Lisää muistia",
+	"Add Model": "Lisää malli",
+	"Add Tag": "",
+	"Add Tags": "Lisää tageja",
+	"Add text content": "",
+	"Add User": "Lisää käyttäjä",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Näiden asetusten säätäminen vaikuttaa kaikkiin käyttäjiin.",
+	"admin": "hallinta",
+	"Admin": "",
+	"Admin Panel": "Hallintapaneeli",
+	"Admin Settings": "Hallinta-asetukset",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Edistyneet parametrit",
+	"Advanced Params": "Edistyneet parametrit",
+	"All chats": "",
+	"All Documents": "Kaikki asiakirjat",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Salli keskustelujen poisto",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Onko sinulla jo tili?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "avustaja",
+	"and": "ja",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "ja luo uusi jaettu linkki.",
+	"API Base URL": "APIn perus-URL",
+	"API Key": "API-avain",
+	"API Key created.": "API-avain luotu.",
+	"API keys": "API-avaimet",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "huhtikuu",
+	"Archive": "Arkisto",
+	"Archive All Chats": "Arkistoi kaikki keskustelut",
+	"Archived Chats": "Arkistoidut keskustelut",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Oletko varma?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Liitä tiedosto",
+	"Attention to detail": "Huomio yksityiskohtiin",
+	"Attribute for Username": "",
+	"Audio": "Ääni",
+	"August": "elokuu",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Vastauksen automaattikopiointi leikepöydälle",
+	"Auto-playback response": "Soita vastaus automaattisesti",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111-perus-URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111-perus-URL vaaditaan.",
+	"Available list": "",
+	"available!": "saatavilla!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Takaisin",
+	"Bad Response": "Epäkelpo vastaus",
+	"Banners": "Bannerit",
+	"Base Model (From)": "Perusmalli (alkaen)",
+	"Batch Size (num_batch)": "",
+	"before": "ennen",
+	"Being lazy": "Oli laiska",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API -avain",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Ohita SSL-varmennus verkkosivustoille",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Peruuta",
+	"Capabilities": "Ominaisuuksia",
+	"Certificate Path": "",
+	"Change Password": "Vaihda salasana",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Keskustelu",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Keskustelu-pallojen käyttöliittymä",
+	"Chat Controls": "",
+	"Chat direction": "Keskustelun suunta",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Keskustelut",
+	"Check Again": "Tarkista uudelleen",
+	"Check for updates": "Tarkista päivitykset",
+	"Checking for updates...": "Tarkistetaan päivityksiä...",
+	"Choose a model before saving...": "Valitse malli ennen tallentamista...",
+	"Chunk Overlap": "Osien päällekkäisyys",
+	"Chunk Params": "Osien parametrit",
+	"Chunk Size": "Osien koko",
+	"Ciphers": "",
+	"Citation": "Sitaatti",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klikkaa tästä saadaksesi apua.",
+	"Click here to": "Klikkaa tästä",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Klikkaa tästä valitaksesi",
+	"Click here to select a csv file.": "Klikkaa tästä valitaksesi CSV-tiedosto.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "klikkaa tästä.",
+	"Click on the user role button to change a user's role.": "Klikkaa käyttäjän roolipainiketta vaihtaaksesi käyttäjän roolia.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Klooni",
+	"Close": "Sulje",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Kokoelma",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI-perus-URL",
+	"ComfyUI Base URL is required.": "ComfyUI-perus-URL vaaditaan.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Komento",
+	"Completions": "",
+	"Concurrent Requests": "Samanaikaiset pyynnöt",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Vahvista salasana",
+	"Confirm your action": "",
+	"Connections": "Yhteydet",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Sisältö",
+	"Content Extraction": "",
+	"Context Length": "Kontekstin pituus",
+	"Continue Response": "Jatka vastausta",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Jaettu keskustelulinkki kopioitu leikepöydälle!",
+	"Copied to clipboard": "",
+	"Copy": "Kopioi",
+	"Copy last code block": "Kopioi viimeisin koodilohko",
+	"Copy last response": "Kopioi viimeisin vastaus",
+	"Copy Link": "Kopioi linkki",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Kopioiminen leikepöydälle onnistui!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Mallin luominen",
+	"Create Account": "Luo tili",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Luo uusi avain",
+	"Create new secret key": "Luo uusi salainen avain",
+	"Created at": "Luotu",
+	"Created At": "Luotu",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Nykyinen malli",
+	"Current Password": "Nykyinen salasana",
+	"Custom": "Mukautettu",
+	"Dark": "Tumma",
+	"Database": "Tietokanta",
+	"December": "joulukuu",
+	"Default": "Oletus",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Oletus (SentenceTransformers)",
+	"Default Model": "Oletusmalli",
+	"Default model updated": "Oletusmalli päivitetty",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Oletuskehotteiden ehdotukset",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Oletuskäyttäjärooli",
+	"Delete": "Poista",
+	"Delete a model": "Poista malli",
+	"Delete All Chats": "Poista kaikki keskustelut",
+	"Delete All Models": "",
+	"Delete chat": "Poista keskustelu",
+	"Delete Chat": "Poista keskustelu",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "poista tämä linkki",
+	"Delete tool?": "",
+	"Delete User": "Poista käyttäjä",
+	"Deleted {{deleteModelTag}}": "Poistettu {{deleteModelTag}}",
+	"Deleted {{name}}": "Poistettu {{nimi}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Kuvaus",
+	"Didn't fully follow instructions": "Ei noudattanut ohjeita täysin",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Tutustu malliin",
+	"Discover a prompt": "Löydä kehote",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Löydä ja lataa mukautettuja kehotteita",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Löydä ja lataa mallien esiasetuksia",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Näytä käyttäjänimi keskustelussa",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Asiakirja",
+	"Documentation": "",
+	"Documents": "Asiakirjat",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ei tee ulkoisia yhteyksiä, ja tietosi pysyvät turvallisesti paikallisesti isännöidyllä palvelimellasi.",
+	"Don't have an account?": "Eikö sinulla ole tiliä?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "En pidä tyylistä",
+	"Done": "",
+	"Download": "Lataa",
+	"Download canceled": "Lataus peruutettu",
+	"Download Database": "Lataa tietokanta",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Pudota tiedostoja tähän lisätäksesi ne keskusteluun",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "esim. '30s', '10m'. Kelpoiset aikayksiköt ovat 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Muokkaa",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Muokkaa käyttäjää",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Sähköposti",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Upotusmalli",
+	"Embedding Model Engine": "Upotusmallin moottori",
+	"Embedding model set to \"{{embedding_model}}\"": "\"{{embedding_model}}\" valittu upotusmalliksi",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Ota yhteisön jakaminen käyttöön",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Salli uudet rekisteröitymiset",
+	"Enable Web Search": "Ota verkkohaku käyttöön",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Varmista, että CSV-tiedostossasi on 4 saraketta seuraavassa järjestyksessä: Nimi, Sähköposti, Salasana, Rooli.",
+	"Enter {{role}} message here": "Kirjoita {{role}} viesti tähän",
+	"Enter a detail about yourself for your LLMs to recall": "Kirjoita tieto itseestäsi LLM:ien muistamiseksi",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Anna Brave Search API -avain",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Syötä osien päällekkäisyys",
+	"Enter Chunk Size": "Syötä osien koko",
+	"Enter description": "",
+	"Enter Github Raw URL": "Kirjoita Github Raw URL-osoite",
+	"Enter Google PSE API Key": "Anna Google PSE API -avain",
+	"Enter Google PSE Engine Id": "Anna Google PSE -moottorin tunnus",
+	"Enter Image Size (e.g. 512x512)": "Syötä kuvan koko (esim. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Syötä kielikoodit",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Syötä mallitagi (esim. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Syötä askelien määrä (esim. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Syötä pisteet",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Kirjoita Searxng-kyselyn URL-osoite",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Anna Serper API -avain",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Anna Serpstack API -avain",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Syötä lopetussekvenssi",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Syötä Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Syötä URL (esim. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Syötä URL (esim. http://localhost:11434)",
+	"Enter Your Email": "Syötä sähköpostiosoitteesi",
+	"Enter Your Full Name": "Syötä koko nimesi",
+	"Enter your message": "",
+	"Enter Your Password": "Syötä salasanasi",
+	"Enter Your Role": "Syötä roolisi",
+	"Enter Your Username": "",
+	"Error": "Virhe",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Kokeellinen",
+	"Explore the cosmos": "",
+	"Export": "Vienti",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Vie kaikki keskustelut (kaikki käyttäjät)",
+	"Export chat (.json)": "",
+	"Export Chats": "Vie keskustelut",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Vie malleja",
+	"Export Presets": "",
+	"Export Prompts": "Vie kehotteet",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "API-avaimen luonti epäonnistui.",
+	"Failed to read clipboard contents": "Leikepöydän sisällön lukeminen epäonnistui",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "helmikuu",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Voit lisätä tarkempia tietoja",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Tiedostotila",
+	"File not found.": "Tiedostoa ei löytynyt.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Sormenjäljen väärentäminen havaittu: Ei voi käyttää alkukirjaimia avatarina. Käytetään oletusprofiilikuvaa.",
+	"Fluidly stream large external response chunks": "Virtaa suuria ulkoisia vastausosia joustavasti",
+	"Focus chat input": "Fokusoi syöttökenttään",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Noudatti ohjeita täydellisesti",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Taajuussakko",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Yleinen",
+	"General Settings": "Yleisasetukset",
+	"Generate Image": "",
+	"Generating search query": "Hakukyselyn luominen",
+	"Generation Info": "Generointitiedot",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Hyvä vastaus",
+	"Google PSE API Key": "Google PSE API -avain",
+	"Google PSE Engine Id": "Google PSE -moduulin tunnus",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "ei ole keskusteluja.",
+	"Hello, {{name}}": "Terve, {{name}}",
+	"Help": "Apua",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Piilota",
+	"Host": "",
+	"How can I help you today?": "Kuinka voin auttaa tänään?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybridihaku",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Kuvagenerointi (kokeellinen)",
+	"Image Generation Engine": "Kuvagenerointimoottori",
+	"Image Settings": "Kuva-asetukset",
+	"Images": "Kuvat",
+	"Import Chats": "Tuo keskustelut",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Mallien tuominen",
+	"Import Presets": "",
+	"Import Prompts": "Tuo kehotteita",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Sisällytä `--api`-parametri suorittaessasi stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Info",
+	"Input commands": "Syötä komennot",
+	"Install from Github URL": "Asenna Githubin URL-osoitteesta",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Käyttöliittymä",
+	"Invalid file format.": "",
+	"Invalid Tag": "Virheellinen tagi",
+	"January": "tammikuu",
+	"Jina API Key": "",
+	"join our Discord for help.": "liity Discordiimme saadaksesi apua.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON-esikatselu",
+	"July": "heinäkuu",
+	"June": "kesäkuu",
+	"JWT Expiration": "JWT:n vanheneminen",
+	"JWT Token": "JWT-token",
+	"Keep Alive": "Pysy aktiivisena",
+	"Key": "",
+	"Keyboard shortcuts": "Pikanäppäimet",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Kieli",
+	"Last Active": "Viimeksi aktiivinen",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Vaalea",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "Kielimallit voivat tehdä virheitä. Varmista tärkeät tiedot.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Tehnyt OpenWebUI-yhteisö",
+	"Make sure to enclose them with": "Varmista, että suljet ne",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Hallitse putkia",
+	"March": "maaliskuu",
+	"Max Tokens (num_predict)": "Tokenien enimmäismäärä (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Enintään 3 mallia voidaan ladata samanaikaisesti. Yritä myöhemmin uudelleen.",
+	"May": "toukokuu",
+	"Memories accessible by LLMs will be shown here.": "Muistitiedostot, joita LLM-ohjelmat käyttävät, näkyvät tässä.",
+	"Memory": "Muisti",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Linkin luomisen jälkeen lähettämiäsi viestejä ei jaeta. Käyttäjät, joilla on URL-osoite, voivat tarkastella jaettua keskustelua.",
+	"Min P": "",
+	"Minimum Score": "Vähimmäispisteet",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY, HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
+	"Model {{modelId}} not found": "Mallia {{modelId}} ei löytynyt",
+	"Model {{modelName}} is not vision capable": "Malli {{modelName}} ei kykene näkökykyyn",
+	"Model {{name}} is now {{status}}": "Malli {{name}} on nyt {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Mallin tiedostojärjestelmäpolku havaittu. Mallin lyhytnimi vaaditaan päivitykseen, ei voi jatkaa.",
+	"Model Filtering": "",
+	"Model ID": "Mallin tunnus",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Mallia ei valittu",
+	"Model Params": "Mallin parametrit",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Mallitiedoston sisältö",
+	"Models": "Mallit",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Lisää",
+	"Name": "Nimi",
+	"Name your knowledge base": "",
+	"New Chat": "Uusi keskustelu",
+	"New folder": "",
+	"New Password": "Uusi salasana",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Ei tuloksia",
+	"No search query generated": "Hakukyselyä ei luotu",
+	"No source available": "Ei lähdettä saatavilla",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Ei lainkaan",
+	"Not factually correct": "Ei faktisesti oikein",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Huom: Jos asetat vähimmäispisteet, haku palauttaa vain asiakirjat, joiden pisteet ovat suurempia tai yhtä suuria kuin vähimmäispistemäärä.",
+	"Notes": "",
+	"Notifications": "Ilmoitukset",
+	"November": "marraskuu",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "lokakuu",
+	"Off": "Pois",
+	"Okay, Let's Go!": "Eikun menoksi!",
+	"OLED Dark": "OLED-tumma",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API poistettu käytöstä",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama-versio",
+	"On": "Päällä",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Vain kirjaimet, numerot ja väliviivat ovat sallittuja komentosarjassa.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hups! Näyttää siltä, että URL on virheellinen. Tarkista se ja yritä uudelleen.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hupsista! Käytät ei-tuettua menetelmää. WebUI pitää palvella backendista.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Avaa uusi keskustelu",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API -asetukset",
+	"OpenAI API Key is required.": "OpenAI API -avain vaaditaan.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/ -avain vaaditaan.",
+	"or": "tai",
+	"Organize your users": "",
+	"Other": "Muu",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Salasana",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF-tiedosto (.pdf)",
+	"PDF Extract Images (OCR)": "PDF-tiedoston kuvien erottelu (OCR)",
+	"pending": "odottaa",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Mikrofonin käyttöoikeus evätty: {{error}}",
+	"Permissions": "",
+	"Personalization": "Henkilökohtaisuus",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Putkistot",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Putkistot Venttiilit",
+	"Plain text (.txt)": "Pelkkä teksti (.txt)",
+	"Playground": "Leikkipaikka",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Positiivinen asenne",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Edelliset 30 päivää",
+	"Previous 7 days": "Edelliset 7 päivää",
+	"Profile Image": "Profiilikuva",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Kehote (esim. Kerro hauska fakta Turusta)",
+	"Prompt Content": "Kehotteen sisältö",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Kehotteen ehdotukset",
+	"Prompt updated successfully": "",
+	"Prompts": "Kehotteet",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Lataa \"{{searchValue}}\" Ollama.comista",
+	"Pull a model from Ollama.com": "Lataa malli Ollama.comista",
+	"Query Generation Prompt": "",
+	"Query Params": "Kyselyparametrit",
+	"RAG Template": "RAG-malline",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Lue ääneen",
+	"Record voice": "Nauhoita ääni",
+	"Redirecting you to OpenWebUI Community": "Ohjataan sinut OpenWebUI-yhteisöön",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "Kieltäytyi, vaikka ei olisi pitänyt",
+	"Regenerate": "Uudelleenluo",
+	"Release Notes": "Julkaisutiedot",
+	"Relevance": "",
+	"Remove": "Poista",
+	"Remove Model": "Poista malli",
+	"Rename": "Nimeä uudelleen",
+	"Reorder Models": "",
+	"Repeat Last N": "Viimeinen N -toisto",
+	"Request Mode": "Pyyntötila",
+	"Reranking Model": "Uudelleenpisteytysmalli",
+	"Reranking model disabled": "Uudelleenpisteytysmalli poistettu käytöstä",
+	"Reranking model set to \"{{reranking_model}}\"": "\"{{reranking_model}}\" valittu uudelleenpisteytysmalliksi",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rooli",
+	"Rosé Pine": "Rosee-mänty",
+	"Rosé Pine Dawn": "Aamuinen Rosee-mänty",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "Tallenna",
+	"Save & Create": "Tallenna ja luo",
+	"Save & Update": "Tallenna ja päivitä",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Keskustelulokien tallentaminen suoraan selaimen tallennustilaan ei ole enää tuettua. Lataa ja poista keskustelulokit napsauttamalla alla olevaa painiketta. Älä huoli, voit helposti tuoda keskustelulokit takaisin backendiin",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Haku",
+	"Search a model": "Hae mallia",
+	"Search Base": "",
+	"Search Chats": "Etsi chatteja",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Hae malleja",
+	"Search options": "",
+	"Search Prompts": "Hae kehotteita",
+	"Search Result Count": "Hakutulosten määrä",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Haettu {{count}} sites_one",
+	"Searched {{count}} sites_other": "Haku {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng-kyselyn URL-osoite",
+	"See readme.md for instructions": "Katso lisää ohjeita readme.md:stä",
+	"See what's new": "Katso, mitä uutta",
+	"Seed": "Siemen",
+	"Select a base model": "Valitse perusmalli",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Valitse malli",
+	"Select a pipeline": "Valitse putki",
+	"Select a pipeline url": "Valitse putken URL-osoite",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Valitse malli",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "Valitut mallit eivät tue kuvasyötteitä",
+	"Semantic distance to query": "",
+	"Send": "Lähetä",
+	"Send a Message": "Lähetä viesti",
+	"Send message": "Lähetä viesti",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "syyskuu",
+	"Serper API Key": "Serper API -avain",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack API -avain",
+	"Server connection verified": "Palvelinyhteys varmennettu",
+	"Set as default": "Aseta oletukseksi",
+	"Set CFG Scale": "",
+	"Set Default Model": "Aseta oletusmalli",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Aseta upotusmalli (esim. {{model}})",
+	"Set Image Size": "Aseta kuvan koko",
+	"Set reranking model (e.g. {{model}})": "Aseta uudelleenpisteytysmalli (esim. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Aseta askelmäärä",
+	"Set Task Model": "Aseta tehtävämalli",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Aseta puheääni",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Asetukset",
+	"Settings saved successfully!": "Asetukset tallennettu onnistuneesti!",
+	"Share": "Jaa",
+	"Share Chat": "Jaa keskustelu",
+	"Share to OpenWebUI Community": "Jaa OpenWebUI-yhteisöön",
+	"Show": "Näytä",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Näytä pikanäppäimet",
+	"Show your support!": "",
+	"Showcased creativity": "Näytti luovuutta",
+	"Sign in": "Kirjaudu sisään",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Kirjaudu ulos",
+	"Sign up": "Rekisteröidy",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Lähde",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Puheentunnistusvirhe: {{error}}",
+	"Speech-to-Text Engine": "Puheentunnistusmoottori",
+	"Stop": "",
+	"Stop Sequence": "Lopetussekvenssi",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "Puheentunnistusasetukset",
+	"Subtitle (e.g. about the Roman Empire)": "Alaotsikko (esim. Rooman valtakunnasta)",
+	"Success": "Onnistui",
+	"Successfully updated.": "Päivitetty onnistuneesti.",
+	"Suggested": "Suositeltu",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Järjestelmä",
+	"System Instructions": "",
+	"System Prompt": "Järjestelmäkehote",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Kerro lisää:",
+	"Temperature": "Lämpötila",
+	"Template": "Malline",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Puhemoottori",
+	"Tfs Z": "TFS Z",
+	"Thanks for your feedback!": "Kiitos palautteestasi!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Pisteytyksen tulee olla arvo välillä 0.0 (0%) ja 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Teema",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Tämä varmistaa, että arvokkaat keskustelusi tallennetaan turvallisesti backend-tietokantaasi. Kiitos!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Perusteellinen selitys",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Vinkki: Päivitä useita muuttujapaikkoja peräkkäin painamalla tabulaattoria keskustelusyötteessä jokaisen korvauksen jälkeen.",
+	"Title": "Otsikko",
+	"Title (e.g. Tell me a fun fact)": "Otsikko (esim. Kerro hauska fakta)",
+	"Title Auto-Generation": "Otsikon automaattinen luonti",
+	"Title cannot be an empty string.": "Otsikko ei voi olla tyhjä.",
+	"Title Generation Prompt": "Otsikon luontikehote",
+	"TLS": "",
+	"To access the available model names for downloading,": "Päästäksesi käsiksi ladattavissa oleviin mallinimiin,",
+	"To access the GGUF models available for downloading,": "Päästäksesi käsiksi ladattavissa oleviin GGUF-malleihin,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Tänään",
+	"Toggle settings": "Kytke asetukset",
+	"Toggle sidebar": "Kytke sivupalkki",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ongelmia Ollama-yhteydessä?",
+	"TTS Model": "",
+	"TTS Settings": "Puheentuottamisasetukset",
+	"TTS Voice": "",
+	"Type": "Tyyppi",
+	"Type Hugging Face Resolve (Download) URL": "Kirjoita Hugging Face -resolve-osoite",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Voi ei! Yhteysongelma {{provider}}:n kanssa.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Päivitä ja kopioi linkki",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Päivitä salasana",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Lataa GGUF-malli",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Lataa tiedostoja",
+	"Upload Pipeline": "",
+	"Upload Progress": "Latauksen eteneminen",
+	"URL": "",
+	"URL Mode": "URL-tila",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Käytä Gravataria",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Käytä alkukirjaimia",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "käyttäjä",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Käyttäjät",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Käytä",
+	"Valid time units:": "Kelvolliset aikayksiköt:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "muuttuja",
+	"variable to have them replaced with clipboard content.": "muuttuja korvataan leikepöydän sisällöllä.",
+	"Version": "Versio",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Varoitus",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Varoitus: Jos päivität tai vaihdat upotusmallia, sinun on tuotava kaikki asiakirjat uudelleen.",
+	"Web": "Web",
+	"Web API": "",
+	"Web Loader Settings": "Web Loader asetukset",
+	"Web Search": "Web-haku",
+	"Web Search Engine": "Web-hakukone",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook-URL",
+	"WebUI Settings": "WebUI-asetukset",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Mitä uutta",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Työtilat",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Kirjoita ehdotettu kehote (esim. Kuka olet?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Kirjoita 50 sanan yhteenveto, joka tiivistää [aihe tai avainsana].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Eilen",
+	"You": "Sinä",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Sinulla ei ole arkistoituja keskusteluja.",
+	"You have shared this chat": "Olet jakanut tämän keskustelun",
+	"You're a helpful assistant.": "Olet avulias apulainen.",
+	"You're now logged in.": "Olet nyt kirjautunut sisään.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Loader-asetukset"
+}
diff --git a/src/lib/i18n/locales/fr-CA/translation.json b/src/lib/i18n/locales/fr-CA/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..8661bb95307ddba8559a674690489d89720ae71d
--- /dev/null
+++ b/src/lib/i18n/locales/fr-CA/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": " 's', 'm', 'h', 'd', 'w' ou '-1' pour une durée illimitée.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(par ex. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(par exemple `sh webui.sh --api`)",
+	"(latest)": "(dernier)",
+	"{{ models }}": "{{ modèles }}",
+	"{{user}}'s Chats": "Discussions de {{user}}",
+	"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un modèle de tâche est utilisé lors de l’exécution de tâches telles que la génération de titres pour les conversations et les requêtes de recherche sur le web.",
+	"a user": "un utilisateur",
+	"About": "À propos",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Compte",
+	"Account Activation Pending": "Activation du compte en attente",
+	"Accurate information": "Information exacte",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Utilisateurs actifs",
+	"Add": "Ajouter",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Ajoutez une brève description de ce que fait ce modèle.",
+	"Add a tag": "Ajouter une balise",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Ajouter une prompt personnalisée",
+	"Add Files": "Ajouter des fichiers",
+	"Add Group": "",
+	"Add Memory": "Ajouter de la mémoire",
+	"Add Model": "Ajouter un modèle",
+	"Add Tag": "",
+	"Add Tags": "Ajouter des balises",
+	"Add text content": "",
+	"Add User": "Ajouter un Utilisateur",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera universellement les changements à tous les utilisateurs.",
+	"admin": "administrateur",
+	"Admin": "Administrateur",
+	"Admin Panel": "Tableau de bord administrateur",
+	"Admin Settings": "Paramètres d'administration",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Les administrateurs ont accès à tous les outils en tout temps ; les utilisateurs ont besoin d'outils affectés par modèle dans l'espace de travail.",
+	"Advanced Parameters": "Paramètres avancés",
+	"Advanced Params": "Paramètres avancés",
+	"All chats": "",
+	"All Documents": "Tous les documents",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Autoriser les voix non locales",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "Autoriser l'emplacement de l'utilisateur",
+	"Allow Voice Interruption in Call": "Autoriser l'interruption vocale pendant un appel",
+	"Already have an account?": "Avez-vous déjà un compte ?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "un assistant",
+	"and": "et",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "et créer un nouveau lien partagé.",
+	"API Base URL": "URL de base de l'API",
+	"API Key": "Clé d'API",
+	"API Key created.": "Clé d'API générée.",
+	"API keys": "Clés d'API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Avril",
+	"Archive": "Archivage",
+	"Archive All Chats": "Archiver toutes les conversations",
+	"Archived Chats": "Conversations archivées",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Êtes-vous certain ?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Joindre un document",
+	"Attention to detail": "Attention aux détails",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Août",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Copie automatique de la réponse vers le presse-papiers",
+	"Auto-playback response": "Réponse de lecture automatique",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Chaîne d'authentification de l'API",
+	"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "L'URL de base {AUTOMATIC1111} est requise.",
+	"Available list": "",
+	"available!": "disponible !",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Retour en arrière",
+	"Bad Response": "Mauvaise réponse",
+	"Banners": "Banniers",
+	"Base Model (From)": "Modèle de base (à partir de)",
+	"Batch Size (num_batch)": "Taille du lot (num_batch)",
+	"before": "avant",
+	"Being lazy": "Être fainéant",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Clé API Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Bypasser la vérification SSL pour les sites web",
+	"Call": "Appeler",
+	"Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT",
+	"Camera": "Appareil photo",
+	"Cancel": "Annuler",
+	"Capabilities": "Capacités",
+	"Certificate Path": "",
+	"Change Password": "Changer le mot de passe",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "Image d'arrière-plan de la fenêtre de chat",
+	"Chat Bubble UI": "Bulles de discussion",
+	"Chat Controls": "",
+	"Chat direction": "Direction du chat",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Conversations",
+	"Check Again": "Vérifiez à nouveau.",
+	"Check for updates": "Vérifier les mises à jour disponibles",
+	"Checking for updates...": "Recherche de mises à jour...",
+	"Choose a model before saving...": "Choisissez un modèle avant de sauvegarder...",
+	"Chunk Overlap": "Chevauchement de blocs",
+	"Chunk Params": "Paramètres d'encombrement",
+	"Chunk Size": "Taille de bloc",
+	"Ciphers": "",
+	"Citation": "Citation",
+	"Clear memory": "Libérer la mémoire",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Cliquez ici pour obtenir de l'aide.",
+	"Click here to": "Cliquez ici pour",
+	"Click here to download user import template file.": "Cliquez ici pour télécharger le fichier modèle d'importation utilisateur.",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Cliquez ici pour sélectionner",
+	"Click here to select a csv file.": "Cliquez ici pour sélectionner un fichier CSV.",
+	"Click here to select a py file.": "Cliquez ici pour sélectionner un fichier .py.",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "cliquez ici.",
+	"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour modifier le rôle d'un utilisateur.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
+	"Clone": "Copie conforme",
+	"Close": "Fermer",
+	"Code execution": "",
+	"Code formatted successfully": "Le code a été formaté avec succès",
+	"Collection": "Collection",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL de base ComfyUI",
+	"ComfyUI Base URL is required.": "L'URL de base ComfyUI est requise.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Commande",
+	"Completions": "",
+	"Concurrent Requests": "Demandes concurrentes",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Confirmer",
+	"Confirm Password": "Confirmer le mot de passe",
+	"Confirm your action": "Confirmez votre action",
+	"Connections": "Connexions",
+	"Contact Admin for WebUI Access": "Contacter l'administrateur pour l'accès à l'interface Web",
+	"Content": "Contenu",
+	"Content Extraction": "",
+	"Context Length": "Longueur du contexte",
+	"Continue Response": "Continuer la réponse",
+	"Continue with {{provider}}": "Continuer avec {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Contrôle comment le texte des messages est divisé pour les demandes de TTS. 'Ponctuation' divise en phrases, 'paragraphes' divise en paragraphes et 'aucun' garde le message comme une seule chaîne.",
+	"Controls": "Contrôles",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "URL du chat copiée dans le presse-papiers\u00a0!",
+	"Copied to clipboard": "",
+	"Copy": "Copie",
+	"Copy last code block": "Copier le dernier bloc de code",
+	"Copy last response": "Copier la dernière réponse",
+	"Copy Link": "Copier le lien",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Créer un modèle",
+	"Create Account": "Créer un compte",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Créer une nouvelle clé principale",
+	"Create new secret key": "Créer une nouvelle clé secrète",
+	"Created at": "Créé à",
+	"Created At": "Créé le",
+	"Created by": "Créé par",
+	"CSV Import": "Import CSV",
+	"Current Model": "Modèle actuel amélioré",
+	"Current Password": "Mot de passe actuel",
+	"Custom": "Sur mesure",
+	"Dark": "Obscur",
+	"Database": "Base de données",
+	"December": "Décembre",
+	"Default": "Par défaut",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Par défaut (Sentence Transformers)",
+	"Default Model": "Modèle standard",
+	"Default model updated": "Modèle par défaut mis à jour",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Suggestions de prompts par défaut",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Rôle utilisateur par défaut",
+	"Delete": "Supprimer",
+	"Delete a model": "Supprimer un modèle",
+	"Delete All Chats": "Supprimer toutes les conversations",
+	"Delete All Models": "",
+	"Delete chat": "Supprimer la conversation",
+	"Delete Chat": "Supprimer la Conversation",
+	"Delete chat?": "Supprimer la conversation ?",
+	"Delete folder?": "",
+	"Delete function?": "Supprimer la fonction ?",
+	"Delete prompt?": "Supprimer la prompt ?",
+	"delete this link": "supprimer ce lien",
+	"Delete tool?": "Effacer l'outil ?",
+	"Delete User": "Supprimer le compte d'utilisateur",
+	"Deleted {{deleteModelTag}}": "Supprimé {{deleteModelTag}}",
+	"Deleted {{name}}": "Supprimé {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Description",
+	"Didn't fully follow instructions": "N'a pas entièrement respecté les instructions",
+	"Disabled": "",
+	"Discover a function": "Découvrez une fonction",
+	"Discover a model": "Découvrir un modèle",
+	"Discover a prompt": "Découvrir une suggestion",
+	"Discover a tool": "Découvrez un outil",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Découvrez, téléchargez et explorez des fonctions personnalisées",
+	"Discover, download, and explore custom prompts": "Découvrez, téléchargez et explorez des prompts personnalisés",
+	"Discover, download, and explore custom tools": "Découvrez, téléchargez et explorez des outils personnalisés",
+	"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préréglages de modèles",
+	"Dismissible": "Fermeture",
+	"Display": "",
+	"Display Emoji in Call": "Afficher les emojis pendant l'appel",
+	"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur à la place de \"Vous\" dans le Chat",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Document",
+	"Documentation": "Documentation",
+	"Documents": "Documents",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ne fait aucune connexion externe et garde vos données en sécurité sur votre serveur local.",
+	"Don't have an account?": "Vous n'avez pas de compte ?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "N'apprécie pas le style",
+	"Done": "Terminé",
+	"Download": "Télécharger",
+	"Download canceled": "Téléchargement annulé",
+	"Download Database": "Télécharger la base de données",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Déposez des fichiers ici pour les ajouter à la conversation",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10 min'. Les unités de temps valides sont 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Modifier",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Modifier la mémoire",
+	"Edit User": "Modifier l'utilisateur",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "E-mail",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Taille du lot d'encodage",
+	"Embedding Model": "Modèle d'embedding",
+	"Embedding Model Engine": "Moteur de modèle d'encodage",
+	"Embedding model set to \"{{embedding_model}}\"": "Modèle d'encodage défini sur « {{embedding_model}} »",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Activer le partage communautaire",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Activer les nouvelles inscriptions",
+	"Enable Web Search": "Activer la recherche sur le Web",
+	"Enabled": "",
+	"Engine": "Moteur",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Vérifiez que votre fichier CSV comprenne les 4 colonnes dans cet ordre : Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Entrez le message {{role}} ici",
+	"Enter a detail about yourself for your LLMs to recall": "Saisissez un détail sur vous-même que vos LLMs pourront se rappeler",
+	"Enter api auth string (e.g. username:password)": "Entrez la chaîne d'authentification de l'API (par ex. nom d'utilisateur:mot de passe)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Entrez la clé API Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Entrez le chevauchement de chunk",
+	"Enter Chunk Size": "Entrez la taille de bloc",
+	"Enter description": "",
+	"Enter Github Raw URL": "Entrez l'URL brute de GitHub",
+	"Enter Google PSE API Key": "Entrez la clé API Google PSE",
+	"Enter Google PSE Engine Id": "Entrez l'identifiant du moteur Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Entrez la taille de l'image (par ex. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Entrez les codes de langue",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Entrez l'étiquette du modèle (par ex. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Entrez le nombre de pas (par ex. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Entrez votre score",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Entrez l'URL de la requête Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Entrez la clé API Serper",
+	"Enter Serply API Key": "Entrez la clé API Serply",
+	"Enter Serpstack API Key": "Entrez la clé API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Entrez la séquence d'arrêt",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "Entrez la clé API Tavily",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Entrez les Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (par ex. {http://127.0.0.1:7860/})",
+	"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (par ex. http://localhost:11434)",
+	"Enter Your Email": "Entrez votre adresse e-mail",
+	"Enter Your Full Name": "Entrez votre nom complet",
+	"Enter your message": "",
+	"Enter Your Password": "Entrez votre mot de passe",
+	"Enter Your Role": "Entrez votre rôle",
+	"Enter Your Username": "",
+	"Error": "Erreur",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Expérimental",
+	"Explore the cosmos": "",
+	"Export": "Exportation",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exporter toutes les conversations (tous les utilisateurs)",
+	"Export chat (.json)": "Exporter la discussion (.json)",
+	"Export Chats": "Exporter les conversations",
+	"Export Config to JSON File": "",
+	"Export Functions": "Exportez les Fonctions",
+	"Export Models": "Exporter les modèles",
+	"Export Presets": "",
+	"Export Prompts": "Exporter les Prompts",
+	"Export to CSV": "",
+	"Export Tools": "Outils d'exportation",
+	"External Models": "Modèles externes",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Échec de la création de la clé API.",
+	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Échec de la mise à jour des paramètres",
+	"Failed to upload file.": "",
+	"February": "Février",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "N'hésitez pas à ajouter des détails spécifiques",
+	"File": "Fichier",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Mode fichier",
+	"File not found.": "Fichier introuvable.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "Le filtre est maintenant désactivé globalement",
+	"Filter is now globally enabled": "Le filtre est désormais activé globalement",
+	"Filters": "Filtres",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Spoofing détecté : impossible d'utiliser les initiales comme avatar. Retour à l'image de profil par défaut.",
+	"Fluidly stream large external response chunks": "Diffuser de manière fluide de larges portions de réponses externes",
+	"Focus chat input": "Se concentrer sur le chat en entrée",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "A parfaitement suivi les instructions",
+	"Forge new paths": "",
+	"Form": "Formulaire",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Pénalité de fréquence",
+	"Function": "",
+	"Function created successfully": "La fonction a été créée avec succès",
+	"Function deleted successfully": "Fonction supprimée avec succès",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "La fonction a été mise à jour avec succès",
+	"Functions": "Fonctions",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "Fonctions importées avec succès",
+	"General": "Général",
+	"General Settings": "Paramètres Généraux",
+	"Generate Image": "Générer une image",
+	"Generating search query": "Génération d'une requête de recherche",
+	"Generation Info": "Informations sur la génération",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Mondial",
+	"Good Response": "Bonne réponse",
+	"Google PSE API Key": "Clé API Google PSE",
+	"Google PSE Engine Id": "ID du moteur de recherche personnalisé de Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "n'a aucune conversation.",
+	"Hello, {{name}}": "Bonjour, {{name}}.",
+	"Help": "Aide",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Cacher",
+	"Host": "",
+	"How can I help you today?": "Comment puis-je vous être utile aujourd'hui ?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Recherche hybride",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Génération d'images (expérimental)",
+	"Image Generation Engine": "Moteur de génération d'images",
+	"Image Settings": "Paramètres de l'image",
+	"Images": "Images",
+	"Import Chats": "Importer les discussions",
+	"Import Config from JSON File": "",
+	"Import Functions": "Import de fonctions",
+	"Import Models": "Importer des modèles",
+	"Import Presets": "",
+	"Import Prompts": "Importer des Enseignes",
+	"Import Tools": "Outils d'importation",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Inclure le drapeau `--api-auth` lors de l'exécution de stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Inclure le drapeau `--api` lorsque vous exécutez stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Info",
+	"Input commands": "Entrez les commandes",
+	"Install from Github URL": "Installer depuis l'URL GitHub",
+	"Instant Auto-Send After Voice Transcription": "Envoi automatique instantané après transcription vocale",
+	"Interface": "Interface utilisateur",
+	"Invalid file format.": "",
+	"Invalid Tag": "Étiquette non valide",
+	"January": "Janvier",
+	"Jina API Key": "",
+	"join our Discord for help.": "Rejoignez notre Discord pour obtenir de l'aide.",
+	"JSON": "JSON",
+	"JSON Preview": "Aperçu JSON",
+	"July": "Juillet",
+	"June": "Juin",
+	"JWT Expiration": "Expiration du jeton JWT",
+	"JWT Token": "Jeton JWT",
+	"Keep Alive": "Rester connecté",
+	"Key": "",
+	"Keyboard shortcuts": "Raccourcis clavier",
+	"Knowledge": "Connaissance",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Langue",
+	"Last Active": "Dernière activité",
+	"Last Modified": "Dernière modification",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Lumineux",
+	"Listening...": "En train d'écouter...",
+	"LLMs can make mistakes. Verify important information.": "Les LLM peuvent faire des erreurs. Vérifiez les informations importantes.",
+	"Local": "",
+	"Local Models": "Modèles locaux",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
+	"Make sure to enclose them with": "Assurez-vous de les inclure dans",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Gérer",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Gérer les pipelines",
+	"March": "Mars",
+	"Max Tokens (num_predict)": "Tokens maximaux (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé en même temps. Veuillez réessayer ultérieurement.",
+	"May": "Mai",
+	"Memories accessible by LLMs will be shown here.": "Les mémoires accessibles par les LLMs seront affichées ici.",
+	"Memory": "Mémoire",
+	"Memory added successfully": "Mémoire ajoutée avec succès",
+	"Memory cleared successfully": "La mémoire a été effacée avec succès",
+	"Memory deleted successfully": "La mémoire a été supprimée avec succès",
+	"Memory updated successfully": "La mémoire a été mise à jour avec succès",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Les messages que vous envoyez après avoir créé votre lien ne seront pas partagés. Les utilisateurs disposant de l'URL pourront voir le chat partagé.",
+	"Min P": "",
+	"Minimum Score": "Score minimal",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm:ss",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
+	"Model {{modelId}} not found": "Modèle {{modelId}} introuvable",
+	"Model {{modelName}} is not vision capable": "Le modèle {{modelName}} n'a pas de capacités visuelles",
+	"Model {{name}} is now {{status}}": "Le modèle {{name}} est désormais {{status}}.",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "Le modèle a été créé avec succès !",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Chemin du système de fichiers de modèle détecté. Le nom court du modèle est requis pour la mise à jour, l'opération ne peut pas être poursuivie.",
+	"Model Filtering": "",
+	"Model ID": "ID du modèle",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modèle non sélectionné",
+	"Model Params": "Paramètres du modèle",
+	"Model Permissions": "",
+	"Model updated successfully": "Le modèle a été mis à jour avec succès",
+	"Modelfile Content": "Contenu du Fichier de Modèle",
+	"Models": "Modèles",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Plus de",
+	"Name": "Nom",
+	"Name your knowledge base": "",
+	"New Chat": "Nouvelle conversation",
+	"New folder": "",
+	"New Password": "Nouveau mot de passe",
+	"No content found": "",
+	"No content to speak": "Rien à signaler",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Aucun fichier sélectionné",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Aucun résultat trouvé",
+	"No search query generated": "Aucune requête de recherche générée",
+	"No source available": "Aucune source n'est disponible",
+	"No users were found.": "",
+	"No valves to update": "Aucune vanne à mettre à jour",
+	"None": "Aucun",
+	"Not factually correct": "Non factuellement correct",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Note : Si vous définissez un score minimum, seuls les documents ayant un score supérieur ou égal à ce score minimum seront retournés par la recherche.",
+	"Notes": "",
+	"Notifications": "Notifications",
+	"November": "Novembre",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Octobre",
+	"Off": "Désactivé",
+	"Okay, Let's Go!": "D'accord, on y va !",
+	"OLED Dark": "Noir OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama désactivée",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Version Ollama améliorée",
+	"On": "Activé",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oups ! Il semble que l'URL soit invalide. Veuillez vérifier à nouveau et réessayer.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oups\u00a0! Vous utilisez une méthode non prise en charge (frontend uniquement). Veuillez servir l'interface Web à partir du backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Ouvrir une nouvelle discussion",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Configuration de l'API OpenAI",
+	"OpenAI API Key is required.": "Une clé API OpenAI est requise.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
+	"or": "ou",
+	"Organize your users": "",
+	"Other": "Autre",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Mot de passe",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Document au format PDF  (.pdf)",
+	"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
+	"pending": "en attente",
+	"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
+	"Permission denied when accessing microphone": "Autorisation refusée lors de l'accès au micro",
+	"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
+	"Permissions": "",
+	"Personalization": "Personnalisation",
+	"Pin": "Épingler",
+	"Pinned": "Épinglé",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Le pipeline a été supprimé avec succès",
+	"Pipeline downloaded successfully": "Le pipeline a été téléchargé avec succès",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Aucun pipelines détecté",
+	"Pipelines Valves": "Vannes de Pipelines",
+	"Plain text (.txt)": "Texte simple (.txt)",
+	"Playground": "Aire de jeux",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Attitude positive",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 derniers jours",
+	"Previous 7 days": "7 derniers jours",
+	"Profile Image": "Image de profil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (par ex. Dites-moi un fait amusant à propos de l'Empire romain)",
+	"Prompt Content": "Contenu du prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Suggestions pour le prompt",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompts",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Récupérer « {{searchValue}} » depuis Ollama.com",
+	"Pull a model from Ollama.com": "Télécharger un modèle depuis Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Paramètres de requête",
+	"RAG Template": "Modèle RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Lire à haute voix",
+	"Record voice": "Enregistrer la voix",
+	"Redirecting you to OpenWebUI Community": "Redirection vers la communauté OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Désignez-vous comme « Utilisateur » (par ex. « L'utilisateur apprend l'espagnol »)",
+	"References from": "",
+	"Refused when it shouldn't have": "Refusé alors qu'il n'aurait pas dû l'être",
+	"Regenerate": "Regénérer",
+	"Release Notes": "Notes de publication",
+	"Relevance": "",
+	"Remove": "Retirer",
+	"Remove Model": "Retirer le modèle",
+	"Rename": "Renommer",
+	"Reorder Models": "",
+	"Repeat Last N": "Répéter les N derniers",
+	"Request Mode": "Mode de Requête",
+	"Reranking Model": "Modèle de ré-ranking",
+	"Reranking model disabled": "Modèle de ré-ranking désactivé",
+	"Reranking model set to \"{{reranking_model}}\"": "Modèle de ré-ranking défini sur « {{reranking_model}} »",
+	"Reset": "Réinitialiser",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Répertoire de téléchargement réinitialisé",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Les notifications de réponse ne peuvent pas être activées car les autorisations du site web ont été refusées. Veuillez visiter les paramètres de votre navigateur pour accorder l'accès nécessaire.",
+	"Response splitting": "Fractionnement de la réponse",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rôle",
+	"Rosé Pine": "Pin rosé",
+	"Rosé Pine Dawn": "Aube de Pin Rosé",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "Courir",
+	"Save": "Enregistrer",
+	"Save & Create": "Enregistrer & Créer",
+	"Save & Update": "Enregistrer & Mettre à jour",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "La sauvegarde des journaux de discussion directement dans le stockage de votre navigateur n'est plus prise en charge. Veuillez prendre un instant pour télécharger et supprimer vos journaux de discussion en cliquant sur le bouton ci-dessous. Pas de soucis, vous pouvez facilement les réimporter depuis le backend via l'interface ci-dessous",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Recherche",
+	"Search a model": "Rechercher un modèle",
+	"Search Base": "",
+	"Search Chats": "Rechercher des conversations",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Fonctions de recherche",
+	"Search Knowledge": "",
+	"Search Models": "Rechercher des modèles",
+	"Search options": "",
+	"Search Prompts": "Recherche de prompts",
+	"Search Result Count": "Nombre de résultats de recherche",
+	"Search the web": "",
+	"Search Tools": "Outils de recherche",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Recherché {{count}} site(s)_one",
+	"Searched {{count}} sites_many": "Recherché {{count}} sites_many",
+	"Searched {{count}} sites_other": "Recherché {{count}} sites_autres",
+	"Searching \"{{searchQuery}}\"": "Recherche de « {{searchQuery}} »",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL de recherche Searxng",
+	"See readme.md for instructions": "Voir le fichier readme.md pour les instructions",
+	"See what's new": "Découvrez les nouvelles fonctionnalités",
+	"Seed": "Graine",
+	"Select a base model": "Sélectionnez un modèle de base",
+	"Select a engine": "Sélectionnez un moteur",
+	"Select a function": "Sélectionnez une fonction",
+	"Select a group": "",
+	"Select a model": "Sélectionnez un modèle",
+	"Select a pipeline": "Sélectionnez un pipeline",
+	"Select a pipeline url": "Sélectionnez l'URL du pipeline",
+	"Select a tool": "Sélectionnez un outil",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Sélectionnez un modèle",
+	"Select only one model to call": "Sélectionnez seulement un modèle pour appeler",
+	"Selected model(s) do not support image inputs": "Les modèle(s) sélectionné(s) ne prennent pas en charge les entrées d'images",
+	"Semantic distance to query": "",
+	"Send": "Envoyer",
+	"Send a Message": "Envoyer un message",
+	"Send message": "Envoyer un message",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Septembre",
+	"Serper API Key": "Clé API Serper",
+	"Serply API Key": "Clé API Serply",
+	"Serpstack API Key": "Clé API Serpstack",
+	"Server connection verified": "Connexion au serveur vérifiée",
+	"Set as default": "Définir comme valeur par défaut",
+	"Set CFG Scale": "",
+	"Set Default Model": "Définir le modèle par défaut",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Définir le modèle d'encodage (par ex. {{model}})",
+	"Set Image Size": "Définir la taille de l'image",
+	"Set reranking model (e.g. {{model}})": "Définir le modèle de reclassement (par ex. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Définir les étapes",
+	"Set Task Model": "Définir le modèle de tâche",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Définir la voix",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Paramètres",
+	"Settings saved successfully!": "Paramètres enregistrés avec succès !",
+	"Share": "Partager",
+	"Share Chat": "Partage de conversation",
+	"Share to OpenWebUI Community": "Partager avec la communauté OpenWebUI",
+	"Show": "Montrer",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Afficher les détails de l'administrateur dans la superposition en attente du compte",
+	"Show shortcuts": "Afficher les raccourcis",
+	"Show your support!": "Montre ton soutien !",
+	"Showcased creativity": "Créativité mise en avant",
+	"Sign in": "S'identifier",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Déconnexion",
+	"Sign up": "Inscrivez-vous",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Source",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale\u00a0: {{error}}",
+	"Speech-to-Text Engine": "Moteur de reconnaissance vocale",
+	"Stop": "",
+	"Stop Sequence": "Séquence d'arrêt",
+	"Stream Chat Response": "",
+	"STT Model": "Modèle de STT",
+	"STT Settings": "Paramètres de STT",
+	"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
+	"Success": "Réussite",
+	"Successfully updated.": "Mise à jour réussie.",
+	"Suggested": "Sugéré",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Système",
+	"System Instructions": "",
+	"System Prompt": "Prompt du système",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Appuyez pour interrompre",
+	"Tavily API Key": "Clé API Tavily",
+	"Tell us more:": "Dites-nous en plus à ce sujet : ",
+	"Temperature": "Température",
+	"Template": "Template",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Moteur de synthèse vocale",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Merci pour vos commentaires !",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Le score doit être une valeur comprise entre 0,0 (0\u00a0%) et 1,0 (100\u00a0%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Thème",
+	"Thinking...": "En train de réfléchir...",
+	"This action cannot be undone. Do you wish to continue?": "Cette action ne peut pas être annulée. Souhaitez-vous continuer ?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos conversations précieuses soient sauvegardées en toute sécurité dans votre base de données backend. Merci !",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Il s'agit d'une fonctionnalité expérimentale, elle peut ne pas fonctionner comme prévu et est sujette à modification à tout moment.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Cela supprimera",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Explication approfondie",
+	"Tika": "Tika",
+	"Tika Server URL required.": "URL du serveur Tika requise.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Conseil\u00a0: mettez à jour plusieurs emplacements de variables consécutivement en appuyant sur la touche Tab dans l’entrée de chat après chaque remplacement.",
+	"Title": "Titre",
+	"Title (e.g. Tell me a fun fact)": "Titre (par ex. raconte-moi un fait amusant)",
+	"Title Auto-Generation": "Génération automatique de titres",
+	"Title cannot be an empty string.": "Le titre ne peut pas être une chaîne de caractères vide.",
+	"Title Generation Prompt": "Prompt de génération de titre",
+	"TLS": "",
+	"To access the available model names for downloading,": "Pour accéder aux noms des modèles disponibles en téléchargement,",
+	"To access the GGUF models available for downloading,": "Pour accéder aux modèles GGUF disponibles en téléchargement,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pour accéder à l'interface Web, veuillez contacter l'administrateur. Les administrateurs peuvent gérer les statuts des utilisateurs depuis le panneau d'administration.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Pour sélectionner des filtres ici, ajoutez-les d'abord à l'espace de travail « Fonctions ». ",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Pour sélectionner des toolkits ici, ajoutez-les d'abord à l'espace de travail « Outils ». ",
+	"Toast notifications for new updates": "",
+	"Today": "Aujourd'hui",
+	"Toggle settings": "Basculer les paramètres",
+	"Toggle sidebar": "Basculer la barre latérale",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Jeton à conserver pour l'actualisation du contexte (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "L'outil a été créé avec succès",
+	"Tool deleted successfully": "Outil supprimé avec succès",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Outil importé avec succès",
+	"Tool Name": "",
+	"Tool updated successfully": "L'outil a été mis à jour avec succès",
+	"Tools": "Outils",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Rencontrez-vous des difficultés pour accéder à Ollama ?",
+	"TTS Model": "Modèle de synthèse vocale",
+	"TTS Settings": "Paramètres de synthèse vocale",
+	"TTS Voice": "Voix TTS",
+	"Type": "Type",
+	"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de Téléchargement Hugging Face Resolve",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh non ! Un problème est survenu lors de la connexion à {{provider}}.",
+	"UI": "Interface utilisateur",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Mise à jour",
+	"Update and Copy Link": "Mettre à jour et copier le lien",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Mettre à jour le mot de passe",
+	"Updated": "",
+	"Updated at": "Mise à jour le",
+	"Updated At": "",
+	"Upload": "Télécharger",
+	"Upload a GGUF model": "Téléverser un modèle GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Télécharger des fichiers",
+	"Upload Pipeline": "Pipeline de téléchargement",
+	"Upload Progress": "Progression de l'envoi",
+	"URL": "",
+	"URL Mode": "Mode d'URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Utilisez Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Utiliser les initiales",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "utiliser mmap (Ollama)",
+	"user": "utilisateur",
+	"User": "",
+	"User location successfully retrieved.": "L'emplacement de l'utilisateur a été récupéré avec succès.",
+	"Username": "",
+	"Users": "Utilisateurs",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Utilisez",
+	"Valid time units:": "Unités de temps valides\u00a0:",
+	"Valves": "Vannes",
+	"Valves updated": "Vannes mises à jour",
+	"Valves updated successfully": "Les vannes ont été mises à jour avec succès",
+	"variable": "variable",
+	"variable to have them replaced with clipboard content.": "variable pour qu'elles soient remplacées par le contenu du presse-papiers.",
+	"Version": "Version améliorée",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Voix",
+	"Voice Input": "",
+	"Warning": "Avertissement !",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avertissement : Si vous mettez à jour ou modifiez votre modèle d'encodage, vous devrez réimporter tous les documents.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Paramètres du chargeur web",
+	"Web Search": "Recherche Web",
+	"Web Search Engine": "Moteur de recherche Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL du webhook",
+	"WebUI Settings": "Paramètres de WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Quoi de neuf",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (local)",
+	"Why?": "",
+	"Widescreen Mode": "Mode Grand Écran",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Espace de travail",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez une suggestion de prompt (par exemple : Qui êtes-vous ?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé de 50 mots qui résume [sujet ou mot-clé].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Hier",
+	"You": "Vous",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des souvenirs via le bouton 'Gérer' ci-dessous, ce qui les rendra plus utiles et adaptés à vos besoins.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Vous n'avez aucune conversation archivée",
+	"You have shared this chat": "Vous avez partagé cette conversation.",
+	"You're a helpful assistant.": "Vous êtes un assistant serviable.",
+	"You're now logged in.": "Vous êtes désormais connecté.",
+	"Your account status is currently pending activation.": "Votre statut de compte est actuellement en attente d'activation.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "Paramètres de l'outil de téléchargement YouTube"
+}
diff --git a/src/lib/i18n/locales/fr-FR/translation.json b/src/lib/i18n/locales/fr-FR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..14722805bb0331aa2540983f569b1c4fd6772abe
--- /dev/null
+++ b/src/lib/i18n/locales/fr-FR/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": " 's', 'm', 'h', 'd', 'w' ou '-1' pour une durée illimitée.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(par ex. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(par exemple `sh webui.sh --api`)",
+	"(latest)": "(dernière version)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Conversations de {{user}}",
+	"{{webUIName}} Backend Required": "Backend {{webUIName}} requis",
+	"*Prompt node ID(s) are required for image generation": "*Les ID de noeud du prompt sont nécessaires pour la génération d’images",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Une nouvelle version (v{{LATEST_VERSION}}) est disponible.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un modèle de tâche est utilisé lors de l’exécution de tâches telles que la génération de titres pour les conversations et les requêtes de recherche sur le web.",
+	"a user": "un utilisateur",
+	"About": "À propos",
+	"Access": "Accès",
+	"Access Control": "Contrôle d'accès",
+	"Accessible to all users": "Accessible à tous les utilisateurs",
+	"Account": "Compte",
+	"Account Activation Pending": "Activation du compte en attente",
+	"Accurate information": "Information exacte",
+	"Actions": "Actions",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activez cette commande en tapant \"/{{COMMAND}}\" dans l'entrée de chat.",
+	"Active Users": "Utilisateurs actifs",
+	"Add": "Ajouter",
+	"Add a model ID": "Ajouter un identifiant de modèle",
+	"Add a short description about what this model does": "Ajoutez une brève description de ce que fait ce modèle.",
+	"Add a tag": "Ajouter un tag",
+	"Add Arena Model": "Ajouter un modèle d'arène",
+	"Add Connection": "Ajouter une connexion",
+	"Add Content": "Ajouter du contenu",
+	"Add content here": "Ajoutez du contenu ici",
+	"Add custom prompt": "Ajouter un prompt personnalisé",
+	"Add Files": "Ajouter des fichiers",
+	"Add Group": "Ajouter un groupe",
+	"Add Memory": "Ajouter de la mémoire",
+	"Add Model": "Ajouter un modèle",
+	"Add Tag": "Ajouter un tag",
+	"Add Tags": "Ajouter des tags",
+	"Add text content": "Ajouter du contenu textuel",
+	"Add User": "Ajouter un utilisateur",
+	"Add User Group": "Ajouter un groupe d'utilisateurs",
+	"Adjusting these settings will apply changes universally to all users.": "L'ajustement de ces paramètres appliquera universellement les changements à tous les utilisateurs.",
+	"admin": "administrateur",
+	"Admin": "Administrateur",
+	"Admin Panel": "Panneau d'administration",
+	"Admin Settings": "Paramètres admin.",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Les administrateurs ont accès à tous les outils en permanence ; les utilisateurs doivent se voir attribuer des outils pour chaque modèle dans l’espace de travail.",
+	"Advanced Parameters": "Paramètres avancés",
+	"Advanced Params": "Paramètres avancés",
+	"All chats": "Toutes les conversations",
+	"All Documents": "Tous les documents",
+	"All models deleted successfully": "Tous les modèles ont été supprimés avec succès",
+	"Allow Chat Delete": "Autoriser la suppression de la conversation",
+	"Allow Chat Deletion": "Autoriser la suppression de l'historique de chat",
+	"Allow Chat Edit": "Autoriser la modification de la conversation",
+	"Allow File Upload": "Autoriser le téléchargement de fichiers",
+	"Allow non-local voices": "Autoriser les voix non locales",
+	"Allow Temporary Chat": "Autoriser le chat éphémère",
+	"Allow User Location": "Autoriser l'emplacement de l'utilisateur",
+	"Allow Voice Interruption in Call": "Autoriser l'interruption vocale pendant un appel",
+	"Already have an account?": "Avez-vous déjà un compte ?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternative au top_p, visant à assurer un équilibre entre qualité et variété. Le paramètre p représente la probabilité minimale pour qu'un token soit pris en compte, par rapport à la probabilité du token le plus probable. Par exemple, avec p=0.05 et le token le plus probable ayant une probabilité de 0.9, les logits ayant une valeur inférieure à 0.045 sont filtrés. (Par défaut : 0.0)",
+	"Amazing": "Incroyable",
+	"an assistant": "un assistant",
+	"and": "et",
+	"and {{COUNT}} more": "et {{COUNT}} autres",
+	"and create a new shared link.": "et créer un nouveau lien partagé.",
+	"API Base URL": "URL de base de l'API",
+	"API Key": "Clé d'API",
+	"API Key created.": "Clé d'API générée.",
+	"API keys": "Clés d'API",
+	"Application DN": "DN de l'application",
+	"Application DN Password": "Mot de passe DN de l'application",
+	"applies to all users with the \"user\" role": "s'applique à tous les utilisateurs ayant le rôle « utilisateur »",
+	"April": "Avril",
+	"Archive": "Archiver",
+	"Archive All Chats": "Archiver toutes les conversations",
+	"Archived Chats": "Conversations archivées",
+	"archived-chat-export": "exportation de conversation archivée",
+	"Are you sure you want to unarchive all archived chats?": "Êtes-vous sûr de vouloir désarchiver toutes les conversations archivées?",
+	"Are you sure?": "Êtes-vous certain ?",
+	"Arena Models": "Modèles d'arène",
+	"Artifacts": "Artéfacts",
+	"Ask a question": "Posez votre question",
+	"Assistant": "Assistant",
+	"Attach file": "Joindre un document",
+	"Attention to detail": "Attention aux détails",
+	"Attribute for Username": "Attribut pour le nom d'utilisateur",
+	"Audio": "Audio",
+	"August": "Août",
+	"Authenticate": "Authentifier",
+	"Auto-Copy Response to Clipboard": "Copie automatique de la réponse vers le presse-papiers",
+	"Auto-playback response": "Lire automatiquement la réponse",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Chaîne d'authentification de l'API",
+	"AUTOMATIC1111 Base URL": "URL de base AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "L'URL de base {AUTOMATIC1111} est requise.",
+	"Available list": "Liste disponible",
+	"available!": "disponible !",
+	"Awful": "Horrible",
+	"Azure AI Speech": "Azure AI Speech",
+	"Azure Region": "Région Azure",
+	"Back": "Retour en arrière",
+	"Bad Response": "Mauvaise réponse",
+	"Banners": "Bannières",
+	"Base Model (From)": "Modèle de base (à partir de)",
+	"Batch Size (num_batch)": "Batch Size (num_batch)",
+	"before": "avant",
+	"Being lazy": "Être fainéant",
+	"Bing Search V7 Endpoint": "Point de terminaison Bing Search V7",
+	"Bing Search V7 Subscription Key": "Clé d'abonnement Bing Search V7",
+	"Brave Search API Key": "Clé API Brave Search",
+	"By {{name}}": "Par {{name}}",
+	"Bypass SSL verification for Websites": "Bypasser la vérification SSL pour les sites web",
+	"Call": "Appeler",
+	"Call feature is not supported when using Web STT engine": "La fonction d'appel n'est pas prise en charge lors de l'utilisation du moteur Web STT",
+	"Camera": "Appareil photo",
+	"Cancel": "Annuler",
+	"Capabilities": "Capacités",
+	"Certificate Path": "Chemin du certificat",
+	"Change Password": "Changer le mot de passe",
+	"Character": "Caractère",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Tracer de nouvelles frontières",
+	"Chat": "Chat",
+	"Chat Background Image": "Image d'arrière-plan de la fenêtre de chat",
+	"Chat Bubble UI": "Bulles de chat",
+	"Chat Controls": "Contrôles du chat",
+	"Chat direction": "Direction du chat",
+	"Chat Overview": "Aperçu du chat",
+	"Chat Permissions": "Autorisations de chat",
+	"Chat Tags Auto-Generation": "Génération automatique des tags",
+	"Chats": "Conversations",
+	"Check Again": "Vérifiez à nouveau.",
+	"Check for updates": "Vérifier les mises à jour disponibles",
+	"Checking for updates...": "Recherche de mises à jour...",
+	"Choose a model before saving...": "Choisissez un modèle avant de sauvegarder...",
+	"Chunk Overlap": "Chevauchement des chunks",
+	"Chunk Params": "Paramètres des chunks",
+	"Chunk Size": "Taille des chunks",
+	"Ciphers": "Chiffres",
+	"Citation": "Citation",
+	"Clear memory": "Effacer la mémoire",
+	"click here": "cliquez ici",
+	"Click here for filter guides.": "Cliquez ici pour les guides de filtrage.",
+	"Click here for help.": "Cliquez ici pour obtenir de l'aide.",
+	"Click here to": "Cliquez ici pour",
+	"Click here to download user import template file.": "Cliquez ici pour télécharger le fichier modèle d'importation des utilisateurs.",
+	"Click here to learn more about faster-whisper and see the available models.": "Cliquez ici pour en savoir plus sur faster-whisper et voir les modèles disponibles.",
+	"Click here to select": "Cliquez ici pour sélectionner",
+	"Click here to select a csv file.": "Cliquez ici pour sélectionner un fichier .csv.",
+	"Click here to select a py file.": "Cliquez ici pour sélectionner un fichier .py.",
+	"Click here to upload a workflow.json file.": "Cliquez ici pour télécharger un fichier workflow.json.",
+	"click here.": "cliquez ici.",
+	"Click on the user role button to change a user's role.": "Cliquez sur le bouton de rôle d'utilisateur pour modifier son rôle.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "L'autorisation d'écriture du presse-papier a été refusée. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
+	"Clone": "Cloner",
+	"Close": "Fermer",
+	"Code execution": "Exécution de code",
+	"Code formatted successfully": "Le code a été formaté avec succès",
+	"Collection": "Collection",
+	"Color": "Couleur",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL de base ComfyUI",
+	"ComfyUI Base URL is required.": "L'URL de base ComfyUI est requise.",
+	"ComfyUI Workflow": "Flux de travaux de ComfyUI",
+	"ComfyUI Workflow Nodes": "Noeud du flux de travaux de ComfyUI",
+	"Command": "Commande",
+	"Completions": "Complétions",
+	"Concurrent Requests": "Demandes concurrentes",
+	"Configure": "Configurer",
+	"Configure Models": "Configurer les modèles",
+	"Confirm": "Confirmer",
+	"Confirm Password": "Confirmer le mot de passe",
+	"Confirm your action": "Confirmer votre action",
+	"Connections": "Connexions",
+	"Contact Admin for WebUI Access": "Contacter l'administrateur pour obtenir l'accès à WebUI",
+	"Content": "Contenu",
+	"Content Extraction": "Extraction du contenu",
+	"Context Length": "Longueur du contexte",
+	"Continue Response": "Continuer la réponse",
+	"Continue with {{provider}}": "Continuer avec {{provider}}",
+	"Continue with Email": "Continuer avec l'email",
+	"Continue with LDAP": "Continuer avec LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Contrôle la façon dont le texte des messages est divisé pour les demandes de Text-to-Speech. « ponctuation » divise en phrases, « paragraphes » divise en paragraphes et « aucun » garde le message en tant que chaîne de texte unique.",
+	"Controls": "Contrôles",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Contrôle l'équilibre entre la cohérence et la diversité de la sortie. Une valeur plus basse produira un texte plus focalisé et cohérent. (Par défaut : 5.0)",
+	"Copied": "Copié",
+	"Copied shared chat URL to clipboard!": "URL du chat copié dans le presse-papiers !",
+	"Copied to clipboard": "Copié dans le presse-papiers",
+	"Copy": "Copier",
+	"Copy last code block": "Copier le dernier bloc de code",
+	"Copy last response": "Copier la dernière réponse",
+	"Copy Link": "Copier le lien",
+	"Copy to clipboard": "Copier dans le presse-papiers",
+	"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
+	"Create": "Créer",
+	"Create a knowledge base": "Créer une base de connaissances",
+	"Create a model": "Créer un modèle",
+	"Create Account": "Créer un compte",
+	"Create Admin Account": "Créer un compte administrateur",
+	"Create Group": "Créer un groupe",
+	"Create Knowledge": "Créer une connaissance",
+	"Create new key": "Créer une nouvelle clé",
+	"Create new secret key": "Créer une nouvelle clé secrète",
+	"Created at": "Créé le",
+	"Created At": "Créé le",
+	"Created by": "Créé par",
+	"CSV Import": "Import CSV",
+	"Current Model": "Modèle actuel",
+	"Current Password": "Mot de passe actuel",
+	"Custom": "Sur mesure",
+	"Dark": "Sombre",
+	"Database": "Base de données",
+	"December": "Décembre",
+	"Default": "Par défaut",
+	"Default (Open AI)": "Par défaut (OpenAI)",
+	"Default (SentenceTransformers)": "Par défaut (Sentence Transformers)",
+	"Default Model": "Modèle standard",
+	"Default model updated": "Modèle par défaut mis à jour",
+	"Default Models": "Modèles par défaut",
+	"Default permissions": "Autorisations par défaut",
+	"Default permissions updated successfully": "Autorisations par défaut mises à jour avec succès",
+	"Default Prompt Suggestions": "Suggestions de prompts par défaut",
+	"Default to 389 or 636 if TLS is enabled": "Par défaut à 389 ou 636 si TLS est activé",
+	"Default to ALL": "Par défaut à TOUS",
+	"Default User Role": "Rôle utilisateur par défaut",
+	"Delete": "Supprimer",
+	"Delete a model": "Supprimer un modèle",
+	"Delete All Chats": "Supprimer toutes les conversations",
+	"Delete All Models": "Supprimer tous les modèles",
+	"Delete chat": "Supprimer la conversation",
+	"Delete Chat": "Supprimer la Conversation",
+	"Delete chat?": "Supprimer la conversation ?",
+	"Delete folder?": "Supprimer le dossier ?",
+	"Delete function?": "Supprimer la fonction ?",
+	"Delete prompt?": "Supprimer le prompt ?",
+	"delete this link": "supprimer ce lien",
+	"Delete tool?": "Effacer l'outil ?",
+	"Delete User": "Supprimer le compte d'utilisateur",
+	"Deleted {{deleteModelTag}}": "Supprimé {{deleteModelTag}}",
+	"Deleted {{name}}": "Supprimé {{name}}",
+	"Deleted User": "Utilisateur supprimé",
+	"Describe your knowledge base and objectives": "Décrivez votre base de connaissances et vos objectifs",
+	"Description": "Description",
+	"Didn't fully follow instructions": "N'a pas entièrement respecté les instructions",
+	"Disabled": "Désactivé",
+	"Discover a function": "Trouvez une fonction",
+	"Discover a model": "Trouvez un modèle",
+	"Discover a prompt": "Trouvez un prompt",
+	"Discover a tool": "Trouvez un outil",
+	"Discover wonders": "Découvrir des merveilles",
+	"Discover, download, and explore custom functions": "Découvrez, téléchargez et explorez des fonctions personnalisées",
+	"Discover, download, and explore custom prompts": "Découvrez, téléchargez et explorez des prompts personnalisés",
+	"Discover, download, and explore custom tools": "Découvrez, téléchargez et explorez des outils personnalisés",
+	"Discover, download, and explore model presets": "Découvrir, télécharger et explorer des préréglages de modèles",
+	"Dismissible": "Fermeture",
+	"Display": "Afficher",
+	"Display Emoji in Call": "Afficher les emojis pendant l'appel",
+	"Display the username instead of You in the Chat": "Afficher le nom d'utilisateur à la place de \"Vous\" dans le chat",
+	"Displays citations in the response": "Affiche les citations dans la réponse",
+	"Dive into knowledge": "Plonger dans les connaissances",
+	"Do not install functions from sources you do not fully trust.": "N'installez pas de fonctions provenant de sources auxquelles vous ne faites pas entièrement confiance.",
+	"Do not install tools from sources you do not fully trust.": "N'installez pas d'outils provenant de sources auxquelles vous ne faites pas entièrement confiance.",
+	"Document": "Document",
+	"Documentation": "Documentation",
+	"Documents": "Documents",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "n'établit aucune connexion externe et garde vos données en sécurité sur votre serveur local.",
+	"Don't have an account?": "Vous n'avez pas de compte ?",
+	"don't install random functions from sources you don't trust.": "n'installez pas de fonctions aléatoires provenant de sources auxquelles vous ne faites pas confiance.",
+	"don't install random tools from sources you don't trust.": "n'installez pas d'outils aléatoires provenant de sources auxquelles vous ne faites pas confiance.",
+	"Don't like the style": "N'apprécie pas le style",
+	"Done": "Terminé",
+	"Download": "Télécharger",
+	"Download canceled": "Téléchargement annulé",
+	"Download Database": "Télécharger la base de données",
+	"Drag and drop a file to upload or select a file to view": "Glissez et déposez un fichier pour le télécharger ou sélectionnez un fichier à visualiser",
+	"Draw": "Match nul",
+	"Drop any files here to add to the conversation": "Déposez des fichiers ici pour les ajouter à la conversation",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "par ex. '30s', '10 min'. Les unités de temps valides sont 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "par ex. un filtre pour retirer les vulgarités du texte",
+	"e.g. My Filter": "par ex. Mon Filtre",
+	"e.g. My Tools": "par ex. Mes Outils",
+	"e.g. my_filter": "par ex. mon_filtre",
+	"e.g. my_tools": "par ex. mes_outils",
+	"e.g. Tools for performing various operations": "par ex. Outils pour effectuer diverses opérations",
+	"Edit": "Modifier",
+	"Edit Arena Model": "Modifier le modèle d'arène",
+	"Edit Connection": "Modifier la connexion",
+	"Edit Default Permissions": "Modifier les autorisations par défaut",
+	"Edit Memory": "Modifier la mémoire",
+	"Edit User": "Modifier l'utilisateur",
+	"Edit User Group": "Modifier le groupe d'utilisateurs",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-mail",
+	"Embark on adventures": "Embarquez pour des aventures",
+	"Embedding Batch Size": "Taille du lot d'embedding",
+	"Embedding Model": "Modèle d'embedding",
+	"Embedding Model Engine": "Moteur de modèle d'embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Modèle d'embedding défini sur « {{embedding_model}} »",
+	"Enable API Key Auth": "Activer l'authentification par clé API",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Activer le partage communautaire",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Activer le verrouillage de la mémoire (mlock) pour empêcher les données du modèle d'être échangées de la RAM. Cette option verrouille l'ensemble de pages de travail du modèle en RAM, garantissant qu'elles ne seront pas échangées vers le disque. Cela peut aider à maintenir les performances en évitant les défauts de page et en assurant un accès rapide aux données.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Activer le mappage de la mémoire (mmap) pour charger les données du modèle. Cette option permet au système d'utiliser le stockage disque comme une extension de la RAM en traitant les fichiers disque comme s'ils étaient en RAM. Cela peut améliorer les performances du modèle en permettant un accès plus rapide aux données. Cependant, cela peut ne pas fonctionner correctement avec tous les systèmes et peut consommer une quantité significative d'espace disque.",
+	"Enable Message Rating": "Activer l'évaluation des messages",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Activer l'échantillonnage Mirostat pour contrôler la perplexité. (Par défaut : 0, 0 = Désactivé, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Activer les nouvelles inscriptions",
+	"Enable Web Search": "Activer la recherche Web",
+	"Enabled": "Activé",
+	"Engine": "Moteur",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Vérifiez que votre fichier CSV comprenne les 4 colonnes dans cet ordre : Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Entrez le message {{role}} ici",
+	"Enter a detail about yourself for your LLMs to recall": "Saisissez un détail sur vous-même que vos LLMs pourront se rappeler",
+	"Enter api auth string (e.g. username:password)": "Entrez la chaîne d'authentification de l'API (par ex. nom d'utilisateur:mot de passe)",
+	"Enter Application DN": "Entrez le DN de l'application",
+	"Enter Application DN Password": "Entrez le mot de passe DN de l'application",
+	"Enter Bing Search V7 Endpoint": "Entrez le point de terminaison Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Entrez la clé d'abonnement Bing Search V7",
+	"Enter Brave Search API Key": "Entrez la clé API Brave Search",
+	"Enter certificate path": "Entrez le chemin du certificat",
+	"Enter CFG Scale (e.g. 7.0)": "Entrez l'échelle CFG (par ex. 7.0)",
+	"Enter Chunk Overlap": "Entrez le chevauchement des chunks",
+	"Enter Chunk Size": "Entrez la taille des chunks",
+	"Enter description": "Entrez la description",
+	"Enter Github Raw URL": "Entrez l'URL brute de GitHub",
+	"Enter Google PSE API Key": "Entrez la clé API Google PSE",
+	"Enter Google PSE Engine Id": "Entrez l'identifiant du moteur Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Entrez la taille de l'image (par ex. 512x512)",
+	"Enter Jina API Key": "Entrez la clé API Jina",
+	"Enter language codes": "Entrez les codes de langue",
+	"Enter Model ID": "Entrez l'ID du modèle",
+	"Enter model tag (e.g. {{modelTag}})": "Entrez le tag du modèle (par ex. {{modelTag}})",
+	"Enter Mojeek Search API Key": "Entrez la clé API Mojeek",
+	"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (par ex. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Entrez le sampler (par ex. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Entrez le planificateur (par ex. Karras)",
+	"Enter Score": "Entrez votre score",
+	"Enter SearchApi API Key": "Entrez la clé API SearchApi",
+	"Enter SearchApi Engine": "Entrez le moteur de recherche SearchApi",
+	"Enter Searxng Query URL": "Entrez l'URL de la requête Searxng",
+	"Enter Seed": "Entrez Seed",
+	"Enter Serper API Key": "Entrez la clé API Serper",
+	"Enter Serply API Key": "Entrez la clé API Serply",
+	"Enter Serpstack API Key": "Entrez la clé API Serpstack",
+	"Enter server host": "Entrez l'hôte du serveur",
+	"Enter server label": "Entrez l'étiquette du serveur",
+	"Enter server port": "Entrez le port du serveur",
+	"Enter stop sequence": "Entrez la séquence d'arrêt",
+	"Enter system prompt": "Entrez le prompt système",
+	"Enter Tavily API Key": "Entrez la clé API Tavily",
+	"Enter Tika Server URL": "Entrez l'URL du serveur Tika",
+	"Enter Top K": "Entrez les Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Entrez l'URL (par ex. {http://127.0.0.1:7860/})",
+	"Enter URL (e.g. http://localhost:11434)": "Entrez l'URL (par ex. http://localhost:11434)",
+	"Enter Your Email": "Entrez votre adresse e-mail",
+	"Enter Your Full Name": "Entrez votre nom complet",
+	"Enter your message": "Entrez votre message",
+	"Enter Your Password": "Entrez votre mot de passe",
+	"Enter Your Role": "Entrez votre rôle",
+	"Enter Your Username": "Entrez votre nom d'utilisateur",
+	"Error": "Erreur",
+	"ERROR": "ERREUR",
+	"Evaluations": "Évaluations",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Exemple: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Exemple: TOUS",
+	"Example: ou=users,dc=foo,dc=example": "Exemple: ou=utilisateurs,dc=foo,dc=exemple",
+	"Example: sAMAccountName or uid or userPrincipalName": "Exemple: sAMAccountName ou uid ou userPrincipalName",
+	"Exclude": "Exclure",
+	"Experimental": "Expérimental",
+	"Explore the cosmos": "Explorer le cosmos",
+	"Export": "Exportation",
+	"Export All Archived Chats": "Exporter toutes les conversations archivées",
+	"Export All Chats (All Users)": "Exporter toutes les conversations (de tous les utilisateurs)",
+	"Export chat (.json)": "Exporter la conversation (.json)",
+	"Export Chats": "Exporter les conversations",
+	"Export Config to JSON File": "Exporter la configuration vers un fichier JSON",
+	"Export Functions": "Exporter des fonctions",
+	"Export Models": "Exporter des modèles",
+	"Export Presets": "Exporter les préréglages",
+	"Export Prompts": "Exporter des prompts",
+	"Export to CSV": "Exporter en CSV",
+	"Export Tools": "Exporter des outils",
+	"External Models": "Modèles externes",
+	"Failed to add file.": "Échec de l'ajout du fichier.",
+	"Failed to create API Key.": "Échec de la création de la clé API.",
+	"Failed to read clipboard contents": "Échec de la lecture du contenu du presse-papiers",
+	"Failed to save models configuration": "Échec de la sauvegarde de la configuration des modèles",
+	"Failed to update settings": "Échec de la mise à jour des paramètres",
+	"Failed to upload file.": "Échec du téléchargement du fichier.",
+	"February": "Février",
+	"Feedback History": "Historique des avis",
+	"Feedbacks": "Avis",
+	"Feel free to add specific details": "N'hésitez pas à ajouter des détails spécifiques",
+	"File": "Fichier",
+	"File added successfully.": "Fichier ajouté avec succès.",
+	"File content updated successfully.": "Contenu du fichier mis à jour avec succès.",
+	"File Mode": "Mode fichier",
+	"File not found.": "Fichier introuvable.",
+	"File removed successfully.": "Fichier supprimé avec succès.",
+	"File size should not exceed {{maxSize}} MB.": "La taille du fichier ne doit pas dépasser {{maxSize}} Mo.",
+	"Files": "Fichiers",
+	"Filter is now globally disabled": "Le filtre est maintenant désactivé globalement",
+	"Filter is now globally enabled": "Le filtre est désormais activé globalement",
+	"Filters": "Filtres",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Spoofing détecté : impossible d'utiliser les initiales comme avatar. Retour à l'image de profil par défaut.",
+	"Fluidly stream large external response chunks": "Streaming fluide de gros chunks de réponses externes",
+	"Focus chat input": "Mettre le focus sur le champ de chat",
+	"Folder deleted successfully": "Dossier supprimé avec succès",
+	"Folder name cannot be empty": "Le nom du dossier ne peut pas être vide",
+	"Folder name cannot be empty.": "Le nom du dossier ne peut pas être vide.",
+	"Folder name updated successfully": "Le nom du dossier a été mis à jour avec succès",
+	"Followed instructions perfectly": "A parfaitement suivi les instructions",
+	"Forge new paths": "Créer de nouveaux chemins",
+	"Form": "Formulaire",
+	"Format your variables using brackets like this:": "Formatez vos variables en utilisant des parenthèses comme ceci :",
+	"Frequency Penalty": "Pénalité de fréquence",
+	"Function": "Fonction",
+	"Function created successfully": "La fonction a été créée avec succès",
+	"Function deleted successfully": "Fonction supprimée avec succès",
+	"Function Description": "Description de la fonction",
+	"Function ID": "ID de la fonction",
+	"Function is now globally disabled": "La fonction est désormais désactivée globalement",
+	"Function is now globally enabled": "La fonction est désormais activée globalement",
+	"Function Name": "Nom de la fonction",
+	"Function updated successfully": "La fonction a été mise à jour avec succès",
+	"Functions": "Fonctions",
+	"Functions allow arbitrary code execution": "Les fonctions permettent l'exécution de code arbitraire",
+	"Functions allow arbitrary code execution.": "Les fonctions permettent l'exécution de code arbitraire.",
+	"Functions imported successfully": "Fonctions importées avec succès",
+	"General": "Général",
+	"General Settings": "Paramètres généraux",
+	"Generate Image": "Générer une image",
+	"Generating search query": "Génération d'une requête de recherche",
+	"Generation Info": "Informations sur la génération",
+	"Get started": "Commencer",
+	"Get started with {{WEBUI_NAME}}": "Commencez avec {{WEBUI_NAME}}",
+	"Global": "Mondial",
+	"Good Response": "Bonne réponse",
+	"Google PSE API Key": "Clé API Google PSE",
+	"Google PSE Engine Id": "ID du moteur de recherche PSE de Google",
+	"Group created successfully": "Groupe créé avec succès",
+	"Group deleted successfully": "Groupe supprimé avec succès",
+	"Group Description": "Description du groupe",
+	"Group Name": "Nom du groupe",
+	"Group updated successfully": "Groupe mis à jour avec succès",
+	"Groups": "Groupes",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Retour haptique",
+	"has no conversations.": "n'a aucune conversation.",
+	"Hello, {{name}}": "Bonjour, {{name}}.",
+	"Help": "Aide",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Aidez-nous à créer le meilleur classement communautaire en partageant votre historique des avis !",
+	"Hex Color": "Couleur Hex",
+	"Hex Color - Leave empty for default color": "Couleur Hex - Laissez vide pour la couleur par défaut",
+	"Hide": "Cacher",
+	"Host": "Hôte",
+	"How can I help you today?": "Comment puis-je vous aider aujourd'hui ?",
+	"How would you rate this response?": "Comment évalueriez-vous cette réponse ?",
+	"Hybrid Search": "Recherche hybride",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Je reconnais avoir lu et compris les implications de mes actions. Je suis conscient des risques associés à l'exécution d'un code arbitraire et j'ai vérifié la fiabilité de la source.",
+	"ID": "ID",
+	"Ignite curiosity": "Éveiller la curiosité",
+	"Image Generation (Experimental)": "Génération d'images (expérimental)",
+	"Image Generation Engine": "Moteur de génération d'images",
+	"Image Settings": "Paramètres de génération d'images",
+	"Images": "Images",
+	"Import Chats": "Importer les conversations",
+	"Import Config from JSON File": "Importer la configuration depuis un fichier JSON",
+	"Import Functions": "Importer des fonctions",
+	"Import Models": "Importer des modèles",
+	"Import Presets": "Importer les préréglages",
+	"Import Prompts": "Importer des prompts",
+	"Import Tools": "Importer des outils",
+	"Include": "Inclure",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Inclure le drapeau `--api-auth` lors de l'exécution de stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Inclure le drapeau `--api` lorsque vous exécutez stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Influence la rapidité avec laquelle l'algorithme répond aux retours du texte généré. Un taux d'apprentissage plus bas entraînera des ajustements plus lents, tandis qu'un taux d'apprentissage plus élevé rendra l'algorithme plus réactif. (Par défaut : 0.1)",
+	"Info": "Info",
+	"Input commands": "Commandes d'entrée",
+	"Install from Github URL": "Installer depuis une URL GitHub",
+	"Instant Auto-Send After Voice Transcription": "Envoi automatique après la transcription",
+	"Interface": "Interface utilisateur",
+	"Invalid file format.": "Format de fichier non valide.",
+	"Invalid Tag": "Tag non valide",
+	"January": "Janvier",
+	"Jina API Key": "Clé API Jina",
+	"join our Discord for help.": "Rejoignez notre Discord pour obtenir de l'aide.",
+	"JSON": "JSON",
+	"JSON Preview": "Aperçu JSON",
+	"July": "Juillet",
+	"June": "Juin",
+	"JWT Expiration": "Expiration du token JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Temps de maintien connecté",
+	"Key": "Clé",
+	"Keyboard shortcuts": "Raccourcis clavier",
+	"Knowledge": "Connaissances",
+	"Knowledge Access": "Accès aux connaissances",
+	"Knowledge created successfully.": "Connaissance créée avec succès.",
+	"Knowledge deleted successfully.": "Connaissance supprimée avec succès.",
+	"Knowledge reset successfully.": "Connaissance réinitialisée avec succès.",
+	"Knowledge updated successfully": "Connaissance mise à jour avec succès",
+	"Label": "Étiquette",
+	"Landing Page Mode": "Mode de la page d'accueil",
+	"Language": "Langue",
+	"Last Active": "Dernière activité",
+	"Last Modified": "Dernière modification",
+	"LDAP": "LDAP",
+	"LDAP server updated": "Serveur LDAP mis à jour",
+	"Leaderboard": "Classement",
+	"Leave empty for unlimited": "Laissez vide pour illimité",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Laissez vide pour inclure tous les modèles depuis le point de terminaison \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Laissez vide pour inclure tous les modèles depuis le point de terminaison \"{{URL}}/models\"",
+	"Leave empty to include all models or select specific models": "Laissez vide pour inclure tous les modèles ou sélectionnez des modèles spécifiques",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Laissez vide pour utiliser le prompt par défaut, ou entrez un prompt personnalisé",
+	"Light": "Clair",
+	"Listening...": "Écoute en cours...",
+	"LLMs can make mistakes. Verify important information.": "Les LLM peuvent faire des erreurs. Vérifiez les informations importantes.",
+	"Local": "Local",
+	"Local Models": "Modèles locaux",
+	"Lost": "Perdu",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Réalisé par la communauté OpenWebUI",
+	"Make sure to enclose them with": "Assurez-vous de les inclure dans",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Veillez à exporter un fichier workflow.json au format API depuis ComfyUI.",
+	"Manage": "Gérer",
+	"Manage Arena Models": "Gérer les modèles d'arène",
+	"Manage Ollama": "Gérer Ollama",
+	"Manage Ollama API Connections": "Gérer les connexions API Ollama",
+	"Manage OpenAI API Connections": "Gérer les connexions API OpenAI",
+	"Manage Pipelines": "Gérer les pipelines",
+	"March": "Mars",
+	"Max Tokens (num_predict)": "Nb max de tokens (num_predict)",
+	"Max Upload Count": "Nombre maximal de téléversements",
+	"Max Upload Size": "Limite de taille de téléversement",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Un maximum de 3 modèles peut être téléchargé en même temps. Veuillez réessayer ultérieurement.",
+	"May": "Mai",
+	"Memories accessible by LLMs will be shown here.": "Les mémoires accessibles par les LLMs seront affichées ici.",
+	"Memory": "Mémoire",
+	"Memory added successfully": "Mémoire ajoutée avec succès",
+	"Memory cleared successfully": "La mémoire a été effacée avec succès",
+	"Memory deleted successfully": "La mémoire a été supprimée avec succès",
+	"Memory updated successfully": "La mémoire a été mise à jour avec succès",
+	"Merge Responses": "Fusionner les réponses",
+	"Message rating should be enabled to use this feature": "L'évaluation des messages doit être activée pour utiliser cette fonctionnalité",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Les messages que vous envoyez après avoir créé votre lien ne seront pas partagés. Les utilisateurs disposant de l'URL pourront voir la conversation partagée.",
+	"Min P": "P min",
+	"Minimum Score": "Score minimal",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm:ss",
+	"Model": "Modèle",
+	"Model '{{modelName}}' has been successfully downloaded.": "Le modèle '{{modelName}}' a été téléchargé avec succès.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Le modèle '{{modelTag}}' est déjà dans la file d'attente pour le téléchargement.",
+	"Model {{modelId}} not found": "Modèle {{modelId}} introuvable",
+	"Model {{modelName}} is not vision capable": "Le modèle {{modelName}} n'a pas de capacités visuelles",
+	"Model {{name}} is now {{status}}": "Le modèle {{name}} est désormais {{status}}.",
+	"Model accepts image inputs": "Le modèle accepte les images en entrée",
+	"Model created successfully!": "Le modèle a été créé avec succès !",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Chemin du système de fichiers de modèle détecté. Le nom court du modèle est requis pour la mise à jour, l'opération ne peut pas être poursuivie.",
+	"Model Filtering": "Filtrage de modèle",
+	"Model ID": "ID du modèle",
+	"Model IDs": "ID des modèles",
+	"Model Name": "Nom du modèle",
+	"Model not selected": "Modèle non sélectionné",
+	"Model Params": "Paramètres du modèle",
+	"Model Permissions": "Autorisations du modèle",
+	"Model updated successfully": "Le modèle a été mis à jour avec succès",
+	"Modelfile Content": "Contenu du Fichier de Modèle",
+	"Models": "Modèles",
+	"Models Access": "Accès aux modèles",
+	"Models configuration saved successfully": "Configuration des modèles enregistrée avec succès",
+	"Mojeek Search API Key": "Clé API Mojeek",
+	"more": "plus",
+	"More": "Plus",
+	"Name": "Nom d'utilisateur",
+	"Name your knowledge base": "Nommez votre base de connaissances",
+	"New Chat": "Nouvelle conversation",
+	"New folder": "Nouveau dossier",
+	"New Password": "Nouveau mot de passe",
+	"No content found": "Aucun contenu trouvé",
+	"No content to speak": "Rien à signaler",
+	"No distance available": "Aucune distance disponible",
+	"No feedbacks found": "Aucun avis trouvé",
+	"No file selected": "Aucun fichier sélectionné",
+	"No files found.": "Aucun fichier trouvé.",
+	"No groups with access, add a group to grant access": "Aucun groupe n'a accès, ajoutez un groupe pour accorder l'accès",
+	"No HTML, CSS, or JavaScript content found.": "Aucun contenu HTML, CSS ou JavaScript trouvé.",
+	"No knowledge found": "Aucune connaissance trouvée",
+	"No model IDs": "Aucun ID de modèle",
+	"No models found": "Aucun modèle trouvé",
+	"No models selected": "Aucun modèle sélectionné",
+	"No results found": "Aucun résultat trouvé",
+	"No search query generated": "Aucune requête de recherche générée",
+	"No source available": "Aucune source n'est disponible",
+	"No users were found.": "Aucun utilisateur trouvé.",
+	"No valves to update": "Aucune vanne à mettre à jour",
+	"None": "Aucun",
+	"Not factually correct": "Non factuellement correct",
+	"Not helpful": "Pas utile",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Note : Si vous définissez un score minimum, seuls les documents ayant un score supérieur ou égal à ce score minimum seront retournés par la recherche.",
+	"Notes": "Notes",
+	"Notifications": "Notifications",
+	"November": "Novembre",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Octobre",
+	"Off": "Désactivé",
+	"Okay, Let's Go!": "D'accord, allons-y !",
+	"OLED Dark": "Noir OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama désactivée",
+	"Ollama API settings updated": "Paramètres de l'API Ollama mis à jour",
+	"Ollama Version": "Version Ollama",
+	"On": "Activé",
+	"Only alphanumeric characters and hyphens are allowed": "Seuls les caractères alphanumériques et les tirets sont autorisés",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Seuls les caractères alphanumériques et les tirets sont autorisés dans la chaîne de commande.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Seules les collections peuvent être modifiées, créez une nouvelle base de connaissance pour modifier/ajouter des documents.",
+	"Only select users and groups with permission can access": "Seuls les utilisateurs et groupes autorisés peuvent accéder",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oups ! Il semble que l'URL soit invalide. Veuillez vérifier à nouveau et réessayer.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Oups ! Des fichiers sont encore en cours de téléversement. Veuillez patienter jusqu'à la fin du téléversement.",
+	"Oops! There was an error in the previous response.": "Oups ! Il y a eu une erreur dans la réponse précédente.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oups\u00a0! Vous utilisez une méthode non prise en charge (frontend uniquement). Veuillez servir l'interface Web à partir du backend.",
+	"Open file": "Ouvrir le fichier",
+	"Open in full screen": "Ouvrir en plein écran",
+	"Open new chat": "Ouvrir une nouvelle conversation",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI utilise faster-whisper en interne.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI utilise SpeechT5 et les embeddings de locuteur CMU Arctic.",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La version Open WebUI (v{{OPEN_WEBUI_VERSION}}) est inférieure à la version requise (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API compatibles OpenAI",
+	"OpenAI API Config": "Configuration de l'API OpenAI",
+	"OpenAI API Key is required.": "Une clé API OpenAI est requise.",
+	"OpenAI API settings updated": "Paramètres de l'API OpenAI mis à jour",
+	"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
+	"or": "ou",
+	"Organize your users": "Organisez vos utilisateurs",
+	"Other": "Autre",
+	"OUTPUT": "SORTIE",
+	"Output format": "Format de sortie",
+	"Overview": "Aperçu",
+	"page": "page",
+	"Password": "Mot de passe",
+	"Paste Large Text as File": "Coller un texte volumineux comme fichier",
+	"PDF document (.pdf)": "Document au format PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
+	"pending": "en attente",
+	"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
+	"Permission denied when accessing microphone": "Autorisation refusée lors de l'accès au micro",
+	"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
+	"Permissions": "Permissions",
+	"Personalization": "Personnalisation",
+	"Pin": "Épingler",
+	"Pinned": "Épinglé",
+	"Pioneer insights": "Explorer de nouvelles perspectives",
+	"Pipeline deleted successfully": "Le pipeline a été supprimé avec succès",
+	"Pipeline downloaded successfully": "Le pipeline a été téléchargé avec succès",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Aucun pipelines détecté",
+	"Pipelines Valves": "Vannes de pipelines",
+	"Plain text (.txt)": "Texte simple (.txt)",
+	"Playground": "Playground",
+	"Please carefully review the following warnings:": "Veuillez lire attentivement les avertissements suivants :",
+	"Please enter a prompt": "Veuillez saisir un prompt",
+	"Please fill in all fields.": "Veuillez remplir tous les champs.",
+	"Please select a model first.": "",
+	"Please select a reason": "Veuillez sélectionner une raison",
+	"Port": "Port",
+	"Positive attitude": "Attitude positive",
+	"Prefix ID": "ID de préfixe",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Le préfixe ID est utilisé pour éviter les conflits avec d'autres connexions en ajoutant un préfixe aux ID de modèle - laissez vide pour désactiver",
+	"Previous 30 days": "30 derniers jours",
+	"Previous 7 days": "7 derniers jours",
+	"Profile Image": "Image de profil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (par ex. Dites-moi un fait amusant à propos de l'Empire romain)",
+	"Prompt Content": "Contenu du prompt",
+	"Prompt created successfully": "Prompt créé avec succès",
+	"Prompt suggestions": "Suggestions pour le prompt",
+	"Prompt updated successfully": "Prompt mis à jour avec succès",
+	"Prompts": "Prompts",
+	"Prompts Access": "Accès aux prompts",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Récupérer « {{searchValue}} » depuis Ollama.com",
+	"Pull a model from Ollama.com": "Télécharger un modèle depuis Ollama.com",
+	"Query Generation Prompt": "Prompt de génération de requête",
+	"Query Params": "Paramètres de requête",
+	"RAG Template": "Modèle RAG",
+	"Rating": "Note",
+	"Re-rank models by topic similarity": "Reclasser les modèles par similarité de sujet",
+	"Read Aloud": "Lire à haute voix",
+	"Record voice": "Enregistrer la voix",
+	"Redirecting you to OpenWebUI Community": "Redirection vers la communauté OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Réduit la probabilité de générer des non-sens. Une valeur plus élevée (par exemple 100) donnera des réponses plus diversifiées, tandis qu'une valeur plus basse (par exemple 10) sera plus conservatrice. (Par défaut : 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Désignez-vous comme « Utilisateur » (par ex. « L'utilisateur apprend l'espagnol »)",
+	"References from": "Références de",
+	"Refused when it shouldn't have": "Refusé alors qu'il n'aurait pas dû l'être",
+	"Regenerate": "Regénérer",
+	"Release Notes": "Notes de mise à jour",
+	"Relevance": "Pertinence",
+	"Remove": "Retirer",
+	"Remove Model": "Retirer le modèle",
+	"Rename": "Renommer",
+	"Reorder Models": "Réorganiser les modèles",
+	"Repeat Last N": "Répéter les N derniers",
+	"Request Mode": "Mode de requête",
+	"Reranking Model": "Modèle de ré-ranking",
+	"Reranking model disabled": "Modèle de ré-ranking désactivé",
+	"Reranking model set to \"{{reranking_model}}\"": "Modèle de ré-ranking défini sur « {{reranking_model}} »",
+	"Reset": "Réinitialiser",
+	"Reset All Models": "Réinitialiser tous les modèles",
+	"Reset Upload Directory": "Réinitialiser le répertoire de téléchargement",
+	"Reset Vector Storage/Knowledge": "Réinitialiser le stockage vectoriel/connaissances",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Les notifications de réponse ne peuvent pas être activées car les autorisations du site web ont été refusées. Veuillez vérifier les paramètres de votre navigateur pour accorder l'accès nécessaire.",
+	"Response splitting": "Fractionnement de la réponse",
+	"Result": "Résultat",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Saisie de texte enrichi pour le chat",
+	"RK": "Rang",
+	"Role": "Rôle",
+	"Rosé Pine": "Pin rosé",
+	"Rosé Pine Dawn": "Aube de Pin Rosé",
+	"RTL": "RTL",
+	"Run": "Exécuter",
+	"Running": "Exécution",
+	"Save": "Enregistrer",
+	"Save & Create": "Enregistrer & Créer",
+	"Save & Update": "Enregistrer & Mettre à jour",
+	"Save As Copy": "Enregistrer comme copie",
+	"Save Tag": "Enregistrer le tag",
+	"Saved": "Enregistré",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "La sauvegarde des journaux de conversation directement dans le stockage de votre navigateur n'est plus prise en charge. Veuillez prendre un instant pour télécharger et supprimer vos journaux de conversation en cliquant sur le bouton ci-dessous. Ne vous inquiétez pas, vous pouvez facilement réimporter vos journaux de conversation dans le backend via",
+	"Scroll to bottom when switching between branches": "Défiler vers le bas lors du passage d'une branche à l'autre",
+	"Search": "Recherche",
+	"Search a model": "Rechercher un modèle",
+	"Search Base": "Base de recherche",
+	"Search Chats": "Rechercher des conversations",
+	"Search Collection": "Rechercher une collection",
+	"Search Filters": "Filtres de recherche",
+	"search for tags": "Rechercher des tags",
+	"Search Functions": "Rechercher des fonctions",
+	"Search Knowledge": "Rechercher des connaissances",
+	"Search Models": "Rechercher des modèles",
+	"Search options": "Options de recherche",
+	"Search Prompts": "Rechercher des prompts",
+	"Search Result Count": "Nombre de résultats de recherche",
+	"Search the web": "Rechercher sur le web",
+	"Search Tools": "Rechercher des outils",
+	"SearchApi API Key": "Clé API SearchApi",
+	"SearchApi Engine": "Moteur de recherche SearchApi",
+	"Searched {{count}} sites_one": "Recherché {{count}} site(s)_one",
+	"Searched {{count}} sites_many": "Recherché {{count}} sites_many",
+	"Searched {{count}} sites_other": "Recherché {{count}} sites_autres",
+	"Searching \"{{searchQuery}}\"": "Recherche de « {{searchQuery}} »",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Recherche des connaissances pour « {{searchQuery}} »",
+	"Searxng Query URL": "URL de recherche Searxng",
+	"See readme.md for instructions": "Voir le fichier readme.md pour les instructions",
+	"See what's new": "Découvrez les nouvelles fonctionnalités",
+	"Seed": "Seed",
+	"Select a base model": "Sélectionnez un modèle de base",
+	"Select a engine": "Sélectionnez un moteur",
+	"Select a function": "Sélectionnez une fonction",
+	"Select a group": "Sélectionner un groupe",
+	"Select a model": "Sélectionnez un modèle",
+	"Select a pipeline": "Sélectionnez un pipeline",
+	"Select a pipeline url": "Sélectionnez l'URL du pipeline",
+	"Select a tool": "Sélectionnez un outil",
+	"Select Engine": "Sélectionnez le moteur",
+	"Select Knowledge": "Sélectionnez une connaissance",
+	"Select model": "Sélectionner un modèle",
+	"Select only one model to call": "Sélectionnez seulement un modèle pour appeler",
+	"Selected model(s) do not support image inputs": "Les modèle(s) sélectionné(s) ne prennent pas en charge les entrées d'images",
+	"Semantic distance to query": "Distance sémantique à la requête",
+	"Send": "Envoyer",
+	"Send a Message": "Envoyer un message",
+	"Send message": "Envoyer un message",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Envoie `stream_options: { include_usage: true }` dans la requête.\nLes fournisseurs pris en charge renverront des informations sur l'utilisation des tokens dans la réponse lorsque cette option est activée.",
+	"September": "Septembre",
+	"Serper API Key": "Clé API Serper",
+	"Serply API Key": "Clé API Serply",
+	"Serpstack API Key": "Clé API Serpstack",
+	"Server connection verified": "Connexion au serveur vérifiée",
+	"Set as default": "Définir comme valeur par défaut",
+	"Set CFG Scale": "Définir la CFG",
+	"Set Default Model": "Définir le modèle par défaut",
+	"Set embedding model": "Définir le modèle d'embedding",
+	"Set embedding model (e.g. {{model}})": "Définir le modèle d'embedding (par ex. {{model}})",
+	"Set Image Size": "Définir la taille de l'image",
+	"Set reranking model (e.g. {{model}})": "Définir le modèle de ré-ranking (par ex. {{model}})",
+	"Set Sampler": "Définir le sampler",
+	"Set Scheduler": "Définir le planificateur",
+	"Set Steps": "Définir le nombre d'étapes",
+	"Set Task Model": "Définir le modèle de tâche",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Définir le nombre de dispositifs GPU utilisés pour le calcul. Cette option contrôle combien de dispositifs GPU (si disponibles) sont utilisés pour traiter les demandes entrantes. L'augmentation de cette valeur peut améliorer de manière significative les performances des modèles optimisés pour l'accélération GPU mais peut également consommer plus d'énergie et de ressources GPU.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Définir le nombre de threads de travail utilisés pour le calcul. Cette option contrôle combien de threads sont utilisés pour traiter les demandes entrantes simultanément. L'augmentation de cette valeur peut améliorer les performances sous de fortes charges de travail concurrentes mais peut également consommer plus de ressources CPU.",
+	"Set Voice": "Choisir la voix",
+	"Set whisper model": "Choisir le modèle Whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Définit la profondeur de recherche du modèle pour prévenir les répétitions. (Par défaut : 64, 0 = désactivé, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Définit la force avec laquelle les répétitions sont pénalisées. Une valeur plus élevée (par exemple 1.5) pénalisera plus fortement les répétitions, tandis qu'une valeur plus basse (par exemple 0.9) sera plus indulgente. (Par défaut : 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Définit la graine de nombre aléatoire à utiliser pour la génération. La définition de cette valeur à un nombre spécifique fera que le modèle générera le même texte pour le même prompt. (Par défaut : aléatoire)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Définit la taille de la fenêtre contextuelle utilisée pour générer le prochain token. (Par défaut : 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Définit les séquences d'arrêt à utiliser. Lorsque ce motif est rencontré, le LLM cessera de générer du texte et retournera. Plusieurs motifs d'arrêt peuvent être définis en spécifiant plusieurs paramètres d'arrêt distincts dans un fichier modèle.",
+	"Settings": "Paramètres",
+	"Settings saved successfully!": "Paramètres enregistrés avec succès !",
+	"Share": "Partager",
+	"Share Chat": "Partage de conversation",
+	"Share to OpenWebUI Community": "Partager avec la communauté OpenWebUI",
+	"Show": "Afficher",
+	"Show \"What's New\" modal on login": "Afficher la fenêtre modale \"Quoi de neuf\" lors de la connexion",
+	"Show Admin Details in Account Pending Overlay": "Afficher les coordonnées de l'administrateur aux comptes en attente",
+	"Show shortcuts": "Afficher les raccourcis",
+	"Show your support!": "Montrez votre soutien !",
+	"Showcased creativity": "Créativité mise en avant",
+	"Sign in": "Connexion",
+	"Sign in to {{WEBUI_NAME}}": "Connectez-vous à {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Connectez-vous à {{WEBUI_NAME}} avec LDAP",
+	"Sign Out": "Déconnexion",
+	"Sign up": "Inscrivez-vous",
+	"Sign up to {{WEBUI_NAME}}": "Inscrivez-vous à {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Connexion à {{WEBUI_NAME}}",
+	"Source": "Source",
+	"Speech Playback Speed": "Vitesse de lecture de la parole",
+	"Speech recognition error: {{error}}": "Erreur de reconnaissance vocale\u00a0: {{error}}",
+	"Speech-to-Text Engine": "Moteur de reconnaissance vocale",
+	"Stop": "Stop",
+	"Stop Sequence": "Séquence d'arrêt",
+	"Stream Chat Response": "Streamer la réponse de la conversation",
+	"STT Model": "Modèle de Speech-to-Text",
+	"STT Settings": "Paramètres de Speech-to-Text",
+	"Subtitle (e.g. about the Roman Empire)": "Sous-titres (par ex. sur l'Empire romain)",
+	"Success": "Réussite",
+	"Successfully updated.": "Mise à jour réussie.",
+	"Suggested": "Suggéré",
+	"Support": "Supporter",
+	"Support this plugin:": "Supporter ce module",
+	"Sync directory": "Synchroniser le répertoire",
+	"System": "Système",
+	"System Instructions": "Instructions système",
+	"System Prompt": "Prompt système",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Prompt de génération de tags",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "L'échantillonnage sans queue est utilisé pour réduire l'impact des tokens moins probables dans la sortie. Une valeur plus élevée (par exemple 2.0) réduira davantage l'impact, tandis qu'une valeur de 1.0 désactive ce paramètre. (par défaut : 1)",
+	"Tap to interrupt": "Appuyez pour interrompre",
+	"Tavily API Key": "Clé API Tavily",
+	"Tell us more:": "Dites-nous en plus à ce sujet : ",
+	"Temperature": "Température",
+	"Template": "Template",
+	"Temporary Chat": "Chat éphémère",
+	"Text Splitter": "Text Splitter",
+	"Text-to-Speech Engine": "Moteur de Text-to-Speech",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Merci pour vos commentaires !",
+	"The Application Account DN you bind with for search": "Le DN du compte de l'application avec lequel vous vous liez pour la recherche",
+	"The base to search for users": "La base pour rechercher des utilisateurs",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "La taille de lot détermine combien de demandes de texte sont traitées ensemble en une fois. Une taille de lot plus grande peut augmenter les performances et la vitesse du modèle, mais elle nécessite également plus de mémoire. (Par défaut : 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Les développeurs de ce plugin sont des bénévoles passionnés issus de la communauté. Si vous trouvez ce plugin utile, merci de contribuer à son développement.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Le classement d'évaluation est basé sur le système de notation Elo et est mis à jour en temps réel.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "L'attribut LDAP qui correspond au nom d'utilisateur que les utilisateurs utilisent pour se connecter.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Le classement est actuellement en version bêta et nous pouvons ajuster les calculs de notation à mesure que nous peaufinons l'algorithme.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "La taille maximale du fichier en Mo. Si la taille du fichier dépasse cette limite, le fichier ne sera pas téléchargé.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Le nombre maximal de fichiers pouvant être utilisés en même temps dans la conversation. Si le nombre de fichiers dépasse cette limite, les fichiers ne seront pas téléchargés.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Le score doit être une valeur comprise entre 0,0 (0\u00a0%) et 1,0 (100\u00a0%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "La température du modèle. Augmenter la température rendra le modèle plus créatif dans ses réponses. (Par défaut : 0.8)",
+	"Theme": "Thème",
+	"Thinking...": "En train de réfléchir...",
+	"This action cannot be undone. Do you wish to continue?": "Cette action ne peut pas être annulée. Souhaitez-vous continuer ?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cela garantit que vos conversations précieuses soient sauvegardées en toute sécurité dans votre base de données backend. Merci !",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Il s'agit d'une fonctionnalité expérimentale, elle peut ne pas fonctionner comme prévu et est sujette à modification à tout moment.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Cette option contrôle combien de tokens sont conservés lors du rafraîchissement du contexte. Par exemple, si ce paramètre est défini à 2, les 2 derniers tokens du contexte de conversation seront conservés. Préserver le contexte peut aider à maintenir la continuité d'une conversation, mais cela peut réduire la capacité à répondre à de nouveaux sujets. (Par défaut : 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Cette option définit le nombre maximum de tokens que le modèle peut générer dans sa réponse. Augmenter cette limite permet au modèle de fournir des réponses plus longues, mais cela peut également augmenter la probabilité de générer du contenu inutile ou non pertinent. (Par défaut : 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Cette option supprimera tous les fichiers existants dans la collection et les remplacera par les fichiers nouvellement téléchargés.",
+	"This response was generated by \"{{model}}\"": "Cette réponse a été générée par \"{{model}}\"",
+	"This will delete": "Cela supprimera",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Cela supprimera <strong>{{NAME}}</strong> et <strong>tout son contenu</strong>.",
+	"This will delete all models including custom models": "Cela supprimera tous les modèles, y compris les modèles personnalisés",
+	"This will delete all models including custom models and cannot be undone.": "Cela supprimera tous les modèles, y compris les modèles personnalisés, et ne peut pas être annulé.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Cela réinitialisera la base de connaissances et synchronisera tous les fichiers. Souhaitez-vous continuer ?",
+	"Thorough explanation": "Explication approfondie",
+	"Tika": "Tika",
+	"Tika Server URL required.": "URL du serveur Tika requise.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Conseil\u00a0: mettez à jour plusieurs emplacements de variables consécutivement en appuyant sur la touche Tab dans l’entrée de chat après chaque remplacement.",
+	"Title": "Titre",
+	"Title (e.g. Tell me a fun fact)": "Titre (par ex. raconte-moi un fait amusant)",
+	"Title Auto-Generation": "Génération automatique des titres",
+	"Title cannot be an empty string.": "Le titre ne peut pas être une chaîne de caractères vide.",
+	"Title Generation Prompt": "Prompt de génération de titre",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Pour accéder aux noms des modèles disponibles,",
+	"To access the GGUF models available for downloading,": "Pour accéder aux modèles GGUF disponibles,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pour accéder à l'interface Web, veuillez contacter l'administrateur. Les administrateurs peuvent gérer les statuts des utilisateurs depuis le panneau d'administration.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Pour attacher une base de connaissances ici, ajoutez-les d'abord à l'espace de travail « Connaissances ».",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Pour protéger votre confidentialité, seules les notes, les identifiants de modèle, les tags et les métadonnées de vos commentaires sont partagés. Vos journaux de discussion restent privés et ne sont pas inclus.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Pour sélectionner des actions ici, ajoutez-les d'abord à l'espace de travail « Fonctions ».",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Pour sélectionner des filtres ici, ajoutez-les d'abord à l'espace de travail « Fonctions ». ",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Pour sélectionner des outils ici, ajoutez-les d'abord à l'espace de travail « Outils ». ",
+	"Toast notifications for new updates": "Notifications toast pour les nouvelles mises à jour",
+	"Today": "Aujourd'hui",
+	"Toggle settings": "Afficher/masquer les paramètres",
+	"Toggle sidebar": "Afficher/masquer la barre latérale",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens à conserver lors du rafraîchissement du contexte (num_keep)",
+	"Too verbose": "Trop détaillé",
+	"Tool created successfully": "L'outil a été créé avec succès",
+	"Tool deleted successfully": "Outil supprimé avec succès",
+	"Tool Description": "Description de l'outil",
+	"Tool ID": "ID de l'outil",
+	"Tool imported successfully": "Outil importé avec succès",
+	"Tool Name": "Nom de l'outil",
+	"Tool updated successfully": "L'outil a été mis à jour avec succès",
+	"Tools": "Outils",
+	"Tools Access": "Accès aux outils",
+	"Tools are a function calling system with arbitrary code execution": "Les outils sont un système d'appel de fonction avec exécution de code arbitraire",
+	"Tools have a function calling system that allows arbitrary code execution": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire",
+	"Tools have a function calling system that allows arbitrary code execution.": "Les outils ont un système d'appel de fonction qui permet l'exécution de code arbitraire.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformers",
+	"Trouble accessing Ollama?": "Problèmes d'accès à Ollama ?",
+	"TTS Model": "Modèle de Text-to-Speech",
+	"TTS Settings": "Paramètres de Text-to-Speech",
+	"TTS Voice": "Voix de Text-to-Speech",
+	"Type": "Type",
+	"Type Hugging Face Resolve (Download) URL": "Entrez l'URL de Téléchargement Hugging Face Resolve",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh non ! Un problème est survenu lors de la connexion à {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Désarchiver tout",
+	"Unarchive All Archived Chats": "Désarchiver toutes les conversations archivées",
+	"Unarchive Chat": "Désarchiver la conversation",
+	"Unlock mysteries": "Déverrouiller les mystères",
+	"Unpin": "Désépingler",
+	"Unravel secrets": "Dévoiler les secrets",
+	"Untagged": "Pas de tag",
+	"Update": "Mise à jour",
+	"Update and Copy Link": "Mettre à jour et copier le lien",
+	"Update for the latest features and improvements.": "Mettez à jour pour bénéficier des dernières fonctionnalités et améliorations.",
+	"Update password": "Mettre à jour le mot de passe",
+	"Updated": "Mis à jour",
+	"Updated at": "Mise à jour le",
+	"Updated At": "Mise à jour le",
+	"Upload": "Téléverser",
+	"Upload a GGUF model": "Téléverser un modèle GGUF",
+	"Upload directory": "Téléverser un dossier",
+	"Upload files": "Téléverser des fichiers",
+	"Upload Files": "Téléverser des fichiers",
+	"Upload Pipeline": "Pipeline de téléchargement",
+	"Upload Progress": "Progression de l'envoi",
+	"URL": "URL",
+	"URL Mode": "Mode d'URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Utilisez '#' dans la zone de saisie du prompt pour charger et inclure vos connaissances.",
+	"Use Gravatar": "Utiliser Gravatar",
+	"Use groups to group your users and assign permissions.": "Utilisez des groupes pour regrouper vos utilisateurs et attribuer des permissions.",
+	"Use Initials": "Utiliser les initiales",
+	"use_mlock (Ollama)": "Utiliser mlock (Ollama)",
+	"use_mmap (Ollama)": "Utiliser mmap (Ollama)",
+	"user": "utilisateur",
+	"User": "Utilisateur",
+	"User location successfully retrieved.": "L'emplacement de l'utilisateur a été récupéré avec succès.",
+	"Username": "Nom d'utilisateur",
+	"Users": "Utilisateurs",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Utilisation du modèle d'arène par défaut avec tous les modèles. Cliquez sur le bouton plus pour ajouter des modèles personnalisés.",
+	"Utilize": "Utilisez",
+	"Valid time units:": "Unités de temps valides\u00a0:",
+	"Valves": "Vannes",
+	"Valves updated": "Vannes mises à jour",
+	"Valves updated successfully": "Les vannes ont été mises à jour avec succès",
+	"variable": "variable",
+	"variable to have them replaced with clipboard content.": "variable pour qu'elles soient remplacées par le contenu du presse-papiers.",
+	"Version": "version:",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} de {{totalVersions}}",
+	"Visibility": "Visibilité",
+	"Voice": "Voix",
+	"Voice Input": "Saisie vocale",
+	"Warning": "Avertissement",
+	"Warning:": "Avertissement :",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Avertissement : Activer cette option permettra aux utilisateurs de télécharger du code arbitraire sur le serveur.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avertissement : Si vous mettez à jour ou modifiez votre modèle d'embedding, vous devrez réimporter tous les documents.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Paramètres du Web Loader",
+	"Web Search": "Recherche Web",
+	"Web Search Engine": "Moteur de recherche Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL du webhook",
+	"WebUI Settings": "Paramètres de WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI fera des requêtes à \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI fera des requêtes à \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Que cherchez-vous à accomplir ?",
+	"What are you working on?": "Sur quoi travaillez-vous ?",
+	"What’s New in": "Quoi de neuf dans",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Lorsqu'il est activé, le modèle répondra à chaque message de chat en temps réel, générant une réponse dès que l'utilisateur envoie un message. Ce mode est utile pour les applications de chat en direct, mais peut affecter les performances sur un matériel plus lent.",
+	"wherever you are": "où que vous soyez",
+	"Whisper (Local)": "Whisper (local)",
+	"Why?": "Pourquoi ?",
+	"Widescreen Mode": "Mode grand écran",
+	"Won": "Gagné",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Fonctionne avec le top-k. Une valeur plus élevée (par ex. 0.95) donnera un texte plus diversifié, tandis qu'une valeur plus basse (par ex. 0.5) générera un texte plus concentré et conservateur. (Par défaut : 0.9)",
+	"Workspace": "Espace de travail",
+	"Workspace Permissions": "Autorisations de l'espace de travail",
+	"Write a prompt suggestion (e.g. Who are you?)": "Écrivez une suggestion de prompt (par exemple : Qui êtes-vous ?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Rédigez un résumé de 50 mots qui résume [sujet ou mot-clé].",
+	"Write something...": "Écrivez quelque chose...",
+	"Write your model template content here": "Écrivez ici le contenu de votre modèle",
+	"Yesterday": "Hier",
+	"You": "Vous",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Vous ne pouvez discuter qu'avec un maximum de {{maxCount}} fichier(s) à la fois.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Vous pouvez personnaliser vos interactions avec les LLM en ajoutant des mémoires à l'aide du bouton « Gérer » ci-dessous, ce qui les rendra plus utiles et mieux adaptées à vos besoins.",
+	"You cannot upload an empty file.": "Vous ne pouvez pas envoyer un fichier vide.",
+	"You do not have permission to upload files.": "Vous n'avez pas la permission de télécharger des fichiers.",
+	"You have no archived conversations.": "Vous n'avez aucune conversation archivée.",
+	"You have shared this chat": "Vous avez partagé cette conversation.",
+	"You're a helpful assistant.": "Vous êtes un assistant efficace.",
+	"You're now logged in.": "Vous êtes désormais connecté.",
+	"Your account status is currently pending activation.": "Votre statut de compte est actuellement en attente d'activation.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "L'intégralité de votre contribution ira directement au développeur du plugin ; Open WebUI ne prend aucun pourcentage. Cependant, la plateforme de financement choisie peut avoir ses propres frais.",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "Paramètres de l'outil de téléchargement YouTube"
+}
diff --git a/src/lib/i18n/locales/he-IL/translation.json b/src/lib/i18n/locales/he-IL/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..e049b32adfcc4b2e01c305c8b1b6d3bd9f2cd795
--- /dev/null
+++ b/src/lib/i18n/locales/he-IL/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' או '-1' ללא תפוגה.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(למשל `sh webui.sh --api`)",
+	"(latest)": "(האחרון)",
+	"{{ models }}": "{{ דגמים }}",
+	"{{user}}'s Chats": "צ'אטים של {{user}}",
+	"{{webUIName}} Backend Required": "נדרש Backend של {{webUIName}}",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "מודל משימה משמש בעת ביצוע משימות כגון יצירת כותרות עבור צ'אטים ושאילתות חיפוש באינטרנט",
+	"a user": "משתמש",
+	"About": "אודות",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "חשבון",
+	"Account Activation Pending": "",
+	"Accurate information": "מידע מדויק",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "הוסף",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "הוסף תיאור קצר אודות אופן הפעולה של מודל זה",
+	"Add a tag": "הוסף תג",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "הוסף פקודה מותאמת אישית",
+	"Add Files": "הוסף קבצים",
+	"Add Group": "",
+	"Add Memory": "הוסף זיכרון",
+	"Add Model": "הוסף מודל",
+	"Add Tag": "",
+	"Add Tags": "הוסף תגים",
+	"Add text content": "",
+	"Add User": "הוסף משתמש",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "התאמת הגדרות אלו תחול על כל המשתמשים.",
+	"admin": "מנהל",
+	"Admin": "",
+	"Admin Panel": "לוח בקרה למנהל",
+	"Admin Settings": "הגדרות מנהל",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "פרמטרים מתקדמים",
+	"Advanced Params": "פרמטרים מתקדמים",
+	"All chats": "",
+	"All Documents": "כל המסמכים",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "אפשר מחיקת צ'אט",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "כבר יש לך חשבון?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "עוזר",
+	"and": "וגם",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "וצור קישור משותף חדש.",
+	"API Base URL": "כתובת URL בסיסית ל-API",
+	"API Key": "מפתח API",
+	"API Key created.": "מפתח API נוצר.",
+	"API keys": "מפתחות API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "אפריל",
+	"Archive": "ארכיון",
+	"Archive All Chats": "אחסן בארכיון את כל הצ'אטים",
+	"Archived Chats": "צ'אטים מאורכבים",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "האם אתה בטוח?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "צרף קובץ",
+	"Attention to detail": "תשומת לב לפרטים",
+	"Attribute for Username": "",
+	"Audio": "אודיו",
+	"August": "אוגוסט",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "העתקה אוטומטית של תגובה ללוח",
+	"Auto-playback response": "תגובת השמעה אוטומטית",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "כתובת URL בסיסית של AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "נדרשת כתובת URL בסיסית של AUTOMATIC1111",
+	"Available list": "",
+	"available!": "זמין!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "חזור",
+	"Bad Response": "תגובה שגויה",
+	"Banners": "באנרים",
+	"Base Model (From)": "דגם בסיס (מ)",
+	"Batch Size (num_batch)": "",
+	"before": "לפני",
+	"Being lazy": "להיות עצלן",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "מפתח API של חיפוש אמיץ",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "עקוף אימות SSL עבור אתרים",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "בטל",
+	"Capabilities": "יכולות",
+	"Certificate Path": "",
+	"Change Password": "שנה סיסמה",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "צ'אט",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "UI של תיבת הדיבור",
+	"Chat Controls": "",
+	"Chat direction": "כיוון צ'אט",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "צ'אטים",
+	"Check Again": "בדוק שוב",
+	"Check for updates": "בדוק עדכונים",
+	"Checking for updates...": "בודק עדכונים...",
+	"Choose a model before saving...": "בחר מודל לפני השמירה...",
+	"Chunk Overlap": "חפיפת נתונים",
+	"Chunk Params": "פרמטרי נתונים",
+	"Chunk Size": "גודל נתונים",
+	"Ciphers": "",
+	"Citation": "ציטוט",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "לחץ כאן לעזרה.",
+	"Click here to": "לחץ כאן כדי",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "לחץ כאן לבחירה",
+	"Click here to select a csv file.": "לחץ כאן לבחירת קובץ csv.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "לחץ כאן.",
+	"Click on the user role button to change a user's role.": "לחץ על כפתור תפקיד המשתמש כדי לשנות את תפקיד המשתמש.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "שיבוט",
+	"Close": "סגור",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "אוסף",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "כתובת URL בסיסית של ComfyUI",
+	"ComfyUI Base URL is required.": "נדרשת כתובת URL בסיסית של ComfyUI",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "פקודה",
+	"Completions": "",
+	"Concurrent Requests": "בקשות בו-זמניות",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "אשר סיסמה",
+	"Confirm your action": "",
+	"Connections": "חיבורים",
+	"Contact Admin for WebUI Access": "",
+	"Content": "תוכן",
+	"Content Extraction": "",
+	"Context Length": "אורך הקשר",
+	"Continue Response": "המשך תגובה",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "העתקת כתובת URL של צ'אט משותף ללוח!",
+	"Copied to clipboard": "",
+	"Copy": "העתק",
+	"Copy last code block": "העתק את בלוק הקוד האחרון",
+	"Copy last response": "העתק את התגובה האחרונה",
+	"Copy Link": "העתק קישור",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "ההעתקה ללוח הייתה מוצלחת!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "יצירת מודל",
+	"Create Account": "צור חשבון",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "צור מפתח חדש",
+	"Create new secret key": "צור מפתח סודי חדש",
+	"Created at": "נוצר ב",
+	"Created At": "נוצר ב",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "המודל הנוכחי",
+	"Current Password": "הסיסמה הנוכחית",
+	"Custom": "מותאם אישית",
+	"Dark": "כהה",
+	"Database": "מסד נתונים",
+	"December": "דצמבר",
+	"Default": "ברירת מחדל",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "ברירת מחדל (SentenceTransformers)",
+	"Default Model": "מודל ברירת מחדל",
+	"Default model updated": "המודל המוגדר כברירת מחדל עודכן",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "הצעות ברירת מחדל לפקודות",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "תפקיד משתמש ברירת מחדל",
+	"Delete": "מחק",
+	"Delete a model": "מחק מודל",
+	"Delete All Chats": "מחק את כל הצ'אטים",
+	"Delete All Models": "",
+	"Delete chat": "מחק צ'אט",
+	"Delete Chat": "מחק צ'אט",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "מחק את הקישור הזה",
+	"Delete tool?": "",
+	"Delete User": "מחק משתמש",
+	"Deleted {{deleteModelTag}}": "נמחק {{deleteModelTag}}",
+	"Deleted {{name}}": "נמחק {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "תיאור",
+	"Didn't fully follow instructions": "לא עקב אחרי ההוראות באופן מלא",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "גלה מודל",
+	"Discover a prompt": "גלה פקודה",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "גלה, הורד, וחקור פקודות מותאמות אישית",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "גלה, הורד, וחקור הגדרות מודל מוגדרות מראש",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "הצג את שם המשתמש במקום 'אתה' בצ'אט",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "מסמך",
+	"Documentation": "",
+	"Documents": "מסמכים",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "לא מבצע חיבורים חיצוניים, והנתונים שלך נשמרים באופן מאובטח בשרת המקומי שלך.",
+	"Don't have an account?": "אין לך חשבון?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "לא אוהב את הסגנון",
+	"Done": "",
+	"Download": "הורד",
+	"Download canceled": "ההורדה בוטלה",
+	"Download Database": "הורד מסד נתונים",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "גרור כל קובץ לכאן כדי להוסיף לשיחה",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "למשל '30s', '10m'. יחידות זמן חוקיות הן 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "ערוך",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "ערוך משתמש",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "דוא\"ל",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "מודל הטמעה",
+	"Embedding Model Engine": "מנוע מודל הטמעה",
+	"Embedding model set to \"{{embedding_model}}\"": "מודל ההטמעה הוגדר ל-\"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "הפיכת שיתוף קהילה לזמין",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "אפשר הרשמות חדשות",
+	"Enable Web Search": "הפיכת חיפוש באינטרנט לזמין",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "ודא שקובץ ה-CSV שלך כולל 4 עמודות בסדר הבא: שם, דוא\"ל, סיסמה, תפקיד.",
+	"Enter {{role}} message here": "הזן הודעת {{role}} כאן",
+	"Enter a detail about yourself for your LLMs to recall": "הזן פרטים על עצמך כדי שLLMs יזכור",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "הזן מפתח API של חיפוש אמיץ",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "הזן חפיפת נתונים",
+	"Enter Chunk Size": "הזן גודל נתונים",
+	"Enter description": "",
+	"Enter Github Raw URL": "הזן כתובת URL של Github Raw",
+	"Enter Google PSE API Key": "הזן מפתח API של Google PSE",
+	"Enter Google PSE Engine Id": "הזן את מזהה מנוע PSE של Google",
+	"Enter Image Size (e.g. 512x512)": "הזן גודל תמונה (למשל 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "הזן קודי שפה",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "הזן תג מודל (למשל {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "הזן מספר שלבים (למשל 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "הזן ציון",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "הזן כתובת URL של שאילתת Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "הזן מפתח API של Serper",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "הזן מפתח API של Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "הזן רצף עצירה",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "הזן Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "הזן כתובת URL (למשל http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "הזן כתובת URL (למשל http://localhost:11434)",
+	"Enter Your Email": "הזן את דוא\"ל שלך",
+	"Enter Your Full Name": "הזן את שמך המלא",
+	"Enter your message": "",
+	"Enter Your Password": "הזן את הסיסמה שלך",
+	"Enter Your Role": "הזן את התפקיד שלך",
+	"Enter Your Username": "",
+	"Error": "שגיאה",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "ניסיוני",
+	"Explore the cosmos": "",
+	"Export": "ייצא",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "ייצוא כל הצ'אטים (כל המשתמשים)",
+	"Export chat (.json)": "",
+	"Export Chats": "ייצוא צ'אטים",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "ייצוא מודלים",
+	"Export Presets": "",
+	"Export Prompts": "ייצוא פקודות",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "יצירת מפתח API נכשלה.",
+	"Failed to read clipboard contents": "קריאת תוכן הלוח נכשלה",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "פברואר",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "נא להוסיף פרטים ספציפיים לפי רצון",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "מצב קובץ",
+	"File not found.": "הקובץ לא נמצא.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "התגלתה הזיית טביעת אצבע: לא ניתן להשתמש בראשי תיבות כאווטאר. משתמש בתמונת פרופיל ברירת מחדל.",
+	"Fluidly stream large external response chunks": "שידור נתונים חיצוניים בקצב רציף",
+	"Focus chat input": "מיקוד הקלט לצ'אט",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "עקב אחר ההוראות במושלמות",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "עונש תדירות",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "כללי",
+	"General Settings": "הגדרות כלליות",
+	"Generate Image": "",
+	"Generating search query": "יצירת שאילתת חיפוש",
+	"Generation Info": "מידע על היצירה",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "תגובה טובה",
+	"Google PSE API Key": "מפתח API של Google PSE",
+	"Google PSE Engine Id": "מזהה מנוע PSE של Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "אין שיחות.",
+	"Hello, {{name}}": "שלום, {{name}}",
+	"Help": "עזרה",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "הסתר",
+	"Host": "",
+	"How can I help you today?": "כיצד אוכל לעזור לך היום?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "חיפוש היברידי",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "יצירת תמונות (ניסיוני)",
+	"Image Generation Engine": "מנוע יצירת תמונות",
+	"Image Settings": "הגדרות תמונה",
+	"Images": "תמונות",
+	"Import Chats": "יבוא צ'אטים",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "ייבוא דגמים",
+	"Import Presets": "",
+	"Import Prompts": "יבוא פקודות",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "כלול את הדגל `--api` בעת הרצת stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "מידע",
+	"Input commands": "פקודות קלט",
+	"Install from Github URL": "התקן מכתובת URL של Github",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "ממשק",
+	"Invalid file format.": "",
+	"Invalid Tag": "תג לא חוקי",
+	"January": "ינואר",
+	"Jina API Key": "",
+	"join our Discord for help.": "הצטרף ל-Discord שלנו לעזרה.",
+	"JSON": "JSON",
+	"JSON Preview": "תצוגה מקדימה של JSON",
+	"July": "יולי",
+	"June": "יוני",
+	"JWT Expiration": "תפוגת JWT",
+	"JWT Token": "אסימון JWT",
+	"Keep Alive": "השאר פעיל",
+	"Key": "",
+	"Keyboard shortcuts": "קיצורי מקלדת",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "שפה",
+	"Last Active": "פעיל לאחרונה",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "בהיר",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "מודלים בשפה טבעית יכולים לטעות. אמת מידע חשוב.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "נוצר על ידי קהילת OpenWebUI",
+	"Make sure to enclose them with": "ודא להקיף אותם עם",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "ניהול צינורות",
+	"March": "מרץ",
+	"Max Tokens (num_predict)": "מקסימום אסימונים (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "ניתן להוריד מקסימום 3 מודלים בו זמנית. אנא נסה שוב מאוחר יותר.",
+	"May": "מאי",
+	"Memories accessible by LLMs will be shown here.": "מזכירים נגישים על ידי LLMs יוצגו כאן.",
+	"Memory": "זיכרון",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "הודעות שתשלח לאחר יצירת הקישור לא ישותפו. משתמשים עם כתובת האתר יוכלו לצפות בצ'אט המשותף.",
+	"Min P": "",
+	"Minimum Score": "ציון מינימלי",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD בMMMM, YYYY",
+	"MMMM DD, YYYY HH:mm": "DD בMMMM, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "המודל '{{modelName}}' הורד בהצלחה.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "המודל '{{modelTag}}' כבר בתור להורדה.",
+	"Model {{modelId}} not found": "המודל {{modelId}} לא נמצא",
+	"Model {{modelName}} is not vision capable": "דגם {{modelName}} אינו בעל יכולת ראייה",
+	"Model {{name}} is now {{status}}": "דגם {{name}} הוא כעת {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "נתיב מערכת הקבצים של המודל זוהה. נדרש שם קצר של המודל לעדכון, לא ניתן להמשיך.",
+	"Model Filtering": "",
+	"Model ID": "מזהה דגם",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "לא נבחר מודל",
+	"Model Params": "פרמס מודל",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "תוכן קובץ מודל",
+	"Models": "מודלים",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "עוד",
+	"Name": "שם",
+	"Name your knowledge base": "",
+	"New Chat": "צ'אט חדש",
+	"New folder": "",
+	"New Password": "סיסמה חדשה",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "לא נמצאו תוצאות",
+	"No search query generated": "לא נוצרה שאילתת חיפוש",
+	"No source available": "אין מקור זמין",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "ללא",
+	"Not factually correct": "לא נכון מבחינה עובדתית",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "הערה: אם תקבע ציון מינימלי, החיפוש יחזיר רק מסמכים עם ציון שגבוה או שווה לציון המינימלי.",
+	"Notes": "",
+	"Notifications": "התראות",
+	"November": "נובמבר",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "אוקטובר",
+	"Off": "כבוי",
+	"Okay, Let's Go!": "בסדר, בואו נתחיל!",
+	"OLED Dark": "OLED כהה",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API מושבת",
+	"Ollama API settings updated": "",
+	"Ollama Version": "גרסת Ollama",
+	"On": "פועל",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "רק תווים אלפאנומריים ומקפים מותרים במחרוזת הפקודה.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "אופס! נראה שהכתובת URL אינה תקינה. אנא בדוק שוב ונסה שנית.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "אופס! אתה משתמש בשיטה לא נתמכת (רק חזית). אנא שרת את ממשק המשתמש האינטרנטי מהשרת האחורי.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "פתח צ'אט חדש",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API של OpenAI",
+	"OpenAI API Config": "תצורת API של OpenAI",
+	"OpenAI API Key is required.": "נדרש מפתח API של OpenAI.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "נדרשת כתובת URL/מפתח של OpenAI.",
+	"or": "או",
+	"Organize your users": "",
+	"Other": "אחר",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "סיסמה",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "מסמך PDF (.pdf)",
+	"PDF Extract Images (OCR)": "חילוץ תמונות מ-PDF (OCR)",
+	"pending": "ממתין",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "ההרשאה נדחתה בעת גישה למיקרופון: {{error}}",
+	"Permissions": "",
+	"Personalization": "תאור",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "צינורות",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "צינורות שסתומים",
+	"Plain text (.txt)": "טקסט פשוט (.txt)",
+	"Playground": "אזור משחקים",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "גישה חיובית",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 הימים הקודמים",
+	"Previous 7 days": "7 הימים הקודמים",
+	"Profile Image": "תמונת פרופיל",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "פקודה (למשל, ספר לי עובדה מעניינת על האימפריה הרומית)",
+	"Prompt Content": "תוכן הפקודה",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "הצעות לפקודות",
+	"Prompt updated successfully": "",
+	"Prompts": "פקודות",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "משוך \"{{searchValue}}\" מ-Ollama.com",
+	"Pull a model from Ollama.com": "משוך מודל מ-Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "פרמטרי שאילתה",
+	"RAG Template": "תבנית RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "קרא בקול",
+	"Record voice": "הקלט קול",
+	"Redirecting you to OpenWebUI Community": "מפנה אותך לקהילת OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "נדחה כאשר לא היה צריך",
+	"Regenerate": "הפק מחדש",
+	"Release Notes": "הערות שחרור",
+	"Relevance": "",
+	"Remove": "הסר",
+	"Remove Model": "הסר מודל",
+	"Rename": "שנה שם",
+	"Reorder Models": "",
+	"Repeat Last N": "חזור על ה-N האחרונים",
+	"Request Mode": "מצב בקשה",
+	"Reranking Model": "מודל דירוג מחדש",
+	"Reranking model disabled": "מודל דירוג מחדש מושבת",
+	"Reranking model set to \"{{reranking_model}}\"": "מודל דירוג מחדש הוגדר ל-\"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "תפקיד",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "שמור",
+	"Save & Create": "שמור וצור",
+	"Save & Update": "שמור ועדכן",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "שמירת יומני צ'אט ישירות באחסון הדפדפן שלך אינה נתמכת יותר. אנא הקדש רגע להוריד ולמחוק את יומני הצ'אט שלך על ידי לחיצה על הכפתור למטה. אל דאגה, באפשרותך לייבא מחדש בקלות את יומני הצ'אט שלך לשרת האחורי דרך",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "חפש",
+	"Search a model": "חפש מודל",
+	"Search Base": "",
+	"Search Chats": "חיפוש צ'אטים",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "חיפוש מודלים",
+	"Search options": "",
+	"Search Prompts": "חפש פקודות",
+	"Search Result Count": "ספירת תוצאות חיפוש",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "חיפש {{count}} sites_one",
+	"Searched {{count}} sites_two": "חיפש {{count}} sites_two",
+	"Searched {{count}} sites_other": "חיפש {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "כתובת URL של שאילתת Searxng",
+	"See readme.md for instructions": "ראה את readme.md להוראות",
+	"See what's new": "ראה מה חדש",
+	"Seed": "זרע",
+	"Select a base model": "בחירת מודל בסיס",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "בחר מודל",
+	"Select a pipeline": "בחר קו צינור",
+	"Select a pipeline url": "בחר כתובת URL של קו צינור",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "בחר מודל",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "דגמים נבחרים אינם תומכים בקלט תמונה",
+	"Semantic distance to query": "",
+	"Send": "שלח",
+	"Send a Message": "שלח הודעה",
+	"Send message": "שלח הודעה",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "ספטמבר",
+	"Serper API Key": "מפתח Serper API",
+	"Serply API Key": "",
+	"Serpstack API Key": "מפתח API של Serpstack",
+	"Server connection verified": "החיבור לשרת אומת",
+	"Set as default": "הגדר כברירת מחדל",
+	"Set CFG Scale": "",
+	"Set Default Model": "הגדר מודל ברירת מחדל",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "הגדר מודל הטמעה (למשל {{model}})",
+	"Set Image Size": "הגדר גודל תמונה",
+	"Set reranking model (e.g. {{model}})": "הגדר מודל דירוג מחדש (למשל {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "הגדר שלבים",
+	"Set Task Model": "הגדרת מודל משימה",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "הגדר קול",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "הגדרות",
+	"Settings saved successfully!": "ההגדרות נשמרו בהצלחה!",
+	"Share": "שתף",
+	"Share Chat": "שתף צ'אט",
+	"Share to OpenWebUI Community": "שתף לקהילת OpenWebUI",
+	"Show": "הצג",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "הצג קיצורי דרך",
+	"Show your support!": "",
+	"Showcased creativity": "הצגת יצירתיות",
+	"Sign in": "הירשם",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "התנתקות",
+	"Sign up": "הרשמה",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "מקור",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "שגיאת תחקור שמע: {{error}}",
+	"Speech-to-Text Engine": "מנוע תחקור שמע",
+	"Stop": "",
+	"Stop Sequence": "סידור עצירה",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "הגדרות חקירה של TTS",
+	"Subtitle (e.g. about the Roman Empire)": "תחקור (לדוגמה: על מעמד הרומי)",
+	"Success": "הצלחה",
+	"Successfully updated.": "עדכון הצלחה.",
+	"Suggested": "מומלץ",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "מערכת",
+	"System Instructions": "",
+	"System Prompt": "תגובת מערכת",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "תרשמו יותר:",
+	"Temperature": "טמפרטורה",
+	"Template": "תבנית",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "מנוע טקסט לדיבור",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "תודה על המשוב שלך!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "ציון צריך להיות ערך בין 0.0 (0%) ל-1.0 (100%)",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "נושא",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "פעולה זו מבטיחה שהשיחות בעלות הערך שלך יישמרו באופן מאובטח במסד הנתונים העורפי שלך. תודה!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "תיאור מפורט",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "טיפ: עדכן חריצים משתנים מרובים ברציפות על-ידי לחיצה על מקש Tab בקלט הצ'אט לאחר כל החלפה.",
+	"Title": "שם",
+	"Title (e.g. Tell me a fun fact)": "שם (לדוגמה: תרגום)",
+	"Title Auto-Generation": "יצירת שם אוטומטית",
+	"Title cannot be an empty string.": "שם לא יכול להיות מחרוזת ריקה.",
+	"Title Generation Prompt": "שאלה ליצירת שם",
+	"TLS": "",
+	"To access the available model names for downloading,": "כדי לגשת לשמות הדגמים הזמינים להורדה,",
+	"To access the GGUF models available for downloading,": "כדי לגשת לדגמי GGUF הזמינים להורדה,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "היום",
+	"Toggle settings": "החלפת מצב של הגדרות",
+	"Toggle sidebar": "החלפת מצב של סרגל הצד",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "קשה לגשת לOllama?",
+	"TTS Model": "",
+	"TTS Settings": "הגדרות TTS",
+	"TTS Voice": "",
+	"Type": "סוג",
+	"Type Hugging Face Resolve (Download) URL": "הקלד כתובת URL של פתרון פנים מחבק (הורד)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "או-הו! אירעה בעיה בהתחברות ל- {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "עדכן ושכפל קישור",
+	"Update for the latest features and improvements.": "",
+	"Update password": "עדכן סיסמה",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "העלה מודל GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "העלאת קבצים",
+	"Upload Pipeline": "",
+	"Upload Progress": "תקדמות העלאה",
+	"URL": "",
+	"URL Mode": "מצב URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "שימוש ב Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "שימוש ב initials",
+	"use_mlock (Ollama)": "use_mlock (אולמה)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "משתמש",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "משתמשים",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "שימוש",
+	"Valid time units:": "יחידות זמן תקינות:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "משתנה",
+	"variable to have them replaced with clipboard content.": "משתנה להחליפו ב- clipboard תוכן.",
+	"Version": "גרסה",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "אזהרה",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "אזהרה: אם תעדכן או תשנה את מודל ההטבעה שלך, יהיה עליך לייבא מחדש את כל המסמכים.",
+	"Web": "רשת",
+	"Web API": "",
+	"Web Loader Settings": "הגדרות טעינת אתר",
+	"Web Search": "חיפוש באינטרנט",
+	"Web Search Engine": "מנוע חיפוש באינטרנט",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL Webhook",
+	"WebUI Settings": "הגדרות WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "מה חדש ב",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "סביבה",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "כתוב הצעה מהירה (למשל, מי אתה?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "כתוב סיכום ב-50 מילים שמסכם [נושא או מילת מפתח].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "אתמול",
+	"You": "אתה",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "אין לך שיחות בארכיון.",
+	"You have shared this chat": "שיתפת את השיחה הזו",
+	"You're a helpful assistant.": "אתה עוזר מועיל.",
+	"You're now logged in.": "כעת אתה מחובר.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "הגדרות Youtube Loader"
+}
diff --git a/src/lib/i18n/locales/hi-IN/translation.json b/src/lib/i18n/locales/hi-IN/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a9124b79158631e0fb49dda44af6f4cb37952b50
--- /dev/null
+++ b/src/lib/i18n/locales/hi-IN/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' बिना किसी समाप्ति के",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
+	"(latest)": "(latest)",
+	"{{ models }}": "{{ मॉडल }}",
+	"{{user}}'s Chats": "{{user}} की चैट",
+	"{{webUIName}} Backend Required": "{{webUIName}} बैकएंड आवश्यक",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "चैट और वेब खोज क्वेरी के लिए शीर्षक उत्पन्न करने जैसे कार्य करते समय कार्य मॉडल का उपयोग किया जाता है",
+	"a user": "एक उपयोगकर्ता",
+	"About": "हमारे बारे में",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "खाता",
+	"Account Activation Pending": "",
+	"Accurate information": "सटीक जानकारी",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "जोड़ें",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "इस मॉडल के बारे में एक संक्षिप्त विवरण जोड़ें",
+	"Add a tag": "एक टैग जोड़े",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "अनुकूल संकेत जोड़ें",
+	"Add Files": "फाइलें जोड़ें",
+	"Add Group": "",
+	"Add Memory": "मेमोरी जोड़ें",
+	"Add Model": "मॉडल जोड़ें",
+	"Add Tag": "",
+	"Add Tags": "टैगों को जोड़ें",
+	"Add text content": "",
+	"Add User": "उपयोगकर्ता जोड़ें",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "इन सेटिंग्स को समायोजित करने से परिवर्तन सभी उपयोगकर्ताओं पर सार्वभौमिक रूप से लागू होंगे।",
+	"admin": "व्यवस्थापक",
+	"Admin": "",
+	"Admin Panel": "व्यवस्थापक पैनल",
+	"Admin Settings": "व्यवस्थापक सेटिंग्स",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "उन्नत पैरामीटर",
+	"Advanced Params": "उन्नत परम",
+	"All chats": "",
+	"All Documents": "सभी डॉक्यूमेंट्स",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "चैट हटाने की अनुमति दें",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "क्या आपके पास पहले से एक खाता मौजूद है?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "एक सहायक",
+	"and": "और",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "और एक नई साझा लिंक बनाएं.",
+	"API Base URL": "एपीआई बेस यूआरएल",
+	"API Key": "एपीआई कुंजी",
+	"API Key created.": "एपीआई कुंजी बनाई गई",
+	"API keys": "एपीआई कुंजियाँ",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "अप्रैल",
+	"Archive": "पुरालेख",
+	"Archive All Chats": "सभी चैट संग्रहीत करें",
+	"Archived Chats": "संग्रहीत चैट",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "क्या आपको यकीन है?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "फ़ाइल atta",
+	"Attention to detail": "विस्तार पर ध्यान",
+	"Attribute for Username": "",
+	"Audio": "ऑडियो",
+	"August": "अगस्त",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "क्लिपबोर्ड पर प्रतिक्रिया ऑटोकॉपी",
+	"Auto-playback response": "ऑटो-प्लेबैक प्रतिक्रिया",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 बेस यूआरएल",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 का बेस यूआरएल आवश्यक है।",
+	"Available list": "",
+	"available!": "उपलब्ध!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "पीछे",
+	"Bad Response": "ख़राब प्रतिक्रिया",
+	"Banners": "बैनर",
+	"Base Model (From)": "बेस मॉडल (से)",
+	"Batch Size (num_batch)": "",
+	"before": "पहले",
+	"Being lazy": "आलसी होना",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave सर्च एपीआई कुंजी",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "वेबसाइटों के लिए SSL सुनिश्चिती को छोड़ें",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "रद्द करें",
+	"Capabilities": "क्षमताओं",
+	"Certificate Path": "",
+	"Change Password": "पासवर्ड बदलें",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "चैट करें",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "चैट बॉली",
+	"Chat Controls": "",
+	"Chat direction": "चैट दिशा",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "सभी चैट",
+	"Check Again": "फिर से जाँचो",
+	"Check for updates": "अपडेट के लिए जाँच",
+	"Checking for updates...": "अपडेट के लिए जांच कर रहा है...",
+	"Choose a model before saving...": "सहेजने से पहले एक मॉडल चुनें...",
+	"Chunk Overlap": "चंक ओवरलैप",
+	"Chunk Params": "चंक पैरामीटर्स",
+	"Chunk Size": "चंक आकार",
+	"Ciphers": "",
+	"Citation": "उद्धरण",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "सहायता के लिए यहां क्लिक करें।",
+	"Click here to": "यहां क्लिक करें",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "चयन करने के लिए यहां क्लिक करें।",
+	"Click here to select a csv file.": "सीएसवी फ़ाइल का चयन करने के लिए यहां क्लिक करें।",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "यहाँ क्लिक करें।",
+	"Click on the user role button to change a user's role.": "उपयोगकर्ता की भूमिका बदलने के लिए उपयोगकर्ता भूमिका बटन पर क्लिक करें।",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "क्लोन",
+	"Close": "बंद करना",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "संग्रह",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI बेस यूआरएल",
+	"ComfyUI Base URL is required.": "ComfyUI का बेस यूआरएल आवश्यक है",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "कमांड",
+	"Completions": "",
+	"Concurrent Requests": "समवर्ती अनुरोध",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "पासवर्ड की पुष्टि कीजिये",
+	"Confirm your action": "",
+	"Connections": "सम्बन्ध",
+	"Contact Admin for WebUI Access": "",
+	"Content": "सामग्री",
+	"Content Extraction": "",
+	"Context Length": "प्रसंग की लंबाई",
+	"Continue Response": "प्रतिक्रिया जारी रखें",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "साझा चैट URL को क्लिपबोर्ड पर कॉपी किया गया!",
+	"Copied to clipboard": "",
+	"Copy": "कॉपी",
+	"Copy last code block": "अंतिम कोड ब्लॉक कॉपी करें",
+	"Copy last response": "अंतिम प्रतिक्रिया कॉपी करें",
+	"Copy Link": "लिंक को कॉपी करें",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "क्लिपबोर्ड पर कॉपी बनाना सफल रहा!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "एक मॉडल बनाएं",
+	"Create Account": "खाता बनाएं",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "नया क्रिप्टोग्राफिक क्षेत्र बनाएं",
+	"Create new secret key": "नया क्रिप्टोग्राफिक क्षेत्र बनाएं",
+	"Created at": "किस समय बनाया गया",
+	"Created At": "किस समय बनाया गया",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "वर्तमान मॉडल",
+	"Current Password": "वर्तमान पासवर्ड",
+	"Custom": "कस्टम संस्करण",
+	"Dark": "डार्क",
+	"Database": "डेटाबेस",
+	"December": "डिसेंबर",
+	"Default": "डिफ़ॉल्ट",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "डिफ़ॉल्ट (SentenceTransformers)",
+	"Default Model": "डिफ़ॉल्ट मॉडल",
+	"Default model updated": "डिफ़ॉल्ट मॉडल अपडेट किया गया",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "डिफ़ॉल्ट प्रॉम्प्ट सुझाव",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "डिफ़ॉल्ट उपयोगकर्ता भूमिका",
+	"Delete": "डिलीट",
+	"Delete a model": "एक मॉडल हटाएँ",
+	"Delete All Chats": "सभी चैट हटाएं",
+	"Delete All Models": "",
+	"Delete chat": "चैट हटाएं",
+	"Delete Chat": "चैट हटाएं",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "इस लिंक को हटाएं",
+	"Delete tool?": "",
+	"Delete User": "उपभोक्ता मिटायें",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} हटा दिया गया",
+	"Deleted {{name}}": "{{name}} हटा दिया गया",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "विवरण",
+	"Didn't fully follow instructions": "निर्देशों का पूरी तरह से पालन नहीं किया",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "एक मॉडल की खोज करें",
+	"Discover a prompt": "प्रॉम्प्ट खोजें",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "कस्टम प्रॉम्प्ट को खोजें, डाउनलोड करें और एक्सप्लोर करें",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "मॉडल प्रीसेट खोजें, डाउनलोड करें और एक्सप्लोर करें",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "चैट में 'आप' के स्थान पर उपयोगकर्ता नाम प्रदर्शित करें",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "दस्तावेज़",
+	"Documentation": "",
+	"Documents": "दस्तावेज़",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "कोई बाहरी कनेक्शन नहीं बनाता है, और आपका डेटा आपके स्थानीय रूप से होस्ट किए गए सर्वर पर सुरक्षित रूप से रहता है।",
+	"Don't have an account?": "कोई खाता नहीं है?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "शैली पसंद नहीं है",
+	"Done": "",
+	"Download": "डाउनलोड",
+	"Download canceled": "डाउनलोड रद्द किया गया",
+	"Download Database": "डेटाबेस डाउनलोड करें",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "बातचीत में जोड़ने के लिए कोई भी फ़ाइल यहां छोड़ें",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "जैसे '30s', '10m', मान्य समय इकाइयाँ 's', 'm', 'h' हैं।",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "संपादित करें",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "यूजर को संपादित करो",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "ईमेल",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "मॉडेल अनुकूलन",
+	"Embedding Model Engine": "एंबेडिंग मॉडल इंजन",
+	"Embedding model set to \"{{embedding_model}}\"": "एम्बेडिंग मॉडल को \"{{embedding_model}}\" पर सेट किया गया",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "समुदाय साझाकरण सक्षम करें",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "नए साइन अप सक्रिय करें",
+	"Enable Web Search": "वेब खोज सक्षम करें",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "सुनिश्चित करें कि आपकी CSV फ़ाइल में इस क्रम में 4 कॉलम शामिल हैं: नाम, ईमेल, पासवर्ड, भूमिका।",
+	"Enter {{role}} message here": "यहां {{role}} संदेश दर्ज करें",
+	"Enter a detail about yourself for your LLMs to recall": "अपने एलएलएम को याद करने के लिए अपने बारे में एक विवरण दर्ज करें",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Brave सर्च एपीआई कुंजी डालें",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "चंक ओवरलैप दर्ज करें",
+	"Enter Chunk Size": "खंड आकार दर्ज करें",
+	"Enter description": "",
+	"Enter Github Raw URL": "Github Raw URL दर्ज करें",
+	"Enter Google PSE API Key": "Google PSE API कुंजी दर्ज करें",
+	"Enter Google PSE Engine Id": "Google PSE इंजन आईडी दर्ज करें",
+	"Enter Image Size (e.g. 512x512)": "छवि का आकार दर्ज करें (उदा. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "भाषा कोड दर्ज करें",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Model tag दर्ज करें (उदा. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "चरणों की संख्या दर्ज करें (उदा. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "स्कोर दर्ज करें",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Searxng क्वेरी URL दर्ज करें",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper API कुंजी दर्ज करें",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "सर्पस्टैक एपीआई कुंजी दर्ज करें",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "स्टॉप अनुक्रम दर्ज करें",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "शीर्ष K दर्ज करें",
+	"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 Your Email": "अपना ईमेल दर्ज करें",
+	"Enter Your Full Name": "अपना पूरा नाम भरें",
+	"Enter your message": "",
+	"Enter Your Password": "अपना पासवर्ड भरें",
+	"Enter Your Role": "अपनी भूमिका दर्ज करें",
+	"Enter Your Username": "",
+	"Error": "चूक",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "प्रयोगात्मक",
+	"Explore the cosmos": "",
+	"Export": "निर्यातित माल",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "सभी चैट निर्यात करें (सभी उपयोगकर्ताओं की)",
+	"Export chat (.json)": "",
+	"Export Chats": "चैट निर्यात करें",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "निर्यात मॉडल",
+	"Export Presets": "",
+	"Export Prompts": "प्रॉम्प्ट निर्यात करें",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "एपीआई कुंजी बनाने में विफल.",
+	"Failed to read clipboard contents": "क्लिपबोर्ड सामग्री पढ़ने में विफल",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "फरवरी",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "विशिष्ट विवरण जोड़ने के लिए स्वतंत्र महसूस करें",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "फ़ाइल मोड",
+	"File not found.": "फ़ाइल प्राप्त नहीं हुई।",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "फ़िंगरप्रिंट स्पूफ़िंग का पता चला: प्रारंभिक अक्षरों को अवतार के रूप में उपयोग करने में असमर्थ। प्रोफ़ाइल छवि को डिफ़ॉल्ट पर डिफ़ॉल्ट किया जा रहा है.",
+	"Fluidly stream large external response chunks": "बड़े बाह्य प्रतिक्रिया खंडों को तरल रूप से प्रवाहित करें",
+	"Focus chat input": "चैट इनपुट पर फ़ोकस करें",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "निर्देशों का पूर्णतः पालन किया",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "फ्रीक्वेंसी पेनल्टी",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "सामान्य",
+	"General Settings": "सामान्य सेटिंग्स",
+	"Generate Image": "",
+	"Generating search query": "खोज क्वेरी जनरेट करना",
+	"Generation Info": "जनरेशन की जानकारी",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "अच्छी प्रतिक्रिया",
+	"Google PSE API Key": "Google PSE API कुंजी",
+	"Google PSE Engine Id": "Google PSE इंजन आईडी",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "कोई बातचीत नहीं है",
+	"Hello, {{name}}": "नमस्ते, {{name}}",
+	"Help": "मदद",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "छुपाएं",
+	"Host": "",
+	"How can I help you today?": "आज मैं आपकी कैसे मदद कर सकता हूँ?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "हाइब्रिड खोज",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "छवि निर्माण (प्रायोगिक)",
+	"Image Generation Engine": "छवि निर्माण इंजन",
+	"Image Settings": "छवि सेटिंग्स",
+	"Images": "इमेजिस",
+	"Import Chats": "चैट आयात करें",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "आयात मॉडल",
+	"Import Presets": "",
+	"Import Prompts": "प्रॉम्प्ट आयात करें",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui चलाते समय `--api` ध्वज शामिल करें",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "सूचना-विषयक",
+	"Input commands": "इनपुट क命",
+	"Install from Github URL": "Github URL से इंस्टॉल करें",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "इंटरफेस",
+	"Invalid file format.": "",
+	"Invalid Tag": "अवैध टैग",
+	"January": "जनवरी",
+	"Jina API Key": "",
+	"join our Discord for help.": "मदद के लिए हमारे डिस्कोर्ड में शामिल हों।",
+	"JSON": "ज्ञान प्रकार",
+	"JSON Preview": "JSON पूर्वावलोकन",
+	"July": "जुलाई",
+	"June": "जुन",
+	"JWT Expiration": "JWT समाप्ति",
+	"JWT Token": "जट टोकन",
+	"Keep Alive": "क्रियाशील रहो",
+	"Key": "",
+	"Keyboard shortcuts": "कीबोर्ड शॉर्टकट",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "भाषा",
+	"Last Active": "पिछली बार सक्रिय",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "सुन",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "एलएलएम गलतियाँ कर सकते हैं। महत्वपूर्ण जानकारी सत्यापित करें.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "OpenWebUI समुदाय द्वारा निर्मित",
+	"Make sure to enclose them with": "उन्हें संलग्न करना सुनिश्चित करें",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "पाइपलाइनों का प्रबंधन करें",
+	"March": "मार्च",
+	"Max Tokens (num_predict)": "अधिकतम टोकन (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "अधिकतम 3 मॉडल एक साथ डाउनलोड किये जा सकते हैं। कृपया बाद में पुन: प्रयास करें।",
+	"May": "मेई",
+	"Memories accessible by LLMs will be shown here.": "एलएलएम द्वारा सुलभ यादें यहां दिखाई जाएंगी।",
+	"Memory": "मेमोरी",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "अपना लिंक बनाने के बाद आपके द्वारा भेजे गए संदेश साझा नहीं किए जाएंगे। यूआरएल वाले यूजर्स शेयर की गई चैट देख पाएंगे।",
+	"Min P": "",
+	"Minimum Score": "न्यूनतम स्कोर",
+	"Mirostat": "मिरोस्टा",
+	"Mirostat Eta": "मिरोस्टा ईटा",
+	"Mirostat Tau": "मिरोस्तात ताऊ",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "मॉडल '{{modelName}}' सफलतापूर्वक डाउनलोड हो गया है।",
+	"Model '{{modelTag}}' is already in queue for downloading.": "मॉडल '{{modelTag}}' पहले से ही डाउनलोड करने के लिए कतार में है।",
+	"Model {{modelId}} not found": "मॉडल {{modelId}} नहीं मिला",
+	"Model {{modelName}} is not vision capable": "मॉडल {{modelName}} दृष्टि सक्षम नहीं है",
+	"Model {{name}} is now {{status}}": "मॉडल {{name}} अब {{status}} है",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "मॉडल फ़ाइल सिस्टम पथ का पता चला. अद्यतन के लिए मॉडल संक्षिप्त नाम आवश्यक है, जारी नहीं रखा जा सकता।",
+	"Model Filtering": "",
+	"Model ID": "मॉडल आईडी",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "मॉडल चयनित नहीं है",
+	"Model Params": "मॉडल Params",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "मॉडल फ़ाइल सामग्री",
+	"Models": "सभी मॉडल",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "और..",
+	"Name": "नाम",
+	"Name your knowledge base": "",
+	"New Chat": "नई चैट",
+	"New folder": "",
+	"New Password": "नया पासवर्ड",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "कोई परिणाम नहीं मिला",
+	"No search query generated": "कोई खोज क्वेरी जनरेट नहीं हुई",
+	"No source available": "कोई स्रोत उपलब्ध नहीं है",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "कोई नहीं",
+	"Not factually correct": "तथ्यात्मक रूप से सही नहीं है",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "ध्यान दें: यदि आप न्यूनतम स्कोर निर्धारित करते हैं, तो खोज केवल न्यूनतम स्कोर से अधिक या उसके बराबर स्कोर वाले दस्तावेज़ वापस लाएगी।",
+	"Notes": "",
+	"Notifications": "सूचनाएं",
+	"November": "नवंबर",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (ओलामा)",
+	"OAuth ID": "",
+	"October": "अक्टूबर",
+	"Off": "बंद",
+	"Okay, Let's Go!": "ठीक है, चलिए चलते हैं!",
+	"OLED Dark": "OLEDescuro",
+	"Ollama": "Ollama",
+	"Ollama API": "ओलामा एपीआई",
+	"Ollama API disabled": "ओलामा एपीआई अक्षम",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama Version",
+	"On": "चालू",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "कमांड स्ट्रिंग में केवल अल्फ़ान्यूमेरिक वर्ण और हाइफ़न की अनुमति है।",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "उफ़! ऐसा लगता है कि यूआरएल अमान्य है. कृपया दोबारा जांचें और पुनः प्रयास करें।",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "उफ़! आप एक असमर्थित विधि (केवल फ्रंटएंड) का उपयोग कर रहे हैं। कृपया बैकएंड से WebUI सर्वे करें।",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "नई चैट खोलें",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API कॉन्फिग",
+	"OpenAI API Key is required.": "OpenAI API कुंजी आवश्यक है",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Key आवश्यक है।",
+	"or": "या",
+	"Organize your users": "",
+	"Other": "अन्य",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "पासवर्ड",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF दस्तावेज़ (.pdf)",
+	"PDF Extract Images (OCR)": "PDF छवियाँ निकालें (OCR)",
+	"pending": "लंबित",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "माइक्रोफ़ोन तक पहुँचने पर अनुमति अस्वीकृत: {{error}}",
+	"Permissions": "",
+	"Personalization": "पेरसनलाइज़मेंट",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "पाइपलाइनों",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "पाइपलाइन वाल्व",
+	"Plain text (.txt)": "सादा पाठ (.txt)",
+	"Playground": "कार्यक्षेत्र",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "सकारात्मक रवैया",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "पिछले 30 दिन",
+	"Previous 7 days": "पिछले 7 दिन",
+	"Profile Image": "प्रोफ़ाइल छवि",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "प्रॉम्प्ट (उदाहरण के लिए मुझे रोमन साम्राज्य के बारे में एक मजेदार तथ्य बताएं)",
+	"Prompt Content": "प्रॉम्प्ट सामग्री",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "प्रॉम्प्ट सुझाव",
+	"Prompt updated successfully": "",
+	"Prompts": "प्रॉम्प्ट",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" को Ollama.com से खींचें",
+	"Pull a model from Ollama.com": "Ollama.com से एक मॉडल खींचें",
+	"Query Generation Prompt": "",
+	"Query Params": "क्वेरी पैरामीटर",
+	"RAG Template": "RAG टेम्पलेट",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "जोर से पढ़ें",
+	"Record voice": "आवाज रिकॉर्ड करना",
+	"Redirecting you to OpenWebUI Community": "आपको OpenWebUI समुदाय पर पुनर्निर्देशित किया जा रहा है",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "जब ऐसा नहीं होना चाहिए था तो मना कर दिया",
+	"Regenerate": "पुनः जेनरेट",
+	"Release Notes": "रिलीज नोट्स",
+	"Relevance": "",
+	"Remove": "हटा दें",
+	"Remove Model": "मोडेल हटाएँ",
+	"Rename": "नाम बदलें",
+	"Reorder Models": "",
+	"Repeat Last N": "अंतिम N दोहराएँ",
+	"Request Mode": "अनुरोध मोड",
+	"Reranking Model": "रीरैकिंग मोड",
+	"Reranking model disabled": "पुनर्रैंकिंग मॉडल अक्षम किया गया",
+	"Reranking model set to \"{{reranking_model}}\"": "रीरैंकिंग मॉडल को \"{{reranking_model}}\" पर \u200b\u200bसेट किया गया",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "भूमिका",
+	"Rosé Pine": "रोसे पिन",
+	"Rosé Pine Dawn": "रोसे पिन डेन",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "सहेजें",
+	"Save & Create": "सहेजें और बनाएं",
+	"Save & Update": "सहेजें और अपडेट करें",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "चैट लॉग को सीधे आपके ब्राउज़र के स्टोरेज में सहेजना अब समर्थित नहीं है। कृपया नीचे दिए गए बटन पर क्लिक करके डाउनलोड करने और अपने चैट लॉग को हटाने के लिए कुछ समय दें। चिंता न करें, आप आसानी से अपने चैट लॉग को बैकएंड पर पुनः आयात कर सकते हैं",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "खोजें",
+	"Search a model": "एक मॉडल खोजें",
+	"Search Base": "",
+	"Search Chats": "चैट खोजें",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "मॉडल खोजें",
+	"Search options": "",
+	"Search Prompts": "प्रॉम्प्ट खोजें",
+	"Search Result Count": "खोज परिणामों की संख्या",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "{{count}} sites_one खोजा गया",
+	"Searched {{count}} sites_other": "{{count}} sites_other खोजा गया",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng क्वेरी URL",
+	"See readme.md for instructions": "निर्देशों के लिए readme.md देखें",
+	"See what's new": "देखें, क्या नया है",
+	"Seed": "सीड्\u200c",
+	"Select a base model": "एक आधार मॉडल का चयन करें",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "एक मॉडल चुनें",
+	"Select a pipeline": "एक पाइपलाइन का चयन करें",
+	"Select a pipeline url": "एक पाइपलाइन url चुनें",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "मॉडल चुनें",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "चयनित मॉडल छवि इनपुट का समर्थन नहीं करते हैं",
+	"Semantic distance to query": "",
+	"Send": "भेज",
+	"Send a Message": "एक संदेश भेजो",
+	"Send message": "मेसेज भेजें",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "सितंबर",
+	"Serper API Key": "Serper API कुंजी",
+	"Serply API Key": "",
+	"Serpstack API Key": "सर्पस्टैक एपीआई कुंजी",
+	"Server connection verified": "सर्वर कनेक्शन सत्यापित",
+	"Set as default": "डिफाल्ट के रूप में सेट",
+	"Set CFG Scale": "",
+	"Set Default Model": "डिफ़ॉल्ट मॉडल सेट करें",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ईम्बेडिंग मॉडल सेट करें (उदाहरण: {{model}})",
+	"Set Image Size": "छवि का आकार सेट करें",
+	"Set reranking model (e.g. {{model}})": "रीकरण मॉडल सेट करें (उदाहरण: {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "चरण निर्धारित करें",
+	"Set Task Model": "कार्य मॉडल सेट करें",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "आवाज सेट करें",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "सेटिंग्स",
+	"Settings saved successfully!": "सेटिंग्स सफलतापूर्वक सहेजी गईं!",
+	"Share": "साझा करें",
+	"Share Chat": "चैट साझा करें",
+	"Share to OpenWebUI Community": "OpenWebUI समुदाय में साझा करें",
+	"Show": "दिखाओ",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "शॉर्टकट दिखाएँ",
+	"Show your support!": "",
+	"Showcased creativity": "रचनात्मकता का प्रदर्शन किया",
+	"Sign in": "साइन इन",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "साइन आउट",
+	"Sign up": "साइन अप",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "स्रोत",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "वाक् पहचान त्रुटि: {{error}}",
+	"Speech-to-Text Engine": "वाक्-से-पाठ इंजन",
+	"Stop": "",
+	"Stop Sequence": "अनुक्रम रोकें",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT सेटिंग्स ",
+	"Subtitle (e.g. about the Roman Empire)": "उपशीर्षक (जैसे रोमन साम्राज्य के बारे में)",
+	"Success": "संपन्न",
+	"Successfully updated.": "सफलतापूर्वक उत्परिवर्तित।",
+	"Suggested": "सुझावी",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "सिस्टम",
+	"System Instructions": "",
+	"System Prompt": "सिस्टम प्रॉम्प्ट",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "हमें और अधिक बताएँ:",
+	"Temperature": "टेंपेरेचर",
+	"Template": "टेम्पलेट",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "टेक्स्ट-टू-स्पीच इंजन",
+	"Tfs Z": "टफ्स Z",
+	"Thanks for your feedback!": "आपकी प्रतिक्रिया के लिए धन्यवाद!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "स्कोर का मान 0.0 (0%) और 1.0 (100%) के बीच होना चाहिए।",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "थीम",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "यह सुनिश्चित करता है कि आपकी मूल्यवान बातचीत आपके बैकएंड डेटाबेस में सुरक्षित रूप से सहेजी गई है। धन्यवाद!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "विस्तृत व्याख्या",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "टिप: प्रत्येक प्रतिस्थापन के बाद चैट इनपुट में टैब कुंजी दबाकर लगातार कई वैरिएबल स्लॉट अपडेट करें।",
+	"Title": "शीर्षक",
+	"Title (e.g. Tell me a fun fact)": "शीर्षक (उदा. मुझे एक मज़ेदार तथ्य बताएं)",
+	"Title Auto-Generation": "शीर्षक ऑटो-जेनरेशन",
+	"Title cannot be an empty string.": "शीर्षक नहीं खाली पाठ हो सकता है.",
+	"Title Generation Prompt": "शीर्षक जनरेशन प्रॉम्प्ट",
+	"TLS": "",
+	"To access the available model names for downloading,": "डाउनलोड करने के लिए उपलब्ध मॉडल नामों तक पहुंचने के लिए,",
+	"To access the GGUF models available for downloading,": "डाउनलोडिंग के लिए उपलब्ध GGUF मॉडल तक पहुँचने के लिए,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "आज",
+	"Toggle settings": "सेटिंग्स टॉगल करें",
+	"Toggle sidebar": "साइडबार टॉगल करें",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "शीर्ष  K",
+	"Top P": "शीर्ष  P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama तक पहुँचने में परेशानी हो रही है?",
+	"TTS Model": "",
+	"TTS Settings": "TTS सेटिंग्स",
+	"TTS Voice": "",
+	"Type": "प्रकार",
+	"Type Hugging Face Resolve (Download) URL": "हगिंग फेस रिज़ॉल्व (डाउनलोड) यूआरएल टाइप करें",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "उह ओह! {{provider}} से कनेक्ट करने में एक समस्या थी।",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "अपडेट करें और लिंक कॉपी करें",
+	"Update for the latest features and improvements.": "",
+	"Update password": "पासवर्ड अपडेट करें",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "GGUF मॉडल अपलोड करें",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "फ़ाइलें अपलोड करें",
+	"Upload Pipeline": "",
+	"Upload Progress": "प्रगति अपलोड करें",
+	"URL": "",
+	"URL Mode": "URL मोड",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gravatar का प्रयोग करें",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "प्रथमाक्षर का प्रयोग करें",
+	"use_mlock (Ollama)": "use_mlock (ओलामा)",
+	"use_mmap (Ollama)": "use_mmap (ओलामा)",
+	"user": "उपयोगकर्ता",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "उपयोगकर्ताओं",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "उपयोग करें",
+	"Valid time units:": "मान्य समय इकाइयाँ:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "वेरिएबल",
+	"variable to have them replaced with clipboard content.": "उन्हें क्लिपबोर्ड सामग्री से बदलने के लिए वेरिएबल।",
+	"Version": "संस्करण",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "चेतावनी",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "चेतावनी: यदि आप अपने एम्बेडिंग मॉडल को अपडेट या बदलते हैं, तो आपको सभी दस्तावेज़ों को फिर से आयात करने की आवश्यकता होगी।",
+	"Web": "वेब",
+	"Web API": "",
+	"Web Loader Settings": "वेब लोडर सेटिंग्स",
+	"Web Search": "वेब खोज",
+	"Web Search Engine": "वेब खोज इंजन",
+	"Web Search Query Generation": "",
+	"Webhook URL": "वेबहुक URL",
+	"WebUI Settings": "WebUI सेटिंग्स",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "इसमें नया क्या है",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "वर्कस्पेस",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "एक त्वरित सुझाव लिखें (जैसे कि आप कौन हैं?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "50 शब्दों में एक सारांश लिखें जो [विषय या कीवर्ड] का सारांश प्रस्तुत करता हो।",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "कल",
+	"You": "आप",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "आपको कोई अंकित चैट नहीं है।",
+	"You have shared this chat": "आपने इस चैट को शेयर किया है",
+	"You're a helpful assistant.": "आप एक सहायक सहायक हैं",
+	"You're now logged in.": "अब आप लॉग इन हो गए हैं",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "यूट्यूब लोडर सेटिंग्स"
+}
diff --git a/src/lib/i18n/locales/hr-HR/translation.json b/src/lib/i18n/locales/hr-HR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..d0680cedf4e5a5e63b3eb6a248cfca73f7edce1d
--- /dev/null
+++ b/src/lib/i18n/locales/hr-HR/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ili '-1' za bez isteka.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(npr. `sh webui.sh --api`)",
+	"(latest)": "(najnovije)",
+	"{{ models }}": "{{ modeli }}",
+	"{{user}}'s Chats": "Razgovori korisnika {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend je potreban",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model zadatka koristi se pri izvođenju zadataka kao što su generiranje naslova za razgovore i upite za pretraživanje weba",
+	"a user": "korisnik",
+	"About": "O aplikaciji",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Račun",
+	"Account Activation Pending": "",
+	"Accurate information": "Točne informacije",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktivni korisnici",
+	"Add": "Dodaj",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Dodajte kratak opis funkcija ovog modela",
+	"Add a tag": "Dodaj oznaku",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Dodaj prilagođeni prompt",
+	"Add Files": "Dodaj datoteke",
+	"Add Group": "",
+	"Add Memory": "Dodaj memoriju",
+	"Add Model": "Dodaj model",
+	"Add Tag": "",
+	"Add Tags": "Dodaj oznake",
+	"Add text content": "",
+	"Add User": "Dodaj korisnika",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Podešavanje će se primijeniti univerzalno na sve korisnike.",
+	"admin": "administrator",
+	"Admin": "Admin",
+	"Admin Panel": "Admin ploča",
+	"Admin Settings": "Admin postavke",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Napredni parametri",
+	"Advanced Params": "Napredni parametri",
+	"All chats": "",
+	"All Documents": "Svi dokumenti",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Dopusti brisanje razgovora",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Dopusti nelokalne glasove",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Već imate račun?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "asistent",
+	"and": "i",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "i stvorite novu dijeljenu vezu.",
+	"API Base URL": "Osnovni URL API-ja",
+	"API Key": "API ključ",
+	"API Key created.": "API ključ je stvoren.",
+	"API keys": "API ključevi",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Travanj",
+	"Archive": "Arhiva",
+	"Archive All Chats": "Arhivirajte sve razgovore",
+	"Archived Chats": "Arhivirani razgovori",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Jeste li sigurni?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Priloži datoteku",
+	"Attention to detail": "Pažnja na detalje",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Kolovoz",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Automatsko kopiranje odgovora u međuspremnik",
+	"Auto-playback response": "Automatska reprodukcija odgovora",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 osnovni URL",
+	"AUTOMATIC1111 Base URL is required.": "Potreban je AUTOMATIC1111 osnovni URL.",
+	"Available list": "",
+	"available!": "dostupno!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Natrag",
+	"Bad Response": "Loš odgovor",
+	"Banners": "Baneri",
+	"Base Model (From)": "Osnovni model (Od)",
+	"Batch Size (num_batch)": "",
+	"before": "prije",
+	"Being lazy": "Biti lijen",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave tražilica - API ključ",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Zaobiđi SSL provjeru za web stranice",
+	"Call": "Poziv",
+	"Call feature is not supported when using Web STT engine": "Značajka poziva nije podržana kada se koristi Web STT mehanizam",
+	"Camera": "Kamera",
+	"Cancel": "Otkaži",
+	"Capabilities": "Mogućnosti",
+	"Certificate Path": "",
+	"Change Password": "Promijeni lozinku",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Razgovor",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Razgovor - Bubble UI",
+	"Chat Controls": "",
+	"Chat direction": "Razgovor - smijer",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Razgovori",
+	"Check Again": "Provjeri ponovo",
+	"Check for updates": "Provjeri za ažuriranja",
+	"Checking for updates...": "Provjeravam ažuriranja...",
+	"Choose a model before saving...": "Odaberite model prije spremanja...",
+	"Chunk Overlap": "Preklapanje dijelova",
+	"Chunk Params": "Parametri dijelova",
+	"Chunk Size": "Veličina dijela",
+	"Ciphers": "",
+	"Citation": "Citiranje",
+	"Clear memory": "Očisti memoriju",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Kliknite ovdje za pomoć.",
+	"Click here to": "Kliknite ovdje za",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Kliknite ovdje za odabir",
+	"Click here to select a csv file.": "Kliknite ovdje da odaberete csv datoteku.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "kliknite ovdje.",
+	"Click on the user role button to change a user's role.": "Kliknite na gumb uloge korisnika za promjenu uloge korisnika.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Kloniraj",
+	"Close": "Zatvori",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Kolekcija",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI osnovni URL",
+	"ComfyUI Base URL is required.": "Potreban je ComfyUI osnovni URL.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Naredba",
+	"Completions": "",
+	"Concurrent Requests": "Istodobni zahtjevi",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Potvrdite lozinku",
+	"Confirm your action": "",
+	"Connections": "Povezivanja",
+	"Contact Admin for WebUI Access": "Kontaktirajte admina za WebUI pristup",
+	"Content": "Sadržaj",
+	"Content Extraction": "",
+	"Context Length": "Dužina konteksta",
+	"Continue Response": "Nastavi odgovor",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "URL dijeljenog razgovora kopiran u međuspremnik!",
+	"Copied to clipboard": "",
+	"Copy": "Kopiraj",
+	"Copy last code block": "Kopiraj zadnji blok koda",
+	"Copy last response": "Kopiraj zadnji odgovor",
+	"Copy Link": "Kopiraj vezu",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Kopiranje u međuspremnik je uspješno!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Izradite model",
+	"Create Account": "Stvori račun",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Stvori novi ključ",
+	"Create new secret key": "Stvori novi tajni ključ",
+	"Created at": "Stvoreno",
+	"Created At": "Stvoreno",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Trenutni model",
+	"Current Password": "Trenutna lozinka",
+	"Custom": "Prilagođeno",
+	"Dark": "Tamno",
+	"Database": "Baza podataka",
+	"December": "Prosinac",
+	"Default": "Zadano",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Zadano (SentenceTransformers)",
+	"Default Model": "Zadani model",
+	"Default model updated": "Zadani model ažuriran",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Zadani prijedlozi prompta",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Zadana korisnička uloga",
+	"Delete": "Izbriši",
+	"Delete a model": "Izbriši model",
+	"Delete All Chats": "Izbriši sve razgovore",
+	"Delete All Models": "",
+	"Delete chat": "Izbriši razgovor",
+	"Delete Chat": "Izbriši razgovor",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "izbriši ovu vezu",
+	"Delete tool?": "",
+	"Delete User": "Izbriši korisnika",
+	"Deleted {{deleteModelTag}}": "Izbrisan {{deleteModelTag}}",
+	"Deleted {{name}}": "Izbrisano {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Opis",
+	"Didn't fully follow instructions": "Nije u potpunosti slijedio upute",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Otkrijte model",
+	"Discover a prompt": "Otkrijte prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Otkrijte, preuzmite i istražite prilagođene prompte",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Otkrijte, preuzmite i istražite unaprijed postavljene modele",
+	"Dismissible": "Odbaciti",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Prikaži korisničko ime umjesto Vas u razgovoru",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Dokument",
+	"Documentation": "Dokumentacija",
+	"Documents": "Dokumenti",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ne uspostavlja vanjske veze, a vaši podaci ostaju sigurno na vašem lokalno hostiranom poslužitelju.",
+	"Don't have an account?": "Nemate račun?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Ne sviđa mi se stil",
+	"Done": "",
+	"Download": "Preuzimanje",
+	"Download canceled": "Preuzimanje otkazano",
+	"Download Database": "Preuzmi bazu podataka",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Spustite bilo koje datoteke ovdje za dodavanje u razgovor",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "npr. '30s','10m'. Važeće vremenske jedinice su 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Uredi",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Uredi korisnika",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Embedding - Veličina batch-a",
+	"Embedding Model": "Embedding model",
+	"Embedding Model Engine": "Embedding model pogon",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding model postavljen na \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Omogući zajedničko korištenje zajednice",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Omogući nove prijave",
+	"Enable Web Search": "Omogući pretraživanje weba",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Provjerite da vaša CSV datoteka uključuje 4 stupca u ovom redoslijedu: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Unesite {{role}} poruku ovdje",
+	"Enter a detail about yourself for your LLMs to recall": "Unesite pojedinosti o sebi da bi učitali memoriju u LLM",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Unesite Brave Search API ključ",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Unesite preklapanje dijelova",
+	"Enter Chunk Size": "Unesite veličinu dijela",
+	"Enter description": "",
+	"Enter Github Raw URL": "Unesite Github sirovi URL",
+	"Enter Google PSE API Key": "Unesite Google PSE API ključ",
+	"Enter Google PSE Engine Id": "Unesite ID Google PSE motora",
+	"Enter Image Size (e.g. 512x512)": "Unesite veličinu slike (npr. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Unesite kodove jezika",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Unesite oznaku modela (npr. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Unesite broj koraka (npr. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Unesite ocjenu",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Unesite URL upita Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Unesite Serper API ključ",
+	"Enter Serply API Key": "Unesite Serply API ključ",
+	"Enter Serpstack API Key": "Unesite Serpstack API ključ",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Unesite sekvencu zaustavljanja",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Unesite Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Unesite URL (npr. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Unesite URL (npr. http://localhost:11434)",
+	"Enter Your Email": "Unesite svoj email",
+	"Enter Your Full Name": "Unesite svoje puno ime",
+	"Enter your message": "",
+	"Enter Your Password": "Unesite svoju lozinku",
+	"Enter Your Role": "Unesite svoju ulogu",
+	"Enter Your Username": "",
+	"Error": "Greška",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Eksperimentalno",
+	"Explore the cosmos": "",
+	"Export": "Izvoz",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Izvoz svih razgovora (svi korisnici)",
+	"Export chat (.json)": "Izvoz četa (.json)",
+	"Export Chats": "Izvoz razgovora",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Izvoz modela",
+	"Export Presets": "",
+	"Export Prompts": "Izvoz prompta",
+	"Export to CSV": "",
+	"Export Tools": "Izvoz alata",
+	"External Models": "Vanjski modeli",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Neuspješno stvaranje API ključa.",
+	"Failed to read clipboard contents": "Neuspješno čitanje sadržaja međuspremnika",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Greška kod ažuriranja postavki",
+	"Failed to upload file.": "",
+	"February": "Veljača",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Slobodno dodajte specifične detalje",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Način datoteke",
+	"File not found.": "Datoteka nije pronađena.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Otkriveno krivotvorenje otisaka prstiju: Nemoguće je koristiti inicijale kao avatar. Postavljanje na zadanu profilnu sliku.",
+	"Fluidly stream large external response chunks": "Glavno strujanje velikih vanjskih dijelova odgovora",
+	"Focus chat input": "Fokusiraj unos razgovora",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Savršeno slijedio upute",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Kazna za učestalost",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Općenito",
+	"General Settings": "Opće postavke",
+	"Generate Image": "Gneriraj sliku",
+	"Generating search query": "Generiranje upita za pretraživanje",
+	"Generation Info": "Informacije o generaciji",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Dobar odgovor",
+	"Google PSE API Key": "Google PSE API ključ",
+	"Google PSE Engine Id": "ID Google PSE modula",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "nema razgovora.",
+	"Hello, {{name}}": "Bok, {{name}}",
+	"Help": "Pomoć",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Sakrij",
+	"Host": "",
+	"How can I help you today?": "Kako vam mogu pomoći danas?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hibridna pretraga",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generiranje slika (eksperimentalno)",
+	"Image Generation Engine": "Stroj za generiranje slika",
+	"Image Settings": "Postavke slike",
+	"Images": "Slike",
+	"Import Chats": "Uvoz razgovora",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Uvoz modela",
+	"Import Presets": "",
+	"Import Prompts": "Uvoz prompta",
+	"Import Tools": "Uvoz alata",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Uključite zastavicu `--api` prilikom pokretanja stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informacije",
+	"Input commands": "Unos naredbi",
+	"Install from Github URL": "Instaliraj s Github URL-a",
+	"Instant Auto-Send After Voice Transcription": "Trenutačno automatsko slanje nakon glasovne transkripcije",
+	"Interface": "Sučelje",
+	"Invalid file format.": "",
+	"Invalid Tag": "Nevažeća oznaka",
+	"January": "Siječanj",
+	"Jina API Key": "",
+	"join our Discord for help.": "pridružite se našem Discordu za pomoć.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON pretpregled",
+	"July": "Srpanj",
+	"June": "Lipanj",
+	"JWT Expiration": "Isticanje JWT-a",
+	"JWT Token": "JWT token",
+	"Keep Alive": "Održavanje živim",
+	"Key": "",
+	"Keyboard shortcuts": "Tipkovnički prečaci",
+	"Knowledge": "Znanje",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Jezik",
+	"Last Active": "Zadnja aktivnost",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Svijetlo",
+	"Listening...": "Slušam...",
+	"LLMs can make mistakes. Verify important information.": "LLM-ovi mogu pogriješiti. Provjerite važne informacije.",
+	"Local": "",
+	"Local Models": "Lokalni modeli",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Izradio OpenWebUI Community",
+	"Make sure to enclose them with": "Provjerite da ih zatvorite s",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Upravljaj",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Upravljanje cjevovodima",
+	"March": "Ožujak",
+	"Max Tokens (num_predict)": "Maksimalan broj tokena (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimalno 3 modela se mogu preuzeti istovremeno. Pokušajte ponovo kasnije.",
+	"May": "Svibanj",
+	"Memories accessible by LLMs will be shown here.": "Ovdje će biti prikazana memorija kojoj mogu pristupiti LLM-ovi.",
+	"Memory": "Memorija",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Poruke koje pošaljete nakon stvaranja veze neće se dijeliti. Korisnici s URL-om moći će vidjeti zajednički chat.",
+	"Min P": "",
+	"Minimum Score": "Minimalna ocjena",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' je uspješno preuzet.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' je već u redu za preuzimanje.",
+	"Model {{modelId}} not found": "Model {{modelId}} nije pronađen",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} ne čita vizualne impute",
+	"Model {{name}} is now {{status}}": "Model {{name}} sada je {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Otkriven put datotečnog sustava modela. Kratko ime modela je potrebno za ažuriranje, nije moguće nastaviti.",
+	"Model Filtering": "",
+	"Model ID": "ID modela",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model nije odabran",
+	"Model Params": "Model parametri",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Sadržaj datoteke modela",
+	"Models": "Modeli",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Više",
+	"Name": "Ime",
+	"Name your knowledge base": "",
+	"New Chat": "Novi razgovor",
+	"New folder": "",
+	"New Password": "Nova lozinka",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Nema rezultata",
+	"No search query generated": "Nije generiran upit za pretraživanje",
+	"No source available": "Nema dostupnog izvora",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Ništa",
+	"Not factually correct": "Nije činjenično točno",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Napomena: Ako postavite minimalnu ocjenu, pretraga će vratiti samo dokumente s ocjenom većom ili jednakom minimalnoj ocjeni.",
+	"Notes": "",
+	"Notifications": "Obavijesti",
+	"November": "Studeni",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Listopad",
+	"Off": "Isključeno",
+	"Okay, Let's Go!": "U redu, idemo!",
+	"OLED Dark": "OLED Tamno",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API je onemogućen",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama verzija",
+	"On": "Uključeno",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Samo alfanumerički znakovi i crtice su dopušteni u naredbenom nizu.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! Izgleda da je URL nevažeći. Molimo provjerite ponovno i pokušajte ponovo.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ups! Koristite nepodržanu metodu (samo frontend). Molimo poslužite WebUI s backend-a.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Otvorite novi razgovor",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API konfiguracija",
+	"OpenAI API Key is required.": "Potreban je OpenAI API ključ.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Potreban je OpenAI URL/ključ.",
+	"or": "ili",
+	"Organize your users": "",
+	"Other": "Ostalo",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Lozinka",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF dokument (.pdf)",
+	"PDF Extract Images (OCR)": "PDF izdvajanje slika (OCR)",
+	"pending": "u tijeku",
+	"Permission denied when accessing media devices": "Dopuštenje je odbijeno prilikom pristupa medijskim uređajima",
+	"Permission denied when accessing microphone": "Dopuštenje je odbijeno prilikom pristupa mikrofonu",
+	"Permission denied when accessing microphone: {{error}}": "Pristup mikrofonu odbijen: {{error}}",
+	"Permissions": "",
+	"Personalization": "Prilagodba",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Cjevovodi",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Ventili za cjevovode",
+	"Plain text (.txt)": "Običan tekst (.txt)",
+	"Playground": "Igralište",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Pozitivan stav",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Prethodnih 30 dana",
+	"Previous 7 days": "Prethodnih 7 dana",
+	"Profile Image": "Profilna slika",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (npr. Reci mi zanimljivost o Rimskom carstvu)",
+	"Prompt Content": "Sadržaj prompta",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Prijedlozi prompta",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompti",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Povucite \"{{searchValue}}\" s Ollama.com",
+	"Pull a model from Ollama.com": "Povucite model s Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parametri upita",
+	"RAG Template": "RAG predložak",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Čitaj naglas",
+	"Record voice": "Snimanje glasa",
+	"Redirecting you to OpenWebUI Community": "Preusmjeravanje na OpenWebUI zajednicu",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Nazivajte se \"Korisnik\" (npr. \"Korisnik uči španjolski\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Odbijen kada nije trebao biti",
+	"Regenerate": "Regeneriraj",
+	"Release Notes": "Bilješke o izdanju",
+	"Relevance": "",
+	"Remove": "Ukloni",
+	"Remove Model": "Ukloni model",
+	"Rename": "Preimenuj",
+	"Reorder Models": "",
+	"Repeat Last N": "Ponovi zadnjih N",
+	"Request Mode": "Način zahtjeva",
+	"Reranking Model": "Model za ponovno rangiranje",
+	"Reranking model disabled": "Model za ponovno rangiranje onemogućen",
+	"Reranking model set to \"{{reranking_model}}\"": "Model za ponovno rangiranje postavljen na \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Poništi upload direktorij",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Uloga",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "Pokrenuto",
+	"Save": "Spremi",
+	"Save & Create": "Spremi i stvori",
+	"Save & Update": "Spremi i ažuriraj",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Spremanje zapisnika razgovora izravno u pohranu vašeg preglednika više nije podržano. Molimo vas da odvojite trenutak za preuzimanje i brisanje zapisnika razgovora klikom na gumb ispod. Ne brinite, možete lako ponovno uvesti zapisnike razgovora u backend putem",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Pretraga",
+	"Search a model": "Pretraži model",
+	"Search Base": "",
+	"Search Chats": "Pretraži razgovore",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Pretražite modele",
+	"Search options": "",
+	"Search Prompts": "Pretraga prompta",
+	"Search Result Count": "Broj rezultata pretraživanja",
+	"Search the web": "",
+	"Search Tools": "Alati za pretraživanje",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Pretraženo {{count}} sites_one",
+	"Searched {{count}} sites_few": "Pretraženo {{count}} sites_few",
+	"Searched {{count}} sites_other": "Pretraženo {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng URL upita",
+	"See readme.md for instructions": "Pogledajte readme.md za upute",
+	"See what's new": "Pogledajte što je novo",
+	"Seed": "Sjeme",
+	"Select a base model": "Odabir osnovnog modela",
+	"Select a engine": "Odaberite pogon",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Odaberite model",
+	"Select a pipeline": "Odabir kanala",
+	"Select a pipeline url": "Odabir URL-a kanala",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Odaberite model",
+	"Select only one model to call": "Odaberite samo jedan model za poziv",
+	"Selected model(s) do not support image inputs": "Odabrani modeli ne podržavaju unose slika",
+	"Semantic distance to query": "",
+	"Send": "Pošalji",
+	"Send a Message": "Pošaljite poruku",
+	"Send message": "Pošalji poruku",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Rujan",
+	"Serper API Key": "Serper API ključ",
+	"Serply API Key": "Serply API ključ",
+	"Serpstack API Key": "Serpstack API API ključ",
+	"Server connection verified": "Veza s poslužiteljem potvrđena",
+	"Set as default": "Postavi kao zadano",
+	"Set CFG Scale": "",
+	"Set Default Model": "Postavi zadani model",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Postavi model za embedding (npr. {{model}})",
+	"Set Image Size": "Postavi veličinu slike",
+	"Set reranking model (e.g. {{model}})": "Postavi model za ponovno rangiranje (npr. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Postavi korake",
+	"Set Task Model": "Postavite model zadatka",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Postavi glas",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Postavke",
+	"Settings saved successfully!": "Postavke su uspješno spremljene!",
+	"Share": "Podijeli",
+	"Share Chat": "Podijeli razgovor",
+	"Share to OpenWebUI Community": "Podijeli u OpenWebUI zajednici",
+	"Show": "Pokaži",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Pokaži prečace",
+	"Show your support!": "",
+	"Showcased creativity": "Prikazana kreativnost",
+	"Sign in": "Prijava",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Odjava",
+	"Sign up": "Registracija",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Izvor",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Pogreška prepoznavanja govora: {{error}}",
+	"Speech-to-Text Engine": "Stroj za prepoznavanje govora",
+	"Stop": "",
+	"Stop Sequence": "Zaustavi sekvencu",
+	"Stream Chat Response": "",
+	"STT Model": "STT model",
+	"STT Settings": "STT postavke",
+	"Subtitle (e.g. about the Roman Empire)": "Podnaslov (npr. o Rimskom carstvu)",
+	"Success": "Uspjeh",
+	"Successfully updated.": "Uspješno ažurirano.",
+	"Suggested": "Predloženo",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Sustav",
+	"System Instructions": "",
+	"System Prompt": "Sistemski prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Recite nam više:",
+	"Temperature": "Temperatura",
+	"Template": "Predložak",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Stroj za pretvorbu teksta u govor",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Hvala na povratnim informacijama!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Ocjena treba biti vrijednost između 0,0 (0%) i 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Razmišljam",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ovo osigurava da su vaši vrijedni razgovori sigurno spremljeni u bazu podataka. Hvala vam!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Ovo je eksperimentalna značajka, možda neće funkcionirati prema očekivanjima i podložna je promjenama u bilo kojem trenutku.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Detaljno objašnjenje",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Savjet: Ažurirajte više mjesta za varijable uzastopno pritiskom na tipku tab u unosu razgovora nakon svake zamjene.",
+	"Title": "Naslov",
+	"Title (e.g. Tell me a fun fact)": "Naslov (npr. Reci mi zanimljivost)",
+	"Title Auto-Generation": "Automatsko generiranje naslova",
+	"Title cannot be an empty string.": "Naslov ne može biti prazni niz.",
+	"Title Generation Prompt": "Prompt za generiranje naslova",
+	"TLS": "",
+	"To access the available model names for downloading,": "Za pristup dostupnim nazivima modela za preuzimanje,",
+	"To access the GGUF models available for downloading,": "Za pristup GGUF modelima dostupnim za preuzimanje,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Za pristup WebUI-u obratite se administratoru. Administratori mogu upravljati statusima korisnika s Admin panela.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Danas",
+	"Toggle settings": "Prebaci postavke",
+	"Toggle sidebar": "Prebaci bočnu traku",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "Alati",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemi s pristupom Ollama?",
+	"TTS Model": "TTS model",
+	"TTS Settings": "TTS postavke",
+	"TTS Voice": "TTS glas",
+	"Type": "Tip",
+	"Type Hugging Face Resolve (Download) URL": "Upišite Hugging Face Resolve (Download) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Pojavio se problem s povezivanjem na {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Ažuriraj i kopiraj vezu",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Ažuriraj lozinku",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Učitaj GGUF model",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Prijenos datoteka",
+	"Upload Pipeline": "Prijenos kanala",
+	"Upload Progress": "Napredak učitavanja",
+	"URL": "",
+	"URL Mode": "URL način",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Koristi Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Koristi inicijale",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "korisnik",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Korisnici",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Iskoristi",
+	"Valid time units:": "Važeće vremenske jedinice:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "varijabla",
+	"variable to have them replaced with clipboard content.": "varijabla za zamjenu sadržajem međuspremnika.",
+	"Version": "Verzija",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Upozorenje",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Upozorenje: Ako ažurirate ili promijenite svoj model za umetanje, morat ćete ponovno uvesti sve dokumente.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Postavke web učitavanja",
+	"Web Search": "Internet pretraga",
+	"Web Search Engine": "Web tražilica",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL webkuke",
+	"WebUI Settings": "WebUI postavke",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Što je novo u",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (lokalno)",
+	"Why?": "",
+	"Widescreen Mode": "Mod širokog zaslona",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Radna ploča",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Napišite prijedlog prompta (npr. Tko si ti?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Napišite sažetak u 50 riječi koji sažima [temu ili ključnu riječ].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Jučer",
+	"You": "Vi",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Možete personalizirati svoje interakcije s LLM-ima dodavanjem uspomena putem gumba 'Upravljanje' u nastavku, čineći ih korisnijima i prilagođenijima vama.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Nemate arhiviranih razgovora.",
+	"You have shared this chat": "Podijelili ste ovaj razgovor",
+	"You're a helpful assistant.": "Vi ste korisni asistent.",
+	"You're now logged in.": "Sada ste prijavljeni.",
+	"Your account status is currently pending activation.": "Status vašeg računa trenutno čeka aktivaciju.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube postavke učitavanja"
+}
diff --git a/src/lib/i18n/locales/hu-HU/translation.json b/src/lib/i18n/locales/hu-HU/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..b5872c9542b57d49ea5325d23db1b4877d31619e
--- /dev/null
+++ b/src/lib/i18n/locales/hu-HU/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' vagy '-1' ha nincs lejárat.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(pl. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(pl. `sh webui.sh --api`)",
+	"(latest)": "(legújabb)",
+	"{{ models }}": "{{ modellek }}",
+	"{{user}}'s Chats": "{{user}} beszélgetései",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend szükséges",
+	"*Prompt node ID(s) are required for image generation": "*Prompt node ID(k) szükségesek a képgeneráláshoz",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Új verzió (v{{LATEST_VERSION}}) érhető el.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "A feladat modell olyan feladatokhoz használatos, mint a beszélgetések címeinek generálása és webes keresési lekérdezések",
+	"a user": "egy felhasználó",
+	"About": "Névjegy",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Fiók",
+	"Account Activation Pending": "Fiók aktiválása folyamatban",
+	"Accurate information": "Pontos információ",
+	"Actions": "Műveletek",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktív felhasználók",
+	"Add": "Hozzáadás",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Adj hozzá egy rövid leírást arról, hogy mit csinál ez a modell",
+	"Add a tag": "Címke hozzáadása",
+	"Add Arena Model": "Arena modell hozzáadása",
+	"Add Connection": "",
+	"Add Content": "Tartalom hozzáadása",
+	"Add content here": "Tartalom hozzáadása ide",
+	"Add custom prompt": "Egyéni prompt hozzáadása",
+	"Add Files": "Fájlok hozzáadása",
+	"Add Group": "",
+	"Add Memory": "Memória hozzáadása",
+	"Add Model": "Modell hozzáadása",
+	"Add Tag": "Címke hozzáadása",
+	"Add Tags": "Címkék hozzáadása",
+	"Add text content": "Szöveges tartalom hozzáadása",
+	"Add User": "Felhasználó hozzáadása",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ezen beállítások módosítása minden felhasználóra érvényes lesz.",
+	"admin": "admin",
+	"Admin": "Admin",
+	"Admin Panel": "Admin Panel",
+	"Admin Settings": "Admin beállítások",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Az adminok mindig hozzáférnek minden eszközhöz; a felhasználóknak modellenként kell eszközöket hozzárendelni a munkaterületen.",
+	"Advanced Parameters": "Haladó paraméterek",
+	"Advanced Params": "Haladó paraméterek",
+	"All chats": "Minden beszélgetés",
+	"All Documents": "Minden dokumentum",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Beszélgetések törlésének engedélyezése",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Nem helyi hangok engedélyezése",
+	"Allow Temporary Chat": "Ideiglenes beszélgetés engedélyezése",
+	"Allow User Location": "Felhasználói helyzet engedélyezése",
+	"Allow Voice Interruption in Call": "Hang megszakítás engedélyezése hívás közben",
+	"Already have an account?": "Már van fiókod?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "egy asszisztens",
+	"and": "és",
+	"and {{COUNT}} more": "és még {{COUNT}} db",
+	"and create a new shared link.": "és hozz létre egy új megosztott linket.",
+	"API Base URL": "API alap URL",
+	"API Key": "API kulcs",
+	"API Key created.": "API kulcs létrehozva.",
+	"API keys": "API kulcsok",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Április",
+	"Archive": "Archiválás",
+	"Archive All Chats": "Minden beszélgetés archiválása",
+	"Archived Chats": "Archivált beszélgetések",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Biztos vagy benne?",
+	"Arena Models": "Arena modellek",
+	"Artifacts": "Műtermékek",
+	"Ask a question": "Kérdezz valamit",
+	"Assistant": "Asszisztens",
+	"Attach file": "Fájl csatolása",
+	"Attention to detail": "Részletekre való odafigyelés",
+	"Attribute for Username": "",
+	"Audio": "Hang",
+	"August": "Augusztus",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Válasz automatikus másolása a vágólapra",
+	"Auto-playback response": "Automatikus válasz lejátszás",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api hitelesítési karakterlánc",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 alap URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 alap URL szükséges.",
+	"Available list": "Elérhető lista",
+	"available!": "elérhető!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI beszéd",
+	"Azure Region": "Azure régió",
+	"Back": "Vissza",
+	"Bad Response": "Rossz válasz",
+	"Banners": "Bannerek",
+	"Base Model (From)": "Alap modell (Forrás)",
+	"Batch Size (num_batch)": "Köteg méret (num_batch)",
+	"before": "előtt",
+	"Being lazy": "Lustaság",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API kulcs",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "SSL ellenőrzés kihagyása weboldalakhoz",
+	"Call": "Hívás",
+	"Call feature is not supported when using Web STT engine": "A hívás funkció nem támogatott Web STT motor használatakor",
+	"Camera": "Kamera",
+	"Cancel": "Mégse",
+	"Capabilities": "Képességek",
+	"Certificate Path": "",
+	"Change Password": "Jelszó módosítása",
+	"Character": "Karakter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Beszélgetés",
+	"Chat Background Image": "Beszélgetés háttérkép",
+	"Chat Bubble UI": "Beszélgetés buborék felület",
+	"Chat Controls": "Beszélgetés vezérlők",
+	"Chat direction": "Beszélgetés iránya",
+	"Chat Overview": "Beszélgetés áttekintés",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "Beszélgetés címkék automatikus generálása",
+	"Chats": "Beszélgetések",
+	"Check Again": "Ellenőrzés újra",
+	"Check for updates": "Frissítések keresése",
+	"Checking for updates...": "Frissítések keresése...",
+	"Choose a model before saving...": "Válassz modellt mentés előtt...",
+	"Chunk Overlap": "Darab átfedés",
+	"Chunk Params": "Darab paraméterek",
+	"Chunk Size": "Darab méret",
+	"Ciphers": "",
+	"Citation": "Idézet",
+	"Clear memory": "Memória törlése",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Kattints ide segítségért.",
+	"Click here to": "Kattints ide",
+	"Click here to download user import template file.": "Kattints ide a felhasználó importálási sablon letöltéséhez.",
+	"Click here to learn more about faster-whisper and see the available models.": "Kattints ide, hogy többet tudj meg a faster-whisperről és lásd az elérhető modelleket.",
+	"Click here to select": "Kattints ide a kiválasztáshoz",
+	"Click here to select a csv file.": "Kattints ide egy CSV fájl kiválasztásához.",
+	"Click here to select a py file.": "Kattints ide egy py fájl kiválasztásához.",
+	"Click here to upload a workflow.json file.": "Kattints ide egy workflow.json fájl feltöltéséhez.",
+	"click here.": "kattints ide.",
+	"Click on the user role button to change a user's role.": "Kattints a felhasználói szerep gombra a felhasználó szerepének módosításához.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Vágólap írási engedély megtagadva. Kérjük, ellenőrizd a böngésző beállításait a szükséges hozzáférés megadásához.",
+	"Clone": "Klónozás",
+	"Close": "Bezárás",
+	"Code execution": "Kód végrehajtás",
+	"Code formatted successfully": "Kód sikeresen formázva",
+	"Collection": "Gyűjtemény",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI alap URL",
+	"ComfyUI Base URL is required.": "ComfyUI alap URL szükséges.",
+	"ComfyUI Workflow": "ComfyUI munkafolyamat",
+	"ComfyUI Workflow Nodes": "ComfyUI munkafolyamat csomópontok",
+	"Command": "Parancs",
+	"Completions": "Kiegészítések",
+	"Concurrent Requests": "Párhuzamos kérések",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Megerősítés",
+	"Confirm Password": "Jelszó megerősítése",
+	"Confirm your action": "Erősítsd meg a műveletet",
+	"Connections": "Kapcsolatok",
+	"Contact Admin for WebUI Access": "Lépj kapcsolatba az adminnal a WebUI hozzáférésért",
+	"Content": "Tartalom",
+	"Content Extraction": "Tartalom kinyerés",
+	"Context Length": "Kontextus hossz",
+	"Continue Response": "Válasz folytatása",
+	"Continue with {{provider}}": "Folytatás {{provider}} szolgáltatóval",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Szabályozd, hogyan legyen felosztva az üzenet szövege a TTS kérésekhez. A 'Központozás' mondatokra bontja, a 'Bekezdések' bekezdésekre bontja, a 'Nincs' pedig egyetlen szövegként kezeli az üzenetet.",
+	"Controls": "Vezérlők",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Másolva",
+	"Copied shared chat URL to clipboard!": "Megosztott beszélgetés URL másolva a vágólapra!",
+	"Copied to clipboard": "Vágólapra másolva",
+	"Copy": "Másolás",
+	"Copy last code block": "Utolsó kódblokk másolása",
+	"Copy last response": "Utolsó válasz másolása",
+	"Copy Link": "Link másolása",
+	"Copy to clipboard": "Másolás a vágólapra",
+	"Copying to clipboard was successful!": "Sikeres másolás a vágólapra!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Modell létrehozása",
+	"Create Account": "Fiók létrehozása",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "Tudás létrehozása",
+	"Create new key": "Új kulcs létrehozása",
+	"Create new secret key": "Új titkos kulcs létrehozása",
+	"Created at": "Létrehozva",
+	"Created At": "Létrehozva",
+	"Created by": "Létrehozta",
+	"CSV Import": "CSV importálás",
+	"Current Model": "Jelenlegi modell",
+	"Current Password": "Jelenlegi jelszó",
+	"Custom": "Egyéni",
+	"Dark": "Sötét",
+	"Database": "Adatbázis",
+	"December": "December",
+	"Default": "Alapértelmezett",
+	"Default (Open AI)": "Alapértelmezett (Open AI)",
+	"Default (SentenceTransformers)": "Alapértelmezett (SentenceTransformers)",
+	"Default Model": "Alapértelmezett modell",
+	"Default model updated": "Alapértelmezett modell frissítve",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Alapértelmezett prompt javaslatok",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Alapértelmezett felhasználói szerep",
+	"Delete": "Törlés",
+	"Delete a model": "Modell törlése",
+	"Delete All Chats": "Minden beszélgetés törlése",
+	"Delete All Models": "",
+	"Delete chat": "Beszélgetés törlése",
+	"Delete Chat": "Beszélgetés törlése",
+	"Delete chat?": "Törli a beszélgetést?",
+	"Delete folder?": "Törli a mappát?",
+	"Delete function?": "Törli a funkciót?",
+	"Delete prompt?": "Törli a promptot?",
+	"delete this link": "link törlése",
+	"Delete tool?": "Törli az eszközt?",
+	"Delete User": "Felhasználó törlése",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} törölve",
+	"Deleted {{name}}": "{{name}} törölve",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Leírás",
+	"Didn't fully follow instructions": "Nem követte teljesen az utasításokat",
+	"Disabled": "Letiltva",
+	"Discover a function": "Funkció felfedezése",
+	"Discover a model": "Modell felfedezése",
+	"Discover a prompt": "Prompt felfedezése",
+	"Discover a tool": "Eszköz felfedezése",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Fedezz fel, tölts le és fedezz fel egyéni funkciókat",
+	"Discover, download, and explore custom prompts": "Fedezz fel, tölts le és fedezz fel egyéni promptokat",
+	"Discover, download, and explore custom tools": "Fedezz fel, tölts le és fedezz fel egyéni eszközöket",
+	"Discover, download, and explore model presets": "Fedezz fel, tölts le és fedezz fel modell beállításokat",
+	"Dismissible": "Elutasítható",
+	"Display": "",
+	"Display Emoji in Call": "Emoji megjelenítése hívásban",
+	"Display the username instead of You in the Chat": "Felhasználónév megjelenítése a 'Te' helyett a beszélgetésben",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Ne telepíts funkciókat olyan forrásokból, amelyekben nem bízol teljesen.",
+	"Do not install tools from sources you do not fully trust.": "Ne telepíts eszközöket olyan forrásokból, amelyekben nem bízol teljesen.",
+	"Document": "Dokumentum",
+	"Documentation": "Dokumentáció",
+	"Documents": "Dokumentumok",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "nem létesít külső kapcsolatokat, és az adataid biztonságban maradnak a helyileg hosztolt szervereden.",
+	"Don't have an account?": "Nincs még fiókod?",
+	"don't install random functions from sources you don't trust.": "ne telepíts véletlenszerű funkciókat olyan forrásokból, amelyekben nem bízol.",
+	"don't install random tools from sources you don't trust.": "ne telepíts véletlenszerű eszközöket olyan forrásokból, amelyekben nem bízol.",
+	"Don't like the style": "Nem tetszik a stílus",
+	"Done": "Kész",
+	"Download": "Letöltés",
+	"Download canceled": "Letöltés megszakítva",
+	"Download Database": "Adatbázis letöltése",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "Rajzolás",
+	"Drop any files here to add to the conversation": "Húzz ide fájlokat a beszélgetéshez való hozzáadáshoz",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "pl. '30s','10m'. Érvényes időegységek: 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Szerkesztés",
+	"Edit Arena Model": "Arena modell szerkesztése",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Memória szerkesztése",
+	"Edit User": "Felhasználó szerkesztése",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Beágyazási köteg méret",
+	"Embedding Model": "Beágyazási modell",
+	"Embedding Model Engine": "Beágyazási modell motor",
+	"Embedding model set to \"{{embedding_model}}\"": "Beágyazási modell beállítva: \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Közösségi megosztás engedélyezése",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Üzenet értékelés engedélyezése",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Új regisztrációk engedélyezése",
+	"Enable Web Search": "Webes keresés engedélyezése",
+	"Enabled": "Engedélyezve",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Győződj meg róla, hogy a CSV fájl tartalmazza ezt a 4 oszlopot ebben a sorrendben: Név, Email, Jelszó, Szerep.",
+	"Enter {{role}} message here": "Írd ide a {{role}} üzenetet",
+	"Enter a detail about yourself for your LLMs to recall": "Adj meg egy részletet magadról, amit az LLM-ek megjegyezhetnek",
+	"Enter api auth string (e.g. username:password)": "Add meg az API hitelesítési karakterláncot (pl. felhasználónév:jelszó)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Add meg a Brave Search API kulcsot",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "Add meg a CFG skálát (pl. 7.0)",
+	"Enter Chunk Overlap": "Add meg a darab átfedést",
+	"Enter Chunk Size": "Add meg a darab méretet",
+	"Enter description": "Add meg a leírást",
+	"Enter Github Raw URL": "Add meg a Github Raw URL-t",
+	"Enter Google PSE API Key": "Add meg a Google PSE API kulcsot",
+	"Enter Google PSE Engine Id": "Add meg a Google PSE motor azonosítót",
+	"Enter Image Size (e.g. 512x512)": "Add meg a kép méretet (pl. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Add meg a nyelvi kódokat",
+	"Enter Model ID": "Add meg a modell azonosítót",
+	"Enter model tag (e.g. {{modelTag}})": "Add meg a modell címkét (pl. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Add meg a lépések számát (pl. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Add meg a mintavételezőt (pl. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Add meg az ütemezőt (pl. Karras)",
+	"Enter Score": "Add meg a pontszámot",
+	"Enter SearchApi API Key": "Add meg a SearchApi API kulcsot",
+	"Enter SearchApi Engine": "Add meg a SearchApi motort",
+	"Enter Searxng Query URL": "Add meg a Searxng lekérdezési URL-t",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Add meg a Serper API kulcsot",
+	"Enter Serply API Key": "Add meg a Serply API kulcsot",
+	"Enter Serpstack API Key": "Add meg a Serpstack API kulcsot",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Add meg a leállítási szekvenciát",
+	"Enter system prompt": "Add meg a rendszer promptot",
+	"Enter Tavily API Key": "Add meg a Tavily API kulcsot",
+	"Enter Tika Server URL": "Add meg a Tika szerver URL-t",
+	"Enter Top K": "Add meg a Top K értéket",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Add meg az URL-t (pl. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Add meg az URL-t (pl. http://localhost:11434)",
+	"Enter Your Email": "Add meg az email címed",
+	"Enter Your Full Name": "Add meg a teljes neved",
+	"Enter your message": "Írd be az üzeneted",
+	"Enter Your Password": "Add meg a jelszavad",
+	"Enter Your Role": "Add meg a szereped",
+	"Enter Your Username": "",
+	"Error": "Hiba",
+	"ERROR": "HIBA",
+	"Evaluations": "Értékelések",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "Kizárás",
+	"Experimental": "Kísérleti",
+	"Explore the cosmos": "",
+	"Export": "Exportálás",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Minden beszélgetés exportálása (minden felhasználó)",
+	"Export chat (.json)": "Beszélgetés exportálása (.json)",
+	"Export Chats": "Beszélgetések exportálása",
+	"Export Config to JSON File": "Konfiguráció exportálása JSON fájlba",
+	"Export Functions": "Funkciók exportálása",
+	"Export Models": "Modellek exportálása",
+	"Export Presets": "",
+	"Export Prompts": "Promptok exportálása",
+	"Export to CSV": "",
+	"Export Tools": "Eszközök exportálása",
+	"External Models": "Külső modellek",
+	"Failed to add file.": "Nem sikerült hozzáadni a fájlt.",
+	"Failed to create API Key.": "Nem sikerült létrehozni az API kulcsot.",
+	"Failed to read clipboard contents": "Nem sikerült olvasni a vágólap tartalmát",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Nem sikerült frissíteni a beállításokat",
+	"Failed to upload file.": "Nem sikerült feltölteni a fájlt.",
+	"February": "Február",
+	"Feedback History": "Visszajelzés előzmények",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Nyugodtan adj hozzá specifikus részleteket",
+	"File": "Fájl",
+	"File added successfully.": "Fájl sikeresen hozzáadva.",
+	"File content updated successfully.": "Fájl tartalom sikeresen frissítve.",
+	"File Mode": "Fájl mód",
+	"File not found.": "Fájl nem található.",
+	"File removed successfully.": "Fájl sikeresen eltávolítva.",
+	"File size should not exceed {{maxSize}} MB.": "A fájl mérete nem haladhatja meg a {{maxSize}} MB-ot.",
+	"Files": "Fájlok",
+	"Filter is now globally disabled": "A szűrő globálisan letiltva",
+	"Filter is now globally enabled": "A szűrő globálisan engedélyezve",
+	"Filters": "Szűrők",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Ujjlenyomat hamisítás észlelve: Nem lehet a kezdőbetűket avatárként használni. Alapértelmezett profilkép használata.",
+	"Fluidly stream large external response chunks": "Nagy külső válasz darabok folyamatos streamelése",
+	"Focus chat input": "Chat bevitel fókuszálása",
+	"Folder deleted successfully": "Mappa sikeresen törölve",
+	"Folder name cannot be empty": "A mappa neve nem lehet üres",
+	"Folder name cannot be empty.": "A mappa neve nem lehet üres.",
+	"Folder name updated successfully": "Mappa neve sikeresen frissítve",
+	"Followed instructions perfectly": "Tökéletesen követte az utasításokat",
+	"Forge new paths": "",
+	"Form": "Űrlap",
+	"Format your variables using brackets like this:": "Formázd a változóidat zárójelekkel így:",
+	"Frequency Penalty": "Gyakorisági büntetés",
+	"Function": "Funkció",
+	"Function created successfully": "Funkció sikeresen létrehozva",
+	"Function deleted successfully": "Funkció sikeresen törölve",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "A funkció globálisan letiltva",
+	"Function is now globally enabled": "A funkció globálisan engedélyezve",
+	"Function Name": "",
+	"Function updated successfully": "Funkció sikeresen frissítve",
+	"Functions": "Funkciók",
+	"Functions allow arbitrary code execution": "A funkciók tetszőleges kód végrehajtását teszik lehetővé",
+	"Functions allow arbitrary code execution.": "A funkciók tetszőleges kód végrehajtását teszik lehetővé.",
+	"Functions imported successfully": "Funkciók sikeresen importálva",
+	"General": "Általános",
+	"General Settings": "Általános beállítások",
+	"Generate Image": "Kép generálása",
+	"Generating search query": "Keresési lekérdezés generálása",
+	"Generation Info": "Generálási információ",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Globális",
+	"Good Response": "Jó válasz",
+	"Google PSE API Key": "Google PSE API kulcs",
+	"Google PSE Engine Id": "Google PSE motor azonosító",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Tapintási visszajelzés",
+	"has no conversations.": "nincsenek beszélgetései.",
+	"Hello, {{name}}": "Helló, {{name}}",
+	"Help": "Segítség",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Segíts nekünk a legjobb közösségi ranglista létrehozásában a visszajelzési előzményeid megosztásával!",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Elrejtés",
+	"Host": "",
+	"How can I help you today?": "Hogyan segíthetek ma?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hibrid keresés",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Elismerem, hogy elolvastam és megértem a cselekedetem következményeit. Tisztában vagyok a tetszőleges kód végrehajtásával járó kockázatokkal, és ellenőriztem a forrás megbízhatóságát.",
+	"ID": "Azonosító",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Képgenerálás (kísérleti)",
+	"Image Generation Engine": "Képgenerálási motor",
+	"Image Settings": "Kép beállítások",
+	"Images": "Képek",
+	"Import Chats": "Beszélgetések importálása",
+	"Import Config from JSON File": "Konfiguráció importálása JSON fájlból",
+	"Import Functions": "Funkciók importálása",
+	"Import Models": "Modellek importálása",
+	"Import Presets": "",
+	"Import Prompts": "Promptok importálása",
+	"Import Tools": "Eszközök importálása",
+	"Include": "Tartalmaz",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Add hozzá a `--api-auth` kapcsolót a stable-diffusion-webui futtatásakor",
+	"Include `--api` flag when running stable-diffusion-webui": "Add hozzá a `--api` kapcsolót a stable-diffusion-webui futtatásakor",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Információ",
+	"Input commands": "Beviteli parancsok",
+	"Install from Github URL": "Telepítés Github URL-ről",
+	"Instant Auto-Send After Voice Transcription": "Azonnali automatikus küldés hangfelismerés után",
+	"Interface": "Felület",
+	"Invalid file format.": "Érvénytelen fájlformátum.",
+	"Invalid Tag": "Érvénytelen címke",
+	"January": "Január",
+	"Jina API Key": "",
+	"join our Discord for help.": "Csatlakozz a Discord szerverünkhöz segítségért.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON előnézet",
+	"July": "Július",
+	"June": "Június",
+	"JWT Expiration": "JWT lejárat",
+	"JWT Token": "JWT token",
+	"Keep Alive": "Kapcsolat fenntartása",
+	"Key": "",
+	"Keyboard shortcuts": "Billentyűparancsok",
+	"Knowledge": "Tudásbázis",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Tudásbázis sikeresen létrehozva.",
+	"Knowledge deleted successfully.": "Tudásbázis sikeresen törölve.",
+	"Knowledge reset successfully.": "Tudásbázis sikeresen visszaállítva.",
+	"Knowledge updated successfully": "Tudásbázis sikeresen frissítve",
+	"Label": "",
+	"Landing Page Mode": "Kezdőlap mód",
+	"Language": "Nyelv",
+	"Last Active": "Utoljára aktív",
+	"Last Modified": "Utoljára módosítva",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "Ranglista",
+	"Leave empty for unlimited": "Hagyja üresen a korlátlan használathoz",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "Hagyja üresen az összes modell használatához, vagy válasszon ki konkrét modelleket",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Hagyja üresen az alapértelmezett prompt használatához, vagy adjon meg egyéni promptot",
+	"Light": "Világos",
+	"Listening...": "Hallgatás...",
+	"LLMs can make mistakes. Verify important information.": "Az LLM-ek hibázhatnak. Ellenőrizze a fontos információkat.",
+	"Local": "",
+	"Local Models": "Helyi modellek",
+	"Lost": "Elveszett",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Az OpenWebUI közösség által készítve",
+	"Make sure to enclose them with": "Győződjön meg róla, hogy körülveszi őket",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Győződjön meg róla, hogy exportál egy workflow.json fájlt API formátumban a ComfyUI-ból.",
+	"Manage": "Kezelés",
+	"Manage Arena Models": "Arena modellek kezelése",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Folyamatok kezelése",
+	"March": "Március",
+	"Max Tokens (num_predict)": "Maximum tokenek (num_predict)",
+	"Max Upload Count": "Maximum feltöltések száma",
+	"Max Upload Size": "Maximum feltöltési méret",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximum 3 modell tölthető le egyszerre. Kérjük, próbálja újra később.",
+	"May": "Május",
+	"Memories accessible by LLMs will be shown here.": "Az LLM-ek által elérhető emlékek itt jelennek meg.",
+	"Memory": "Memória",
+	"Memory added successfully": "Memória sikeresen hozzáadva",
+	"Memory cleared successfully": "Memória sikeresen törölve",
+	"Memory deleted successfully": "Memória sikeresen törölve",
+	"Memory updated successfully": "Memória sikeresen frissítve",
+	"Merge Responses": "Válaszok egyesítése",
+	"Message rating should be enabled to use this feature": "Az üzenetértékelésnek engedélyezve kell lennie ehhez a funkcióhoz",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "A link létrehozása után küldött üzenetei nem lesznek megosztva. A URL-lel rendelkező felhasználók megtekinthetik a megosztott beszélgetést.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimum pontszám",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "YYYY. MMMM DD.",
+	"MMMM DD, YYYY HH:mm": "YYYY. MMMM DD. HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "YYYY. MMMM DD. hh:mm:ss A",
+	"Model": "Modell",
+	"Model '{{modelName}}' has been successfully downloaded.": "A '{{modelName}}' modell sikeresen letöltve.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "A '{{modelTag}}' modell már a letöltési sorban van.",
+	"Model {{modelId}} not found": "A {{modelId}} modell nem található",
+	"Model {{modelName}} is not vision capable": "A {{modelName}} modell nem képes képfeldolgozásra",
+	"Model {{name}} is now {{status}}": "A {{name}} modell most {{status}} állapotban van",
+	"Model accepts image inputs": "A modell elfogad képbemenetet",
+	"Model created successfully!": "Modell sikeresen létrehozva!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modell fájlrendszer útvonal észlelve. A modell rövid neve szükséges a frissítéshez, nem folytatható.",
+	"Model Filtering": "",
+	"Model ID": "Modell azonosító",
+	"Model IDs": "",
+	"Model Name": "Modell neve",
+	"Model not selected": "Nincs kiválasztva modell",
+	"Model Params": "Modell paraméterek",
+	"Model Permissions": "",
+	"Model updated successfully": "Modell sikeresen frissítve",
+	"Modelfile Content": "Modellfájl tartalom",
+	"Models": "Modellek",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "több",
+	"More": "Több",
+	"Name": "Név",
+	"Name your knowledge base": "",
+	"New Chat": "Új beszélgetés",
+	"New folder": "Új mappa",
+	"New Password": "Új jelszó",
+	"No content found": "Nem található tartalom",
+	"No content to speak": "Nincs felolvasható tartalom",
+	"No distance available": "Nincs elérhető távolság",
+	"No feedbacks found": "Nem található visszajelzés",
+	"No file selected": "Nincs kiválasztva fájl",
+	"No files found.": "Nem található fájl.",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "Nem található HTML, CSS vagy JavaScript tartalom.",
+	"No knowledge found": "Nem található tudásbázis",
+	"No model IDs": "",
+	"No models found": "Nem található modell",
+	"No models selected": "",
+	"No results found": "Nincs találat",
+	"No search query generated": "Nem generálódott keresési lekérdezés",
+	"No source available": "Nincs elérhető forrás",
+	"No users were found.": "",
+	"No valves to update": "Nincs frissítendő szelep",
+	"None": "Nincs",
+	"Not factually correct": "Tényszerűen nem helyes",
+	"Not helpful": "Nem segítőkész",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Megjegyzés: Ha minimum pontszámot állít be, a keresés csak olyan dokumentumokat ad vissza, amelyek pontszáma nagyobb vagy egyenlő a minimum pontszámmal.",
+	"Notes": "Jegyzetek",
+	"Notifications": "Értesítések",
+	"November": "November",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth azonosító",
+	"October": "Október",
+	"Off": "Ki",
+	"Okay, Let's Go!": "Rendben, kezdjük!",
+	"OLED Dark": "OLED sötét",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API letiltva",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama verzió",
+	"On": "Be",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Csak alfanumerikus karakterek és kötőjelek engedélyezettek a parancssorban.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Csak gyűjtemények szerkeszthetők, hozzon létre új tudásbázist dokumentumok szerkesztéséhez/hozzáadásához.",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppá! Úgy tűnik, az URL érvénytelen. Kérjük, ellenőrizze és próbálja újra.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Hoppá! Még vannak feltöltés alatt álló fájlok. Kérjük, várja meg a feltöltés befejezését.",
+	"Oops! There was an error in the previous response.": "Hoppá! Hiba történt az előző válaszban.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hoppá! Nem támogatott módszert használ (csak frontend). Kérjük, szolgálja ki a WebUI-t a backend-ről.",
+	"Open file": "Fájl megnyitása",
+	"Open in full screen": "Megnyitás teljes képernyőn",
+	"Open new chat": "Új beszélgetés megnyitása",
+	"Open WebUI uses faster-whisper internally.": "Az Open WebUI belsőleg a faster-whispert használja.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Az Open WebUI verzió (v{{OPEN_WEBUI_VERSION}}) alacsonyabb, mint a szükséges verzió (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API konfiguráció",
+	"OpenAI API Key is required.": "OpenAI API kulcs szükséges.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/kulcs szükséges.",
+	"or": "vagy",
+	"Organize your users": "",
+	"Other": "Egyéb",
+	"OUTPUT": "KIMENET",
+	"Output format": "Kimeneti formátum",
+	"Overview": "Áttekintés",
+	"page": "oldal",
+	"Password": "Jelszó",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF dokumentum (.pdf)",
+	"PDF Extract Images (OCR)": "PDF képek kinyerése (OCR)",
+	"pending": "függőben",
+	"Permission denied when accessing media devices": "Hozzáférés megtagadva a médiaeszközökhöz",
+	"Permission denied when accessing microphone": "Hozzáférés megtagadva a mikrofonhoz",
+	"Permission denied when accessing microphone: {{error}}": "Hozzáférés megtagadva a mikrofonhoz: {{error}}",
+	"Permissions": "",
+	"Personalization": "Személyre szabás",
+	"Pin": "Rögzítés",
+	"Pinned": "Rögzítve",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Folyamat sikeresen törölve",
+	"Pipeline downloaded successfully": "Folyamat sikeresen letöltve",
+	"Pipelines": "Folyamatok",
+	"Pipelines Not Detected": "Folyamatok nem észlelhetők",
+	"Pipelines Valves": "Folyamat szelepek",
+	"Plain text (.txt)": "Egyszerű szöveg (.txt)",
+	"Playground": "Játszótér",
+	"Please carefully review the following warnings:": "Kérjük, gondosan tekintse át a következő figyelmeztetéseket:",
+	"Please enter a prompt": "Kérjük, adjon meg egy promptot",
+	"Please fill in all fields.": "Kérjük, töltse ki az összes mezőt.",
+	"Please select a model first.": "",
+	"Please select a reason": "Kérjük, válasszon egy okot",
+	"Port": "",
+	"Positive attitude": "Pozitív hozzáállás",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Előző 30 nap",
+	"Previous 7 days": "Előző 7 nap",
+	"Profile Image": "Profilkép",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (pl. Mondj egy érdekes tényt a Római Birodalomról)",
+	"Prompt Content": "Prompt tartalom",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Prompt javaslatok",
+	"Prompt updated successfully": "",
+	"Prompts": "Promptok",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "\"{{searchValue}}\" letöltése az Ollama.com-ról",
+	"Pull a model from Ollama.com": "Modell letöltése az Ollama.com-ról",
+	"Query Generation Prompt": "",
+	"Query Params": "Lekérdezési paraméterek",
+	"RAG Template": "RAG sablon",
+	"Rating": "Értékelés",
+	"Re-rank models by topic similarity": "Modellek újrarangsorolása téma hasonlóság alapján",
+	"Read Aloud": "Felolvasás",
+	"Record voice": "Hang rögzítése",
+	"Redirecting you to OpenWebUI Community": "Átirányítás az OpenWebUI közösséghez",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Hivatkozzon magára \"Felhasználó\"-ként (pl. \"A Felhasználó spanyolul tanul\")",
+	"References from": "Hivatkozások innen",
+	"Refused when it shouldn't have": "Elutasítva, amikor nem kellett volna",
+	"Regenerate": "Újragenerálás",
+	"Release Notes": "Kiadási jegyzetek",
+	"Relevance": "Relevancia",
+	"Remove": "Eltávolítás",
+	"Remove Model": "Modell eltávolítása",
+	"Rename": "Átnevezés",
+	"Reorder Models": "",
+	"Repeat Last N": "Utolsó N ismétlése",
+	"Request Mode": "Kérési mód",
+	"Reranking Model": "Újrarangsoroló modell",
+	"Reranking model disabled": "Újrarangsoroló modell letiltva",
+	"Reranking model set to \"{{reranking_model}}\"": "Újrarangsoroló modell beállítva erre: \"{{reranking_model}}\"",
+	"Reset": "Visszaállítás",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Feltöltési könyvtár visszaállítása",
+	"Reset Vector Storage/Knowledge": "Vektor tárhely/tudásbázis visszaállítása",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "A válasz értesítések nem aktiválhatók, mert a weboldal engedélyei meg lettek tagadva. Kérjük, látogasson el a böngésző beállításaihoz a szükséges hozzáférés megadásához.",
+	"Response splitting": "Válasz felosztás",
+	"Result": "Eredmény",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Formázott szövegbevitel a chathez",
+	"RK": "RK",
+	"Role": "Szerep",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Futtatás",
+	"Running": "Fut",
+	"Save": "Mentés",
+	"Save & Create": "Mentés és létrehozás",
+	"Save & Update": "Mentés és frissítés",
+	"Save As Copy": "Mentés másolatként",
+	"Save Tag": "Címke mentése",
+	"Saved": "Mentve",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "A csevegési naplók közvetlen mentése a böngésző tárolójába már nem támogatott. Kérjük, szánjon egy percet a csevegési naplók letöltésére és törlésére az alábbi gomb megnyomásával. Ne aggódjon, könnyen újra importálhatja a csevegési naplókat a backend-be",
+	"Scroll to bottom when switching between branches": "Görgetés az aljára ágak közötti váltáskor",
+	"Search": "Keresés",
+	"Search a model": "Modell keresése",
+	"Search Base": "",
+	"Search Chats": "Beszélgetések keresése",
+	"Search Collection": "Gyűjtemény keresése",
+	"Search Filters": "",
+	"search for tags": "címkék keresése",
+	"Search Functions": "Funkciók keresése",
+	"Search Knowledge": "Tudásbázis keresése",
+	"Search Models": "Modellek keresése",
+	"Search options": "",
+	"Search Prompts": "Promptok keresése",
+	"Search Result Count": "Keresési találatok száma",
+	"Search the web": "",
+	"Search Tools": "Eszközök keresése",
+	"SearchApi API Key": "SearchApi API kulcs",
+	"SearchApi Engine": "SearchApi motor",
+	"Searched {{count}} sites_one": "{{count}} oldal keresve",
+	"Searched {{count}} sites_other": "{{count}} oldal keresve",
+	"Searching \"{{searchQuery}}\"": "Keresés: \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Tudásbázis keresése: \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng lekérdezési URL",
+	"See readme.md for instructions": "Lásd a readme.md fájlt az útmutatásért",
+	"See what's new": "Újdonságok megtekintése",
+	"Seed": "Seed",
+	"Select a base model": "Válasszon egy alapmodellt",
+	"Select a engine": "Válasszon egy motort",
+	"Select a function": "Válasszon egy funkciót",
+	"Select a group": "",
+	"Select a model": "Válasszon egy modellt",
+	"Select a pipeline": "Válasszon egy folyamatot",
+	"Select a pipeline url": "Válasszon egy folyamat URL-t",
+	"Select a tool": "Válasszon egy eszközt",
+	"Select Engine": "Motor kiválasztása",
+	"Select Knowledge": "Tudásbázis kiválasztása",
+	"Select model": "Modell kiválasztása",
+	"Select only one model to call": "Csak egy modellt válasszon ki hívásra",
+	"Selected model(s) do not support image inputs": "A kiválasztott modell(ek) nem támogatják a képbemenetet",
+	"Semantic distance to query": "Szemantikai távolság a lekérdezéshez",
+	"Send": "Küldés",
+	"Send a Message": "Üzenet küldése",
+	"Send message": "Üzenet küldése",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "A kérésben elküldi a `stream_options: { include_usage: true }` opciót.\nA támogatott szolgáltatók token használati információt küldenek vissza a válaszban, ha be van állítva.",
+	"September": "Szeptember",
+	"Serper API Key": "Serper API kulcs",
+	"Serply API Key": "Serply API kulcs",
+	"Serpstack API Key": "Serpstack API kulcs",
+	"Server connection verified": "Szerverkapcsolat ellenőrizve",
+	"Set as default": "Beállítás alapértelmezettként",
+	"Set CFG Scale": "CFG skála beállítása",
+	"Set Default Model": "Alapértelmezett modell beállítása",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Beágyazási modell beállítása (pl. {{model}})",
+	"Set Image Size": "Képméret beállítása",
+	"Set reranking model (e.g. {{model}})": "Újrarangsoroló modell beállítása (pl. {{model}})",
+	"Set Sampler": "Mintavételező beállítása",
+	"Set Scheduler": "Ütemező beállítása",
+	"Set Steps": "Lépések beállítása",
+	"Set Task Model": "Feladat modell beállítása",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Hang beállítása",
+	"Set whisper model": "Whisper modell beállítása",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Beállítások",
+	"Settings saved successfully!": "Beállítások sikeresen mentve!",
+	"Share": "Megosztás",
+	"Share Chat": "Beszélgetés megosztása",
+	"Share to OpenWebUI Community": "Megosztás az OpenWebUI közösséggel",
+	"Show": "Mutat",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Admin részletek megjelenítése a függő fiók átfedésben",
+	"Show shortcuts": "Gyorsbillentyűk megjelenítése",
+	"Show your support!": "Mutassa meg támogatását!",
+	"Showcased creativity": "Kreativitás bemutatva",
+	"Sign in": "Bejelentkezés",
+	"Sign in to {{WEBUI_NAME}}": "Bejelentkezés ide: {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Kijelentkezés",
+	"Sign up": "Regisztráció",
+	"Sign up to {{WEBUI_NAME}}": "Regisztráció ide: {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Bejelentkezés ide: {{WEBUI_NAME}}",
+	"Source": "Forrás",
+	"Speech Playback Speed": "Beszéd lejátszási sebesség",
+	"Speech recognition error: {{error}}": "Beszédfelismerési hiba: {{error}}",
+	"Speech-to-Text Engine": "Beszéd-szöveg motor",
+	"Stop": "Leállítás",
+	"Stop Sequence": "Leállítási szekvencia",
+	"Stream Chat Response": "Chat válasz streamelése",
+	"STT Model": "STT modell",
+	"STT Settings": "STT beállítások",
+	"Subtitle (e.g. about the Roman Empire)": "Alcím (pl. a Római Birodalomról)",
+	"Success": "Siker",
+	"Successfully updated.": "Sikeresen frissítve.",
+	"Suggested": "Javasolt",
+	"Support": "Támogatás",
+	"Support this plugin:": "Támogassa ezt a bővítményt:",
+	"Sync directory": "Könyvtár szinkronizálása",
+	"System": "Rendszer",
+	"System Instructions": "Rendszer utasítások",
+	"System Prompt": "Rendszer prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Címke generálási prompt",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Koppintson a megszakításhoz",
+	"Tavily API Key": "Tavily API kulcs",
+	"Tell us more:": "Mondjon többet:",
+	"Temperature": "Hőmérséklet",
+	"Template": "Sablon",
+	"Temporary Chat": "Ideiglenes chat",
+	"Text Splitter": "Szöveg felosztó",
+	"Text-to-Speech Engine": "Szöveg-beszéd motor",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Köszönjük a visszajelzést!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "A bővítmény fejlesztői lelkes önkéntesek a közösségből. Ha hasznosnak találja ezt a bővítményt, kérjük, fontolja meg a fejlesztéséhez való hozzájárulást.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Az értékelési ranglista az Elo értékelési rendszeren alapul és valós időben frissül.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "A ranglista jelenleg béta verzióban van, és az algoritmus finomítása során módosíthatjuk az értékelési számításokat.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "A maximális fájlméret MB-ban. Ha a fájlméret meghaladja ezt a limitet, a fájl nem lesz feltöltve.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "A chatben egyszerre használható fájlok maximális száma. Ha a fájlok száma meghaladja ezt a limitet, a fájlok nem lesznek feltöltve.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "A pontszámnak 0,0 (0%) és 1,0 (100%) közötti értéknek kell lennie.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Téma",
+	"Thinking...": "Gondolkodik...",
+	"This action cannot be undone. Do you wish to continue?": "Ez a művelet nem vonható vissza. Szeretné folytatni?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ez biztosítja, hogy értékes beszélgetései biztonságosan mentésre kerüljenek a backend adatbázisban. Köszönjük!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Ez egy kísérleti funkció, lehet, hogy nem a várt módon működik és bármikor változhat.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Ez az opció törli az összes meglévő fájlt a gyűjteményben és lecseréli őket az újonnan feltöltött fájlokkal.",
+	"This response was generated by \"{{model}}\"": "Ezt a választ a \"{{model}}\" generálta",
+	"This will delete": "Ez törölni fogja",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Ez törölni fogja a <strong>{{NAME}}</strong>-t és <strong>minden tartalmát</strong>.",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Ez visszaállítja a tudásbázist és szinkronizálja az összes fájlt. Szeretné folytatni?",
+	"Thorough explanation": "Alapos magyarázat",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika szerver URL szükséges.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tipp: Frissítsen több változó helyet egymás után a tab billentyű megnyomásával a chat bevitelben minden helyettesítés után.",
+	"Title": "Cím",
+	"Title (e.g. Tell me a fun fact)": "Cím (pl. Mondj egy érdekes tényt)",
+	"Title Auto-Generation": "Cím automatikus generálása",
+	"Title cannot be an empty string.": "A cím nem lehet üres karakterlánc.",
+	"Title Generation Prompt": "Cím generálási prompt",
+	"TLS": "",
+	"To access the available model names for downloading,": "A letölthető modellek nevének eléréséhez,",
+	"To access the GGUF models available for downloading,": "A letölthető GGUF modellek eléréséhez,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "A WebUI eléréséhez kérjük, forduljon az adminisztrátorhoz. Az adminisztrátorok az Admin Panelen keresztül kezelhetik a felhasználói státuszokat.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "A tudásbázis csatolásához először adja hozzá őket a \"Knowledge\" munkaterülethez.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Adatai védelme érdekében a visszajelzésből csak az értékelések, modell azonosítók, címkék és metaadatok kerülnek megosztásra - a chat előzményei privátak maradnak és nem kerülnek megosztásra.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "A műveletek kiválasztásához először adja hozzá őket a \"Functions\" munkaterülethez.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "A szűrők kiválasztásához először adja hozzá őket a \"Functions\" munkaterülethez.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Az eszközkészletek kiválasztásához először adja hozzá őket a \"Tools\" munkaterülethez.",
+	"Toast notifications for new updates": "Felugró értesítések az új frissítésekről",
+	"Today": "Ma",
+	"Toggle settings": "Beállítások be/ki",
+	"Toggle sidebar": "Oldalsáv be/ki",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Megőrzendő tokenek kontextus frissítéskor (num_keep)",
+	"Too verbose": "Túl bőbeszédű",
+	"Tool created successfully": "Eszköz sikeresen létrehozva",
+	"Tool deleted successfully": "Eszköz sikeresen törölve",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Eszköz sikeresen importálva",
+	"Tool Name": "",
+	"Tool updated successfully": "Eszköz sikeresen frissítve",
+	"Tools": "Eszközök",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Az eszközök olyan függvényhívó rendszert alkotnak, amely tetszőleges kód végrehajtását teszi lehetővé",
+	"Tools have a function calling system that allows arbitrary code execution": "Az eszközök olyan függvényhívó rendszerrel rendelkeznek, amely lehetővé teszi tetszőleges kód végrehajtását",
+	"Tools have a function calling system that allows arbitrary code execution.": "Az eszközök olyan függvényhívó rendszerrel rendelkeznek, amely lehetővé teszi tetszőleges kód végrehajtását.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problémája van az Ollama elérésével?",
+	"TTS Model": "TTS modell",
+	"TTS Settings": "TTS beállítások",
+	"TTS Voice": "TTS hang",
+	"Type": "Típus",
+	"Type Hugging Face Resolve (Download) URL": "Adja meg a Hugging Face Resolve (Letöltési) URL-t",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Hoppá! Probléma merült fel a {{provider}} kapcsolódás során.",
+	"UI": "Felhasználói felület",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Rögzítés feloldása",
+	"Unravel secrets": "",
+	"Untagged": "Címkézetlen",
+	"Update": "Frissítés",
+	"Update and Copy Link": "Frissítés és link másolása",
+	"Update for the latest features and improvements.": "Frissítsen a legújabb funkciókért és fejlesztésekért.",
+	"Update password": "Jelszó frissítése",
+	"Updated": "Frissítve",
+	"Updated at": "Frissítve ekkor",
+	"Updated At": "Frissítve ekkor",
+	"Upload": "Feltöltés",
+	"Upload a GGUF model": "GGUF modell feltöltése",
+	"Upload directory": "Könyvtár feltöltése",
+	"Upload files": "Fájlok feltöltése",
+	"Upload Files": "Fájlok feltöltése",
+	"Upload Pipeline": "Pipeline feltöltése",
+	"Upload Progress": "Feltöltési folyamat",
+	"URL": "",
+	"URL Mode": "URL mód",
+	"Use '#' in the prompt input to load and include your knowledge.": "Használja a '#' karaktert a prompt bevitelénél a tudásbázis betöltéséhez és felhasználásához.",
+	"Use Gravatar": "Gravatar használata",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Monogram használata",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "felhasználó",
+	"User": "Felhasználó",
+	"User location successfully retrieved.": "Felhasználó helye sikeresen lekérve.",
+	"Username": "",
+	"Users": "Felhasználók",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Az alapértelmezett aréna modell használata az összes modellel. Kattintson a plusz gombra egyéni modellek hozzáadásához.",
+	"Utilize": "Használat",
+	"Valid time units:": "Érvényes időegységek:",
+	"Valves": "Szelepek",
+	"Valves updated": "Szelepek frissítve",
+	"Valves updated successfully": "Szelepek sikeresen frissítve",
+	"variable": "változó",
+	"variable to have them replaced with clipboard content.": "változó, hogy a vágólap tartalmával helyettesítse őket.",
+	"Version": "Verzió",
+	"Version {{selectedVersion}} of {{totalVersions}}": "{{selectedVersion}}. verzió a {{totalVersions}}-ból",
+	"Visibility": "",
+	"Voice": "Hang",
+	"Voice Input": "Hangbevitel",
+	"Warning": "Figyelmeztetés",
+	"Warning:": "Figyelmeztetés:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Figyelmeztetés: Ha frissíti vagy megváltoztatja a beágyazási modellt, minden dokumentumot újra kell importálnia.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Web betöltő beállítások",
+	"Web Search": "Webes keresés",
+	"Web Search Engine": "Webes keresőmotor",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI beállítások",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (helyi)",
+	"Why?": "",
+	"Widescreen Mode": "Szélesvásznú mód",
+	"Won": "Nyert",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Munkaterület",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Írjon egy prompt javaslatot (pl. Ki vagy te?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Írjon egy 50 szavas összefoglalót a [téma vagy kulcsszó]-ról.",
+	"Write something...": "Írjon valamit...",
+	"Write your model template content here": "",
+	"Yesterday": "Tegnap",
+	"You": "Ön",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Egyszerre maximum {{maxCount}} fájllal tud csevegni.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Az LLM-ekkel való interakcióit személyre szabhatja emlékek hozzáadásával a lenti 'Kezelés' gomb segítségével, így azok még hasznosabbak és személyre szabottabbak lesznek.",
+	"You cannot upload an empty file.": "Nem tölthet fel üres fájlt.",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Nincsenek archivált beszélgetései.",
+	"You have shared this chat": "Megosztotta ezt a beszélgetést",
+	"You're a helpful assistant.": "Ön egy segítőkész asszisztens.",
+	"You're now logged in.": "Sikeresen bejelentkezett.",
+	"Your account status is currently pending activation.": "Fiókja jelenleg aktiválásra vár.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "A teljes hozzájárulása közvetlenül a bővítmény fejlesztőjéhez kerül; az Open WebUI nem vesz le százalékot. Azonban a választott támogatási platformnak lehetnek saját díjai.",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube betöltő beállítások"
+}
diff --git a/src/lib/i18n/locales/id-ID/translation.json b/src/lib/i18n/locales/id-ID/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..88dd99421fc916864418404a1e92ef2fa0a2199a
--- /dev/null
+++ b/src/lib/i18n/locales/id-ID/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' atau '-1' untuk tidak ada kedaluwarsa.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(contoh: `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(contoh: `sh webui.sh --api`)",
+	"(latest)": "(terbaru)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Obrolan {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Diperlukan Backend",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model tugas digunakan saat melakukan tugas seperti membuat judul untuk obrolan dan kueri penelusuran web",
+	"a user": "seorang pengguna",
+	"About": "Tentang",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Akun",
+	"Account Activation Pending": "Aktivasi Akun Tertunda",
+	"Accurate information": "Informasi yang akurat",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Pengguna Aktif",
+	"Add": "Tambah",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Tambahkan deskripsi singkat tentang apa yang dilakukan model ini",
+	"Add a tag": "Menambahkan tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Tambahkan prompt khusus",
+	"Add Files": "Menambahkan File",
+	"Add Group": "",
+	"Add Memory": "Menambahkan Memori",
+	"Add Model": "Tambahkan Model",
+	"Add Tag": "",
+	"Add Tags": "Tambahkan Tag",
+	"Add text content": "",
+	"Add User": "Tambah Pengguna",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Menyesuaikan pengaturan ini akan menerapkan perubahan secara universal ke semua pengguna.",
+	"admin": "admin",
+	"Admin": "Admin",
+	"Admin Panel": "Panel Admin",
+	"Admin Settings": "Pengaturan Admin",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Admin memiliki akses ke semua alat setiap saat; pengguna memerlukan alat yang ditetapkan per model di ruang kerja.",
+	"Advanced Parameters": "Parameter Lanjutan",
+	"Advanced Params": "Parameter Lanjutan",
+	"All chats": "",
+	"All Documents": "Semua Dokumen",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Izinkan Penghapusan Obrolan",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Izinkan suara non-lokal",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "Izinkan Lokasi Pengguna",
+	"Allow Voice Interruption in Call": "Izinkan Gangguan Suara dalam Panggilan",
+	"Already have an account?": "Sudah memiliki akun?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "asisten",
+	"and": "dan",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "dan membuat tautan bersama baru.",
+	"API Base URL": "URL Dasar API",
+	"API Key": "Kunci API",
+	"API Key created.": "Kunci API dibuat.",
+	"API keys": "Kunci API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "April",
+	"Archive": "Arsipkan",
+	"Archive All Chats": "Arsipkan Semua Obrolan",
+	"Archived Chats": "Obrolan yang Diarsipkan",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Apakah Anda yakin?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Lampirkan file",
+	"Attention to detail": "Perhatian terhadap detail",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Agustus",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Tanggapan Salin Otomatis ke Papan Klip",
+	"Auto-playback response": "Respons pemutaran otomatis",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "URL Dasar AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 URL Dasar diperlukan.",
+	"Available list": "",
+	"available!": "tersedia!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Kembali",
+	"Bad Response": "Respons Buruk",
+	"Banners": "Spanduk",
+	"Base Model (From)": "Model Dasar (Dari)",
+	"Batch Size (num_batch)": "Ukuran Batch (num_batch)",
+	"before": "sebelum",
+	"Being lazy": "Menjadi malas",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Kunci API Pencarian Berani",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Lewati verifikasi SSL untuk Situs Web",
+	"Call": "Panggilan",
+	"Call feature is not supported when using Web STT engine": "Fitur panggilan tidak didukung saat menggunakan mesin Web STT",
+	"Camera": "Kamera",
+	"Cancel": "Batal",
+	"Capabilities": "Kemampuan",
+	"Certificate Path": "",
+	"Change Password": "Ubah Kata Sandi",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Obrolan",
+	"Chat Background Image": "Gambar Latar Belakang Obrolan",
+	"Chat Bubble UI": "UI Gelembung Obrolan",
+	"Chat Controls": "",
+	"Chat direction": "Arah obrolan",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Obrolan",
+	"Check Again": "Periksa Lagi",
+	"Check for updates": "Memeriksa pembaruan",
+	"Checking for updates...": "Memeriksa pembaruan...",
+	"Choose a model before saving...": "Pilih model sebelum menyimpan...",
+	"Chunk Overlap": "Tumpang Tindih Potongan",
+	"Chunk Params": "Parameter Potongan",
+	"Chunk Size": "Ukuran Potongan",
+	"Ciphers": "",
+	"Citation": "Kutipan",
+	"Clear memory": "Menghapus memori",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klik di sini untuk bantuan.",
+	"Click here to": "Klik di sini untuk",
+	"Click here to download user import template file.": "Klik di sini untuk mengunduh file templat impor pengguna.",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Klik di sini untuk memilih",
+	"Click here to select a csv file.": "Klik di sini untuk memilih file csv.",
+	"Click here to select a py file.": "Klik di sini untuk memilih file py.",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "Klik di sini.",
+	"Click on the user role button to change a user's role.": "Klik tombol peran pengguna untuk mengubah peran pengguna.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Izin menulis papan klip ditolak. Periksa pengaturan peramban Anda untuk memberikan akses yang diperlukan.",
+	"Clone": "Kloning",
+	"Close": "Tutup",
+	"Code execution": "",
+	"Code formatted successfully": "Kode berhasil diformat",
+	"Collection": "Koleksi",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL Dasar ComfyUI",
+	"ComfyUI Base URL is required.": "URL Dasar ComfyUI diperlukan.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Perintah",
+	"Completions": "",
+	"Concurrent Requests": "Permintaan Bersamaan",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Konfirmasi",
+	"Confirm Password": "Konfirmasi Kata Sandi",
+	"Confirm your action": "Konfirmasi tindakan Anda",
+	"Connections": "Koneksi",
+	"Contact Admin for WebUI Access": "Hubungi Admin untuk Akses WebUI",
+	"Content": "Konten",
+	"Content Extraction": "",
+	"Context Length": "Panjang Konteks",
+	"Continue Response": "Lanjutkan Tanggapan",
+	"Continue with {{provider}}": "Lanjutkan dengan {{penyedia}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Menyalin URL obrolan bersama ke papan klip!",
+	"Copied to clipboard": "",
+	"Copy": "Menyalin",
+	"Copy last code block": "Salin blok kode terakhir",
+	"Copy last response": "Salin tanggapan terakhir",
+	"Copy Link": "Salin Tautan",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Penyalinan ke papan klip berhasil!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Buat model",
+	"Create Account": "Buat Akun",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Buat kunci baru",
+	"Create new secret key": "Buat kunci rahasia baru",
+	"Created at": "Dibuat di",
+	"Created At": "Dibuat di",
+	"Created by": "Dibuat oleh",
+	"CSV Import": "Impor CSV",
+	"Current Model": "Model Saat Ini",
+	"Current Password": "Kata Sandi Saat Ini",
+	"Custom": "Kustom",
+	"Dark": "Gelap",
+	"Database": "Basis data",
+	"December": "Desember",
+	"Default": "Default",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Default (Pengubah Kalimat)",
+	"Default Model": "Model Default",
+	"Default model updated": "Model default diperbarui",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Saran Permintaan Default",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Peran Pengguna Default",
+	"Delete": "Menghapus",
+	"Delete a model": "Menghapus model",
+	"Delete All Chats": "Menghapus Semua Obrolan",
+	"Delete All Models": "",
+	"Delete chat": "Menghapus obrolan",
+	"Delete Chat": "Menghapus Obrolan",
+	"Delete chat?": "Menghapus obrolan?",
+	"Delete folder?": "",
+	"Delete function?": "Fungsi hapus?",
+	"Delete prompt?": "Perintah hapus?",
+	"delete this link": "hapus tautan ini",
+	"Delete tool?": "Hapus alat?",
+	"Delete User": "Menghapus Pengguna",
+	"Deleted {{deleteModelTag}}": "Menghapus {{deleteModelTag}}",
+	"Deleted {{name}}": "Menghapus {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Deskripsi",
+	"Didn't fully follow instructions": "Tidak sepenuhnya mengikuti instruksi",
+	"Disabled": "",
+	"Discover a function": "Menemukan sebuah fungsi",
+	"Discover a model": "Menemukan sebuah model",
+	"Discover a prompt": "Temukan petunjuk",
+	"Discover a tool": "Menemukan alat",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Menemukan, mengunduh, dan menjelajahi fungsi khusus",
+	"Discover, download, and explore custom prompts": "Temukan, unduh, dan jelajahi prompt khusus",
+	"Discover, download, and explore custom tools": "Menemukan, mengunduh, dan menjelajahi alat khusus",
+	"Discover, download, and explore model presets": "Menemukan, mengunduh, dan menjelajahi preset model",
+	"Dismissible": "Tidak dapat digunakan",
+	"Display": "",
+	"Display Emoji in Call": "Menampilkan Emoji dalam Panggilan",
+	"Display the username instead of You in the Chat": "Menampilkan nama pengguna, bukan Anda di Obrolan",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Dokumen",
+	"Documentation": "Dokumentasi",
+	"Documents": "Dokumen",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "tidak membuat koneksi eksternal apa pun, dan data Anda tetap aman di server yang dihosting secara lokal.",
+	"Don't have an account?": "Tidak memiliki akun?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Tidak suka gayanya",
+	"Done": "Selesai",
+	"Download": "Unduh",
+	"Download canceled": "Unduh dibatalkan",
+	"Download Database": "Unduh Basis Data",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Letakkan file apa pun di sini untuk ditambahkan ke percakapan",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "misalnya '30-an', '10m'. Satuan waktu yang valid adalah 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Edit",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Edit Memori",
+	"Edit User": "Edit Pengguna",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Menyematkan Ukuran Batch",
+	"Embedding Model": "Model Penyematan",
+	"Embedding Model Engine": "Mesin Model Penyematan",
+	"Embedding model set to \"{{embedding_model}}\"": "Model penyematan diatur ke \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Aktifkan Berbagi Komunitas",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Aktifkan Pendaftaran Baru",
+	"Enable Web Search": "Aktifkan Pencarian Web",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Pastikan file CSV Anda menyertakan 4 kolom dengan urutan sebagai berikut: Nama, Email, Kata Sandi, Peran.",
+	"Enter {{role}} message here": "Masukkan pesan {{role}} di sini",
+	"Enter a detail about yourself for your LLMs to recall": "Masukkan detail tentang diri Anda untuk diingat oleh LLM Anda",
+	"Enter api auth string (e.g. username:password)": "Masukkan string pengesahan API (misalnya nama pengguna: kata sandi)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Masukkan Kunci API Pencarian Berani",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Masukkan Tumpang Tindih Chunk",
+	"Enter Chunk Size": "Masukkan Ukuran Potongan",
+	"Enter description": "",
+	"Enter Github Raw URL": "Masukkan URL Mentah Github",
+	"Enter Google PSE API Key": "Masukkan Kunci API Google PSE",
+	"Enter Google PSE Engine Id": "Masukkan Id Mesin Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Masukkan Ukuran Gambar (mis. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Masukkan kode bahasa",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Masukkan tag model (misalnya {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Masukkan Jumlah Langkah (mis. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Masukkan Skor",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Masukkan URL Kueri Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Masukkan Kunci API Serper",
+	"Enter Serply API Key": "Masukkan Kunci API Serply",
+	"Enter Serpstack API Key": "Masukkan Kunci API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Masukkan urutan berhenti",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "Masukkan Kunci API Tavily",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Masukkan Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Masukkan URL (mis. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Masukkan URL (mis. http://localhost:11434)",
+	"Enter Your Email": "Masukkan Email Anda",
+	"Enter Your Full Name": "Masukkan Nama Lengkap Anda",
+	"Enter your message": "",
+	"Enter Your Password": "Masukkan Kata Sandi Anda",
+	"Enter Your Role": "Masukkan Peran Anda",
+	"Enter Your Username": "",
+	"Error": "Kesalahan",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Percobaan",
+	"Explore the cosmos": "",
+	"Export": "Ekspor",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Ekspor Semua Obrolan (Semua Pengguna)",
+	"Export chat (.json)": "Ekspor obrolan (.json)",
+	"Export Chats": "Ekspor Obrolan",
+	"Export Config to JSON File": "",
+	"Export Functions": "Fungsi Ekspor",
+	"Export Models": "Model Ekspor",
+	"Export Presets": "",
+	"Export Prompts": "Perintah Ekspor",
+	"Export to CSV": "",
+	"Export Tools": "Alat Ekspor",
+	"External Models": "Model Eksternal",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Gagal membuat API Key.",
+	"Failed to read clipboard contents": "Gagal membaca konten papan klip",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Gagal memperbarui pengaturan",
+	"Failed to upload file.": "",
+	"February": "Februari",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Jangan ragu untuk menambahkan detail spesifik",
+	"File": "Berkas",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Mode File",
+	"File not found.": "File tidak ditemukan.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "Filter sekarang dinonaktifkan secara global",
+	"Filter is now globally enabled": "Filter sekarang diaktifkan secara global",
+	"Filters": "Filter",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Pemalsuan sidik jari terdeteksi: Tidak dapat menggunakan inisial sebagai avatar. Default ke gambar profil default.",
+	"Fluidly stream large external response chunks": "Mengalirkan potongan respons eksternal yang besar dengan lancar",
+	"Focus chat input": "Memfokuskan input obrolan",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Mengikuti instruksi dengan sempurna",
+	"Forge new paths": "",
+	"Form": "Formulir",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Penalti Frekuensi",
+	"Function": "",
+	"Function created successfully": "Fungsi berhasil dibuat",
+	"Function deleted successfully": "Fungsi berhasil dihapus",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "Fungsi berhasil diperbarui",
+	"Functions": "Fungsi",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "Fungsi berhasil diimpor",
+	"General": "Umum",
+	"General Settings": "Pengaturan Umum",
+	"Generate Image": "Menghasilkan Gambar",
+	"Generating search query": "Membuat kueri penelusuran",
+	"Generation Info": "Info Pembuatan",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Global",
+	"Good Response": "Respons yang Baik",
+	"Google PSE API Key": "Kunci API Google PSE",
+	"Google PSE Engine Id": "Id Mesin Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "tidak memiliki percakapan.",
+	"Hello, {{name}}": "Halo, {{name}}",
+	"Help": "Bantuan",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Sembunyikan",
+	"Host": "",
+	"How can I help you today?": "Ada yang bisa saya bantu hari ini?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Pencarian Hibrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Pembuatan Gambar (Eksperimental)",
+	"Image Generation Engine": "Mesin Pembuat Gambar",
+	"Image Settings": "Pengaturan Gambar",
+	"Images": "Gambar",
+	"Import Chats": "Impor Obrolan",
+	"Import Config from JSON File": "",
+	"Import Functions": "Fungsi Impor",
+	"Import Models": "Model Impor",
+	"Import Presets": "",
+	"Import Prompts": "Petunjuk Impor",
+	"Import Tools": "Alat Impor",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Sertakan bendera `--api-auth` saat menjalankan stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Sertakan bendera `--api` saat menjalankan stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Info",
+	"Input commands": "Perintah masukan",
+	"Install from Github URL": "Instal dari URL Github",
+	"Instant Auto-Send After Voice Transcription": "Kirim Otomatis Instan Setelah Transkripsi Suara",
+	"Interface": "Antarmuka",
+	"Invalid file format.": "",
+	"Invalid Tag": "Tag tidak valid",
+	"January": "Januari",
+	"Jina API Key": "",
+	"join our Discord for help.": "bergabunglah dengan Discord kami untuk mendapatkan bantuan.",
+	"JSON": "JSON",
+	"JSON Preview": "Pratinjau JSON",
+	"July": "Juli",
+	"June": "Juni",
+	"JWT Expiration": "Kedaluwarsa JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Tetap Hidup",
+	"Key": "",
+	"Keyboard shortcuts": "Pintasan keyboard",
+	"Knowledge": "Pengetahuan",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Bahasa",
+	"Last Active": "Terakhir Aktif",
+	"Last Modified": "Terakhir Dimodifikasi",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Cahaya",
+	"Listening...": "Mendengarkan",
+	"LLMs can make mistakes. Verify important information.": "LLM dapat membuat kesalahan. Verifikasi informasi penting.",
+	"Local": "",
+	"Local Models": "Model Lokal",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Dibuat oleh Komunitas OpenWebUI",
+	"Make sure to enclose them with": "Pastikan untuk melampirkannya dengan",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Mengelola",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Mengelola Saluran Pipa",
+	"March": "Maret",
+	"Max Tokens (num_predict)": "Token Maksimal (num_prediksi)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimal 3 model dapat diunduh secara bersamaan. Silakan coba lagi nanti.",
+	"May": "Mei",
+	"Memories accessible by LLMs will be shown here.": "Memori yang dapat diakses oleh LLM akan ditampilkan di sini.",
+	"Memory": "Memori",
+	"Memory added successfully": "Memori berhasil ditambahkan",
+	"Memory cleared successfully": "Memori berhasil dihapus",
+	"Memory deleted successfully": "Memori berhasil dihapus",
+	"Memory updated successfully": "Memori berhasil diperbarui",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Pesan yang Anda kirim setelah membuat tautan tidak akan dibagikan. Pengguna yang memiliki URL tersebut akan dapat melihat obrolan yang dibagikan.",
+	"Min P": "",
+	"Minimum Score": "Skor Minimum",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH: mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY jj: mm: dd A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' telah berhasil diunduh.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' sudah berada dalam antrean untuk diunduh.",
+	"Model {{modelId}} not found": "Model {{modelId}} tidak ditemukan",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} tidak dapat dilihat",
+	"Model {{name}} is now {{status}}": "Model {{name}} sekarang menjadi {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "Model berhasil dibuat!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Jalur sistem berkas model terdeteksi. Nama pendek model diperlukan untuk pembaruan, tidak dapat dilanjutkan.",
+	"Model Filtering": "",
+	"Model ID": "ID Model",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model tidak dipilih",
+	"Model Params": "Parameter Model",
+	"Model Permissions": "",
+	"Model updated successfully": "Model berhasil diperbarui",
+	"Modelfile Content": "Konten File Model",
+	"Models": "Model",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Lainnya",
+	"Name": "Nama",
+	"Name your knowledge base": "",
+	"New Chat": "Obrolan Baru",
+	"New folder": "",
+	"New Password": "Kata Sandi Baru",
+	"No content found": "",
+	"No content to speak": "Tidak ada konten untuk dibicarakan",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Tidak ada file yang dipilih",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Tidak ada hasil yang ditemukan",
+	"No search query generated": "Tidak ada permintaan pencarian yang dibuat",
+	"No source available": "Tidak ada sumber yang tersedia",
+	"No users were found.": "",
+	"No valves to update": "Tidak ada katup untuk diperbarui",
+	"None": "Tidak ada",
+	"Not factually correct": "Tidak benar secara faktual",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Catatan: Jika Anda menetapkan skor minimum, pencarian hanya akan mengembalikan dokumen dengan skor yang lebih besar atau sama dengan skor minimum.",
+	"Notes": "",
+	"Notifications": "Pemberitahuan",
+	"November": "November",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Oktober",
+	"Off": "Mati",
+	"Okay, Let's Go!": "Oke, Ayo Kita Pergi!",
+	"OLED Dark": "OLED Gelap",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama dinonaktifkan",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versi Ollama",
+	"On": "Aktif",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Hanya karakter alfanumerik dan tanda hubung yang diizinkan dalam string perintah.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! Sepertinya URL tidak valid. Mohon periksa ulang dan coba lagi.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ups! Anda menggunakan metode yang tidak didukung (hanya untuk frontend). Silakan sajikan WebUI dari backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Buka obrolan baru",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Konfigurasi API OpenAI",
+	"OpenAI API Key is required.": "Diperlukan Kunci API OpenAI.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Diperlukan URL/Kunci OpenAI.",
+	"or": "atau",
+	"Organize your users": "",
+	"Other": "Lainnya",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Kata sandi",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Dokumen PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Ekstrak Gambar PDF (OCR)",
+	"pending": "tertunda",
+	"Permission denied when accessing media devices": "Izin ditolak saat mengakses perangkat media",
+	"Permission denied when accessing microphone": "Izin ditolak saat mengakses mikrofon",
+	"Permission denied when accessing microphone: {{error}}": "Izin ditolak saat mengakses mikrofon: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalisasi",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline berhasil dihapus",
+	"Pipeline downloaded successfully": "Saluran pipa berhasil diunduh",
+	"Pipelines": "Saluran pipa",
+	"Pipelines Not Detected": "Saluran Pipa Tidak Terdeteksi",
+	"Pipelines Valves": "Katup Saluran Pipa",
+	"Plain text (.txt)": "Teks biasa (.txt)",
+	"Playground": "Taman bermain",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Sikap positif",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 hari sebelumnya",
+	"Previous 7 days": "7 hari sebelumnya",
+	"Profile Image": "Gambar Profil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Permintaan (mis. Ceritakan sebuah fakta menarik tentang Kekaisaran Romawi)",
+	"Prompt Content": "Konten yang Diminta",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Saran yang diminta",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompt",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Tarik \"{{searchValue}}\" dari Ollama.com",
+	"Pull a model from Ollama.com": "Tarik model dari Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parameter Kueri",
+	"RAG Template": "Templat RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Baca dengan Keras",
+	"Record voice": "Rekam suara",
+	"Redirecting you to OpenWebUI Community": "Mengarahkan Anda ke Komunitas OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Merujuk diri Anda sebagai \"Pengguna\" (misalnya, \"Pengguna sedang belajar bahasa Spanyol\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Menolak ketika seharusnya tidak",
+	"Regenerate": "Regenerasi",
+	"Release Notes": "Catatan Rilis",
+	"Relevance": "",
+	"Remove": "Hapus",
+	"Remove Model": "Hapus Model",
+	"Rename": "Ganti nama",
+	"Reorder Models": "",
+	"Repeat Last N": "Ulangi N Terakhir",
+	"Request Mode": "Mode Permintaan",
+	"Reranking Model": "Model Pemeringkatan Ulang",
+	"Reranking model disabled": "Model pemeringkatan ulang dinonaktifkan",
+	"Reranking model set to \"{{reranking_model}}\"": "Model pemeringkatan diatur ke \"{{reranking_model}}\"",
+	"Reset": "Atur Ulang",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Setel Ulang Direktori Unggahan",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Notifikasi respons tidak dapat diaktifkan karena izin situs web telah ditolak. Silakan kunjungi pengaturan browser Anda untuk memberikan akses yang diperlukan.",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Peran",
+	"Rosé Pine": "Pinus Rosé",
+	"Rosé Pine Dawn": "Rosé Pine Fajar",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "Berjalan",
+	"Save": "Simpan",
+	"Save & Create": "Simpan & Buat",
+	"Save & Update": "Simpan & Perbarui",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Menyimpan log obrolan secara langsung ke penyimpanan browser Anda tidak lagi didukung. Mohon luangkan waktu sejenak untuk mengunduh dan menghapus log obrolan Anda dengan mengeklik tombol di bawah ini. Jangan khawatir, Anda dapat dengan mudah mengimpor kembali log obrolan Anda ke backend melalui",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Cari",
+	"Search a model": "Mencari model",
+	"Search Base": "",
+	"Search Chats": "Cari Obrolan",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Fungsi Pencarian",
+	"Search Knowledge": "",
+	"Search Models": "Cari Model",
+	"Search options": "",
+	"Search Prompts": "Perintah Pencarian",
+	"Search Result Count": "Jumlah Hasil Pencarian",
+	"Search the web": "",
+	"Search Tools": "Alat Pencarian",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Mencari {{count}} situs_satu",
+	"Searched {{count}} sites_other": "Mencari {{count}} situs_lain",
+	"Searching \"{{searchQuery}}\"": "Mencari \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL Kueri Pencarian Searxng",
+	"See readme.md for instructions": "Lihat readme.md untuk instruksi",
+	"See what's new": "Lihat apa yang baru",
+	"Seed": "Benih",
+	"Select a base model": "Pilih model dasar",
+	"Select a engine": "Pilih mesin",
+	"Select a function": "Memilih fungsi",
+	"Select a group": "",
+	"Select a model": "Pilih model",
+	"Select a pipeline": "Pilih saluran pipa",
+	"Select a pipeline url": "Pilih url saluran pipa",
+	"Select a tool": "Pilih alat",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Pilih model",
+	"Select only one model to call": "Pilih hanya satu model untuk dipanggil",
+	"Selected model(s) do not support image inputs": "Model yang dipilih tidak mendukung input gambar",
+	"Semantic distance to query": "",
+	"Send": "Kirim",
+	"Send a Message": "Kirim Pesan",
+	"Send message": "Kirim pesan",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "September",
+	"Serper API Key": "Kunci API Serper",
+	"Serply API Key": "Kunci API Serply",
+	"Serpstack API Key": "Kunci API Serpstack",
+	"Server connection verified": "Koneksi server diverifikasi",
+	"Set as default": "Ditetapkan sebagai default",
+	"Set CFG Scale": "",
+	"Set Default Model": "Tetapkan Model Default",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Tetapkan model penyematan (mis. {{model}})",
+	"Set Image Size": "Mengatur Ukuran Gambar",
+	"Set reranking model (e.g. {{model}})": "Tetapkan model pemeringkatan ulang (mis. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Tetapkan Langkah",
+	"Set Task Model": "Tetapkan Model Tugas",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Mengatur Suara",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Pengaturan",
+	"Settings saved successfully!": "Pengaturan berhasil disimpan!",
+	"Share": "Berbagi",
+	"Share Chat": "Bagikan Obrolan",
+	"Share to OpenWebUI Community": "Bagikan ke Komunitas OpenWebUI",
+	"Show": "Tampilkan",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Tampilkan Detail Admin di Hamparan Akun Tertunda",
+	"Show shortcuts": "Tampilkan pintasan",
+	"Show your support!": "Tunjukkan dukungan Anda!",
+	"Showcased creativity": "Menampilkan kreativitas",
+	"Sign in": "Masuk",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Keluar",
+	"Sign up": "Daftar",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Sumber",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Kesalahan pengenalan suara: {{error}}",
+	"Speech-to-Text Engine": "Mesin Pengenal Ucapan ke Teks",
+	"Stop": "",
+	"Stop Sequence": "Hentikan Urutan",
+	"Stream Chat Response": "",
+	"STT Model": "Model STT",
+	"STT Settings": "Pengaturan STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtitle (misalnya tentang Kekaisaran Romawi)",
+	"Success": "Berhasil",
+	"Successfully updated.": "Berhasil diperbarui.",
+	"Suggested": "Disarankan",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Sistem",
+	"System Instructions": "",
+	"System Prompt": "Permintaan Sistem",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Ketuk untuk menyela",
+	"Tavily API Key": "Kunci API Tavily",
+	"Tell us more:": "Beri tahu kami lebih lanjut:",
+	"Temperature": "Suhu",
+	"Template": "Templat",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Mesin Teks-ke-Suara",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Terima kasih atas umpan balik Anda!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Nilai yang diberikan haruslah nilai antara 0,0 (0%) dan 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Berpikir",
+	"This action cannot be undone. Do you wish to continue?": "Tindakan ini tidak dapat dibatalkan. Apakah Anda ingin melanjutkan?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ini akan memastikan bahwa percakapan Anda yang berharga disimpan dengan aman ke basis data backend. Terima kasih!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Ini adalah fitur eksperimental, mungkin tidak berfungsi seperti yang diharapkan dan dapat berubah sewaktu-waktu.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Ini akan menghapus",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Penjelasan menyeluruh",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Perbarui beberapa slot variabel secara berurutan dengan menekan tombol tab di input obrolan setelah setiap penggantian.",
+	"Title": "Judul",
+	"Title (e.g. Tell me a fun fact)": "Judul (misalnya, Ceritakan sebuah fakta menarik)",
+	"Title Auto-Generation": "Pembuatan Judul Secara Otomatis",
+	"Title cannot be an empty string.": "Judul tidak boleh berupa string kosong.",
+	"Title Generation Prompt": "Perintah Pembuatan Judul",
+	"TLS": "",
+	"To access the available model names for downloading,": "Untuk mengakses nama model yang tersedia untuk diunduh,",
+	"To access the GGUF models available for downloading,": "Untuk mengakses model GGUF yang tersedia untuk diunduh,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Untuk mengakses WebUI, hubungi administrator. Admin dapat mengelola status pengguna dari Panel Admin.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Untuk memilih filter di sini, tambahkan filter ke ruang kerja \"Fungsi\" terlebih dahulu.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Untuk memilih perangkat di sini, tambahkan ke ruang kerja \"Alat\" terlebih dahulu.",
+	"Toast notifications for new updates": "",
+	"Today": "Hari ini",
+	"Toggle settings": "Beralih pengaturan",
+	"Toggle sidebar": "Beralih bilah sisi",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Token Untuk Menyimpan Penyegaran Konteks (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Alat berhasil dibuat",
+	"Tool deleted successfully": "Alat berhasil dihapus",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Alat berhasil diimpor",
+	"Tool Name": "",
+	"Tool updated successfully": "Alat berhasil diperbarui",
+	"Tools": "Alat",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "K atas",
+	"Top P": "P Atas",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Kesulitan mengakses Ollama?",
+	"TTS Model": "Model TTS",
+	"TTS Settings": "Pengaturan TTS",
+	"TTS Voice": "Suara TTS",
+	"Type": "Ketik",
+	"Type Hugging Face Resolve (Download) URL": "Ketik Hugging Face Resolve (Unduh) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Ada masalah saat menyambung ke {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Memperbarui",
+	"Update and Copy Link": "Perbarui dan Salin Tautan",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Perbarui kata sandi",
+	"Updated": "",
+	"Updated at": "Diperbarui di",
+	"Updated At": "",
+	"Upload": "Unggah",
+	"Upload a GGUF model": "Unggah model GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Unggah File",
+	"Upload Pipeline": "Unggah Pipeline",
+	"Upload Progress": "Kemajuan Unggah",
+	"URL": "",
+	"URL Mode": "Mode URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gunakan Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Gunakan Inisial",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "pengguna",
+	"User": "",
+	"User location successfully retrieved.": "Lokasi pengguna berhasil diambil.",
+	"Username": "",
+	"Users": "Pengguna",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Memanfaatkan",
+	"Valid time units:": "Unit waktu yang valid:",
+	"Valves": "Katup",
+	"Valves updated": "Katup diperbarui",
+	"Valves updated successfully": "Katup berhasil diperbarui",
+	"variable": "variabel",
+	"variable to have them replaced with clipboard content.": "variabel untuk diganti dengan konten papan klip.",
+	"Version": "Versi",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Suara",
+	"Voice Input": "",
+	"Warning": "Peringatan",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Peringatan: Jika Anda memperbarui atau mengubah model penyematan, Anda harus mengimpor ulang semua dokumen.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Pengaturan Pemuat Web",
+	"Web Search": "Pencarian Web",
+	"Web Search Engine": "Mesin Pencari Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL pengait web",
+	"WebUI Settings": "Pengaturan WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Apa yang Baru di",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Bisikan (Lokal)",
+	"Why?": "",
+	"Widescreen Mode": "Mode Layar Lebar",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Ruang Kerja",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Menulis saran cepat (misalnya Siapa kamu?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Tulis ringkasan dalam 50 kata yang merangkum [topik atau kata kunci].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Kemarin",
+	"You": "Anda",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Anda dapat mempersonalisasi interaksi Anda dengan LLM dengan menambahkan kenangan melalui tombol 'Kelola' di bawah ini, sehingga lebih bermanfaat dan disesuaikan untuk Anda.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Anda tidak memiliki percakapan yang diarsipkan.",
+	"You have shared this chat": "Anda telah membagikan obrolan ini",
+	"You're a helpful assistant.": "Anda adalah asisten yang membantu.",
+	"You're now logged in.": "Anda sekarang sudah masuk.",
+	"Your account status is currently pending activation.": "Status akun Anda saat ini sedang menunggu aktivasi.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Pengaturan Pemuat Youtube"
+}
diff --git a/src/lib/i18n/locales/ie-GA/translation.json b/src/lib/i18n/locales/ie-GA/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..bdfea9ccc8b071ff1bad71f942f9edbf4e33c99f
--- /dev/null
+++ b/src/lib/i18n/locales/ie-GA/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' nó '-1' gan aon éag.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(m.sh. `sh webui.sh --api --api-auth username_password `)",
+	"(e.g. `sh webui.sh --api`)": "(m.sh. `sh webui.sh --api`)",
+	"(latest)": "(is déanaí)",
+	"{{ models }}": "{{models}}",
+	"{{user}}'s Chats": "Comhráite {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Ceoldeireadh Riachtanach",
+	"*Prompt node ID(s) are required for image generation": "* Tá ID nód pras ag teastáil chun íomhá a ghiniúint",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Tá leagan nua (v {{LATEST_VERSION}}) ar fáil anois.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Úsáidtear samhail tasc agus tascanna á ndéanamh agat mar theidil a ghiniúint do chomhráite agus ceisteanna cuardaigh gréasáin",
+	"a user": "úsáideoir",
+	"About": "Maidir",
+	"Access": "Rochtain",
+	"Access Control": "Rialaithe Rochtana",
+	"Accessible to all users": "Inrochtana do gach úsáideoir",
+	"Account": "Cuntas",
+	"Account Activation Pending": "Gníomhachtaithe Cuntas",
+	"Accurate information": "Faisnéis chruinn",
+	"Actions": "Gníomhartha",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Gníomhachtaigh an t-ordú seo trí \"/{{COMMAND}}\" a chlóscríobh chun ionchur comhrá a dhéanamh.",
+	"Active Users": "Úsáideoirí Gníomhacha",
+	"Add": "Cuir",
+	"Add a model ID": "Cuir ID samhail leis",
+	"Add a short description about what this model does": "Cuir cur síos gairid leis faoin méid a dhéanann an tsamhail seo",
+	"Add a tag": "Cuir clib leis",
+	"Add Arena Model": "Cuir Múnla Arena leis",
+	"Add Connection": "Cuir Ceangal leis",
+	"Add Content": "Cuir Ábhar leis",
+	"Add content here": "Cuir ábhar anseo",
+	"Add custom prompt": "Cuir pras saincheaptha leis",
+	"Add Files": "Cuir Comhaid",
+	"Add Group": "Cuir Grúpa leis",
+	"Add Memory": "Cuir Cuimhne",
+	"Add Model": "Cuir múnla leis",
+	"Add Tag": "Cuir Clib leis",
+	"Add Tags": "Cuir Clibeanna leis",
+	"Add text content": "Cuir ábhar téacs leis",
+	"Add User": "Cuir Úsáideoir leis",
+	"Add User Group": "Cuir Grúpa Úsáideoirí leis",
+	"Adjusting these settings will apply changes universally to all users.": "Cuirfear na socruithe seo ag coigeartú athruithe go huilíoch ar gach úsáideoir.",
+	"admin": "riarachán",
+	"Admin": "Riarachán",
+	"Admin Panel": "Painéal Riaracháin",
+	"Admin Settings": "Socruithe Riaracháin",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Tá rochtain ag riarthóirí ar gach uirlis i gcónaí; teastaíonn uirlisí sannta in aghaidh an tsamhail sa spás oibre ó úsáideoirí.",
+	"Advanced Parameters": "Paraiméadair Casta",
+	"Advanced Params": "Paraiméid Casta",
+	"All chats": "Gach comhrá",
+	"All Documents": "Gach Doiciméad",
+	"All models deleted successfully": "Scriosadh na samhlacha go léir go rathúil",
+	"Allow Chat Delete": "Ceadaigh Comhrá a Scriosadh",
+	"Allow Chat Deletion": "Cead Scriosadh Comhrá",
+	"Allow Chat Edit": "Ceadaigh Eagarthóireacht Comhrá",
+	"Allow File Upload": "Ceadaigh Uaslódáil Comhad",
+	"Allow non-local voices": "Lig guthanna neamh-áitiúla",
+	"Allow Temporary Chat": "Cead Comhrá Sealadach",
+	"Allow User Location": "Ceadaigh Suíomh Úsáideora",
+	"Allow Voice Interruption in Call": "Ceadaigh Briseadh Guth i nGlao",
+	"Already have an account?": "Tá cuntas agat cheana féin?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Rogha eile seachas an top_p, agus tá sé mar aidhm aige cothromaíocht cáilíochta agus éagsúlachta a chinntiú. Léiríonn an paraiméadar p an dóchúlacht íosta go mbreithneofar comhartha, i gcoibhneas le dóchúlacht an chomhartha is dóichí. Mar shampla, le p=0.05 agus dóchúlacht 0.9 ag an comhartha is dóichí, déantar logits le luach níos lú ná 0.045 a scagadh amach. (Réamhshocrú: 0.0)",
+	"Amazing": "Iontach",
+	"an assistant": "cúntóir",
+	"and": "agus",
+	"and {{COUNT}} more": "agus {{COUNT}} eile",
+	"and create a new shared link.": "agus cruthaigh nasc nua roinnte.",
+	"API Base URL": "URL Bonn API",
+	"API Key": "Eochair API",
+	"API Key created.": "Cruthaíodh Eochair API.",
+	"API keys": "Eochracha API",
+	"Application DN": "Feidhmchlár DN",
+	"Application DN Password": "Feidhmchlár DN Pasfhocal",
+	"applies to all users with the \"user\" role": "baineann sé le gach úsáideoir a bhfuil an ról \"úsáideoir\" aige",
+	"April": "Aibreán",
+	"Archive": "Cartlann",
+	"Archive All Chats": "Cartlann Gach Comhrá",
+	"Archived Chats": "Comhráite Cartlann",
+	"archived-chat-export": "gcartlann-comhrá-onnmhairiú",
+	"Are you sure you want to unarchive all archived chats?": "An bhfuil tú cinnte gur mhaith leat gach comhrá cartlainne a dhíchartlannú?",
+	"Are you sure?": "An bhfuil tú cinnte?",
+	"Arena Models": "Múnlaí Airéine",
+	"Artifacts": "Déantáin",
+	"Ask a question": "Cuir ceist",
+	"Assistant": "Cúntóir",
+	"Attach file": "Ceangail comhad",
+	"Attention to detail": "Aird ar mhionsonraí",
+	"Attribute for Username": "Tréith don Ainm Úsáideora",
+	"Audio": "Fuaim",
+	"August": "Lúnasa",
+	"Authenticate": "Fíordheimhnigh",
+	"Auto-Copy Response to Clipboard": "Freagra AutoCopy go Gearrthaisce",
+	"Auto-playback response": "Freagra uathsheinm",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Uathoibríoch1111",
+	"AUTOMATIC1111 Api Auth String": "UATHOMATIC1111 Api Auth Teaghrán",
+	"AUTOMATIC1111 Base URL": "UATHOMATIC1111 BunURL",
+	"AUTOMATIC1111 Base URL is required.": "Tá URL bonn UATHOMATIC1111 ag teastáil.",
+	"Available list": "Liosta atá ar fáil",
+	"available!": "ar fáil!",
+	"Awful": "Uafásach",
+	"Azure AI Speech": "Óráid Azure AI",
+	"Azure Region": "Réigiún Azure",
+	"Back": "Ar ais",
+	"Bad Response": "Droch-fhreagra",
+	"Banners": "Meirgí",
+	"Base Model (From)": "Múnla Bonn (Ó)",
+	"Batch Size (num_batch)": "Méid Baisc (num_batch)",
+	"before": "roimh",
+	"Being lazy": "A bheith leisciúil",
+	"Bing Search V7 Endpoint": "Cuardach Bing V7 Críochphointe",
+	"Bing Search V7 Subscription Key": "Eochair Síntiúis Bing Cuardach V7",
+	"Brave Search API Key": "Eochair API Cuardaigh Brave",
+	"By {{name}}": "Le {{name}}",
+	"Bypass SSL verification for Websites": "Seachbhachtar fíorú SSL do Láithreáin",
+	"Call": "Glaoigh",
+	"Call feature is not supported when using Web STT engine": "Ní thacaítear le gné glaonna agus inneall Web STT á úsáid",
+	"Camera": "Ceamara",
+	"Cancel": "Cealaigh",
+	"Capabilities": "Cumais",
+	"Certificate Path": "Cosán Teastais",
+	"Change Password": "Athraigh Pasfhocal",
+	"Character": "Carachtar",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Cairt teorainneacha nua",
+	"Chat": "Comhrá",
+	"Chat Background Image": "Íomhá Cúlra Comhrá",
+	"Chat Bubble UI": "Comhrá Bubble UI",
+	"Chat Controls": "Rialuithe Comhrá",
+	"Chat direction": "Treo comhrá",
+	"Chat Overview": "Forbhreathnú ar an",
+	"Chat Permissions": "Ceadanna Comhrá",
+	"Chat Tags Auto-Generation": "Clibeanna Comhrá Auto-Giniúint",
+	"Chats": "Comhráite",
+	"Check Again": "Seiceáil Arís",
+	"Check for updates": "Seiceáil nuashonruithe",
+	"Checking for updates...": "Seiceáil le haghaidh nuashonruithe...",
+	"Choose a model before saving...": "Roghnaigh samhail sula sábhálann tú...",
+	"Chunk Overlap": "Forluí smután",
+	"Chunk Params": "Chunk Params",
+	"Chunk Size": "Méid an Píosa",
+	"Ciphers": "Cipéirí",
+	"Citation": "Lua",
+	"Clear memory": "Cuimhne ghlan",
+	"click here": "cliceáil anseo",
+	"Click here for filter guides.": "Cliceáil anseo le haghaidh treoracha scagaire.",
+	"Click here for help.": "Cliceáil anseo le haghaidh cabhair.",
+	"Click here to": "Cliceáil anseo chun",
+	"Click here to download user import template file.": "Cliceáil anseo chun an comhad iompórtála úsáideora a íoslódáil.",
+	"Click here to learn more about faster-whisper and see the available models.": "Cliceáil anseo chun níos mó a fhoghlaim faoi cogar níos tapúla agus na múnlaí atá ar fáil a fheiceáil.",
+	"Click here to select": "Cliceáil anseo chun roghnú",
+	"Click here to select a csv file.": "Cliceáil anseo chun comhad csv a roghnú.",
+	"Click here to select a py file.": "Cliceáil anseo chun comhad py a roghnú.",
+	"Click here to upload a workflow.json file.": "Cliceáil anseo chun comhad workflow.json a uaslódáil.",
+	"click here.": "cliceáil anseo.",
+	"Click on the user role button to change a user's role.": "Cliceáil ar an gcnaipe ról úsáideora chun ról úsáideora a athrú.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Diúltaíodh cead scríofa an ghearrthaisce. Seiceáil socruithe do bhrabhsálaí chun an rochtain riachtanach a dheonú.",
+	"Clone": "Clón",
+	"Close": "Dún",
+	"Code execution": "Cód a fhorghníomhú",
+	"Code formatted successfully": "Cód formáidithe go rathúil",
+	"Collection": "Bailiúchán",
+	"Color": "Dath",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL Bonn ComfyUI",
+	"ComfyUI Base URL is required.": "Teastaíonn URL ComfyUI Base.",
+	"ComfyUI Workflow": "Sreabhadh Oibre ComfyUI",
+	"ComfyUI Workflow Nodes": "Nóid Sreabhadh Oibre ComfyUI",
+	"Command": "Ordú",
+	"Completions": "Críochnaithe",
+	"Concurrent Requests": "Iarrataí Comhthéime",
+	"Configure": "Cumraigh",
+	"Configure Models": "",
+	"Confirm": "Deimhnigh",
+	"Confirm Password": "Deimhnigh Pasfhocal",
+	"Confirm your action": "Deimhnigh do ghníomh",
+	"Connections": "Naisc",
+	"Contact Admin for WebUI Access": "Déan teagmháil le Riarachán le haghaidh Rochtana WebUI",
+	"Content": "Ábhar",
+	"Content Extraction": "Straibhadh Ábhar",
+	"Context Length": "Fad Comhthéacs",
+	"Continue Response": "Leanúint ar aghaidh",
+	"Continue with {{provider}}": "Lean ar aghaidh le {{provider}}",
+	"Continue with Email": "Lean ar aghaidh le Ríomhphost",
+	"Continue with LDAP": "Lean ar aghaidh le LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Rialú conas a roinntear téacs teachtaireachta d'iarratais TTS. Roinneann 'poncaíocht' ina abairtí, scoilteann 'míreanna' i míreanna, agus coinníonn 'aon' an teachtaireacht mar shreang amháin.",
+	"Controls": "Rialuithe",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Rialaíonn sé an chothromaíocht idir comhleanúnachas agus éagsúlacht an aschuir. Beidh téacs níos dírithe agus níos soiléire mar thoradh ar luach níos ísle. (Réamhshocrú: 5.0)",
+	"Copied": "Cóipeáladh",
+	"Copied shared chat URL to clipboard!": "Cóipeáladh URL an chomhrá roinnte chuig an ngearrthaisce!",
+	"Copied to clipboard": "Cóipeáilte go gear",
+	"Copy": "Cóipeáil",
+	"Copy last code block": "Cóipeáil bloc cód deireanach",
+	"Copy last response": "Cóipeáil an fhreagairt",
+	"Copy Link": "Cóipeáil Nasc",
+	"Copy to clipboard": "Cóipeáil chuig an ngearrthaisce",
+	"Copying to clipboard was successful!": "D'éirigh le cóipeáil chuig an ngearrthaisce!",
+	"Create": "Cruthaigh",
+	"Create a knowledge base": "Cruthaigh bonn eolais",
+	"Create a model": "Cruthaigh samhail",
+	"Create Account": "Cruthaigh Cuntas",
+	"Create Admin Account": "Cruthaigh Cuntas Riaracháin",
+	"Create Group": "Cruthaigh Grúpa",
+	"Create Knowledge": "Cruthaigh Eolais",
+	"Create new key": "Cruthaigh eochair nua",
+	"Create new secret key": "Cruthaigh eochair rúnda nua",
+	"Created at": "Cruthaithe ag",
+	"Created At": "Cruthaithe Ag",
+	"Created by": "Cruthaithe ag",
+	"CSV Import": "Iompórtáil CSV",
+	"Current Model": "Samhail Reatha",
+	"Current Password": "Pasfhocal Reatha",
+	"Custom": "Saincheaptha",
+	"Dark": "Dorcha",
+	"Database": "Bunachar Sonraí",
+	"December": "Nollaig",
+	"Default": "Réamhshocraithe",
+	"Default (Open AI)": "Réamhshocraithe (Oscail AI)",
+	"Default (SentenceTransformers)": "Réamhshocraithe (SentenceTransFormers)",
+	"Default Model": "Samhail Réamhshocraithe",
+	"Default model updated": "An tsamhail réamhshocraithe",
+	"Default Models": "",
+	"Default permissions": "Ceadanna réamhshocraithe",
+	"Default permissions updated successfully": "D'éirigh le ceadanna réamhshocraithe a nuashonrú",
+	"Default Prompt Suggestions": "Moltaí Pras Réamhshocraithe",
+	"Default to 389 or 636 if TLS is enabled": "Réamhshocrú go 389 nó 636 má tá TLS cumasaithe",
+	"Default to ALL": "Réamhshocrú do GACH",
+	"Default User Role": "Ról Úsáideora Réamhshoc",
+	"Delete": "Scrios",
+	"Delete a model": "Scrios samhail",
+	"Delete All Chats": "Scrios Gach Comhrá",
+	"Delete All Models": "Scrios Gach Múnla",
+	"Delete chat": "Scrios comhrá",
+	"Delete Chat": "Scrios Comhrá",
+	"Delete chat?": "Scrios comhrá?",
+	"Delete folder?": "Scrios fillteán?",
+	"Delete function?": "Scrios feidhm?",
+	"Delete prompt?": "Scrios pras?",
+	"delete this link": "scrios an nasc seo",
+	"Delete tool?": "Uirlis a scriosadh?",
+	"Delete User": "Scrios Úsáideoir",
+	"Deleted {{deleteModelTag}}": "Scriosta {{deleteModelTag}}",
+	"Deleted {{name}}": "Scriosta {{name}}",
+	"Deleted User": "Úsáideoir Scriosta",
+	"Describe your knowledge base and objectives": "Déan cur síos ar do bhunachar eolais agus do chuspóirí",
+	"Description": "Cur síos",
+	"Didn't fully follow instructions": "Níor lean sé treoracha go hiomlán",
+	"Disabled": "Díchumasaithe",
+	"Discover a function": "Faigh amach feidhm",
+	"Discover a model": "Faigh amach samhail",
+	"Discover a prompt": "Faigh amach pras",
+	"Discover a tool": "Faigh amach uirlis",
+	"Discover wonders": "Faigh amach iontais",
+	"Discover, download, and explore custom functions": "Faigh amach, íoslódáil agus iniúchadh feidhmeanna saincheaptha",
+	"Discover, download, and explore custom prompts": "Leideanna saincheaptha a fháil amach, a íoslódáil agus a iniúchadh",
+	"Discover, download, and explore custom tools": "Uirlisí saincheaptha a fháil amach, íoslódáil agus iniúchadh",
+	"Discover, download, and explore model presets": "Réamhshocruithe samhail a fháil amach, a íoslódáil agus a iniúchadh",
+	"Dismissible": "Dífhostaithe",
+	"Display": "Taispeáin",
+	"Display Emoji in Call": "Taispeáin Emoji i nGlao",
+	"Display the username instead of You in the Chat": "Taispeáin an t-ainm úsáideora in ionad Tú sa Comhrá",
+	"Displays citations in the response": "Taispeánann sé luanna sa fhreagra",
+	"Dive into knowledge": "Léim isteach eolas",
+	"Do not install functions from sources you do not fully trust.": "Ná suiteáil feidhmeanna ó fhoinsí nach bhfuil muinín iomlán agat.",
+	"Do not install tools from sources you do not fully trust.": "Ná suiteáil uirlisí ó fhoinsí nach bhfuil muinín iomlán agat.",
+	"Document": "Doiciméad",
+	"Documentation": "Doiciméadú",
+	"Documents": "Doiciméid",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ní dhéanann sé aon naisc sheachtracha, agus fanann do chuid sonraí go slán ar do fhreastalaí a óstáiltear go háitiúil.",
+	"Don't have an account?": "Níl cuntas agat?",
+	"don't install random functions from sources you don't trust.": "ná suiteáil feidhmeanna randamacha ó fhoinsí nach bhfuil muinín agat.",
+	"don't install random tools from sources you don't trust.": "ná suiteáil uirlisí randamacha ó fhoinsí nach bhfuil muinín agat.",
+	"Don't like the style": "Ná thaitníonn an stíl",
+	"Done": "Déanta",
+	"Download": "Íoslódáil",
+	"Download canceled": "Íoslódáil cealaithe",
+	"Download Database": "Íoslódáil Bunachair",
+	"Drag and drop a file to upload or select a file to view": "Tarraing agus scaoil comhad le huaslódáil nó roghnaigh comhad le féachaint air",
+	"Draw": "Tarraing",
+	"Drop any files here to add to the conversation": "Scaoil aon chomhaid anseo le cur leis an gcomhrá",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "m.sh. '30s', '10m'. Is iad aonaid ama bailí ná 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "m.h. Scagaire chun profanity a bhaint as téacs",
+	"e.g. My Filter": "m.sh. Mo Scagaire",
+	"e.g. My Tools": "e.g. Mo Uirlisí",
+	"e.g. my_filter": "m.sh. mo_scagaire",
+	"e.g. my_tools": "m.sh. mo_uirlisí",
+	"e.g. Tools for performing various operations": "m.sh. Uirlisí chun oibríochtaí éagsúla a dhéanamh",
+	"Edit": "Cuir in eagar",
+	"Edit Arena Model": "Cuir Samhail Airéine in Eagar",
+	"Edit Connection": "Cuir Ceangal in Eagar",
+	"Edit Default Permissions": "Cuir Ceadanna Réamhshocraithe in Eagar",
+	"Edit Memory": "Cuir Cuimhne in eagar",
+	"Edit User": "Cuir Úsáideoir in eagar",
+	"Edit User Group": "Cuir Grúpa Úsáideoirí in Eagar",
+	"ElevenLabs": "Eleven Labs",
+	"Email": "Ríomhphost",
+	"Embark on adventures": "Dul ar eachtraí",
+	"Embedding Batch Size": "Méid Baisc a ionchorprú",
+	"Embedding Model": "Múnla Leabháilte",
+	"Embedding Model Engine": "Inneall Múnla Ionchorprú",
+	"Embedding model set to \"{{embedding_model}}\"": "Samhail leabaithe atá socraithe go \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Cumasaigh Fíordheimhniú Eochracha API",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Cumasaigh Comhroinnt Pobail",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Cumasaigh Glasáil Cuimhne (mlock) chun sonraí samhaltaithe a chosc ó RAM. Glasálann an rogha seo sraith oibre leathanaigh an mhúnla isteach i RAM, ag cinntiú nach ndéanfar iad a mhalartú go diosca. Is féidir leis seo cabhrú le feidhmíocht a choinneáil trí lochtanna leathanaigh a sheachaint agus rochtain tapa ar shonraí a chinntiú.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Cumasaigh Mapáil Cuimhne (mmap) chun sonraí samhla a lódáil. Ligeann an rogha seo don chóras stóráil diosca a úsáid mar leathnú ar RAM trí chomhaid diosca a chóireáil amhail is dá mba i RAM iad. Is féidir leis seo feidhmíocht na samhla a fheabhsú trí rochtain níos tapúla ar shonraí a cheadú. Mar sin féin, d'fhéadfadh sé nach n-oibreoidh sé i gceart le gach córas agus féadfaidh sé méid suntasach spáis diosca a ithe.",
+	"Enable Message Rating": "Cumasaigh Rátáil Teachtai",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Cumasaigh sampláil Mirostat chun seachrán a rialú. (Réamhshocrú: 0, 0 = Díchumasaithe, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Cumasaigh Clárúcháin Nua",
+	"Enable Web Search": "Cumasaigh Cuardach Gréasáin",
+	"Enabled": "Cumasaithe",
+	"Engine": "Inneall",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Déan cinnte go bhfuil 4 cholún san ord seo i do chomhad CSV: Ainm, Ríomhphost, Pasfhocal, Ról.",
+	"Enter {{role}} message here": "Cuir isteach teachtaireacht {{role}} anseo",
+	"Enter a detail about yourself for your LLMs to recall": "Cuir isteach mionsonraí fút féin chun do LLManna a mheabhrú",
+	"Enter api auth string (e.g. username:password)": "Cuir isteach sreang auth api (m.sh. ainm úsáideora: pasfhocal)",
+	"Enter Application DN": "Cuir isteach Feidhmchlár DN",
+	"Enter Application DN Password": "Iontráil Feidhmchlár DN Pasfhocal",
+	"Enter Bing Search V7 Endpoint": "Cuir isteach Cuardach Bing V7 Críochphointe",
+	"Enter Bing Search V7 Subscription Key": "Cuir isteach Eochair Síntiúis Bing Cuardach V7",
+	"Enter Brave Search API Key": "Cuir isteach Eochair API Brave Cuardach",
+	"Enter certificate path": "Cuir isteach cosán an teastais",
+	"Enter CFG Scale (e.g. 7.0)": "Cuir isteach Scála CFG (m.sh. 7.0)",
+	"Enter Chunk Overlap": "Cuir isteach Chunk Forluí",
+	"Enter Chunk Size": "Cuir isteach Méid an Chunc",
+	"Enter description": "Tarraing",
+	"Enter Github Raw URL": "Cuir isteach URL Github Raw",
+	"Enter Google PSE API Key": "Cuir isteach Eochair API Google PSE",
+	"Enter Google PSE Engine Id": "Cuir isteach ID Inneall Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Iontráil Méid Íomhá (m.sh. 512x512)",
+	"Enter Jina API Key": "Cuir isteach Eochair API Jina",
+	"Enter language codes": "Cuir isteach cóid teanga",
+	"Enter Model ID": "Iontráil ID Mhúnla",
+	"Enter model tag (e.g. {{modelTag}})": "Cuir isteach chlib samhail (m.sh. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Iontráil Líon na gCéimeanna (m.sh. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Cuir isteach Sampler (m.sh. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Cuir isteach Sceidealóir (m.sh. Karras)",
+	"Enter Score": "Iontráil Scór",
+	"Enter SearchApi API Key": "Cuir isteach Eochair API SearchAPI",
+	"Enter SearchApi Engine": "Cuir isteach Inneall SearchAPI",
+	"Enter Searxng Query URL": "Cuir isteach URL Ceist Searxng",
+	"Enter Seed": "Cuir isteach Síl",
+	"Enter Serper API Key": "Cuir isteach Eochair API Serper",
+	"Enter Serply API Key": "Cuir isteach Eochair API Serply",
+	"Enter Serpstack API Key": "Cuir isteach Eochair API Serpstack",
+	"Enter server host": "Cuir isteach óstach freastalaí",
+	"Enter server label": "Cuir isteach lipéad freastalaí",
+	"Enter server port": "Cuir isteach port freastalaí",
+	"Enter stop sequence": "Cuir isteach seicheamh stad",
+	"Enter system prompt": "Cuir isteach an chóras pras",
+	"Enter Tavily API Key": "Cuir isteach eochair API Tavily",
+	"Enter Tika Server URL": "Cuir isteach URL freastalaí Tika",
+	"Enter Top K": "Cuir isteach Barr K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Iontráil URL (m.sh. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Iontráil URL (m.sh. http://localhost:11434)",
+	"Enter Your Email": "Cuir isteach do Ríomhphost",
+	"Enter Your Full Name": "Cuir isteach d'Ainm Iomlán",
+	"Enter your message": "Cuir isteach do theachtaireacht",
+	"Enter Your Password": "Cuir isteach do phasfhocal",
+	"Enter Your Role": "Cuir isteach do Ról",
+	"Enter Your Username": "Cuir isteach D'Ainm Úsáideora",
+	"Error": "Earráid",
+	"ERROR": "EARRÁID",
+	"Evaluations": "Meastóireachtaí",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Sampla: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Sampla: GACH",
+	"Example: ou=users,dc=foo,dc=example": "Sampla: ou=úsáideoirí,dc=foo,dc=sampla",
+	"Example: sAMAccountName or uid or userPrincipalName": "Sampla: sAMAaccountName nó uid nó userPrincipalName",
+	"Exclude": "Eisigh",
+	"Experimental": "Turgnamhach",
+	"Explore the cosmos": "Déan iniúchadh ar an cosmos",
+	"Export": "Easpórtáil",
+	"Export All Archived Chats": "Easpórtáil Gach Comhrá Cartlainne",
+	"Export All Chats (All Users)": "Easpórtáil gach comhrá (Gach Úsáideoir)",
+	"Export chat (.json)": "Easpórtáil comhrá (.json)",
+	"Export Chats": "Comhráite Easpórtá",
+	"Export Config to JSON File": "Easpórtáil Cumraíocht chuig Comhad JSON",
+	"Export Functions": "Feidhmeanna Easp",
+	"Export Models": "Múnlaí a Easpórtáil",
+	"Export Presets": "Easpórtáil Gach Comhrá Cartlainne",
+	"Export Prompts": "Leideanna Easpórtála",
+	"Export to CSV": "Easpórtáil go CSV",
+	"Export Tools": "Uirlisí Easpór",
+	"External Models": "Múnlaí Seachtracha",
+	"Failed to add file.": "Theip ar an gcomhad a chur leis.",
+	"Failed to create API Key.": "Theip ar an eochair API a chruthú.",
+	"Failed to read clipboard contents": "Theip ar ábhar gearrthaisce a lé",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Theip ar shocruithe a nuashonrú",
+	"Failed to upload file.": "Theip ar uaslódáil an chomhaid.",
+	"February": "Feabhra",
+	"Feedback History": "Stair Aiseolais",
+	"Feedbacks": "Aiseolas",
+	"Feel free to add specific details": "Ná bíodh leisce ort sonraí ar leith a chur leis",
+	"File": "Comhad",
+	"File added successfully.": "D'éirigh leis an gcomhad a chur leis.",
+	"File content updated successfully.": "D'éirigh le hábhar an chomhaid a nuashonrú.",
+	"File Mode": "Mód Comhad",
+	"File not found.": "Níor aimsíodh an comhad.",
+	"File removed successfully.": "D'éirigh le baint an chomhaid.",
+	"File size should not exceed {{maxSize}} MB.": "Níor chóir go mbeadh méid an chomhaid níos mó ná {{maxSize}} MB.",
+	"Files": "Comhaid",
+	"Filter is now globally disabled": "Tá an scagaire faoi mhíchumas go domhanda",
+	"Filter is now globally enabled": "Tá an scagaire cumasaithe go domhanda anois",
+	"Filters": "Scagairí",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Braithíodh spoofing méarloirg: Ní féidir teachlitreacha a úsáid mar avatar. Réamhshocrú ar íomhá próifíle réamhshocraithe.",
+	"Fluidly stream large external response chunks": "Sruthaigh codanna móra freagartha seachtracha go sreabhach",
+	"Focus chat input": "Ionchur comhrá fócas",
+	"Folder deleted successfully": "Scriosadh an fillteán go rathúil",
+	"Folder name cannot be empty": "Ní féidir ainm fillteáin a bheith folamh",
+	"Folder name cannot be empty.": "Ní féidir ainm fillteáin a bheith folamh.",
+	"Folder name updated successfully": "D'éirigh le hainm an fhillteáin a nuashonrú",
+	"Followed instructions perfectly": "Lean treoracha go foirfe",
+	"Forge new paths": "Déan cosáin nua a chruthú",
+	"Form": "Foirm",
+	"Format your variables using brackets like this:": "Formáidigh na hathróga ag baint úsáide as lúibíní mar seo:",
+	"Frequency Penalty": "Pionós Minicíochta",
+	"Function": "Feidhm",
+	"Function created successfully": "Cruthaíodh feidhm go rathúil",
+	"Function deleted successfully": "Feidhm scriosta go rathúil",
+	"Function Description": "Cur síos ar Fheidhm",
+	"Function ID": "ID Feidhme",
+	"Function is now globally disabled": "Tá an fheidhm faoi mhíchumas go domhanda",
+	"Function is now globally enabled": "Tá feidhm cumasaithe go domhanda anois",
+	"Function Name": "Ainm Feidhme",
+	"Function updated successfully": "Feidhm nuashonraithe",
+	"Functions": "Feidhmeanna",
+	"Functions allow arbitrary code execution": "Ligeann feidhmeanna forghníomhú cód",
+	"Functions allow arbitrary code execution.": "Ceadaíonn feidhmeanna forghníomhú cód treallach.",
+	"Functions imported successfully": "Feidhmeanna allmhairi",
+	"General": "Ginearálta",
+	"General Settings": "Socruithe Ginearálta",
+	"Generate Image": "Ginigh Íomhá",
+	"Generating search query": "Giniúint ceist cuardaigh",
+	"Generation Info": "Eolas Giniúin",
+	"Get started": "Cuir tús leis",
+	"Get started with {{WEBUI_NAME}}": "Cuir tús le {{WEBUI_NAME}}",
+	"Global": "Domhanda",
+	"Good Response": "Freagra Mhaith",
+	"Google PSE API Key": "Eochair API Google PSE",
+	"Google PSE Engine Id": "ID Inneall Google PSE",
+	"Group created successfully": "Grúpa cruthaithe go rathúil",
+	"Group deleted successfully": "D'éirigh le scriosadh an ghrúpa",
+	"Group Description": "Cur síos ar an nGrúpa",
+	"Group Name": "Ainm an Ghrúpa",
+	"Group updated successfully": "D'éirigh le nuashonrú an ghrúpa",
+	"Groups": "Grúpaí",
+	"h:mm a": "h: mm a",
+	"Haptic Feedback": "Aiseolas Haptic",
+	"has no conversations.": "níl aon chomhráite aige.",
+	"Hello, {{name}}": "Dia duit, {{name}}",
+	"Help": "Cabhair",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Cabhraigh linn an clár ceannairí pobail is fearr a chruthú trí do stair aiseolais a roinnt!",
+	"Hex Color": "Dath Heics",
+	"Hex Color - Leave empty for default color": "Dath Heics - Fág folamh don dath réamhshocraithe",
+	"Hide": "Folaigh",
+	"Host": "Óstach",
+	"How can I help you today?": "Conas is féidir liom cabhrú leat inniu?",
+	"How would you rate this response?": "Cad é mar a mheasfá an freagra seo?",
+	"Hybrid Search": "Cuardach Hibrideach",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Admhaím gur léigh mé agus tuigim impleachtaí mo ghníomhaíochta. Táim ar an eolas faoi na rioscaí a bhaineann le cód treallach a fhorghníomhú agus tá iontaofacht na foinse fíoraithe agam.",
+	"ID": "ID",
+	"Ignite curiosity": "Las fiosracht",
+	"Image Generation (Experimental)": "Giniúint Íomhá (Turgnaimh)",
+	"Image Generation Engine": "Inneall Giniúna Íomh",
+	"Image Settings": "Socruithe Íomhá",
+	"Images": "Íomhánna",
+	"Import Chats": "Comhráite iompórtá",
+	"Import Config from JSON File": "Cumraíocht Iompórtáil ó Chomhad JSON",
+	"Import Functions": "Feidhmeanna Iom",
+	"Import Models": "Múnlaí a Iompórtáil",
+	"Import Presets": "Réamhshocruithe Iompórtáil",
+	"Import Prompts": "Leideanna Iompórtála",
+	"Import Tools": "Uirlisí Iomp",
+	"Include": "Cuir san áireamh",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Cuir bratach `--api-auth` san áireamh agus webui stable-diffusion-reatha á rith",
+	"Include `--api` flag when running stable-diffusion-webui": "Cuir bratach `--api` san áireamh agus webui cobhsaí-scaipthe á rith",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Bíonn tionchar aige ar chomh tapa agus a fhreagraíonn an t-algartam d’aiseolas ón téacs ginte. Beidh coigeartuithe níos moille mar thoradh ar ráta foghlama níos ísle, agus déanfaidh ráta foghlama níos airde an t-algartam níos freagraí. (Réamhshocrú: 0.1)",
+	"Info": "Eolas",
+	"Input commands": "Orduithe ionchuir",
+	"Install from Github URL": "Suiteáil ó Github URL",
+	"Instant Auto-Send After Voice Transcription": "Seoladh Uathoibríoch Láithreach Tar éis",
+	"Interface": "Comhéadan",
+	"Invalid file format.": "Formáid comhaid neamhbhailí.",
+	"Invalid Tag": "Clib neamhbhailí",
+	"January": "Eanáir",
+	"Jina API Key": "Jina API Eochair",
+	"join our Discord for help.": "bí inár Discord chun cabhair a fháil.",
+	"JSON": "JSON",
+	"JSON Preview": "Réamhamharc JSON",
+	"July": "Lúil",
+	"June": "Meitheamh",
+	"JWT Expiration": "Éag JWT",
+	"JWT Token": "Comhartha JWT",
+	"Keep Alive": "Coinnigh Beo",
+	"Key": "Eochair",
+	"Keyboard shortcuts": "Aicearraí méarchlár",
+	"Knowledge": "Eolas",
+	"Knowledge Access": "Rochtain Eolais",
+	"Knowledge created successfully.": "Eolas cruthaithe go rathúil.",
+	"Knowledge deleted successfully.": "D'éirigh leis an eolas a scriosadh.",
+	"Knowledge reset successfully.": "D'éirigh le hathshocrú eolais.",
+	"Knowledge updated successfully": "D'éirigh leis an eolas a nuashonrú",
+	"Label": "Lipéad",
+	"Landing Page Mode": "Mód Leathanach Tuirlingthe",
+	"Language": "Teanga",
+	"Last Active": "Gníomhach Deiridh",
+	"Last Modified": "Athraithe Deiridh",
+	"LDAP": "LDAP",
+	"LDAP server updated": "Nuashonraíodh freastalaí LDAP",
+	"Leaderboard": "An Clár Ceannairí",
+	"Leave empty for unlimited": "Fág folamh le haghaidh neamhtheoranta",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Fág folamh chun gach múnla ó chríochphointe \"{{URL}}/api/tags\" a chur san áireamh",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Fág folamh chun gach múnla ón gcríochphointe \"{{URL}}/models\" a chur san áireamh",
+	"Leave empty to include all models or select specific models": "Fág folamh chun gach múnla a chur san áireamh nó roghnaigh múnlaí sonracha",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Fág folamh chun an pras réamhshocraithe a úsáid, nó cuir isteach pras saincheaptha",
+	"Light": "Solas",
+	"Listening...": "Éisteacht...",
+	"LLMs can make mistakes. Verify important information.": "Is féidir le LLManna botúin a dhéanamh. Fíoraigh faisnéis thábhachtach.",
+	"Local": "Áitiúil",
+	"Local Models": "Múnlaí Áitiúla",
+	"Lost": "Cailleadh",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Déanta ag OpenWebUI Community",
+	"Make sure to enclose them with": "Déan cinnte iad a cheangal le",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Déan cinnte comhad workflow.json a onnmhairiú mar fhormáid API ó ComfyUI.",
+	"Manage": "Bainistiú",
+	"Manage Arena Models": "Bainistigh Múnlaí Airéine",
+	"Manage Ollama": "Bainistigh Ollama",
+	"Manage Ollama API Connections": "Bainistigh Naisc API Ollama",
+	"Manage OpenAI API Connections": "Bainistigh Naisc API OpenAI",
+	"Manage Pipelines": "Bainistigh píblín",
+	"March": "Márta",
+	"Max Tokens (num_predict)": "Comharthaí Uasta (num_predicate)",
+	"Max Upload Count": "Líon Uaslódála Max",
+	"Max Upload Size": "Méid Uaslódála Max",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Is féidir uasmhéid de 3 mhúnla a íoslódáil ag an am Bain triail as arís níos déanaí.",
+	"May": "Bealtaine",
+	"Memories accessible by LLMs will be shown here.": "Taispeánfar cuimhní atá inrochtana ag LLManna anseo.",
+	"Memory": "Cuimhne",
+	"Memory added successfully": "Cuireadh cuimhne leis go",
+	"Memory cleared successfully": "Cuimhne glanta go rathúil",
+	"Memory deleted successfully": "Cuimhne scriosta go rathúil",
+	"Memory updated successfully": "Cuimhne nuashonraithe",
+	"Merge Responses": "Cumaisc Freagraí",
+	"Message rating should be enabled to use this feature": "Ba cheart rátáil teachtaireachta a chumasú chun an ghné seo a úsáid",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Ní roinnfear teachtaireachtaí a sheolann tú tar éis do nasc a chruthú. Beidh úsáideoirí leis an URL in ann féachaint ar an gcomhrá roinnte.",
+	"Min P": "Min P",
+	"Minimum Score": "Scór Íosta",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM LL, BBBB",
+	"MMMM DD, YYYY HH:mm": "MMMM LL, BBBB UU:nn",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM LL, BBBB uu:nn:ss A",
+	"Model": "Múnla",
+	"Model '{{modelName}}' has been successfully downloaded.": "Rinneadh an tsamhail '{{modelName}}' a íoslódáil go rathúil.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Tá múnla ‘{{modelTag}}’ sa scuaine cheana féin le híoslódáil.",
+	"Model {{modelId}} not found": "Múnla {{modelId}} gan aimsiú",
+	"Model {{modelName}} is not vision capable": "Níl samhail {{modelName}} in ann amharc",
+	"Model {{name}} is now {{status}}": "Tá samhail {{name}} {{status}} anois",
+	"Model accepts image inputs": "Glacann múnla le hionchuir",
+	"Model created successfully!": "Cruthaíodh múnla go rathúil!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Braitheadh \u200b\u200bconair chóras comhad samhail. Teastaíonn mionainm múnla le haghaidh nuashonraithe, ní féidir leanúint ar aghaidh.",
+	"Model Filtering": "Scagadh Múnla",
+	"Model ID": "ID Múnla",
+	"Model IDs": "IDanna Múnla",
+	"Model Name": "Ainm Múnla",
+	"Model not selected": "Múnla nach roghnaíodh",
+	"Model Params": "Múnla Params",
+	"Model Permissions": "Ceadanna Múnla",
+	"Model updated successfully": "An tsamhail nuashonraithe",
+	"Modelfile Content": "Ábhar Modelfile",
+	"Models": "Múnlaí",
+	"Models Access": "Rochtain Múnlaí",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "níos mó",
+	"More": "Tuilleadh",
+	"Name": "Ainm",
+	"Name your knowledge base": "Cuir ainm ar do bhunachar eolais",
+	"New Chat": "Comhrá Nua",
+	"New folder": "Fillteán nua",
+	"New Password": "Pasfhocal Nua",
+	"No content found": "Níor aimsíodh aon ábhar",
+	"No content to speak": "Níl aon ábhar le labhairt",
+	"No distance available": "Níl achar ar fáil",
+	"No feedbacks found": "Níor aimsíodh aon aiseolas",
+	"No file selected": "Níl aon chomhad roghnaithe",
+	"No files found.": "Níor aimsíodh aon chomhaid.",
+	"No groups with access, add a group to grant access": "Gan aon ghrúpa a bhfuil rochtain acu, cuir grúpa leis chun rochtain a dheonú",
+	"No HTML, CSS, or JavaScript content found.": "Níor aimsíodh aon ábhar HTML, CSS nó JavaScript.",
+	"No knowledge found": "Níor aimsíodh aon eolas",
+	"No model IDs": "Gan IDanna múnla",
+	"No models found": "Níor aimsíodh aon mhúnlaí",
+	"No models selected": "",
+	"No results found": "Níl aon torthaí le fáil",
+	"No search query generated": "Ní ghintear aon cheist cuardaigh",
+	"No source available": "Níl aon fhoinse ar fáil",
+	"No users were found.": "Níor aimsíodh aon úsáideoirí.",
+	"No valves to update": "Gan comhlaí le nuashonrú",
+	"None": "Dada",
+	"Not factually correct": "Níl sé ceart go fírineach",
+	"Not helpful": "Gan a bheith cabhrach",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nóta: Má shocraíonn tú íosscór, ní thabharfaidh an cuardach ach doiciméid a bhfuil scór níos mó ná nó cothrom leis an scór íosta ar ais.",
+	"Notes": "Nótaí",
+	"Notifications": "Fógraí",
+	"November": "Samhain",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "Aitheantas OAuth",
+	"October": "Deireadh Fómhair",
+	"Off": "Lasmuigh",
+	"Okay, Let's Go!": "Ceart go leor, Déanaimis Téigh!",
+	"OLED Dark": "OLED Dorcha",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API faoi mhíchumas",
+	"Ollama API settings updated": "Nuashonraíodh socruithe Olama API",
+	"Ollama Version": "Leagan Ollama",
+	"On": "Ar",
+	"Only alphanumeric characters and hyphens are allowed": "Ní cheadaítear ach carachtair alfa-uimhriúla agus fleiscíní",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Ní cheadaítear ach carachtair alfauméireacha agus braithíní sa sreangán ordaithe.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Ní féidir ach bailiúcháin a chur in eagar, bonn eolais nua a chruthú chun doiciméid a chur in eagar/a chur leis.",
+	"Only select users and groups with permission can access": "Ní féidir ach le húsáideoirí roghnaithe agus le grúpaí a bhfuil cead acu rochtain a fháil",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! Is cosúil go bhfuil an URL neamhbhailí. Seiceáil faoi dhó le do thoil agus iarracht arís.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Úps! Tá comhaid fós á n-uaslódáil. Fan go mbeidh an uaslódáil críochnaithe.",
+	"Oops! There was an error in the previous response.": "Úps! Bhí earráid sa fhreagra roimhe seo.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ups! Tá modh gan tacaíocht á úsáid agat (tosaigh amháin). Freastal ar an WebUI ón gcúltaca le do thoil.",
+	"Open file": "Oscail comhad",
+	"Open in full screen": "Oscail i scáileán iomlán",
+	"Open new chat": "Oscail comhrá nua",
+	"Open WebUI uses faster-whisper internally.": "Úsáideann Open WebUI cogar níos tapúla go hinmheánach.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Úsáideann Open WebUI úsáidí SpeechT5 agus CMU leabaithe cainteoir Artach.",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Tá leagan WebUI oscailte (v{{OPEN_WEBUI_VERSION}}) níos ísle ná an leagan riachtanach (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Cumraíocht API OpenAI",
+	"OpenAI API Key is required.": "Tá Eochair API OpenAI ag teastáil.",
+	"OpenAI API settings updated": "Nuashonraíodh socruithe OpenAI API",
+	"OpenAI URL/Key required.": "Teastaíonn URL/eochair OpenAI.",
+	"or": "nó",
+	"Organize your users": "Eagraigh do chuid úsáideoirí",
+	"Other": "Eile",
+	"OUTPUT": "ASCHUR",
+	"Output format": "Formáid aschuir",
+	"Overview": "Forbhreathnú",
+	"page": "leathanach",
+	"Password": "Pasfhocal",
+	"Paste Large Text as File": "Greamaigh Téacs Mór mar Chomhad",
+	"PDF document (.pdf)": "Doiciméad PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Íomhánna Sliocht PDF (OCR)",
+	"pending": "ar feitheamh",
+	"Permission denied when accessing media devices": "Cead diúltaithe nuair a bhíonn rochtain agat",
+	"Permission denied when accessing microphone": "Cead diúltaithe agus tú ag rochtain ar",
+	"Permission denied when accessing microphone: {{error}}": "Cead diúltaithe agus tú ag teacht ar mhicreafón: {{error}}",
+	"Permissions": "Ceadanna",
+	"Personalization": "Pearsantú",
+	"Pin": "Bioráin",
+	"Pinned": "Pinneáilte",
+	"Pioneer insights": "Léargais ceannródaí",
+	"Pipeline deleted successfully": "Scriosta píblíne go rathúil",
+	"Pipeline downloaded successfully": "Íoslódáilte píblíne",
+	"Pipelines": "Píblínte",
+	"Pipelines Not Detected": "Níor aimsíodh píblínte",
+	"Pipelines Valves": "Comhlaí Píblíne",
+	"Plain text (.txt)": "Téacs simplí (.txt)",
+	"Playground": "Clós súgartha",
+	"Please carefully review the following warnings:": "Déan athbhreithniú cúramach ar na rabhaidh seo a leanas le do thoil:",
+	"Please enter a prompt": "Cuir isteach leid",
+	"Please fill in all fields.": "Líon isteach gach réimse le do thoil.",
+	"Please select a model first.": "",
+	"Please select a reason": "Roghnaigh cúis le do thoil",
+	"Port": "Port",
+	"Positive attitude": "Dearcadh dearfach",
+	"Prefix ID": "Aitheantas Réimír",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Úsáidtear Aitheantas Réimír chun coinbhleachtaí le naisc eile a sheachaint trí réimír a chur le haitheantas na samhla - fág folamh le díchumasú",
+	"Previous 30 days": "30 lá roimhe seo",
+	"Previous 7 days": "7 lá roimhe seo",
+	"Profile Image": "Íomhá Próifíl",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Pras (m.sh. inis dom fíric spraíúil faoin Impireacht Rómhánach)",
+	"Prompt Content": "Ábhar Pras",
+	"Prompt created successfully": "Leid cruthaithe go rathúil",
+	"Prompt suggestions": "Moltaí pras",
+	"Prompt updated successfully": "D'éirigh leis an leid a nuashonrú",
+	"Prompts": "Leabhair",
+	"Prompts Access": "Rochtain ar Chuirí",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Tarraing \"{{searchValue}}\" ó Ollama.com",
+	"Pull a model from Ollama.com": "Tarraing múnla ó Ollama.com",
+	"Query Generation Prompt": "Cuirí Ginearáil Ceisteanna",
+	"Query Params": "Fiosrúcháin Params",
+	"RAG Template": "Teimpléad RAG",
+	"Rating": "Rátáil",
+	"Re-rank models by topic similarity": "Athrangaigh múnlaí de réir cosúlachta topaicí",
+	"Read Aloud": "Léigh Ard",
+	"Record voice": "Taifead guth",
+	"Redirecting you to OpenWebUI Community": "Tú a atreorú chuig OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Laghdaíonn sé an dóchúlacht go giniúint nonsense. Tabharfaidh luach níos airde (m.sh. 100) freagraí níos éagsúla, agus beidh luach níos ísle (m.sh. 10) níos coimeádaí. (Réamhshocrú: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Tagairt duit féin mar \"Úsáideoir\" (m.sh., \"Tá an úsáideoir ag foghlaim Spáinnis\")",
+	"References from": "Tagairtí ó",
+	"Refused when it shouldn't have": "Diúltaíodh nuair nár chóir dó",
+	"Regenerate": "Athghiniúint",
+	"Release Notes": "Nótaí Scaoilte",
+	"Relevance": "Ábharthacht",
+	"Remove": "Bain",
+	"Remove Model": "Bain Múnla",
+	"Rename": "Athainmnigh",
+	"Reorder Models": "",
+	"Repeat Last N": "Déan an N deireanach arís",
+	"Request Mode": "Mód Iarratais",
+	"Reranking Model": "Múnla Athrangú",
+	"Reranking model disabled": "Samhail athrangú faoi mhíchumas",
+	"Reranking model set to \"{{reranking_model}}\"": "Samhail athrangú socraithe go \"{{reranking_model}}\"",
+	"Reset": "Athshocraigh",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Athshocraigh Eolaire Uas",
+	"Reset Vector Storage/Knowledge": "Athshocraigh Stóráil/Eolas Veicteoir",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Ní féidir fógraí freagartha a ghníomhachtú toisc gur diúltaíodh ceadanna an tsuímh Ghréasáin. Tabhair cuairt ar do shocruithe brabhsálaí chun an rochtain riachtanach a dheonú.",
+	"Response splitting": "Scoilt freagartha",
+	"Result": "Toradh",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Ionchur Saibhir Téacs don Chomhrá",
+	"RK": "RK",
+	"Role": "Ról",
+	"Rosé Pine": "Pine Rosé",
+	"Rosé Pine Dawn": "Rose Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Rith",
+	"Running": "Ag rith",
+	"Save": "Sábháil",
+	"Save & Create": "Sábháil & Cruthaigh",
+	"Save & Update": "Sábháil & Nuashonraigh",
+	"Save As Copy": "Sábháil Mar Cóip",
+	"Save Tag": "Sábháil Clib",
+	"Saved": "Shábháil",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Ní thacaítear le logaí comhrá a shábháil go díreach chuig stóráil do bhrabhsálaí Tóg nóiméad chun do logaí comhrá a íoslódáil agus a scriosadh trí chliceáil an cnaipe thíos. Ná bíodh imní ort, is féidir leat do logaí comhrá a athiompórtáil go héasca chuig an gcúltaca trí",
+	"Scroll to bottom when switching between branches": "Scrollaigh go bun agus tú ag athrú idir brainsí",
+	"Search": "Cuardaigh",
+	"Search a model": "Cuardaigh múnla",
+	"Search Base": "Bonn Cuardaigh",
+	"Search Chats": "Cuardaigh Comhráite",
+	"Search Collection": "Bailiúchán Cuardaigh",
+	"Search Filters": "Scagairí Cuardaigh",
+	"search for tags": "cuardach le haghaidh clibeanna",
+	"Search Functions": "Feidhmeanna Cuardaigh",
+	"Search Knowledge": "Cuardaigh Eolais",
+	"Search Models": "Múnlaí Cuardaigh",
+	"Search options": "Roghanna cuardaigh",
+	"Search Prompts": "Leideanna Cuardaigh",
+	"Search Result Count": "Líon Torthaí Cuardaigh",
+	"Search the web": "Cuardaigh an gréasán",
+	"Search Tools": "Uirlisí Cuardaigh",
+	"SearchApi API Key": "Eochair API SearchAPI",
+	"SearchApi Engine": "Inneall SearchAPI",
+	"Searched {{count}} sites_one": "Cuardaigh {{count}} sites_one",
+	"Searched {{count}} sites_other": "Cuardaigh {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Ag cuardach \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Cuardach Eolas do \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL ceisteanna cuardaigh",
+	"See readme.md for instructions": "Féach readme.md le haghaidh treoracha",
+	"See what's new": "Féach cad atá nua",
+	"Seed": "Síol",
+	"Select a base model": "Roghnaigh bunmhúnla",
+	"Select a engine": "Roghnaigh inneall",
+	"Select a function": "Roghnaigh feidhm",
+	"Select a group": "Roghnaigh grúpa",
+	"Select a model": "Roghnaigh samhail",
+	"Select a pipeline": "Roghnaigh píblíne",
+	"Select a pipeline url": "Roghnaigh url píblíne",
+	"Select a tool": "Roghnaigh uirlis",
+	"Select Engine": "Roghnaigh Inneall",
+	"Select Knowledge": "Roghnaigh Eolais",
+	"Select model": "Roghnaigh samhail",
+	"Select only one model to call": "Roghnaigh ach samhail amháin le glaoch",
+	"Selected model(s) do not support image inputs": "Ní thacaíonn samhail(í) roghnaithe le hionchur íomhá",
+	"Semantic distance to query": "Fad shéimeantach le fiosrú",
+	"Send": "Seol",
+	"Send a Message": "Seol Teachtaireacht",
+	"Send message": "Seol teachtaireacht",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Seolann `stream_options: { include_usage: true }` san iarratas.\nTabharfaidh soláthraithe a fhaigheann tacaíocht faisnéis úsáide chomharthaí ar ais sa fhreagra nuair a bheidh sé socraithe.",
+	"September": "Meán Fómhair",
+	"Serper API Key": "Serper API Eochair",
+	"Serply API Key": "Eochair API Serply",
+	"Serpstack API Key": "Eochair API Serpstack",
+	"Server connection verified": "Ceangal freastalaí fíoraithe",
+	"Set as default": "Socraigh mar réamhshocraithe",
+	"Set CFG Scale": "Socraigh Scála CFG",
+	"Set Default Model": "Socraigh Samhail Réamhshocrú",
+	"Set embedding model": "Socraigh samhail leabaithe",
+	"Set embedding model (e.g. {{model}})": "Socraigh samhail leabaithe (m.sh. {{model}})",
+	"Set Image Size": "Socraigh Méid Íomhá",
+	"Set reranking model (e.g. {{model}})": "Socraigh samhail athrangú (m.sh. {{model}})",
+	"Set Sampler": "Socraigh Sampler",
+	"Set Scheduler": "Socraigh Sceidealóir",
+	"Set Steps": "Socraigh Céimeanna",
+	"Set Task Model": "Socraigh Samhail Tasc",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Socraigh líon na bhfeistí GPU a úsáidtear le haghaidh ríomh. Rialaíonn an rogha seo cé mhéad gléas GPU (má tá siad ar fáil) a úsáidtear chun iarratais isteach a phróiseáil. Is féidir leis an luach seo a mhéadú feabhas suntasach a chur ar fheidhmíocht do mhúnlaí atá optamaithe le haghaidh luasghéarú GPU ach d’fhéadfadh go n-ídíonn siad níos mó cumhachta agus acmhainní GPU freisin.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Socraigh líon na snáitheanna oibrithe a úsáidtear le haghaidh ríomh. Rialaíonn an rogha seo cé mhéad snáithe a úsáidtear chun iarratais a thagann isteach a phróiseáil i gcomhthráth. D'fhéadfadh méadú ar an luach seo feidhmíocht a fheabhsú faoi ualaí oibre comhairgeadra ard ach féadfaidh sé níos mó acmhainní LAP a úsáid freisin.",
+	"Set Voice": "Socraigh Guth",
+	"Set whisper model": "Socraigh múnla cogar",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Socraíonn sé cé chomh fada siar is atá an tsamhail le breathnú siar chun athrá a chosc. (Réamhshocrú: 64, 0 = díchumasaithe, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Socraíonn sé cé chomh láidir is féidir pionós a ghearradh ar athrá. Cuirfidh luach níos airde (m.sh., 1.5) pionós níos láidre ar athrá, agus beidh luach níos ísle (m.sh., 0.9) níos boige. (Réamhshocrú: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Socraíonn sé an síol uimhir randamach a úsáid le haghaidh giniúna. Má shocraítear é seo ar uimhir shainiúil, ginfidh an tsamhail an téacs céanna don leid céanna. (Réamhshocrú: randamach)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Socraíonn sé méid na fuinneoige comhthéacs a úsáidtear chun an chéad chomhartha eile a ghiniúint. (Réamhshocrú: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Socraíonn sé na stadanna le húsáid. Nuair a thagtar ar an bpatrún seo, stopfaidh an LLM ag giniúint téacs agus ag filleadh. Is féidir patrúin stad iolracha a shocrú trí pharaiméadair stadanna iolracha a shonrú i gcomhad samhail.",
+	"Settings": "Socruithe",
+	"Settings saved successfully!": "Socruithe sábhálta go rathúil!",
+	"Share": "Comhroinn",
+	"Share Chat": "Comhroinn Comhrá",
+	"Share to OpenWebUI Community": "Comhroinn le Pobal OpenWebUI",
+	"Show": "Taispeáin",
+	"Show \"What's New\" modal on login": "Taispeáin módúil \"Cad atá Nua\" ar logáil isteach",
+	"Show Admin Details in Account Pending Overlay": "Taispeáin Sonraí Riaracháin sa Chuntas ar Feitheamh Forleagan",
+	"Show shortcuts": "Taispeáin aicearraí",
+	"Show your support!": "Taispeáin do thacaíocht!",
+	"Showcased creativity": "Cruthaitheacht léirithe",
+	"Sign in": "Sínigh isteach",
+	"Sign in to {{WEBUI_NAME}}": "Sínigh isteach ar {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Sínigh isteach ar {{WEBUI_NAME}} le LDAP",
+	"Sign Out": "Sínigh Amach",
+	"Sign up": "Cláraigh",
+	"Sign up to {{WEBUI_NAME}}": "Cláraigh le {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Ag síniú isteach ar {{WEBUI_NAME}}",
+	"Source": "Foinse",
+	"Speech Playback Speed": "Luas Athsheinm Urlabhra",
+	"Speech recognition error: {{error}}": "Earráid aitheantais cainte: {{error}}",
+	"Speech-to-Text Engine": "Inneall Cainte-go-Téacs",
+	"Stop": "Stad",
+	"Stop Sequence": "Stop Seicheamh",
+	"Stream Chat Response": "Freagra Comhrá Sruth",
+	"STT Model": "Múnla STT",
+	"STT Settings": "Socruithe STT",
+	"Subtitle (e.g. about the Roman Empire)": "Fotheideal (m.sh. faoin Impireacht Rómhánach)",
+	"Success": "Rath",
+	"Successfully updated.": "Nuashonraithe go rathúil.",
+	"Suggested": "Molta",
+	"Support": "Tacaíocht",
+	"Support this plugin:": "Tacaigh leis an mbreiseán seo:",
+	"Sync directory": "Eolaire sioncronaithe",
+	"System": "Córas",
+	"System Instructions": "Treoracha Córas",
+	"System Prompt": "Córas Pras",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Clibeanna Giniúint Pras",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Úsáidtear sampláil saor ó eireabaill chun tionchar na n-chomharthaí ón aschur nach bhfuil chomh dóchúil céanna a laghdú. Laghdóidh luach níos airde (m.sh., 2.0) an tionchar níos mó, agus díchumasaíonn luach 1.0 an socrú seo. (réamhshocraithe: 1)",
+	"Tap to interrupt": "Tapáil chun cur isteach",
+	"Tavily API Key": "Eochair API Tavily",
+	"Tell us more:": "Inis dúinn níos mó:",
+	"Temperature": "Teocht",
+	"Template": "Teimpléad",
+	"Temporary Chat": "Comhrá Sealadach",
+	"Text Splitter": "Scoilteoir Téacs",
+	"Text-to-Speech Engine": "Inneall téacs-go-labhra",
+	"Tfs Z": "TFS Z",
+	"Thanks for your feedback!": "Go raibh maith agat as do chuid aiseolas!",
+	"The Application Account DN you bind with for search": "An Cuntas Feidhmchláir DN a nascann tú leis le haghaidh cuardaigh",
+	"The base to search for users": "An bonn chun cuardach a dhéanamh ar úsáideoirí",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Cinneann méid an bhaisc cé mhéad iarratas téacs a phróiseáiltear le chéile ag an am céanna. Is féidir le méid baisc níos airde feidhmíocht agus luas an mhúnla a mhéadú, ach éilíonn sé níos mó cuimhne freisin. (Réamhshocrú: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Is deonacha paiseanta ón bpobal iad na forbróirí taobh thiar den bhreiseán seo. Má aimsíonn an breiseán seo cabhrach leat, smaoinigh ar rannchuidiú lena fhorbairt.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Tá an clár ceannairí meastóireachta bunaithe ar chóras rátála Elo agus déantar é a nuashonrú i bhfíor-am.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "An tréith LDAP a mhapálann don ainm úsáideora a úsáideann úsáideoirí chun síniú isteach.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Tá an clár ceannairí i béite faoi láthair, agus d'fhéadfaimis na ríomhanna rátála a choigeartú de réir mar a dhéanfaimid an t-algartam a bheachtú.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Uasmhéid an chomhaid i MB. Má sháraíonn méid an chomhaid an teorainn seo, ní uaslódófar an comhad.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "An líon uasta na gcomhaid is féidir a úsáid ag an am céanna i gcomhrá. Má sháraíonn líon na gcomhaid an teorainn seo, ní uaslódófar na comhaid.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Ba chóir go mbeadh an scór ina luach idir 0.0 (0%) agus 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Teocht an mhúnla. Déanfaidh méadú ar an teocht an freagra múnla níos cruthaithí. (Réamhshocrú: 0.8)",
+	"Theme": "Téama",
+	"Thinking...": "Smaointeoireacht...",
+	"This action cannot be undone. Do you wish to continue?": "Ní féidir an gníomh seo a chur ar ais. Ar mhaith leat leanúint ar aghaidh?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Cinntíonn sé seo go sábhálfar do chomhráite luachmhara go daingean i do bhunachar sonraí cúltaca Go raibh maith agat!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Is gné turgnamhach í seo, b'fhéidir nach bhfeidhmeoidh sé mar a bhíothas ag súil leis agus tá sé faoi réir athraithe ag am ar bith.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Rialaíonn an rogha seo cé mhéad comhartha a chaomhnaítear agus an comhthéacs á athnuachan. Mar shampla, má shocraítear go 2 é, coinneofar an 2 chomhartha dheireanacha de chomhthéacs an chomhrá. Is féidir le comhthéacs a chaomhnú cabhrú le leanúnachas comhrá a choinneáil, ach d’fhéadfadh sé laghdú a dhéanamh ar an gcumas freagairt do thopaicí nua. (Réamhshocrú: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Socraíonn an rogha seo an t-uaslíon comharthaí is féidir leis an tsamhail a ghiniúint ina fhreagra. Tríd an teorainn seo a mhéadú is féidir leis an tsamhail freagraí níos faide a sholáthar, ach d’fhéadfadh go méadódh sé an dóchúlacht go nginfear ábhar neamhchabhrach nó nach mbaineann le hábhar. (Réamhshocrú: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Scriosfaidh an rogha seo gach comhad atá sa bhailiúchán agus cuirfear comhaid nua-uaslódála ina n-ionad.",
+	"This response was generated by \"{{model}}\"": "Gin an freagra seo ag \"{{model}}\"",
+	"This will delete": "Scriosfaidh sé seo",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Scriosfaidh sé seo <strong>{{NAME}}</strong> agus <strong>a bhfuil ann go léir</strong>.",
+	"This will delete all models including custom models": "Scriosfaidh sé seo gach múnla lena n-áirítear samhlacha saincheaptha",
+	"This will delete all models including custom models and cannot be undone.": "Scriosfaidh sé seo gach samhail lena n-áirítear samhlacha saincheaptha agus ní féidir é a chealú.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Déanfaidh sé seo an bonn eolais a athshocrú agus gach comhad a shioncronú. Ar mhaith leat leanúint ar aghaidh?",
+	"Thorough explanation": "Míniú críochnúil",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Teastaíonn URL Freastalaí Tika.",
+	"Tiktoken": "Tictoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Leid: Nuashonraigh sliotáin iolracha athróg as a chéile trí bhrú ar an eochair cluaisín san ionchur comhrá tar éis gach athsholáthair.",
+	"Title": "Teideal",
+	"Title (e.g. Tell me a fun fact)": "Teideal (m.sh. inis dom fíric spraíúil)",
+	"Title Auto-Generation": "Teideal Auto-Generation",
+	"Title cannot be an empty string.": "Ní féidir leis an teideal a bheith ina teaghrán folamh.",
+	"Title Generation Prompt": "Pras Giniúint Teideal",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Chun teacht ar na hainmneacha múnla atá ar fáil le híoslódáil,",
+	"To access the GGUF models available for downloading,": "Chun rochtain a fháil ar na múnlaí GGUF atá ar fáil le híoslódáil,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Chun rochtain a fháil ar an WebUI, déan teagmháil leis an riarthóir le do thoil. Is féidir le riarthóirí stádas úsáideora a bhainistiú ón bPainéal Riaracháin.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Chun an bonn eolais a cheangal anseo, cuir leis an spás oibre \"Eolas\" iad ar dtús.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Chun do phríobháideachas a chosaint, ní roinntear ach rátálacha, aitheantais mhúnla, clibeanna agus meiteashonraí ó d’aiseolas - fanann do logaí comhrá príobháideach agus níl siad san áireamh.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Chun gníomhartha a roghnú anseo, cuir iad leis an spás oibre \"Feidhmeanna\" ar dtús.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Chun scagairí a roghnú anseo, cuir iad leis an spás oibre \"Feidhmeanna\" ar dtús.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Chun trealamh uirlisí a roghnú anseo, cuir iad leis an spás oibre \"Uirlisí\" ar dtús.",
+	"Toast notifications for new updates": "Fógraí tósta le haghaidh nuashonruithe nua",
+	"Today": "Inniu",
+	"Toggle settings": "Socraigh socruithe",
+	"Toggle sidebar": "Athraigh barra taobh",
+	"Token": "Comhartha",
+	"Tokens To Keep On Context Refresh (num_keep)": "Comharthaí le Coinneáil ar Athnuachan Comhthéacs (num_keep)",
+	"Too verbose": "Ró-fhocal",
+	"Tool created successfully": "Uirlis cruthaithe go rathúil",
+	"Tool deleted successfully": "Uirlis scriosta go rathúil",
+	"Tool Description": "Cur síos ar an Uirlis",
+	"Tool ID": "ID Uirlis",
+	"Tool imported successfully": "Uirlis iompórtáilte",
+	"Tool Name": "Ainm Uirlis",
+	"Tool updated successfully": "An uirlis nuashonraithe",
+	"Tools": "Uirlisí",
+	"Tools Access": "Rochtain Uirlisí",
+	"Tools are a function calling system with arbitrary code execution": "Is córas glaonna feidhme iad uirlisí le forghníomhú cód treallach",
+	"Tools have a function calling system that allows arbitrary code execution": "Tá córas glaonna feidhme ag uirlisí a cheadaíonn forghníomhú cód treallach",
+	"Tools have a function calling system that allows arbitrary code execution.": "Tá córas glaonna feidhme ag uirlisí a cheadaíonn forghníomhú cód treallach.",
+	"Top K": "Barr K",
+	"Top P": "Barr P",
+	"Transformers": "Claochladáin",
+	"Trouble accessing Ollama?": "Deacracht teacht ar Ollama?",
+	"TTS Model": "TTS Múnla",
+	"TTS Settings": "Socruithe TTS",
+	"TTS Voice": "Guth TTS",
+	"Type": "Cineál",
+	"Type Hugging Face Resolve (Download) URL": "Cineál Hugging Face Resolve (Íoslódáil) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Bhí ceist ann ag nascadh le {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Díchartlannaigh Uile",
+	"Unarchive All Archived Chats": "Díchartlannaigh Gach Comhrá Cartlainne",
+	"Unarchive Chat": "Comhrá a dhíchartlannú",
+	"Unlock mysteries": "Díghlasáil rúndiamhra",
+	"Unpin": "Díphoráil",
+	"Unravel secrets": "Rúin a réiteach",
+	"Untagged": "Gan chlib",
+	"Update": "Nuashonraigh",
+	"Update and Copy Link": "Nuashonraigh agus Cóipeáil Nasc",
+	"Update for the latest features and improvements.": "Nuashonrú le haghaidh na gnéithe agus na feabhsuithe is déanaí.",
+	"Update password": "Nuashonrú pasfhocal",
+	"Updated": "Nuashonraithe",
+	"Updated at": "Nuashonraithe ag",
+	"Updated At": "Nuashonraithe Ag",
+	"Upload": "Uaslódáil",
+	"Upload a GGUF model": "Uaslódáil samhail GGUF",
+	"Upload directory": "Uaslódáil eolaire",
+	"Upload files": "Uaslódáil comhaid",
+	"Upload Files": "Uaslódáil Comhaid",
+	"Upload Pipeline": "Uaslódáil píblíne",
+	"Upload Progress": "Uaslódáil an Dul",
+	"URL": "URL",
+	"URL Mode": "Mód URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Úsáid '#' san ionchur pras chun do chuid eolais a lódáil agus a chur san áireamh.",
+	"Use Gravatar": "Úsáid Gravatar",
+	"Use groups to group your users and assign permissions.": "Úsáid grúpaí chun d'úsáideoirí a ghrúpáil agus ceadanna a shannadh",
+	"Use Initials": "Úsáid ceannlitreacha",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "úsáideoir",
+	"User": "Úsáideoir",
+	"User location successfully retrieved.": "Fuarthas suíomh an úsáideora go rathúil.",
+	"Username": "Ainm Úsáideora",
+	"Users": "Úsáideoirí",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Ag baint úsáide as an múnla réimse réamhshocraithe le gach múnlaí. Cliceáil ar an gcnaipe móide chun múnlaí saincheaptha a chur leis.",
+	"Utilize": "Úsáid",
+	"Valid time units:": "Aonaid ama bailí:",
+	"Valves": "Comhlaí",
+	"Valves updated": "Comhlaí dáta",
+	"Valves updated successfully": "Comhlaí nuashonraíodh",
+	"variable": "athraitheach",
+	"variable to have them replaced with clipboard content.": "athróg chun ábhar gearrthaisce a chur in ionad iad.",
+	"Version": "Leagan",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Leagan {{selectedVersion}} de {{totalVersions}}",
+	"Visibility": "Infheictheacht",
+	"Voice": "Guth",
+	"Voice Input": "Ionchur Gutha",
+	"Warning": "Rabhadh",
+	"Warning:": "Rabhadh:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Rabhadh: Cuirfidh sé seo ar chumas úsáideoirí cód treallach a uaslódáil ar an bhfreastalaí.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Rabhadh: Má nuashonraíonn tú nó má athraíonn tú do mhúnla leabaithe, beidh ort gach doiciméad a athiompórtáil.",
+	"Web": "Gréasán",
+	"Web API": "API Gréasáin",
+	"Web Loader Settings": "Socruithe Luchtaire Gréasáin",
+	"Web Search": "Cuardach Gréasáin",
+	"Web Search Engine": "Inneall Cuardaigh Gréasáin",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL Webhook",
+	"WebUI Settings": "Socruithe WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "Déanfaidh WebUI iarratais ar \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "Déanfaidh WebUI iarratais ar \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Cad atá tú ag iarraidh a bhaint amach?",
+	"What are you working on?": "Cad air a bhfuil tú ag obair?",
+	"What’s New in": "Cad atá Nua i",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Nuair a bheidh sé cumasaithe, freagróidh an tsamhail gach teachtaireacht comhrá i bhfíor-am, ag giniúint freagra a luaithe a sheolann an t-úsáideoir teachtaireacht. Tá an mód seo úsáideach le haghaidh feidhmchláir chomhrá beo, ach d’fhéadfadh tionchar a bheith aige ar fheidhmíocht ar chrua-earraí níos moille.",
+	"wherever you are": "aon áit a bhfuil tú",
+	"Whisper (Local)": "Whisper (Áitiúil)",
+	"Why?": "Cén fáth?",
+	"Widescreen Mode": "Mód Leathanscáileán",
+	"Won": "Bhuaigh",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Oibríonn sé le barr-k. Beidh téacs níos éagsúla mar thoradh ar luach níos airde (m.sh., 0.95), agus ginfidh luach níos ísle (m.sh., 0.5) téacs níos dírithe agus níos coimeádaí. (Réamhshocrú: 0.9)",
+	"Workspace": "Spás oibre",
+	"Workspace Permissions": "Ceadanna Spás Oibre",
+	"Write a prompt suggestion (e.g. Who are you?)": "Scríobh moladh pras (m.sh. Cé hé tú?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Scríobh achoimre i 50 focal a dhéanann achoimre ar [ábhar nó eochairfhocal].",
+	"Write something...": "Scríobh rud...",
+	"Write your model template content here": "Scríobh do mhúnla ábhar teimpléad anseo",
+	"Yesterday": "Inné",
+	"You": "Tú",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Ní féidir leat comhrá a dhéanamh ach le comhad {{maxCount}} ar a mhéad ag an am.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Is féidir leat do chuid idirghníomhaíochtaí le LLManna a phearsantú ach cuimhní cinn a chur leis tríd an gcnaipe 'Bainistigh' thíos, rud a fhágann go mbeidh siad níos cabhrach agus níos oiriúnaí duit.",
+	"You cannot upload an empty file.": "Ní féidir leat comhad folamh a uaslódáil.",
+	"You do not have permission to upload files.": "Níl cead agat comhaid a uaslódáil.",
+	"You have no archived conversations.": "Níl aon chomhráite cartlainne agat.",
+	"You have shared this chat": "Tá an comhrá seo roinnte agat",
+	"You're a helpful assistant.": "Is cúntóir cabhrach tú.",
+	"You're now logged in.": "Tá tú logáilte isteach anois.",
+	"Your account status is currently pending activation.": "Tá stádas do chuntais ar feitheamh faoi ghníomhachtú.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Rachaidh do ranníocaíocht iomlán go díreach chuig an bhforbróir breiseán; Ní ghlacann Open WebUI aon chéatadán. Mar sin féin, d'fhéadfadh a tháillí féin a bheith ag an ardán maoinithe roghnaithe.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Socruithe Luchtaire Youtube"
+}
diff --git a/src/lib/i18n/locales/it-IT/translation.json b/src/lib/i18n/locales/it-IT/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..0d57aecbf57a9124615117de3e00be1aa5439c50
--- /dev/null
+++ b/src/lib/i18n/locales/it-IT/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' o '-1' per nessuna scadenza.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(p.e. `sh webui.sh --api`)",
+	"(latest)": "(ultima)",
+	"{{ models }}": "{{ modelli }}",
+	"{{user}}'s Chats": "{{user}} Chat",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend richiesto",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un modello di attività viene utilizzato durante l'esecuzione di attività come la generazione di titoli per chat e query di ricerca Web",
+	"a user": "un utente",
+	"About": "Informazioni",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Account",
+	"Account Activation Pending": "",
+	"Accurate information": "Informazioni accurate",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "Aggiungi",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Aggiungi una breve descrizione di ciò che fa questo modello",
+	"Add a tag": "Aggiungi un tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Aggiungi un prompt custom",
+	"Add Files": "Aggiungi file",
+	"Add Group": "",
+	"Add Memory": "Aggiungi memoria",
+	"Add Model": "Aggiungi modello",
+	"Add Tag": "",
+	"Add Tags": "Aggiungi tag",
+	"Add text content": "",
+	"Add User": "Aggiungi utente",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "La modifica di queste impostazioni applicherà le modifiche universalmente a tutti gli utenti.",
+	"admin": "amministratore",
+	"Admin": "",
+	"Admin Panel": "Pannello di amministrazione",
+	"Admin Settings": "Impostazioni amministratore",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Parametri avanzati",
+	"Advanced Params": "Parametri avanzati",
+	"All chats": "",
+	"All Documents": "Tutti i documenti",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Consenti l'eliminazione della chat",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Hai già un account?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "un assistente",
+	"and": "e",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "e crea un nuovo link condiviso.",
+	"API Base URL": "URL base API",
+	"API Key": "Chiave API",
+	"API Key created.": "Chiave API creata.",
+	"API keys": "Chiavi API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Aprile",
+	"Archive": "Archivio",
+	"Archive All Chats": "Archivia tutte le chat",
+	"Archived Chats": "Chat archiviate",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Sei sicuro?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Allega file",
+	"Attention to detail": "Attenzione ai dettagli",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Agosto",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Copia automatica della risposta negli appunti",
+	"Auto-playback response": "Riproduzione automatica della risposta",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "URL base AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "L'URL base AUTOMATIC1111 è obbligatorio.",
+	"Available list": "",
+	"available!": "disponibile!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Indietro",
+	"Bad Response": "Risposta non valida",
+	"Banners": "Banner",
+	"Base Model (From)": "Modello base (da)",
+	"Batch Size (num_batch)": "",
+	"before": "prima",
+	"Being lazy": "Essere pigri",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Chiave API di ricerca Brave",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Aggira la verifica SSL per i siti web",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Annulla",
+	"Capabilities": "Funzionalità",
+	"Certificate Path": "",
+	"Change Password": "Cambia password",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chat",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "UI bolle chat",
+	"Chat Controls": "",
+	"Chat direction": "Direzione chat",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chat",
+	"Check Again": "Controlla di nuovo",
+	"Check for updates": "Controlla aggiornamenti",
+	"Checking for updates...": "Controllo aggiornamenti...",
+	"Choose a model before saving...": "Scegli un modello prima di salvare...",
+	"Chunk Overlap": "Sovrapposizione chunk",
+	"Chunk Params": "Parametri chunk",
+	"Chunk Size": "Dimensione chunk",
+	"Ciphers": "",
+	"Citation": "Citazione",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Clicca qui per aiuto.",
+	"Click here to": "Clicca qui per",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Clicca qui per selezionare",
+	"Click here to select a csv file.": "Clicca qui per selezionare un file csv.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "clicca qui.",
+	"Click on the user role button to change a user's role.": "Clicca sul pulsante del ruolo utente per modificare il ruolo di un utente.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Clone",
+	"Close": "Chiudi",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Collezione",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL base ComfyUI",
+	"ComfyUI Base URL is required.": "L'URL base ComfyUI è obbligatorio.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Comando",
+	"Completions": "",
+	"Concurrent Requests": "Richieste simultanee",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Conferma password",
+	"Confirm your action": "",
+	"Connections": "Connessioni",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Contenuto",
+	"Content Extraction": "",
+	"Context Length": "Lunghezza contesto",
+	"Continue Response": "Continua risposta",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "URL della chat condivisa copiato negli appunti!",
+	"Copied to clipboard": "",
+	"Copy": "Copia",
+	"Copy last code block": "Copia ultimo blocco di codice",
+	"Copy last response": "Copia ultima risposta",
+	"Copy Link": "Copia link",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Copia negli appunti riuscita!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Creare un modello",
+	"Create Account": "Crea account",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Crea nuova chiave",
+	"Create new secret key": "Crea nuova chiave segreta",
+	"Created at": "Creato il",
+	"Created At": "Creato il",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Modello corrente",
+	"Current Password": "Password corrente",
+	"Custom": "Personalizzato",
+	"Dark": "Scuro",
+	"Database": "Database",
+	"December": "Dicembre",
+	"Default": "Predefinito",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Predefinito (SentenceTransformers)",
+	"Default Model": "Modello di default",
+	"Default model updated": "Modello predefinito aggiornato",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Suggerimenti prompt predefiniti",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Ruolo utente predefinito",
+	"Delete": "Elimina",
+	"Delete a model": "Elimina un modello",
+	"Delete All Chats": "Elimina tutte le chat",
+	"Delete All Models": "",
+	"Delete chat": "Elimina chat",
+	"Delete Chat": "Elimina chat",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "elimina questo link",
+	"Delete tool?": "",
+	"Delete User": "Elimina utente",
+	"Deleted {{deleteModelTag}}": "Eliminato {{deleteModelTag}}",
+	"Deleted {{name}}": "Eliminato {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Descrizione",
+	"Didn't fully follow instructions": "Non ha seguito completamente le istruzioni",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Scopri un modello",
+	"Discover a prompt": "Scopri un prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Scopri, scarica ed esplora prompt personalizzati",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Scopri, scarica ed esplora i preset del modello",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Visualizza il nome utente invece di Tu nella chat",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Documento",
+	"Documentation": "",
+	"Documents": "Documenti",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "non effettua connessioni esterne e i tuoi dati rimangono al sicuro sul tuo server ospitato localmente.",
+	"Don't have an account?": "Non hai un account?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Non ti piace lo stile",
+	"Done": "",
+	"Download": "Scarica",
+	"Download canceled": "Scaricamento annullato",
+	"Download Database": "Scarica database",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Trascina qui i file da aggiungere alla conversazione",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ad esempio '30s','10m'. Le unità di tempo valide sono 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Modifica",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Modifica utente",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Modello di embedding",
+	"Embedding Model Engine": "Motore del modello di embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Modello di embedding impostato su \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Abilita la condivisione della community",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Abilita nuove iscrizioni",
+	"Enable Web Search": "Abilita ricerca Web",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assicurati che il tuo file CSV includa 4 colonne in questo ordine: Nome, Email, Password, Ruolo.",
+	"Enter {{role}} message here": "Inserisci il messaggio per {{role}} qui",
+	"Enter a detail about yourself for your LLMs to recall": "Inserisci un dettaglio su di te per che i LLM possano ricordare",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Inserisci la chiave API di Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Inserisci la sovrapposizione chunk",
+	"Enter Chunk Size": "Inserisci la dimensione chunk",
+	"Enter description": "",
+	"Enter Github Raw URL": "Immettere l'URL grezzo di Github",
+	"Enter Google PSE API Key": "Inserisci la chiave API PSE di Google",
+	"Enter Google PSE Engine Id": "Inserisci l'ID motore PSE di Google",
+	"Enter Image Size (e.g. 512x512)": "Inserisci la dimensione dell'immagine (ad esempio 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Inserisci i codici lingua",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Inserisci il tag del modello (ad esempio {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Inserisci il numero di passaggi (ad esempio 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Inserisci il punteggio",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Immettere l'URL della query Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Inserisci la chiave API Serper",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Inserisci la chiave API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Inserisci la sequenza di arresto",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Inserisci Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Inserisci URL (ad esempio http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Inserisci URL (ad esempio http://localhost:11434)",
+	"Enter Your Email": "Inserisci la tua email",
+	"Enter Your Full Name": "Inserisci il tuo nome completo",
+	"Enter your message": "",
+	"Enter Your Password": "Inserisci la tua password",
+	"Enter Your Role": "Inserisci il tuo ruolo",
+	"Enter Your Username": "",
+	"Error": "Errore",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Sperimentale",
+	"Explore the cosmos": "",
+	"Export": "Esportazione",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Esporta tutte le chat (tutti gli utenti)",
+	"Export chat (.json)": "",
+	"Export Chats": "Esporta chat",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Esporta modelli",
+	"Export Presets": "",
+	"Export Prompts": "Esporta prompt",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Impossibile creare la chiave API.",
+	"Failed to read clipboard contents": "Impossibile leggere il contenuto degli appunti",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "Febbraio",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Sentiti libero/a di aggiungere dettagli specifici",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Modalità file",
+	"File not found.": "File non trovato.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Rilevato spoofing delle impronte digitali: impossibile utilizzare le iniziali come avatar. Ripristino all'immagine del profilo predefinita.",
+	"Fluidly stream large external response chunks": "Trasmetti in modo fluido blocchi di risposta esterni di grandi dimensioni",
+	"Focus chat input": "Metti a fuoco l'input della chat",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Ha seguito le istruzioni alla perfezione",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Penalità di frequenza",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Generale",
+	"General Settings": "Impostazioni generali",
+	"Generate Image": "",
+	"Generating search query": "Generazione di query di ricerca",
+	"Generation Info": "Informazioni generazione",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Buona risposta",
+	"Google PSE API Key": "Chiave API PSE di Google",
+	"Google PSE Engine Id": "ID motore PSE di Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "non ha conversazioni.",
+	"Hello, {{name}}": "Ciao, {{name}}",
+	"Help": "Aiuto",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Nascondi",
+	"Host": "",
+	"How can I help you today?": "Come posso aiutarti oggi?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Ricerca ibrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generazione di immagini (sperimentale)",
+	"Image Generation Engine": "Motore di generazione immagini",
+	"Image Settings": "Impostazioni immagine",
+	"Images": "Immagini",
+	"Import Chats": "Importa chat",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Importazione di modelli",
+	"Import Presets": "",
+	"Import Prompts": "Importa prompt",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Includi il flag `--api` quando esegui stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informazioni",
+	"Input commands": "Comandi di input",
+	"Install from Github URL": "Eseguire l'installazione dall'URL di Github",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Interfaccia",
+	"Invalid file format.": "",
+	"Invalid Tag": "Tag non valido",
+	"January": "Gennaio",
+	"Jina API Key": "",
+	"join our Discord for help.": "unisciti al nostro Discord per ricevere aiuto.",
+	"JSON": "JSON",
+	"JSON Preview": "Anteprima JSON",
+	"July": "Luglio",
+	"June": "Giugno",
+	"JWT Expiration": "Scadenza JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Mantieni attivo",
+	"Key": "",
+	"Keyboard shortcuts": "Scorciatoie da tastiera",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Lingua",
+	"Last Active": "Ultima attività",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Chiaro",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "Gli LLM possono commettere errori. Verifica le informazioni importanti.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Realizzato dalla comunità OpenWebUI",
+	"Make sure to enclose them with": "Assicurati di racchiuderli con",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Gestire le pipeline",
+	"March": "Marzo",
+	"Max Tokens (num_predict)": "Numero massimo di gettoni (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "È possibile scaricare un massimo di 3 modelli contemporaneamente. Riprova più tardi.",
+	"May": "Maggio",
+	"Memories accessible by LLMs will be shown here.": "I memori accessibili ai LLM saranno mostrati qui.",
+	"Memory": "Memoria",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "I messaggi inviati dopo la creazione del link non verranno condivisi. Gli utenti con l'URL saranno in grado di visualizzare la chat condivisa.",
+	"Min P": "",
+	"Minimum Score": "Punteggio minimo",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Il modello '{{modelName}}' è stato scaricato con successo.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Il modello '{{modelTag}}' è già in coda per il download.",
+	"Model {{modelId}} not found": "Modello {{modelId}} non trovato",
+	"Model {{modelName}} is not vision capable": "Il modello {{modelName}} non è in grado di vedere",
+	"Model {{name}} is now {{status}}": "Il modello {{name}} è ora {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Percorso del filesystem del modello rilevato. Il nome breve del modello è richiesto per l'aggiornamento, impossibile continuare.",
+	"Model Filtering": "",
+	"Model ID": "ID modello",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modello non selezionato",
+	"Model Params": "Parametri del modello",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Contenuto del file modello",
+	"Models": "Modelli",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Altro",
+	"Name": "Nome",
+	"Name your knowledge base": "",
+	"New Chat": "Nuova chat",
+	"New folder": "",
+	"New Password": "Nuova password",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Nessun risultato trovato",
+	"No search query generated": "Nessuna query di ricerca generata",
+	"No source available": "Nessuna fonte disponibile",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Nessuno",
+	"Not factually correct": "Non corretto dal punto di vista fattuale",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: se imposti un punteggio minimo, la ricerca restituirà solo i documenti con un punteggio maggiore o uguale al punteggio minimo.",
+	"Notes": "",
+	"Notifications": "Notifiche desktop",
+	"November": "Novembre",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Ottobre",
+	"Off": "Disattivato",
+	"Okay, Let's Go!": "Ok, andiamo!",
+	"OLED Dark": "OLED scuro",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API Ollama disabilitata",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versione Ollama",
+	"On": "Attivato",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Nella stringa di comando sono consentiti solo caratteri alfanumerici e trattini.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ops! Sembra che l'URL non sia valido. Si prega di ricontrollare e riprovare.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ops! Stai utilizzando un metodo non supportato (solo frontend). Si prega di servire la WebUI dal backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Apri nuova chat",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Configurazione API OpenAI",
+	"OpenAI API Key is required.": "La chiave API OpenAI è obbligatoria.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Chiave OpenAI obbligatori.",
+	"or": "o",
+	"Organize your users": "",
+	"Other": "Altro",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Password",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Documento PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Estrazione immagini PDF (OCR)",
+	"pending": "in sospeso",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Autorizzazione negata durante l'accesso al microfono: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalizzazione",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Condutture",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Valvole per tubazioni",
+	"Plain text (.txt)": "Testo normale (.txt)",
+	"Playground": "Terreno di gioco",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Attitudine positiva",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Ultimi 30 giorni",
+	"Previous 7 days": "Ultimi 7 giorni",
+	"Profile Image": "Immagine del profilo",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (ad esempio Dimmi un fatto divertente sull'Impero Romano)",
+	"Prompt Content": "Contenuto del prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Suggerimenti prompt",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompt",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Estrai \"{{searchValue}}\" da Ollama.com",
+	"Pull a model from Ollama.com": "Estrai un modello da Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parametri query",
+	"RAG Template": "Modello RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Leggi ad alta voce",
+	"Record voice": "Registra voce",
+	"Redirecting you to OpenWebUI Community": "Reindirizzamento alla comunità OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "Rifiutato quando non avrebbe dovuto",
+	"Regenerate": "Rigenera",
+	"Release Notes": "Note di rilascio",
+	"Relevance": "",
+	"Remove": "Rimuovi",
+	"Remove Model": "Rimuovi modello",
+	"Rename": "Rinomina",
+	"Reorder Models": "",
+	"Repeat Last N": "Ripeti ultimi N",
+	"Request Mode": "Modalità richiesta",
+	"Reranking Model": "Modello di riclassificazione",
+	"Reranking model disabled": "Modello di riclassificazione disabilitato",
+	"Reranking model set to \"{{reranking_model}}\"": "Modello di riclassificazione impostato su \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Ruolo",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "Salva",
+	"Save & Create": "Salva e crea",
+	"Save & Update": "Salva e aggiorna",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Il salvataggio dei registri della chat direttamente nell'archivio del browser non è più supportato. Si prega di dedicare un momento per scaricare ed eliminare i registri della chat facendo clic sul pulsante in basso. Non preoccuparti, puoi facilmente reimportare i registri della chat nel backend tramite",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Cerca",
+	"Search a model": "Cerca un modello",
+	"Search Base": "",
+	"Search Chats": "Cerca nelle chat",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Cerca modelli",
+	"Search options": "",
+	"Search Prompts": "Cerca prompt",
+	"Search Result Count": "Conteggio dei risultati della ricerca",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Ricercato {{count}} sites_one",
+	"Searched {{count}} sites_many": "Ricercato {{count}} sites_many",
+	"Searched {{count}} sites_other": "Ricercato {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng Query URL",
+	"See readme.md for instructions": "Vedi readme.md per le istruzioni",
+	"See what's new": "Guarda le novità",
+	"Seed": "Seme",
+	"Select a base model": "Selezionare un modello di base",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Seleziona un modello",
+	"Select a pipeline": "Selezionare una tubazione",
+	"Select a pipeline url": "Selezionare l'URL di una pipeline",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Seleziona modello",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "I modelli selezionati non supportano l'input di immagini",
+	"Semantic distance to query": "",
+	"Send": "Invia",
+	"Send a Message": "Invia un messaggio",
+	"Send message": "Invia messaggio",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Settembre",
+	"Serper API Key": "Chiave API Serper",
+	"Serply API Key": "",
+	"Serpstack API Key": "Chiave API Serpstack",
+	"Server connection verified": "Connessione al server verificata",
+	"Set as default": "Imposta come predefinito",
+	"Set CFG Scale": "",
+	"Set Default Model": "Imposta modello predefinito",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Imposta modello di embedding (ad esempio {{model}})",
+	"Set Image Size": "Imposta dimensione immagine",
+	"Set reranking model (e.g. {{model}})": "Imposta modello di riclassificazione (ad esempio {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Imposta passaggi",
+	"Set Task Model": "Imposta modello di attività",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Imposta voce",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Impostazioni",
+	"Settings saved successfully!": "Impostazioni salvate con successo!",
+	"Share": "Condividi",
+	"Share Chat": "Condividi chat",
+	"Share to OpenWebUI Community": "Condividi con la comunità OpenWebUI",
+	"Show": "Mostra",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Mostra",
+	"Show your support!": "",
+	"Showcased creativity": "Creatività messa in mostra",
+	"Sign in": "Accedi",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Esci",
+	"Sign up": "Registrati",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Fonte",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Errore di riconoscimento vocale: {{error}}",
+	"Speech-to-Text Engine": "Motore da voce a testo",
+	"Stop": "",
+	"Stop Sequence": "Sequenza di arresto",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "Impostazioni STT",
+	"Subtitle (e.g. about the Roman Empire)": "Sottotitolo (ad esempio sull'Impero Romano)",
+	"Success": "Successo",
+	"Successfully updated.": "Aggiornato con successo.",
+	"Suggested": "Suggerito",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Sistema",
+	"System Instructions": "",
+	"System Prompt": "Prompt di sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Raccontaci di più:",
+	"Temperature": "Temperatura",
+	"Template": "Modello",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Motore da testo a voce",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Grazie per il tuo feedback!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Il punteggio dovrebbe essere un valore compreso tra 0.0 (0%) e 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ciò garantisce che le tue preziose conversazioni siano salvate in modo sicuro nel tuo database backend. Grazie!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Spiegazione dettagliata",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Suggerimento: aggiorna più slot di variabili consecutivamente premendo il tasto tab nell'input della chat dopo ogni sostituzione.",
+	"Title": "Titolo",
+	"Title (e.g. Tell me a fun fact)": "Titolo (ad esempio Dimmi un fatto divertente)",
+	"Title Auto-Generation": "Generazione automatica del titolo",
+	"Title cannot be an empty string.": "Il titolo non può essere una stringa vuota.",
+	"Title Generation Prompt": "Prompt di generazione del titolo",
+	"TLS": "",
+	"To access the available model names for downloading,": "Per accedere ai nomi dei modelli disponibili per il download,",
+	"To access the GGUF models available for downloading,": "Per accedere ai modelli GGUF disponibili per il download,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Oggi",
+	"Toggle settings": "Attiva/disattiva impostazioni",
+	"Toggle sidebar": "Attiva/disattiva barra laterale",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemi di accesso a Ollama?",
+	"TTS Model": "",
+	"TTS Settings": "Impostazioni TTS",
+	"TTS Voice": "",
+	"Type": "Digitare",
+	"Type Hugging Face Resolve (Download) URL": "Digita l'URL di Hugging Face Resolve (Download)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Si è verificato un problema durante la connessione a {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Aggiorna e copia link",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Aggiorna password",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Carica un modello GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Carica file",
+	"Upload Pipeline": "",
+	"Upload Progress": "Avanzamento caricamento",
+	"URL": "",
+	"URL Mode": "Modalità URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Usa Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Usa iniziali",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "utente",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Utenti",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Utilizza",
+	"Valid time units:": "Unità di tempo valide:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "variabile",
+	"variable to have them replaced with clipboard content.": "variabile per farli sostituire con il contenuto degli appunti.",
+	"Version": "Versione",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Avvertimento",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Attenzione: se aggiorni o cambi il tuo modello di embedding, dovrai reimportare tutti i documenti.",
+	"Web": "Web",
+	"Web API": "",
+	"Web Loader Settings": "Impostazioni del caricatore Web",
+	"Web Search": "Ricerca sul Web",
+	"Web Search Engine": "Motore di ricerca Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL webhook",
+	"WebUI Settings": "Impostazioni WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Novità in",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Area di lavoro",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Scrivi un suggerimento per il prompt (ad esempio Chi sei?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Scrivi un riassunto in 50 parole che riassume [argomento o parola chiave].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Ieri",
+	"You": "Tu",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Non hai conversazioni archiviate.",
+	"You have shared this chat": "Hai condiviso questa chat",
+	"You're a helpful assistant.": "Sei un assistente utile.",
+	"You're now logged in.": "Ora hai effettuato l'accesso.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Impostazioni del caricatore Youtube"
+}
diff --git a/src/lib/i18n/locales/ja-JP/translation.json b/src/lib/i18n/locales/ja-JP/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..e1ed3d08f8f86d991b7f5ad7041c1333e3e70b5c
--- /dev/null
+++ b/src/lib/i18n/locales/ja-JP/translation.json
@@ -0,0 +1,1024 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' または '-1' で無期限。",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(例: `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(例: `sh webui.sh --api`)",
+	"(latest)": "(最新)",
+	"{{ models }}": "{{ モデル }}",
+	"{{user}}'s Chats": "{{user}} のチャット",
+	"{{webUIName}} Backend Required": "{{webUIName}} バックエンドが必要です",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "新しいバージョンが利用可能です。",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "タスクモデルは、チャットやウェブ検索クエリのタイトルの生成などのタスクを実行するときに使用されます",
+	"a user": "ユーザー",
+	"About": "概要",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "アカウント",
+	"Account Activation Pending": "アカウント承認待ち",
+	"Accurate information": "情報が正確",
+	"Actions": "アクション",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "アクティブユーザー",
+	"Add": "追加",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "このモデルの機能に関する簡単な説明を追加します",
+	"Add a tag": "タグを追加",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "コンテンツを追加",
+	"Add content here": "ここへコンテンツを追加",
+	"Add custom prompt": "カスタムプロンプトを追加",
+	"Add Files": "ファイルを追加",
+	"Add Group": "",
+	"Add Memory": "メモリを追加",
+	"Add Model": "モデルを追加",
+	"Add Tag": "タグを追加",
+	"Add Tags": "タグを追加",
+	"Add text content": "コンテンツを追加",
+	"Add User": "ユーザーを追加",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "これらの設定を調整すると、すべてのユーザーに変更が適用されます。",
+	"admin": "管理者",
+	"Admin": "管理者",
+	"Admin Panel": "管理者パネル",
+	"Admin Settings": "管理者設定",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "管理者は全てのツールにアクセス出来ます。ユーザーはワークスペースのモデル毎に割り当てて下さい。",
+	"Advanced Parameters": "詳細パラメーター",
+	"Advanced Params": "高度なパラメータ",
+	"All chats": "",
+	"All Documents": "全てのドキュメント",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "チャットの削除を許可",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "ローカル以外のボイスを許可",
+	"Allow Temporary Chat": "一時的なチャットを許可",
+	"Allow User Location": "ユーザーロケーションの許可",
+	"Allow Voice Interruption in Call": "通話中に音声の割り込みを許可",
+	"Already have an account?": "すでにアカウントをお持ちですか?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "アシスタント",
+	"and": "および",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "し、新しい共有リンクを作成します。",
+	"API Base URL": "API ベース URL",
+	"API Key": "API キー",
+	"API Key created.": "API キーが作成されました。",
+	"API keys": "API キー",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "4月",
+	"Archive": "アーカイブ",
+	"Archive All Chats": "すべてのチャットをアーカイブする",
+	"Archived Chats": "チャット記録",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "よろしいですか?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "質問して下さい。",
+	"Assistant": "",
+	"Attach file": "ファイルを添付する",
+	"Attention to detail": "詳細に注意する",
+	"Attribute for Username": "",
+	"Audio": "オーディオ",
+	"August": "8月",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "クリップボードへの応答の自動コピー",
+	"Auto-playback response": "応答の自動再生",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111のAuthを入力",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 ベース URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 ベース URL が必要です。",
+	"Available list": "利用可能リスト",
+	"available!": "利用可能!",
+	"Awful": "",
+	"Azure AI Speech": "AzureAIスピーチ",
+	"Azure Region": "Azureリージョン",
+	"Back": "戻る",
+	"Bad Response": "応答が悪い",
+	"Banners": "バナー",
+	"Base Model (From)": "ベースモデル (From)",
+	"Batch Size (num_batch)": "バッチサイズ (num_batch)",
+	"before": "より前",
+	"Being lazy": "怠惰な",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search APIキー",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "SSL 検証をバイパスする",
+	"Call": "コール",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "カメラ",
+	"Cancel": "キャンセル",
+	"Capabilities": "資格",
+	"Certificate Path": "",
+	"Change Password": "パスワードを変更",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "チャット",
+	"Chat Background Image": "チャットの背景画像",
+	"Chat Bubble UI": "チャットバブルUI",
+	"Chat Controls": "チャットコントロール",
+	"Chat direction": "チャットの方向",
+	"Chat Overview": "チャット概要",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "チャット",
+	"Check Again": "再確認",
+	"Check for updates": "アップデートを確認",
+	"Checking for updates...": "アップデートを確認しています...",
+	"Choose a model before saving...": "保存する前にモデルを選択してください...",
+	"Chunk Overlap": "チャンクオーバーラップ",
+	"Chunk Params": "チャンクパラメーター",
+	"Chunk Size": "チャンクサイズ",
+	"Ciphers": "",
+	"Citation": "引用文",
+	"Clear memory": "メモリをクリア",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "ヘルプについてはここをクリックしてください。",
+	"Click here to": "ここをクリックして",
+	"Click here to download user import template file.": "ユーザーテンプレートをインポートするにはここをクリックしてください。",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "選択するにはここをクリックしてください",
+	"Click here to select a csv file.": "CSVファイルを選択するにはここをクリックしてください。",
+	"Click here to select a py file.": "Pythonスクリプトファイルを選択するにはここをクリックしてください。",
+	"Click here to upload a workflow.json file.": "workflow.jsonファイルをアップロードするにはここをクリックしてください。",
+	"click here.": "ここをクリックしてください。",
+	"Click on the user role button to change a user's role.": "ユーザーの役割を変更するには、ユーザー役割ボタンをクリックしてください。",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "クリップボードへの書き込み許可がありません。ブラウザ設定を確認し許可してください。",
+	"Clone": "クローン",
+	"Close": "閉じる",
+	"Code execution": "",
+	"Code formatted successfully": "コードフォーマットに成功しました",
+	"Collection": "コレクション",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUIベースURL",
+	"ComfyUI Base URL is required.": "ComfyUIベースURLが必要です。",
+	"ComfyUI Workflow": "ComfyUIワークフロー",
+	"ComfyUI Workflow Nodes": "ComfyUIワークフローノード",
+	"Command": "コマンド",
+	"Completions": "",
+	"Concurrent Requests": "同時リクエスト",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "確認",
+	"Confirm Password": "パスワードを確認",
+	"Confirm your action": "あなたのアクションの確認",
+	"Connections": "接続",
+	"Contact Admin for WebUI Access": "WEBUIへの接続について管理者に問い合わせ下さい。",
+	"Content": "コンテンツ",
+	"Content Extraction": "コンテンツ抽出",
+	"Context Length": "コンテキストの長さ",
+	"Continue Response": "続きの応答",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "コントロール",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "コピー",
+	"Copied shared chat URL to clipboard!": "共有チャットURLをクリップボードにコピーしました!",
+	"Copied to clipboard": "クリップボードにコピーしました。",
+	"Copy": "コピー",
+	"Copy last code block": "最後のコードブロックをコピー",
+	"Copy last response": "最後の応答をコピー",
+	"Copy Link": "リンクをコピー",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "クリップボードへのコピーが成功しました!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "モデルを作成する",
+	"Create Account": "アカウントを作成",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "知識データ作成",
+	"Create new key": "新しいキーを作成",
+	"Create new secret key": "新しいシークレットキーを作成",
+	"Created at": "作成日時",
+	"Created At": "作成日時",
+	"Created by": "",
+	"CSV Import": "CSVインポート",
+	"Current Model": "現在のモデル",
+	"Current Password": "現在のパスワード",
+	"Custom": "カスタム",
+	"Dark": "ダーク",
+	"Database": "データベース",
+	"December": "12月",
+	"Default": "デフォルト",
+	"Default (Open AI)": "デフォルト(OpenAI)",
+	"Default (SentenceTransformers)": "デフォルト (SentenceTransformers)",
+	"Default Model": "デフォルトモデル",
+	"Default model updated": "デフォルトモデルが更新されました",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "デフォルトのプロンプトの提案",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "デフォルトのユーザー役割",
+	"Delete": "削除",
+	"Delete a model": "モデルを削除",
+	"Delete All Chats": "すべてのチャットを削除",
+	"Delete All Models": "",
+	"Delete chat": "チャットを削除",
+	"Delete Chat": "チャットを削除",
+	"Delete chat?": "チャットを削除しますか?",
+	"Delete folder?": "",
+	"Delete function?": "Functionを削除しますか?",
+	"Delete prompt?": "プロンプトを削除しますか?",
+	"delete this link": "このリンクを削除します",
+	"Delete tool?": "ツールを削除しますか?",
+	"Delete User": "ユーザーを削除",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} を削除しました",
+	"Deleted {{name}}": "{{name}}を削除しました",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "説明",
+	"Didn't fully follow instructions": "説明に沿って操作していませんでした",
+	"Disabled": "無効",
+	"Discover a function": "Functionを探す",
+	"Discover a model": "モデルを探す",
+	"Discover a prompt": "プロンプトを探す",
+	"Discover a tool": "ツールを探す",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "カスタムFunctionを探してダウンロードする",
+	"Discover, download, and explore custom prompts": "カスタムプロンプトを探してダウンロードする",
+	"Discover, download, and explore custom tools": "カスタムツールを探てしダウンロードする",
+	"Discover, download, and explore model presets": "モデルプリセットを探してダウンロードする",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "コールで絵文字を表示",
+	"Display the username instead of You in the Chat": "チャットで「あなた」の代わりにユーザー名を表示",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "信頼できないソースからFunctionをインストールしないでください。",
+	"Do not install tools from sources you do not fully trust.": "信頼出来ないソースからツールをインストールしないでください。",
+	"Document": "ドキュメント",
+	"Documentation": "ドキュメント",
+	"Documents": "ドキュメント",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "外部接続を行わず、データはローカルでホストされているサーバー上に安全に保持されます。",
+	"Don't have an account?": "アカウントをお持ちではありませんか?",
+	"don't install random functions from sources you don't trust.": "信頼出来ないソースからランダムFunctionをインストールしないでください。",
+	"don't install random tools from sources you don't trust.": "信頼出来ないソースからランダムツールをインストールしないでください。",
+	"Don't like the style": "デザインが好きでない",
+	"Done": "完了",
+	"Download": "ダウンロード",
+	"Download canceled": "ダウンロードをキャンセルしました",
+	"Download Database": "データベースをダウンロード",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "会話を追加するには、ここにファイルをドロップしてください",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例: '30秒'、'10分'。有効な時間単位は '秒'、'分'、'時間' です。",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "編集",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "メモリを編集",
+	"Edit User": "ユーザーを編集",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "メールアドレス",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "埋め込みモデルバッチサイズ",
+	"Embedding Model": "埋め込みモデル",
+	"Embedding Model Engine": "埋め込みモデルエンジン",
+	"Embedding model set to \"{{embedding_model}}\"": "埋め込みモデルを\"{{embedding_model}}\"に設定しました",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "コミュニティ共有を有効にする",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "メッセージ評価を有効にする",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "新規登録を有効にする",
+	"Enable Web Search": "ウェブ検索を有効にする",
+	"Enabled": "有効",
+	"Engine": "エンジン",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "CSVファイルに4つの列が含まれていることを確認してください: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "{{role}} メッセージをここに入力してください",
+	"Enter a detail about yourself for your LLMs to recall": "LLM が記憶するために、自分についての詳細を入力してください",
+	"Enter api auth string (e.g. username:password)": "API AuthStringを入力(例: Username:Password)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Brave Search APIキーの入力",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "CFGスケースを入力してください (例: 7.0)",
+	"Enter Chunk Overlap": "チャンクオーバーラップを入力してください",
+	"Enter Chunk Size": "チャンクサイズを入力してください",
+	"Enter description": "",
+	"Enter Github Raw URL": "Github Raw URLを入力",
+	"Enter Google PSE API Key": "Google PSE APIキーの入力",
+	"Enter Google PSE Engine Id": "Google PSE エンジン ID を入力します。",
+	"Enter Image Size (e.g. 512x512)": "画像サイズを入力してください (例: 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "言語コードを入力してください",
+	"Enter Model ID": "モデルIDを入力してください。",
+	"Enter model tag (e.g. {{modelTag}})": "モデルタグを入力してください (例: {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "ステップ数を入力してください (例: 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "サンプラーを入力してください(e.g. Euler a)。",
+	"Enter Scheduler (e.g. Karras)": "スケジューラーを入力してください。(e.g. Karras)",
+	"Enter Score": "スコアを入力してください",
+	"Enter SearchApi API Key": "SearchApi API Keyを入力してください。",
+	"Enter SearchApi Engine": "SearchApi Engineを入力してください。",
+	"Enter Searxng Query URL": "SearxngクエリURLを入力",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper APIキーの入力",
+	"Enter Serply API Key": "Serply API Keyを入力してください。",
+	"Enter Serpstack API Key": "Serpstack APIキーの入力",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "ストップシーケンスを入力してください",
+	"Enter system prompt": "システムプロンプト入力",
+	"Enter Tavily API Key": "Tavily API Keyを入力してください。",
+	"Enter Tika Server URL": "Tika Server URLを入力してください。",
+	"Enter Top K": "トップ K を入力してください",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "URL を入力してください (例: http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "URL を入力してください (例: http://localhost:11434)",
+	"Enter Your Email": "メールアドレスを入力してください",
+	"Enter Your Full Name": "フルネームを入力してください",
+	"Enter your message": "メッセージを入力してください",
+	"Enter Your Password": "パスワードを入力してください",
+	"Enter Your Role": "ロールを入力してください",
+	"Enter Your Username": "",
+	"Error": "エラー",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "実験的",
+	"Explore the cosmos": "",
+	"Export": "エクスポート",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "すべてのチャットをエクスポート (すべてのユーザー)",
+	"Export chat (.json)": "チャットをエクスポート(.json)",
+	"Export Chats": "チャットをエクスポート",
+	"Export Config to JSON File": "設定をJSONファイルでエクスポート",
+	"Export Functions": "Functionのエクスポート",
+	"Export Models": "モデルのエクスポート",
+	"Export Presets": "",
+	"Export Prompts": "プロンプトをエクスポート",
+	"Export to CSV": "",
+	"Export Tools": "ツールのエクスポート",
+	"External Models": "外部モデル",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "APIキーの作成に失敗しました。",
+	"Failed to read clipboard contents": "クリップボードの内容を読み取れませんでした",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "設定アップデート失敗",
+	"Failed to upload file.": "ファイルアップロード失敗",
+	"February": "2月",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "詳細を追加してください",
+	"File": "ファイル",
+	"File added successfully.": "ファイル追加が成功しました。",
+	"File content updated successfully.": "ファイルコンテンツ追加が成功しました。",
+	"File Mode": "ファイルモード",
+	"File not found.": "ファイルが見つかりません。",
+	"File removed successfully.": "ファイル削除が成功しました。",
+	"File size should not exceed {{maxSize}} MB.": "ファイルサイズ最大値{{maxSize}} MB",
+	"Files": "ファイル",
+	"Filter is now globally disabled": "グローバルフィルタが無効です。",
+	"Filter is now globally enabled": "グローバルフィルタが有効です。",
+	"Filters": "フィルター",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "指紋のなりすましが検出されました: イニシャルをアバターとして使用できません。デフォルトのプロファイル画像にデフォルト設定されています。",
+	"Fluidly stream large external response chunks": "大規模な外部応答チャンクをスムーズにストリーミングする",
+	"Focus chat input": "チャット入力をフォーカス",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "完全に指示に従った",
+	"Forge new paths": "",
+	"Form": "フォーム",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "頻度ペナルティ",
+	"Function": "",
+	"Function created successfully": "Functionの作成が成功しました。",
+	"Function deleted successfully": "Functionの削除が成功しました。",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Functionはグローバルで無効です。",
+	"Function is now globally enabled": "Functionはグローバルで有効です。",
+	"Function Name": "",
+	"Function updated successfully": "Functionのアップデートが成功しました。",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "Functionsのインポートが成功しました",
+	"General": "一般",
+	"General Settings": "一般設定",
+	"Generate Image": "",
+	"Generating search query": "検索クエリの生成",
+	"Generation Info": "生成情報",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "グローバル",
+	"Good Response": "良い応答",
+	"Google PSE API Key": "Google PSE APIキー",
+	"Google PSE Engine Id": "Google PSE エンジン ID",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "触覚フィードバック",
+	"has no conversations.": "対話はありません。",
+	"Hello, {{name}}": "こんにちは、{{name}} さん",
+	"Help": "ヘルプ",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "非表示",
+	"Host": "",
+	"How can I help you today?": "今日はどのようにお手伝いしましょうか?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "ブリッジ検索",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "画像生成 (実験的)",
+	"Image Generation Engine": "画像生成エンジン",
+	"Image Settings": "画像設定",
+	"Images": "画像",
+	"Import Chats": "チャットをインポート",
+	"Import Config from JSON File": "設定をJSONファイルからインポート",
+	"Import Functions": "Functionのインポート",
+	"Import Models": "モデルのインポート",
+	"Import Presets": "",
+	"Import Prompts": "プロンプトをインポート",
+	"Import Tools": "ツールのインポート",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webuiを実行する際に`--api`フラグを含める",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "情報",
+	"Input commands": "入力コマンド",
+	"Install from Github URL": "Github URLからインストール",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "インターフェース",
+	"Invalid file format.": "",
+	"Invalid Tag": "無効なタグ",
+	"January": "1月",
+	"Jina API Key": "",
+	"join our Discord for help.": "ヘルプについては、Discord に参加してください。",
+	"JSON": "JSON",
+	"JSON Preview": "JSON プレビュー",
+	"July": "7月",
+	"June": "6月",
+	"JWT Expiration": "JWT 有効期限",
+	"JWT Token": "JWT トークン",
+	"Keep Alive": "キープアライブ",
+	"Key": "",
+	"Keyboard shortcuts": "キーボードショートカット",
+	"Knowledge": "知識",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "知識の作成に成功しました",
+	"Knowledge deleted successfully.": "知識の削除に成功しました",
+	"Knowledge reset successfully.": "知識のリセットに成功しました",
+	"Knowledge updated successfully": "知識のアップデートに成功しました",
+	"Label": "",
+	"Landing Page Mode": "ランディングページモード",
+	"Language": "言語",
+	"Last Active": "最終アクティブ",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "空欄なら無制限",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "カスタムプロンプトを入力。空欄ならデフォルトプロンプト",
+	"Light": "ライト",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLM は間違いを犯す可能性があります。重要な情報を検証してください。",
+	"Local": "",
+	"Local Models": "ローカルモデル",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "OpenWebUI コミュニティによって作成",
+	"Make sure to enclose them with": "必ず次で囲んでください",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "管理",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "パイプラインの管理",
+	"March": "3月",
+	"Max Tokens (num_predict)": "最大トークン数 (num_predict)",
+	"Max Upload Count": "最大アップロード数",
+	"Max Upload Size": "最大アップロードサイズ",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "同時にダウンロードできるモデルは最大 3 つです。後でもう一度お試しください。",
+	"May": "5月",
+	"Memories accessible by LLMs will be shown here.": "LLM がアクセスできるメモリはここに表示されます。",
+	"Memory": "メモリ",
+	"Memory added successfully": "メモリに追加されました。",
+	"Memory cleared successfully": "メモリをクリアしました。",
+	"Memory deleted successfully": "メモリを削除しました。",
+	"Memory updated successfully": "メモリアップデート成功",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "リンクを作成した後、送信したメッセージは共有されません。URL を持つユーザーは共有チャットを閲覧できます。",
+	"Min P": "",
+	"Minimum Score": "最低スコア",
+	"Mirostat": "ミロスタット",
+	"Mirostat Eta": "ミロスタット Eta",
+	"Mirostat Tau": "ミロスタット Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "モデル '{{modelName}}' が正常にダウンロードされました。",
+	"Model '{{modelTag}}' is already in queue for downloading.": "モデル '{{modelTag}}' はすでにダウンロード待ち行列に入っています。",
+	"Model {{modelId}} not found": "モデル {{modelId}} が見つかりません",
+	"Model {{modelName}} is not vision capable": "モデル {{modelName}} は視覚に対応していません",
+	"Model {{name}} is now {{status}}": "モデル {{name}} は {{status}} になりました。",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "モデルファイルシステムパスが検出されました。モデルの短縮名が必要です。更新できません。",
+	"Model Filtering": "",
+	"Model ID": "モデルID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "モデルが選択されていません",
+	"Model Params": "モデルパラメータ",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "モデルファイルの内容",
+	"Models": "モデル",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "もっと見る",
+	"Name": "名前",
+	"Name your knowledge base": "",
+	"New Chat": "新しいチャット",
+	"New folder": "",
+	"New Password": "新しいパスワード",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "知識が見つかりません",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "結果が見つかりません",
+	"No search query generated": "検索クエリは生成されません",
+	"No source available": "使用可能なソースがありません",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "何一つ",
+	"Not factually correct": "実事上正しくない",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:最小スコアを設定した場合、検索は最小スコア以上のスコアを持つドキュメントのみを返します。",
+	"Notes": "",
+	"Notifications": "デスクトップ通知",
+	"November": "11月",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "10月",
+	"Off": "オフ",
+	"Okay, Let's Go!": "OK、始めましょう!",
+	"OLED Dark": "OLED ダーク",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API が無効になっています",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama バージョン",
+	"On": "オン",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "コマンド文字列には英数字とハイフンのみが許可されています。",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "おっと! URL が無効なようです。もう一度確認してやり直してください。",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "おっと! サポートされていない方法 (フロントエンドのみ) を使用しています。バックエンドから WebUI を提供してください。",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "新しいチャットを開く",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API 設定",
+	"OpenAI API Key is required.": "OpenAI API キーが必要です。",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Key が必要です。",
+	"or": "または",
+	"Organize your users": "",
+	"Other": "その他",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "パスワード",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF ドキュメント (.pdf)",
+	"PDF Extract Images (OCR)": "PDF 画像抽出 (OCR)",
+	"pending": "保留中",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "マイクへのアクセス時に権限が拒否されました: {{error}}",
+	"Permissions": "",
+	"Personalization": "個人化",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "パイプライン",
+	"Pipelines Not Detected": "パイプラインは検出されませんでした",
+	"Pipelines Valves": "パイプラインバルブ",
+	"Plain text (.txt)": "プレーンテキスト (.txt)",
+	"Playground": "プレイグラウンド",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "前向きな態度",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "前の30日間",
+	"Previous 7 days": "前の7日間",
+	"Profile Image": "プロフィール画像",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "プロンプト(例:ローマ帝国についての楽しい事を教えてください)",
+	"Prompt Content": "プロンプトの内容",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "プロンプトの提案",
+	"Prompt updated successfully": "",
+	"Prompts": "プロンプト",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com から \"{{searchValue}}\" をプル",
+	"Pull a model from Ollama.com": "Ollama.com からモデルをプル",
+	"Query Generation Prompt": "",
+	"Query Params": "クエリパラメーター",
+	"RAG Template": "RAG テンプレート",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "読み上げ",
+	"Record voice": "音声を録音",
+	"Redirecting you to OpenWebUI Community": "OpenWebUI コミュニティにリダイレクトしています",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "拒否すべきでないのに拒否した",
+	"Regenerate": "再生成",
+	"Release Notes": "リリースノート",
+	"Relevance": "",
+	"Remove": "削除",
+	"Remove Model": "モデルを削除",
+	"Rename": "名前を変更",
+	"Reorder Models": "",
+	"Repeat Last N": "最後の N を繰り返す",
+	"Request Mode": "リクエストモード",
+	"Reranking Model": "モデルの再ランキング",
+	"Reranking model disabled": "再ランキングモデルが無効です",
+	"Reranking model set to \"{{reranking_model}}\"": "再ランキングモデルを \"{{reranking_model}}\" に設定しました",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "アップロードディレクトリをリセット",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "応答の分割",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "役割",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "保存",
+	"Save & Create": "保存して作成",
+	"Save & Update": "保存して更新",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "チャットログをブラウザのストレージに直接保存する機能はサポートされなくなりました。下のボタンをクリックして、チャットログをダウンロードして削除してください。ご心配なく。チャットログは、次の方法でバックエンドに簡単に再インポートできます。",
+	"Scroll to bottom when switching between branches": "ブランチの切り替え時にボタンをスクロールする",
+	"Search": "検索",
+	"Search a model": "モデルを検索",
+	"Search Base": "",
+	"Search Chats": "チャットの検索",
+	"Search Collection": "Collectionの検索",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Functionの検索",
+	"Search Knowledge": "知識の検索",
+	"Search Models": "モデル検索",
+	"Search options": "",
+	"Search Prompts": "プロンプトを検索",
+	"Search Result Count": "検索結果数",
+	"Search the web": "",
+	"Search Tools": "ツールの検索",
+	"SearchApi API Key": "SearchApiのAPIKey",
+	"SearchApi Engine": "SearchApiエンジン",
+	"Searched {{count}} sites_other": "{{count}} sites_other検索",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng クエリ URL",
+	"See readme.md for instructions": "手順については readme.md を参照してください",
+	"See what's new": "新機能を見る",
+	"Seed": "シード",
+	"Select a base model": "基本モデルの選択",
+	"Select a engine": "エンジンの選択",
+	"Select a function": "Functionの選択",
+	"Select a group": "",
+	"Select a model": "モデルを選択",
+	"Select a pipeline": "パイプラインの選択",
+	"Select a pipeline url": "パイプラインの URL を選択する",
+	"Select a tool": "ツールの選択",
+	"Select Engine": "エンジンの選択",
+	"Select Knowledge": "知識の選択",
+	"Select model": "モデルを選択",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "一部のモデルは画像入力をサポートしていません",
+	"Semantic distance to query": "",
+	"Send": "送信",
+	"Send a Message": "メッセージを送信",
+	"Send message": "メッセージを送信",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "9月",
+	"Serper API Key": "Serper APIキー",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack APIキー",
+	"Server connection verified": "サーバー接続が確認されました",
+	"Set as default": "デフォルトに設定",
+	"Set CFG Scale": "",
+	"Set Default Model": "デフォルトモデルを設定",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "埋め込みモデルを設定します(例:{{model}})",
+	"Set Image Size": "画像サイズを設定",
+	"Set reranking model (e.g. {{model}})": "モデルを設定します(例:{{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "ステップを設定",
+	"Set Task Model": "タスクモデルの設定",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "音声を設定",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "設定",
+	"Settings saved successfully!": "設定が正常に保存されました!",
+	"Share": "共有",
+	"Share Chat": "チャットを共有",
+	"Share to OpenWebUI Community": "OpenWebUI コミュニティに共有",
+	"Show": "表示",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "表示",
+	"Show your support!": "",
+	"Showcased creativity": "創造性を披露",
+	"Sign in": "サインイン",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "サインアウト",
+	"Sign up": "サインアップ",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "ソース",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "音声認識エラー: {{error}}",
+	"Speech-to-Text Engine": "音声テキスト変換エンジン",
+	"Stop": "",
+	"Stop Sequence": "ストップシーケンス",
+	"Stream Chat Response": "",
+	"STT Model": "STTモデル",
+	"STT Settings": "STT設定",
+	"Subtitle (e.g. about the Roman Empire)": "タイトル (例: ローマ帝国)",
+	"Success": "成功",
+	"Successfully updated.": "正常に更新されました。",
+	"Suggested": "提案",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "システム",
+	"System Instructions": "",
+	"System Prompt": "システムプロンプト",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "もっと話してください:",
+	"Temperature": "温度",
+	"Template": "テンプレート",
+	"Temporary Chat": "一時的なチャット",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "テキスト音声変換エンジン",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "ご意見ありがとうございます!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "スコアは0.0(0%)から1.0(100%)の間の値にしてください。",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "テーマ",
+	"Thinking...": "思考中...",
+	"This action cannot be undone. Do you wish to continue?": "このアクションは取り消し不可です。続けますか?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "これは、貴重な会話がバックエンドデータベースに安全に保存されることを保証します。ありがとうございます!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "実験的機能であり正常動作しない場合があります。",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "詳細な説明",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "ヒント: 各置換後にチャット入力で Tab キーを押すことで、複数の変数スロットを連続して更新できます。",
+	"Title": "タイトル",
+	"Title (e.g. Tell me a fun fact)": "タイトル (例: 楽しい事を教えて)",
+	"Title Auto-Generation": "タイトル自動生成",
+	"Title cannot be an empty string.": "タイトルは空文字列にできません。",
+	"Title Generation Prompt": "タイトル生成プロンプト",
+	"TLS": "",
+	"To access the available model names for downloading,": "ダウンロード可能なモデル名にアクセスするには、",
+	"To access the GGUF models available for downloading,": "ダウンロード可能な GGUF モデルにアクセスするには、",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "今日",
+	"Toggle settings": "設定を切り替え",
+	"Toggle sidebar": "サイドバーを切り替え",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "トップ K",
+	"Top P": "トップ P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama へのアクセスに問題がありますか?",
+	"TTS Model": "TTSモデル",
+	"TTS Settings": "TTS 設定",
+	"TTS Voice": "TTSボイス",
+	"Type": "種類",
+	"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (ダウンロード) URL を入力してください",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "おっと! {{provider}} への接続に問題が発生しました。",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "リンクの更新とコピー",
+	"Update for the latest features and improvements.": "",
+	"Update password": "パスワードを更新",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "アップロード",
+	"Upload a GGUF model": "GGUF モデルをアップロード",
+	"Upload directory": "アップロードディレクトリ",
+	"Upload files": "アップロードファイル",
+	"Upload Files": "ファイルのアップロード",
+	"Upload Pipeline": "アップロードパイプライン",
+	"Upload Progress": "アップロードの進行状況",
+	"URL": "",
+	"URL Mode": "URL モード",
+	"Use '#' in the prompt input to load and include your knowledge.": "#を入力すると知識データを参照することが出来ます。",
+	"Use Gravatar": "Gravatar を使用する",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "初期値を使用する",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "ユーザー",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "ユーザー",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "活用",
+	"Valid time units:": "有効な時間単位:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "変数",
+	"variable to have them replaced with clipboard content.": "クリップボードの内容に置き換える変数。",
+	"Version": "バージョン",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "ボイス",
+	"Voice Input": "",
+	"Warning": "警告",
+	"Warning:": "警告:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告: 埋め込みモデルを更新または変更した場合は、すべてのドキュメントを再インポートする必要があります。",
+	"Web": "ウェブ",
+	"Web API": "ウェブAPI",
+	"Web Loader Settings": "Web 読み込み設定",
+	"Web Search": "ウェブ検索",
+	"Web Search Engine": "ウェブ検索エンジン",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI 設定",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "新機能",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "ワイドスクリーンモード",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "ワークスペース",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "プロンプトの提案を書いてください (例: あなたは誰ですか?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "[トピックまたはキーワード] を要約する 50 語の概要を書いてください。",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "昨日",
+	"You": "あなた",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "これまでにアーカイブされた会話はありません。",
+	"You have shared this chat": "このチャットを共有しました",
+	"You're a helpful assistant.": "あなたは有能なアシスタントです。",
+	"You're now logged in.": "ログインしました。",
+	"Your account status is currently pending activation.": "あなたのアカウント状態は現在登録認証待ちです。",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTubeローダー設定(日本語はja)"
+}
diff --git a/src/lib/i18n/locales/ka-GE/translation.json b/src/lib/i18n/locales/ka-GE/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..c752e07f4182ad2b47a11b1edb3b886b8e700d59
--- /dev/null
+++ b/src/lib/i18n/locales/ka-GE/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ან '-1' ვადის გასვლისთვის.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(მაგ. `sh webui.sh --api`)",
+	"(latest)": "(უახლესი)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}}-ის ჩათები",
+	"{{webUIName}} Backend Required": "{{webUIName}} საჭიროა ბექენდი",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "დავალების მოდელი გამოიყენება ისეთი ამოცანების შესრულებისას, როგორიცაა ჩეთების სათაურების გენერირება და ვებ – ძიების მოთხოვნები",
+	"a user": "მომხმარებელი",
+	"About": "შესახებ",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "ანგარიში",
+	"Account Activation Pending": "",
+	"Accurate information": "დიდი ინფორმაცია",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "დამატება",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "დაამატეთ მოკლე აღწერა იმის შესახებ, თუ რას აკეთებს ეს მოდელი",
+	"Add a tag": "დაამატე ტეგი",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "პირველადი მოთხოვნის დამატება",
+	"Add Files": "ფაილების დამატება",
+	"Add Group": "",
+	"Add Memory": "მემორიის დამატება",
+	"Add Model": "მოდელის დამატება",
+	"Add Tag": "",
+	"Add Tags": "ტეგების დამატება",
+	"Add text content": "",
+	"Add User": "მომხმარებლის დამატება",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "ამ პარამეტრების რეგულირება ცვლილებებს უნივერსალურად გამოიყენებს ყველა მომხმარებლისთვის",
+	"admin": "ადმინისტრატორი",
+	"Admin": "",
+	"Admin Panel": "ადმინ პანელი",
+	"Admin Settings": "ადმინისტრატორის ხელსაწყოები",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "დამატებითი პარამეტრები",
+	"Advanced Params": "მოწინავე პარამები",
+	"All chats": "",
+	"All Documents": "ყველა დოკუმენტი",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "მიმოწერის წაშლის დაშვება",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "უკვე გაქვს ანგარიში?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "ასისტენტი",
+	"and": "და",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "და შექმენით ახალი გაზიარებული ბმული.",
+	"API Base URL": "API საბაზისო URL",
+	"API Key": "API გასაღები",
+	"API Key created.": "API გასაღები შექმნილია.",
+	"API keys": "API გასაღები",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "აპრილი",
+	"Archive": "არქივი",
+	"Archive All Chats": "არქივი ყველა ჩატი",
+	"Archived Chats": "ჩატის ისტორიის არქივი",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "დარწმუნებული ხარ?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "ფაილის ჩაწერა",
+	"Attention to detail": "დეტალური მიმართვა",
+	"Attribute for Username": "",
+	"Audio": "ხმოვანი",
+	"August": "აგვისტო",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "პასუხის ავტომატური კოპირება ბუფერში",
+	"Auto-playback response": "ავტომატური დაკვრის პასუხი",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 საბაზისო მისამართი",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 საბაზისო მისამართი აუცილებელია",
+	"Available list": "",
+	"available!": "ხელმისაწვდომია!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "უკან",
+	"Bad Response": "ხარვეზი",
+	"Banners": "რეკლამა",
+	"Base Model (From)": "საბაზო მოდელი (-დან)",
+	"Batch Size (num_batch)": "",
+	"before": "ადგილზე",
+	"Being lazy": "ჩაიტყვევა",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API გასაღები",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "SSL-ის ვერიფიკაციის გააუქმება ვებსაიტებზე",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "გაუქმება",
+	"Capabilities": "შესაძლებლობები",
+	"Certificate Path": "",
+	"Change Password": "პაროლის შეცვლა",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "მიმოწერა",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "ჩატის ბულბი",
+	"Chat Controls": "",
+	"Chat direction": "ჩატის მიმართულება",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "მიმოწერები",
+	"Check Again": "თავიდან შემოწმება",
+	"Check for updates": "განახლებების ძიება",
+	"Checking for updates...": "მიმდინარეობს განახლებების ძიება...",
+	"Choose a model before saving...": "აირჩიეთ მოდელი შენახვამდე...",
+	"Chunk Overlap": "გადახურვა ფრაგმენტულია",
+	"Chunk Params": "გადახურვის პარამეტრები",
+	"Chunk Size": "გადახურვის ზომა",
+	"Ciphers": "",
+	"Citation": "ციტატა",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "დახმარებისთვის, დააკლიკე აქ",
+	"Click here to": "დააკლიკე აქ",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "ასარჩევად, დააკლიკე აქ",
+	"Click here to select a csv file.": "ასარჩევად, დააკლიკე აქ",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "დააკლიკე აქ",
+	"Click on the user role button to change a user's role.": "დააკლიკეთ მომხმარებლის როლის ღილაკს რომ შეცვალოთ მომხმარების როლი",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "კლონი",
+	"Close": "დახურვა",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "ნაკრები",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI საბაზისო URL",
+	"ComfyUI Base URL is required.": "ComfyUI საბაზისო URL აუცილებელია.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "ბრძანება",
+	"Completions": "",
+	"Concurrent Requests": "თანმხლები მოთხოვნები",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "პაროლის დამოწმება",
+	"Confirm your action": "",
+	"Connections": "კავშირები",
+	"Contact Admin for WebUI Access": "",
+	"Content": "კონტენტი",
+	"Content Extraction": "",
+	"Context Length": "კონტექსტის სიგრძე",
+	"Continue Response": "პასუხის გაგრძელება",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "ყავს ჩათის URL-ი კლიპბორდში!",
+	"Copied to clipboard": "",
+	"Copy": "კოპირება",
+	"Copy last code block": "ბოლო ბლოკის კოპირება",
+	"Copy last response": "ბოლო პასუხის კოპირება",
+	"Copy Link": "კოპირება",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "კლავიატურაზე კოპირება წარმატებით დასრულდა",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "შექმენით მოდელი",
+	"Create Account": "ანგარიშის შექმნა",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "პირადი ღირებულბრის შექმნა",
+	"Create new secret key": "პირადი ღირებულბრის შექმნა",
+	"Created at": "შექმნილია",
+	"Created At": "შექმნილია",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "მიმდინარე მოდელი",
+	"Current Password": "მიმდინარე პაროლი",
+	"Custom": "საკუთარი",
+	"Dark": "მუქი",
+	"Database": "მონაცემთა ბაზა",
+	"December": "დეკემბერი",
+	"Default": "დეფოლტი",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "დეფოლტ (SentenceTransformers)",
+	"Default Model": "ნაგულისხმები მოდელი",
+	"Default model updated": "დეფოლტ მოდელი განახლებულია",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "დეფოლტ პრომპტი პირველი პირველი",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "მომხმარებლის დეფოლტ როლი",
+	"Delete": "წაშლა",
+	"Delete a model": "მოდელის წაშლა",
+	"Delete All Chats": "ყველა ჩატის წაშლა",
+	"Delete All Models": "",
+	"Delete chat": "შეტყობინების წაშლა",
+	"Delete Chat": "შეტყობინების წაშლა",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "ბმულის წაშლა",
+	"Delete tool?": "",
+	"Delete User": "მომხმარებლის წაშლა",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} წაშლილია",
+	"Deleted {{name}}": "Deleted {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "აღწერა",
+	"Didn't fully follow instructions": "ვერ ყველა ინფორმაციისთვის ვერ ხელახლა ჩაწერე",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "გაიგეთ მოდელი",
+	"Discover a prompt": "აღმოაჩინეთ მოთხოვნა",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მორგებული მოთხოვნები",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "აღმოაჩინეთ, ჩამოტვირთეთ და შეისწავლეთ მოდელის წინასწარ პარამეტრები",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "ჩატში აჩვენე მომხმარებლის სახელი თქვენს ნაცვლად",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "დოკუმენტი",
+	"Documentation": "",
+	"Documents": "დოკუმენტები",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "არ ამყარებს გარე კავშირებს და თქვენი მონაცემები უსაფრთხოდ რჩება თქვენს ადგილობრივ სერვერზე.",
+	"Don't have an account?": "არ გაქვს ანგარიში?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "არ ეთიკურია ფართოდ",
+	"Done": "",
+	"Download": "ჩამოტვირთვა გაუქმებულია",
+	"Download canceled": "ჩამოტვირთვა გაუქმებულია",
+	"Download Database": "გადმოწერე მონაცემთა ბაზა",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "გადაიტანეთ ფაილები აქ, რათა დაამატოთ ისინი მიმოწერაში",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "მაგალითად, '30წ', '10მ'. მოქმედი დროის ერთეულები: 'წ', 'წთ', 'სთ'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "რედაქტირება",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "მომხმარებლის ედიტირება",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "ელ-ფოსტა",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "ჩასმის ძირითადი პროგრამა",
+	"Embedding Model Engine": "ჩასმის ძირითადი პროგრამა",
+	"Embedding model set to \"{{embedding_model}}\"": "ჩასმის ძირითადი პროგრამა ჩართულია \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "საზოგადოების გაზიარების ჩართვა",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "ახალი რეგისტრაციების ჩართვა",
+	"Enable Web Search": "ვებ ძიების ჩართვა",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "გთხოვთ, უზრუნველყოთ, რომთქვევის CSV-ფაილი შეიცავს 4 ველი, ჩაწერილი ორივე ველი უდრის პირველი ველით.",
+	"Enter {{role}} message here": "შეიყვანე {{role}} შეტყობინება აქ",
+	"Enter a detail about yourself for your LLMs to recall": "შეიყვანე დეტალი ჩემთათვის, რომ ჩვენი LLMs-ს შეიძლოს აღაქვს",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "შეიყვანეთ Brave Search API გასაღები",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "შეიყვანეთ ნაწილის გადახურვა",
+	"Enter Chunk Size": "შეიყვანე ბლოკის ზომა",
+	"Enter description": "",
+	"Enter Github Raw URL": "შეიყვანეთ Github Raw URL",
+	"Enter Google PSE API Key": "შეიყვანეთ Google PSE API გასაღები",
+	"Enter Google PSE Engine Id": "შეიყვანეთ Google PSE ძრავის ID",
+	"Enter Image Size (e.g. 512x512)": "შეიყვანეთ სურათის ზომა (მაგ. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "შეიყვანეთ ენის კოდი",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "შეიყვანეთ მოდელის ტეგი (მაგ. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "შეიყვანეთ ნაბიჯების რაოდენობა (მაგ. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "შეიყვანეთ ქულა",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "შეიყვანეთ Searxng Query URL",
+	"Enter Seed": "",
+	"Enter Serper API Key": "შეიყვანეთ Serper API Key",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "შეიყვანეთ Serpstack API Key",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "შეიყვანეთ ტოპ თანმიმდევრობა",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"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://localhost:11434)": "შეიყვანეთ მისამართი (მაგალითად http://localhost:11434)",
+	"Enter Your Email": "შეიყვანეთ თქვენი ელ-ფოსტა",
+	"Enter Your Full Name": "შეიყვანეთ თქვენი სრული სახელი",
+	"Enter your message": "",
+	"Enter Your Password": "შეიყვანეთ თქვენი პაროლი",
+	"Enter Your Role": "შეიყვანეთ თქვენი როლი",
+	"Enter Your Username": "",
+	"Error": "შეცდომა",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "ექსპერიმენტალური",
+	"Explore the cosmos": "",
+	"Export": "ექსპორტი",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "ექსპორტი ყველა ჩათი (ყველა მომხმარებელი)",
+	"Export chat (.json)": "",
+	"Export Chats": "მიმოწერის ექსპორტირება",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "ექსპორტის მოდელები",
+	"Export Presets": "",
+	"Export Prompts": "მოთხოვნების ექსპორტი",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "API ღილაკის შექმნა ვერ მოხერხდა.",
+	"Failed to read clipboard contents": "ბუფერში შიგთავსის წაკითხვა ვერ მოხერხდა",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "თებერვალი",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "უფასოდ დაამატეთ დეტალები",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "ფაილური რეჟიმი",
+	"File not found.": "ფაილი ვერ მოიძებნა",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "აღმოჩენილია თითის ანაბეჭდის გაყალბება: ინიციალების გამოყენება ავატარად შეუძლებელია. დეფოლტ პროფილის დეფოლტ სურათი.",
+	"Fluidly stream large external response chunks": "თხევადი ნაკადი დიდი გარე საპასუხო ნაწილაკების",
+	"Focus chat input": "ჩეთის შეყვანის ფოკუსი",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "ყველა ინსტრუქცია უზრუნველყოფა",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "სიხშირის ჯარიმა",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "ზოგადი",
+	"General Settings": "ზოგადი პარამეტრები",
+	"Generate Image": "",
+	"Generating search query": "საძიებო მოთხოვნის გენერირება",
+	"Generation Info": "გენერაციის ინფორმაცია",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "დიდი პასუხი",
+	"Google PSE API Key": "Google PSE API გასაღები",
+	"Google PSE Engine Id": "Google PSE ძრავის Id",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "არა უფლება ჩაწერა",
+	"Hello, {{name}}": "გამარჯობა, {{name}}",
+	"Help": "დახმარება",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "დამალვა",
+	"Host": "",
+	"How can I help you today?": "როგორ შემიძლია დაგეხმარო დღეს?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "ჰიბრიდური ძებნა",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "სურათების გენერაცია (ექსპერიმენტული)",
+	"Image Generation Engine": "სურათის გენერაციის ძრავა",
+	"Image Settings": "სურათის პარამეტრები",
+	"Images": "სურათები",
+	"Import Chats": "მიმოწერების იმპორტი",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "იმპორტის მოდელები",
+	"Import Presets": "",
+	"Import Prompts": "მოთხოვნების იმპორტი",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "ჩართეთ `--api` დროშა stable-diffusion-webui-ის გაშვებისას",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "ინფორმაცია",
+	"Input commands": "შეყვანით ბრძანებებს",
+	"Install from Github URL": "დააინსტალირეთ Github URL- დან",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "ინტერფეისი",
+	"Invalid file format.": "",
+	"Invalid Tag": "არასწორი ტეგი",
+	"January": "იანვარი",
+	"Jina API Key": "",
+	"join our Discord for help.": "შეუერთდით ჩვენს Discord-ს დახმარებისთვის",
+	"JSON": "JSON",
+	"JSON Preview": "JSON გადახედვა",
+	"July": "ივნისი",
+	"June": "ივლა",
+	"JWT Expiration": "JWT-ის ვადა",
+	"JWT Token": "JWT ტოკენი",
+	"Keep Alive": "აქტიურად დატოვება",
+	"Key": "",
+	"Keyboard shortcuts": "კლავიატურის მალსახმობები",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "ენა",
+	"Last Active": "ბოლო აქტიური",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "მსუბუქი",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "შესაძლოა LLM-ებმა შეცდომები დაუშვან. გადაამოწმეთ მნიშვნელოვანი ინფორმაცია.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "დამზადებულია OpenWebUI საზოგადოების მიერ",
+	"Make sure to enclose them with": "დარწმუნდით, რომ დაურთეთ ისინი",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "მილსადენების მართვა",
+	"March": "მარტივი",
+	"Max Tokens (num_predict)": "მაქს ტოკენსი (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "მაქსიმუმ 3 მოდელის ჩამოტვირთვა შესაძლებელია ერთდროულად. Გთხოვთ სცადოთ მოგვიანებით.",
+	"May": "მაი",
+	"Memories accessible by LLMs will be shown here.": "ლლმ-ს აქვს ხელმისაწვდომი მემორიები აქ იქნება.",
+	"Memory": "მემორია",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "შეტყობინებები, რომელსაც თქვენ აგზავნით თქვენი ბმულის შექმნის შემდეგ, არ იქნება გაზიარებული. URL– ის მქონე მომხმარებლებს შეეძლებათ ნახონ საერთო ჩატი.",
+	"Min P": "",
+	"Minimum Score": "მინიმალური ქულა",
+	"Mirostat": "მიროსტატი",
+	"Mirostat Eta": "მიროსტატი ეტა",
+	"Mirostat Tau": "მიროსტატი ტაუ",
+	"MMMM DD, YYYY": "თვე დღე, წელი",
+	"MMMM DD, YYYY HH:mm": "თვე დღე, წელი HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "მოდელი „{{modelName}}“ წარმატებით ჩამოიტვირთა.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "მოდელი „{{modelTag}}“ უკვე ჩამოტვირთვის რიგშია.",
+	"Model {{modelId}} not found": "მოდელი {{modelId}} ვერ მოიძებნა",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} is not vision capable",
+	"Model {{name}} is now {{status}}": "Model {{name}} is now {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "აღმოჩენილია მოდელის ფაილური სისტემის გზა. განახლებისთვის საჭიროა მოდელის მოკლე სახელი, გაგრძელება შეუძლებელია.",
+	"Model Filtering": "",
+	"Model ID": "მოდელის ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "მოდელი არ არის არჩეული",
+	"Model Params": "მოდელის პარამები",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "მოდელური ფაილის კონტენტი",
+	"Models": "მოდელები",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "ვრცლად",
+	"Name": "სახელი",
+	"Name your knowledge base": "",
+	"New Chat": "ახალი მიმოწერა",
+	"New folder": "",
+	"New Password": "ახალი პაროლი",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "ჩვენ ვერ პოულობით ნაპოვნი ჩაწერები",
+	"No search query generated": "ძიების მოთხოვნა არ არის გენერირებული",
+	"No source available": "წყარო არ არის ხელმისაწვდომი",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "არცერთი",
+	"Not factually correct": "არ ვეთანხმები პირდაპირ ვერც ვეთანხმები",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "შენიშვნა: თუ თქვენ დააყენებთ მინიმალურ ქულას, ძებნა დააბრუნებს მხოლოდ დოკუმენტებს მინიმალური ქულის მეტი ან ტოლი ქულით.",
+	"Notes": "",
+	"Notifications": "შეტყობინება",
+	"November": "ნოემბერი",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (ოლამა)",
+	"OAuth ID": "",
+	"October": "ოქტომბერი",
+	"Off": "გამორთვა",
+	"Okay, Let's Go!": "კარგი, წავედით!",
+	"OLED Dark": "OLED მუქი",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API გამორთულია",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama ვერსია",
+	"On": "ჩართვა",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "ბრძანების სტრიქონში დაშვებულია მხოლოდ ალფანუმერული სიმბოლოები და დეფისები.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "უი! როგორც ჩანს, მისამართი არასწორია. გთხოვთ, გადაამოწმოთ და ისევ სცადოთ.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "უპს! თქვენ იყენებთ მხარდაუჭერელ მეთოდს (მხოლოდ frontend). გთხოვთ, მოემსახუროთ WebUI-ს ბექენდიდან",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "ახალი მიმოწერის გახსნა",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API პარამეტრები",
+	"OpenAI API Key is required.": "OpenAI API გასაღები აუცილებელია",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Key აუცილებელია",
+	"or": "ან",
+	"Organize your users": "",
+	"Other": "სხვა",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "პაროლი",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF დოკუმენტი (.pdf)",
+	"PDF Extract Images (OCR)": "PDF იდან ამოღებული სურათები (OCR)",
+	"pending": "ლოდინის რეჟიმშია",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "ნებართვა უარყოფილია მიკროფონზე წვდომისას: {{error}}",
+	"Permissions": "",
+	"Personalization": "პერსონალიზაცია",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "მილსადენები",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "მილსადენების სარქველები",
+	"Plain text (.txt)": "ტექსტი (.txt)",
+	"Playground": "სათამაშო მოედანი",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "პოზიტიური ანგარიში",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "უკან 30 დღე",
+	"Previous 7 days": "უკან 7 დღე",
+	"Profile Image": "პროფილის სურათი",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (მაგ. მითხარი სახალისო ფაქტი რომის იმპერიის შესახებ)",
+	"Prompt Content": "მოთხოვნის შინაარსი",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "მოთხოვნის რჩევები",
+	"Prompt updated successfully": "",
+	"Prompts": "მოთხოვნები",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "ჩაიამოვეთ \"{{searchValue}}\" Ollama.com-იდან",
+	"Pull a model from Ollama.com": "Ollama.com იდან მოდელის გადაწერა ",
+	"Query Generation Prompt": "",
+	"Query Params": "პარამეტრების ძიება",
+	"RAG Template": "RAG შაბლონი",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "ხმის ჩაწერა",
+	"Record voice": "ხმის ჩაწერა",
+	"Redirecting you to OpenWebUI Community": "გადამისამართდებით OpenWebUI საზოგადოებაში",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "უარა, როგორც უნდა იყოს",
+	"Regenerate": "ხელახლა გენერირება",
+	"Release Notes": "Გამოშვების შენიშვნები",
+	"Relevance": "",
+	"Remove": "პოპულარობის რაოდენობა",
+	"Remove Model": "პოპულარობის რაოდენობა",
+	"Rename": "პოპულარობის რაოდენობა",
+	"Reorder Models": "",
+	"Repeat Last N": "გაიმეორეთ ბოლო N",
+	"Request Mode": "მოთხოვნის რეჟიმი",
+	"Reranking Model": "რექვექტირება",
+	"Reranking model disabled": "რექვექტირება არაა ჩართული",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking model set to \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "როლი",
+	"Rosé Pine": "ვარდისფერი ფიჭვის ხე",
+	"Rosé Pine Dawn": "ვარდისფერი ფიჭვის გარიჟრაჟი",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "შენახვა",
+	"Save & Create": "დამახსოვრება და შექმნა",
+	"Save & Update": "დამახსოვრება და განახლება",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ჩეთის ისტორიის შენახვა პირდაპირ თქვენი ბრაუზერის საცავში აღარ არის მხარდაჭერილი. გთხოვთ, დაუთმოთ და წაშალოთ თქვენი ჩატის ჟურნალები ქვემოთ მოცემულ ღილაკზე დაწკაპუნებით. არ ინერვიულოთ, თქვენ შეგიძლიათ მარტივად ხელახლა შემოიტანოთ თქვენი ჩეთის ისტორია ბექენდში",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "ძიება",
+	"Search a model": "მოდელის ძიება",
+	"Search Base": "",
+	"Search Chats": "ჩატების ძებნა",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "საძიებო მოდელები",
+	"Search options": "",
+	"Search Prompts": "მოთხოვნების ძიება",
+	"Search Result Count": "ძიების შედეგების რაოდენობა",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Searched {{count}} sites_one",
+	"Searched {{count}} sites_other": "Searched {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng Query URL",
+	"See readme.md for instructions": "იხილეთ readme.md ინსტრუქციებისთვის",
+	"See what's new": "სიახლეების ნახვა",
+	"Seed": "სიდი",
+	"Select a base model": "აირჩიეთ ბაზის მოდელი",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "მოდელის არჩევა",
+	"Select a pipeline": "აირჩიეთ მილსადენი",
+	"Select a pipeline url": "აირჩიეთ მილსადენის url",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "მოდელის არჩევა",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "შერჩეული მოდელი (ებ) ი არ უჭერს მხარს გამოსახულების შეყვანას",
+	"Semantic distance to query": "",
+	"Send": "გაგზავნა",
+	"Send a Message": "შეტყობინების გაგზავნა",
+	"Send message": "შეტყობინების გაგზავნა",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "სექტემბერი",
+	"Serper API Key": "Serper API Key",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack API Key",
+	"Server connection verified": "სერვერთან კავშირი დადასტურებულია",
+	"Set as default": "დეფოლტად დაყენება",
+	"Set CFG Scale": "",
+	"Set Default Model": "დეფოლტ მოდელის დაყენება",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ჩვენება მოდელის დაყენება (მაგ. {{model}})",
+	"Set Image Size": "სურათის ზომის დაყენება",
+	"Set reranking model (e.g. {{model}})": "რეტარირება მოდელის დაყენება (მაგ. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "ნაბიჯების დაყენება",
+	"Set Task Model": "დააყენეთ სამუშაო მოდელი",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "ხმის დაყენება",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "ხელსაწყოები",
+	"Settings saved successfully!": "პარამეტრები წარმატებით განახლდა!",
+	"Share": "გაზიარება",
+	"Share Chat": "გაზიარება",
+	"Share to OpenWebUI Community": "გააზიარე OpenWebUI საზოგადოებაში ",
+	"Show": "ჩვენება",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "მალსახმობების ჩვენება",
+	"Show your support!": "",
+	"Showcased creativity": "ჩვენებული ქონება",
+	"Sign in": "ავტორიზაცია",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "გასვლა",
+	"Sign up": "რეგისტრაცია",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "წყარო",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "მეტყველების ამოცნობის შეცდომა: {{error}}",
+	"Speech-to-Text Engine": "ხმოვან-ტექსტური ძრავი",
+	"Stop": "",
+	"Stop Sequence": "შეჩერების თანმიმდევრობა",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "მეტყველების ამოცნობის პარამეტრები",
+	"Subtitle (e.g. about the Roman Empire)": "სუბტიტრები (მაგ. რომის იმპერიის შესახებ)",
+	"Success": "წარმატება",
+	"Successfully updated.": "წარმატებით განახლდა",
+	"Suggested": "პირდაპირ პოპულარული",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "სისტემა",
+	"System Instructions": "",
+	"System Prompt": "სისტემური მოთხოვნა",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "ჩვენთან დავუკავშირდით",
+	"Temperature": "ტემპერატურა",
+	"Template": "შაბლონი",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "ტექსტურ-ხმოვანი ძრავი",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "მადლობა გამოხმაურებისთვის!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "ქულა 0.0 (0%) და 1.0 (100%) ჩაშენებული უნდა იყოს.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "თემა",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "ეს უზრუნველყოფს, რომ თქვენი ძვირფასი საუბრები უსაფრთხოდ შეინახება თქვენს backend მონაცემთა ბაზაში. Გმადლობთ!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "ვრცლად აღწერა",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "რჩევა: განაახლეთ რამდენიმე ცვლადი სლოტი თანმიმდევრულად, ყოველი ჩანაცვლების შემდეგ ჩატის ღილაკზე დაჭერით.",
+	"Title": "სათაური",
+	"Title (e.g. Tell me a fun fact)": "სათაური (მაგ. გაიხსნე რაღაც ხარისხი)",
+	"Title Auto-Generation": "სათაურის ავტო-გენერაცია",
+	"Title cannot be an empty string.": "სათაური ცარიელი ველი ვერ უნდა იყოს.",
+	"Title Generation Prompt": "სათაურის გენერაციის მოთხოვნა ",
+	"TLS": "",
+	"To access the available model names for downloading,": "ჩამოტვირთვისთვის ხელმისაწვდომი მოდელების სახელებზე წვდომისთვის",
+	"To access the GGUF models available for downloading,": "ჩასატვირთად ხელმისაწვდომი GGUF მოდელებზე წვდომისთვის",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "დღეს",
+	"Toggle settings": "პარამეტრების გადართვა",
+	"Toggle sidebar": "გვერდითი ზოლის გადართვა",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "ტოპ K",
+	"Top P": "ტოპ P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama-ს ვერ უკავშირდები?",
+	"TTS Model": "",
+	"TTS Settings": "TTS პარამეტრები",
+	"TTS Voice": "",
+	"Type": "ტიპი",
+	"Type Hugging Face Resolve (Download) URL": "სცადე გადმოწერო Hugging Face Resolve URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "{{provider}}-თან დაკავშირების პრობლემა წარმოიშვა.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "განახლება და ბმულის კოპირება",
+	"Update for the latest features and improvements.": "",
+	"Update password": "პაროლის განახლება",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "GGUF მოდელის ატვირთვა",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "ატვირთეთ ფაილები",
+	"Upload Pipeline": "",
+	"Upload Progress": "პროგრესის ატვირთვა",
+	"URL": "",
+	"URL Mode": "URL რეჟიმი",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "გამოიყენე Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "გამოიყენე ინიციალები",
+	"use_mlock (Ollama)": "use_mlock (ოლამა)",
+	"use_mmap (Ollama)": "use_mmap (ოლამა)",
+	"user": "მომხმარებელი",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "მომხმარებლები",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "გამოყენება",
+	"Valid time units:": "მოქმედი დროის ერთეულები",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "ცვლადი",
+	"variable to have them replaced with clipboard content.": "ცვლადი, რომ შეცვალოს ისინი ბუფერში შიგთავსით.",
+	"Version": "ვერსია",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "გაფრთხილება",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "გაფრთხილება: თუ განაახლებთ ან შეცვლით ჩანერგვის მოდელს, მოგიწევთ ყველა დოკუმენტის ხელახლა იმპორტი.",
+	"Web": "ვები",
+	"Web API": "",
+	"Web Loader Settings": "ვების ჩატარების პარამეტრები",
+	"Web Search": "ვებ ძებნა",
+	"Web Search Engine": "ვებ საძიებო სისტემა",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI პარამეტრები",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "რა არის ახალი",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "ვულერი",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "დაწერეთ მოკლე წინადადება (მაგ. ვინ ხარ?",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "დაწერეთ რეზიუმე 50 სიტყვით, რომელიც აჯამებს [თემას ან საკვანძო სიტყვას].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "აღდგენა",
+	"You": "ჩემი",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "არ ხართ არქივირებული განხილვები.",
+	"You have shared this chat": "ამ ჩატის გააგზავნა",
+	"You're a helpful assistant.": "თქვენ სასარგებლო ასისტენტი ხართ.",
+	"You're now logged in.": "თქვენ შესული ხართ.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Loader Settings"
+}
diff --git a/src/lib/i18n/locales/ko-KR/translation.json b/src/lib/i18n/locales/ko-KR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..a9540e5840c3fdf5a723a8d893d008ec7a2cd7c7
--- /dev/null
+++ b/src/lib/i18n/locales/ko-KR/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "만료 없음은 's', 'm', 'h', 'd', 'w' 아니면 '-1' 중 하나를 사용하세요.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(예: `sh webui.sh --api --api-auth 사용자이름_비밀번호`)",
+	"(e.g. `sh webui.sh --api`)": "(예: `sh webui.sh --api`)",
+	"(latest)": "(최근)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}}의 채팅",
+	"{{webUIName}} Backend Required": "{{webUIName}} 백엔드가 필요합니다.",
+	"*Prompt node ID(s) are required for image generation": "사진 생성을 위해 프롬포트 노드 ID가 필요합니다",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "최신 버전 (v{{LATEST_VERSION}})이 가능합니다",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "작업 모델은 채팅 및 웹 검색 쿼리에 대한 제목 생성 등의 작업 수행 시 사용됩니다.",
+	"a user": "사용자",
+	"About": "정보",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "계정",
+	"Account Activation Pending": "계정 활성화 대기",
+	"Accurate information": "정확한 정보",
+	"Actions": "행동",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "활성 사용자",
+	"Add": "추가",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "모델의 기능에 대한 간단한 설명 추가",
+	"Add a tag": "태그 추가",
+	"Add Arena Model": "아레나 모델 추가",
+	"Add Connection": "",
+	"Add Content": "내용 추가",
+	"Add content here": "여기에 내용을 추가하세요",
+	"Add custom prompt": "사용자 정의 프롬프트 추가",
+	"Add Files": "파일 추가",
+	"Add Group": "",
+	"Add Memory": "메모리 추가",
+	"Add Model": "모델 추가",
+	"Add Tag": "태그 추가",
+	"Add Tags": "태그 추가",
+	"Add text content": "글 추가",
+	"Add User": "사용자 추가",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "위와 같이 설정시 모든 사용자에게 적용됩니다.",
+	"admin": "관리자",
+	"Admin": "관리자",
+	"Admin Panel": "관리자 패널",
+	"Admin Settings": "관리자 설정",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "관리자는 항상 모든 도구에 접근할 수 있지만, 사용자는 워크스페이스에서 모델마다 도구를 할당받아야 합니다.",
+	"Advanced Parameters": "고급 매개변수",
+	"Advanced Params": "고급 매개변수",
+	"All chats": "모든 채팅",
+	"All Documents": "모든 문서",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "채팅 삭제 허용",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "외부 음성 허용",
+	"Allow Temporary Chat": "임시 채팅 허용",
+	"Allow User Location": "사용자 위치 활용 허용",
+	"Allow Voice Interruption in Call": "음성 기능에서 음성 방해 허용",
+	"Already have an account?": "이미 계정이 있으신가요?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "어시스턴트",
+	"and": "그리고",
+	"and {{COUNT}} more": "그리고 {{COUNT}} 더",
+	"and create a new shared link.": "새로운 공유 링크를 생성합니다.",
+	"API Base URL": "API 기본 URL",
+	"API Key": "API 키",
+	"API Key created.": "API 키가 생성되었습니다.",
+	"API keys": "API 키",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "4월",
+	"Archive": "보관",
+	"Archive All Chats": "모든 채팅 보관",
+	"Archived Chats": "보관된 채팅",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "확실합니까?",
+	"Arena Models": "아레나 모델",
+	"Artifacts": "아티팩트",
+	"Ask a question": "질문하기",
+	"Assistant": "어시스턴트",
+	"Attach file": "파일 첨부",
+	"Attention to detail": "세부 사항에 대한 주의",
+	"Attribute for Username": "",
+	"Audio": "오디오",
+	"August": "8월",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "응답을 클립보드에 자동 복사",
+	"Auto-playback response": "응답 자동 재생",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "Automatic1111 API 인증 문자",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 기본 URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 기본 URL 설정이 필요합니다.",
+	"Available list": "가능한 목록",
+	"available!": "사용 가능!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI 음성",
+	"Azure Region": "Azure 지역",
+	"Back": "뒤로가기",
+	"Bad Response": "잘못된 응답",
+	"Banners": "배너",
+	"Base Model (From)": "기본 모델(시작)",
+	"Batch Size (num_batch)": "배치 크기 (num_batch)",
+	"before": "이전",
+	"Being lazy": "게으름 피우기",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API 키",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "웹 사이트에 대한 SSL 검증 무시: ",
+	"Call": "음성 기능",
+	"Call feature is not supported when using Web STT engine": "웹 STT 엔진 사용 시, 음성 기능은 지원되지 않습니다.",
+	"Camera": "카메라",
+	"Cancel": "취소",
+	"Capabilities": "기능",
+	"Certificate Path": "",
+	"Change Password": "비밀번호 변경",
+	"Character": "캐릭터",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "채팅",
+	"Chat Background Image": "채팅 배경 이미지",
+	"Chat Bubble UI": "버블형 채팅 UI",
+	"Chat Controls": "채팅 제어",
+	"Chat direction": "채팅 방향",
+	"Chat Overview": "채팅",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "채팅 태그 자동생성",
+	"Chats": "채팅",
+	"Check Again": "다시 확인",
+	"Check for updates": "업데이트 확인",
+	"Checking for updates...": "업데이트 확인중...",
+	"Choose a model before saving...": "저장하기 전에 모델을 선택하세요...",
+	"Chunk Overlap": "Chunk 오버랩",
+	"Chunk Params": "Chunk 매개변수",
+	"Chunk Size": "Chunk 크기",
+	"Ciphers": "",
+	"Citation": "인용",
+	"Clear memory": "메모리 초기화",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "도움말을 보려면 여기를 클릭하세요.",
+	"Click here to": "여기를 클릭하면",
+	"Click here to download user import template file.": "사용자 삽입 템플렛 파일을 다운받으려면 여기를 클릭하세요",
+	"Click here to learn more about faster-whisper and see the available models.": "빠른 속삭임에 대해 배우거나 가능한 모델을 보려면 여기를 클릭하세요",
+	"Click here to select": "선택하려면 여기를 클릭하세요.",
+	"Click here to select a csv file.": "csv 파일을 선택하려면 여기를 클릭하세요.",
+	"Click here to select a py file.": "py 파일을 선택하려면 여기를 클릭하세요.",
+	"Click here to upload a workflow.json file.": "workflow.json 파일을 업로드하려면 여기를 클릭하세요",
+	"click here.": "여기를 클릭하세요.",
+	"Click on the user role button to change a user's role.": "사용자 역할 버튼을 클릭하여 사용자의 역할을 변경하세요.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "클립보드 사용 권한이 거절되었습니다. 필요한 접근을 사용하기 위해 브라우져 설정을 확인 부탁드립니다.",
+	"Clone": "복제",
+	"Close": "닫기",
+	"Code execution": "코드 실행",
+	"Code formatted successfully": "성공적으로 코드가 생성되었습니다",
+	"Collection": "컬렉션",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI 기본 URL",
+	"ComfyUI Base URL is required.": "ComfyUI 기본 URL이 필요합니다.",
+	"ComfyUI Workflow": "ComfyUI 워크플로",
+	"ComfyUI Workflow Nodes": "ComfyUI 워크플로 노드",
+	"Command": "명령",
+	"Completions": "완성됨",
+	"Concurrent Requests": "동시 요청 수",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "확인",
+	"Confirm Password": "비밀번호 확인",
+	"Confirm your action": "액션 확인",
+	"Connections": "연결",
+	"Contact Admin for WebUI Access": "WebUI 접속을 위해서는 관리자에게 연락에 연락하십시오",
+	"Content": "내용",
+	"Content Extraction": "내용 추출",
+	"Context Length": "내용 길이",
+	"Continue Response": "대화 계속",
+	"Continue with {{provider}}": "{{provider}}로 계속",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "TTS 요청에 메시지가 어떻게 나뉘어지는지 제어하십시오. '문장 부호'는 문장으로 나뉘고, '문단'은 문단으로 나뉘고, '없음'은 메세지를 하나의 문자열로 인식합니다.",
+	"Controls": "제어",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "복사됨",
+	"Copied shared chat URL to clipboard!": "채팅 공유 URL이 클립보드에 복사되었습니다!",
+	"Copied to clipboard": "클립보드에 복사되었습니다",
+	"Copy": "복사",
+	"Copy last code block": "마지막 코드 블록 복사",
+	"Copy last response": "마지막 응답 복사",
+	"Copy Link": "링크 복사",
+	"Copy to clipboard": "클립보드에 복사",
+	"Copying to clipboard was successful!": "성공적으로 클립보드에 복사되었습니다!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "모델 만들기",
+	"Create Account": "계정 만들기",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "지식 만들기",
+	"Create new key": "새 키 만들기",
+	"Create new secret key": "새 비밀 키 만들기",
+	"Created at": "생성일",
+	"Created At": "생성일",
+	"Created by": "생성자",
+	"CSV Import": "CSV 가져오기",
+	"Current Model": "현재 모델",
+	"Current Password": "현재 비밀번호",
+	"Custom": "사용자 정의",
+	"Dark": "다크",
+	"Database": "데이터베이스",
+	"December": "12월",
+	"Default": "기본값",
+	"Default (Open AI)": "기본값 (Open AI)",
+	"Default (SentenceTransformers)": "기본값 (SentenceTransformers)",
+	"Default Model": "기본 모델",
+	"Default model updated": "기본 모델이 업데이트되었습니다.",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "기본 프롬프트 제안",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "기본 사용자 역할",
+	"Delete": "삭제",
+	"Delete a model": "모델 삭제",
+	"Delete All Chats": "모든 채팅 삭제",
+	"Delete All Models": "",
+	"Delete chat": "채팅 삭제",
+	"Delete Chat": "채팅 삭제",
+	"Delete chat?": "채팅을 삭제하겠습니까?",
+	"Delete folder?": "폴더를 삭제하시겠습니까?",
+	"Delete function?": "함수를 삭제하시겠습니까?",
+	"Delete prompt?": "프롬포트를 삭제하시겠습니까?",
+	"delete this link": "이 링크를 삭제합니다.",
+	"Delete tool?": "도구를 삭제하시겠습니까?",
+	"Delete User": "사용자 삭제",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} 삭제됨",
+	"Deleted {{name}}": "{{name}}을(를) 삭제했습니다.",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "설명",
+	"Didn't fully follow instructions": "완전히 지침을 따르지 않음",
+	"Disabled": "제한됨",
+	"Discover a function": "함수 검색",
+	"Discover a model": "모델 검색",
+	"Discover a prompt": "프롬프트 검색",
+	"Discover a tool": "도구 검색",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "사용자 정의 함수 검색, 다운로드 및 탐색",
+	"Discover, download, and explore custom prompts": "사용자 정의 프롬프트 검색, 다운로드 및 탐색",
+	"Discover, download, and explore custom tools": "사용자 정의 도구 검색, 다운로드 및 탐색",
+	"Discover, download, and explore model presets": "모델 사전 설정 검색, 다운로드 및 탐색",
+	"Dismissible": "제외가능",
+	"Display": "",
+	"Display Emoji in Call": "음성기능에서 이모지 표시",
+	"Display the username instead of You in the Chat": "채팅에서 '당신' 대신 사용자 이름 표시",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "불분명한 출처를 가진 함수를 설치하지마세요",
+	"Do not install tools from sources you do not fully trust.": "불분명한 출처를 가진 도구를 설치하지마세요",
+	"Document": "문서",
+	"Documentation": "문서 조사",
+	"Documents": "문서",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "외부와 어떠한 연결도 하지 않으며, 데이터는 로컬에서 호스팅되는 서버에 안전하게 유지됩니다.",
+	"Don't have an account?": "계정이 없으신가요?",
+	"don't install random functions from sources you don't trust.": "불분명한 출처를 가진 임의의 함수를 설치하지마세요",
+	"don't install random tools from sources you don't trust.": "불분명한 출처를 가진 임의의 도구를 설치하지마세요",
+	"Don't like the style": "스타일이 마음에 안 드시나요?",
+	"Done": "완료됨",
+	"Download": "다운로드",
+	"Download canceled": "다운로드 취소",
+	"Download Database": "데이터베이스 다운로드",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "그리기",
+	"Drop any files here to add to the conversation": "대화에 추가할 파일을 여기에 드롭하세요.",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "예: '30초','10분'. 유효한 시간 단위는 '초', '분', '시'입니다.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "편집",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "메모리 편집",
+	"Edit User": "사용자 편집",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "이메일",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "임베딩 배치 크기",
+	"Embedding Model": "임베딩 모델",
+	"Embedding Model Engine": "임베딩 모델 엔진",
+	"Embedding model set to \"{{embedding_model}}\"": "임베딩 모델을 \"{{embedding_model}}\"로 설정함",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "커뮤니티 공유 활성화",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "메시지 평가 활성화",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "새 회원가입 활성화",
+	"Enable Web Search": "웹 검색 활성화",
+	"Enabled": "활성화됨",
+	"Engine": "엔진",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "CSV 파일에 이름, 이메일, 비밀번호, 역할 4개의 열이 순서대로 포함되어 있는지 확인하세요.",
+	"Enter {{role}} message here": "여기에 {{role}} 메시지 입력",
+	"Enter a detail about yourself for your LLMs to recall": "자신에 대한 세부사항을 입력하여 LLM들이 기억할 수 있도록 하세요.",
+	"Enter api auth string (e.g. username:password)": "API 인증 문자 입력 (예: 사용자 이름:비밀번호)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Brave Search API Key 입력",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "CFG Scale 입력 (예: 7.0)",
+	"Enter Chunk Overlap": "청크 오버랩 입력",
+	"Enter Chunk Size": "청크 크기 입력",
+	"Enter description": "설명 입력",
+	"Enter Github Raw URL": "Github Raw URL 입력",
+	"Enter Google PSE API Key": "Google PSE API 키 입력",
+	"Enter Google PSE Engine Id": "Google PSE 엔진 ID 입력",
+	"Enter Image Size (e.g. 512x512)": "이미지 크기 입력(예: 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "언어 코드 입력",
+	"Enter Model ID": "모델 ID 입력",
+	"Enter model tag (e.g. {{modelTag}})": "모델 태그 입력(예: {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "단계 수 입력(예: 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "샘플러 입력 (예: 오일러 a(Euler a))",
+	"Enter Scheduler (e.g. Karras)": "스케쥴러 입력 (예: 카라스(Karras))",
+	"Enter Score": "점수 입력",
+	"Enter SearchApi API Key": "SearchApi API 키 입력",
+	"Enter SearchApi Engine": "SearchApi 엔진 입력",
+	"Enter Searxng Query URL": "Searxng 쿼리 URL 입력",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper API 키 입력",
+	"Enter Serply API Key": "Serply API 키 입력",
+	"Enter Serpstack API Key": "Serpstack API 키 입력",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "중지 시퀀스 입력",
+	"Enter system prompt": "시스템 프롬포트 입력",
+	"Enter Tavily API Key": "Tavily API 키 입력",
+	"Enter Tika Server URL": "Tika 서버 URL 입력",
+	"Enter Top K": "Top K 입력",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "URL 입력(예: http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "URL 입력(예: http://localhost:11434)",
+	"Enter Your Email": "이메일 입력",
+	"Enter Your Full Name": "이름 입력",
+	"Enter your message": "메세지 입력",
+	"Enter Your Password": "비밀번호 입력",
+	"Enter Your Role": "역할 입력",
+	"Enter Your Username": "",
+	"Error": "오류",
+	"ERROR": "오류",
+	"Evaluations": "평가",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "미포함",
+	"Experimental": "실험적",
+	"Explore the cosmos": "",
+	"Export": "내보내기",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "모든 채팅 내보내기(모든 사용자)",
+	"Export chat (.json)": "채팅 내보내기 (.json)",
+	"Export Chats": "채팅 내보내기",
+	"Export Config to JSON File": "Config를 JSON 파일로 내보내기",
+	"Export Functions": "함수 내보내기",
+	"Export Models": "모델 내보내기",
+	"Export Presets": "",
+	"Export Prompts": "프롬프트 내보내기",
+	"Export to CSV": "",
+	"Export Tools": "도구 내보내기",
+	"External Models": "외부 모델",
+	"Failed to add file.": "파일추가에 실패했습니다",
+	"Failed to create API Key.": "API 키 생성에 실패했습니다.",
+	"Failed to read clipboard contents": "클립보드 내용 가져오기를 실패하였습니다.",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "설정 업데이트에 실패하였습니다.",
+	"Failed to upload file.": "파일 업로드에 실패했습니다",
+	"February": "2월",
+	"Feedback History": "피드백 기록",
+	"Feedbacks": "",
+	"Feel free to add specific details": "자세한 내용을 자유롭게 추가하세요.",
+	"File": "파일",
+	"File added successfully.": "성공적으로 파일이 추가되었습니다",
+	"File content updated successfully.": "성공적으로 내용이 업데이트되었습니다",
+	"File Mode": "파일 모드",
+	"File not found.": "파일을 찾을 수 없습니다.",
+	"File removed successfully.": "성공적으로 파일이 제거되었습니다",
+	"File size should not exceed {{maxSize}} MB.": "파일 사이즈가 {{maxSize}} MB를 초과하면 안됩니다.",
+	"Files": "파일",
+	"Filter is now globally disabled": "전반적으로 필터 비활성화됨",
+	"Filter is now globally enabled": "전반적으로 필터 활성화됨",
+	"Filters": "필터",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingerprint spoofing 감지: 이니셜을 아바타로 사용할 수 없습니다. 기본 프로필 이미지로 설정합니다.",
+	"Fluidly stream large external response chunks": "대규모 외부 응답 청크를 유연하게 스트리밍",
+	"Focus chat input": "채팅 입력창에 포커스",
+	"Folder deleted successfully": "성공적으로 폴터가 생성되었습니다",
+	"Folder name cannot be empty": "폴더 이름을 작성해주세요",
+	"Folder name cannot be empty.": "폴더 이름을 작성해주세요",
+	"Folder name updated successfully": "성공적으로 폴더 이름이 저장되었습니다",
+	"Followed instructions perfectly": "명령을 완벽히 수행함",
+	"Forge new paths": "",
+	"Form": "폼",
+	"Format your variables using brackets like this:": "변수를 다음과 같이 괄호를 사용하여 생성하세요",
+	"Frequency Penalty": "빈도 페널티",
+	"Function": "함수",
+	"Function created successfully": "성공적으로 함수가 생성되었습니다",
+	"Function deleted successfully": "성공적으로 함수가 삭제되었습니다",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "전반적으로 함수 비활성화됨",
+	"Function is now globally enabled": "전반적으로 함수 활성화됨",
+	"Function Name": "",
+	"Function updated successfully": "성공적으로 함수가 업데이트되었습니다",
+	"Functions": "함수",
+	"Functions allow arbitrary code execution": "함수로 임이의 코드 실행 허용하기",
+	"Functions allow arbitrary code execution.": "함수가 임이의 코드를 실행하도록 허용하였습니다",
+	"Functions imported successfully": "성공적으로 함수가 가져왔습니다",
+	"General": "일반",
+	"General Settings": "일반 설정",
+	"Generate Image": "이미지 생성",
+	"Generating search query": "검색 쿼리 생성",
+	"Generation Info": "생성 정보",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "글로벌",
+	"Good Response": "좋은 응답",
+	"Google PSE API Key": "Google PSE API 키",
+	"Google PSE Engine Id": "Google PSE 엔진 ID",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "햅틱 피드백",
+	"has no conversations.": "대화가 없습니다.",
+	"Hello, {{name}}": "안녕하세요, {{name}}",
+	"Help": "도움말",
+	"Help us create the best community leaderboard by sharing your feedback history!": "당신의 피드백 기록을 공유함으로서 최고의 커뮤니티 리더보드를 만드는데 도와주세요!",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "숨기기",
+	"Host": "",
+	"How can I help you today?": "오늘 어떻게 도와드릴까요?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "하이브리드 검색",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "ID",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "이미지 생성(실험적)",
+	"Image Generation Engine": "이미지 생성 엔진",
+	"Image Settings": "이미지 설정",
+	"Images": "이미지",
+	"Import Chats": "채팅 가져오기",
+	"Import Config from JSON File": "JSON 파일에서 Config 불러오기",
+	"Import Functions": "함수 가져오기",
+	"Import Models": "모델 가져오기",
+	"Import Presets": "",
+	"Import Prompts": "프롬프트 가져오기",
+	"Import Tools": "도구 가져오기",
+	"Include": "포함",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "stable-diffusion-webui를 실행 시 `--api-auth` 플래그를 포함하세요",
+	"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui를 실행 시 `--api` 플래그를 포함하세요",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "정보",
+	"Input commands": "명령어 입력",
+	"Install from Github URL": "Github URL에서 설치",
+	"Instant Auto-Send After Voice Transcription": "음성 변환 후 즉시 자동 전송",
+	"Interface": "인터페이스",
+	"Invalid file format.": "잘못된 파일 형식",
+	"Invalid Tag": "잘못된 태그",
+	"January": "1월",
+	"Jina API Key": "",
+	"join our Discord for help.": "도움말을 보려면 Discord에 가입하세요.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON 미리 보기",
+	"July": "7월",
+	"June": "6월",
+	"JWT Expiration": "JWT 만료",
+	"JWT Token": "JWT 토큰",
+	"Keep Alive": "계속 유지하기",
+	"Key": "",
+	"Keyboard shortcuts": "키보드 단축키",
+	"Knowledge": "지식 기반",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "성공적으로 지식 기반이 생성되었습니다",
+	"Knowledge deleted successfully.": "성공적으로 지식 기반이 삭제되었습니다",
+	"Knowledge reset successfully.": "성공적으로 지식 기반이 초기화되었습니다",
+	"Knowledge updated successfully": "성공적으로 지식 기반이 업데이트되었습니다",
+	"Label": "",
+	"Landing Page Mode": "랜딩페이지 모드",
+	"Language": "언어",
+	"Last Active": "최근 활동",
+	"Last Modified": "마지막 수정",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "리더보드",
+	"Leave empty for unlimited": "무제한을 위해 빈칸으로 남겨두세요",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "특정 모델을 선택하거나 모든 모델을 포함하고 싶으면 빈칸으로 남겨두세요",
+	"Leave empty to use the default prompt, or enter a custom prompt": "기본 프롬포트를 사용 시 빈칸으로 남겨두세요, 아니면 커스텀 프롬포트를 입력하세요",
+	"Light": "Light",
+	"Listening...": "듣는 중...",
+	"LLMs can make mistakes. Verify important information.": "LLM에 오류가 있을 수 있습니다. 중요한 정보는 확인이 필요합니다.",
+	"Local": "",
+	"Local Models": "로컬 모델",
+	"Lost": "잃어버림",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "OpenWebUI 커뮤니티에 의해 개발됨",
+	"Make sure to enclose them with": "꼭 다음으로 감싸세요:",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "꼭 workflow.json 파일을 ComfyUI의 API 형식대로 내보내세요",
+	"Manage": "관리",
+	"Manage Arena Models": "아레나 모델 관리",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "파이프라인 관리",
+	"March": "3월",
+	"Max Tokens (num_predict)": "최대 토큰(num_predict)",
+	"Max Upload Count": "업로드 최대 수",
+	"Max Upload Size": "업로드 최대 사이즈",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "최대 3개의 모델을 동시에 다운로드할 수 있습니다. 나중에 다시 시도하세요.",
+	"May": "5월",
+	"Memories accessible by LLMs will be shown here.": "LLM에서 접근할 수 있는 메모리는 여기에 표시됩니다.",
+	"Memory": "메모리",
+	"Memory added successfully": "성공적으로 메모리가 추가되었습니다",
+	"Memory cleared successfully": "성공적으로 메모리가 정리되었습니다",
+	"Memory deleted successfully": "성공적으로 메모리가 삭제되었습니다",
+	"Memory updated successfully": "성공적으로 메모리가 업데이트되었습니다",
+	"Merge Responses": "응답들 결합하기",
+	"Message rating should be enabled to use this feature": "이 기능을 사용하려면 메시지 평가가 활성화되어야합니다",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "링크 생성 후에 보낸 메시지는 공유되지 않습니다. URL이 있는 사용자는 공유된 채팅을 볼 수 있습니다.",
+	"Min P": "최소 P",
+	"Minimum Score": "최소 점수",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "모델델",
+	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' 모델이 성공적으로 다운로드되었습니다.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' 모델은 이미 다운로드 대기열에 있습니다.",
+	"Model {{modelId}} not found": "{{modelId}} 모델을 찾을 수 없습니다.",
+	"Model {{modelName}} is not vision capable": "{{modelName}} 모델은 비전을 사용할 수 없습니다.",
+	"Model {{name}} is now {{status}}": "{{name}} 모델은 이제 {{status}} 상태입니다.",
+	"Model accepts image inputs": "모델이 이미지 삽입을 허용합니다",
+	"Model created successfully!": "성공적으로 모델이 생성되었습니다",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "모델 파일 시스템 경로가 감지되었습니다. 업데이트하려면 모델 단축 이름이 필요하며 계속할 수 없습니다.",
+	"Model Filtering": "",
+	"Model ID": "모델 ID",
+	"Model IDs": "",
+	"Model Name": "모델 이름",
+	"Model not selected": "모델이 선택되지 않았습니다.",
+	"Model Params": "모델 파라미터",
+	"Model Permissions": "",
+	"Model updated successfully": "성공적으로 모델이 업데이트되었습니다",
+	"Modelfile Content": "Modelfile 내용",
+	"Models": "모델",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "더보기",
+	"More": "더보기",
+	"Name": "이름",
+	"Name your knowledge base": "",
+	"New Chat": "새 채팅",
+	"New folder": "새 폴더",
+	"New Password": "새 비밀번호",
+	"No content found": "내용을 찾을 수 없음",
+	"No content to speak": "음성 출력할 내용을 찾을 수 없음",
+	"No distance available": "거리 불가능",
+	"No feedbacks found": "피드백 없음",
+	"No file selected": "파일이 선택되지 않음",
+	"No files found.": "파일 없음",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "HTML, CSS, JavaScript이 발견되지 않음",
+	"No knowledge found": "지식 기반 없음",
+	"No model IDs": "",
+	"No models found": "모델 없음",
+	"No models selected": "",
+	"No results found": "결과 없음",
+	"No search query generated": "검색어가 생성되지 않았습니다.",
+	"No source available": "사용 가능한 소스 없음",
+	"No users were found.": "",
+	"No valves to update": "업데이트 할 변수 없음",
+	"None": "없음",
+	"Not factually correct": "사실상 맞지 않음",
+	"Not helpful": "도움이 되지않음",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "참고: 최소 점수를 설정하면, 검색 결과로 최소 점수 이상의 점수를 가진 문서만 반환합니다.",
+	"Notes": "노트",
+	"Notifications": "알림",
+	"November": "11월",
+	"num_gpu (Ollama)": "num_gpu (올라마(Ollama))",
+	"num_thread (Ollama)": "num_thread (올라마(Ollama))",
+	"OAuth ID": "OAuth ID",
+	"October": "10월",
+	"Off": "끄기",
+	"Okay, Let's Go!": "좋아요, 시작합시다!",
+	"OLED Dark": "OLED Dark",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API 비활성화",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama 버전",
+	"On": "켜기",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "명령어 문자열에는 영문자, 숫자 및 하이픈만 허용됩니다.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "가지고 있는 컬렉션만 수정 가능합니다, 새 지식 기반을 생성하여 문서를 수정 혹은 추가하십시오",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "이런! URL이 잘못된 것 같습니다. 다시 한번 확인하고 다시 시도해주세요.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "이런! 파일이 계속 업로드중 입니다. 업로드가 완료될 때까지 잠시만 기다려주세요",
+	"Oops! There was an error in the previous response.": "이런! 이전 응답에 에러가 있었던 것 같습니다",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "이런! 지원되지 않는 방식(프론트엔드만)을 사용하고 계십니다. 백엔드에서 WebUI를 제공해주세요.",
+	"Open file": "파일 열기",
+	"Open in full screen": "전체화면으로 열기",
+	"Open new chat": "새 채팅 열기",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI는 내부적으로 패스트 위스퍼를 사용합니다.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "열린 WebUI 버젼(v{{OPEN_WEBUI_VERSION}})은 최소 버젼 (v{{REQUIRED_VERSION}})보다 낮습니다",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API 설정",
+	"OpenAI API Key is required.": "OpenAI API 키가 필요합니다.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/키가 필요합니다.",
+	"or": "또는",
+	"Organize your users": "",
+	"Other": "기타",
+	"OUTPUT": "출력력",
+	"Output format": "출력 형식",
+	"Overview": "개요",
+	"page": "페이지",
+	"Password": "비밀번호",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF 문서(.pdf)",
+	"PDF Extract Images (OCR)": "PDF 이미지 추출(OCR)",
+	"pending": "보류 중",
+	"Permission denied when accessing media devices": "미디어 장치 접근 권한이 거부되었습니다.",
+	"Permission denied when accessing microphone": "마이크 접근 권한이 거부되었습니다.",
+	"Permission denied when accessing microphone: {{error}}": "마이크 접근 권환이 거부되었습니다: {{error}}",
+	"Permissions": "",
+	"Personalization": "개인화",
+	"Pin": "고정",
+	"Pinned": "고정됨",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "성공적으로 파이프라인이 삭제되었습니다",
+	"Pipeline downloaded successfully": "성공적으로 파이프라인이 설치되었습니다",
+	"Pipelines": "파이프라인",
+	"Pipelines Not Detected": "파이프라인 발견되지않음",
+	"Pipelines Valves": "파이프라인 밸브",
+	"Plain text (.txt)": "일반 텍스트(.txt)",
+	"Playground": "놀이터",
+	"Please carefully review the following warnings:": "다음 주의를 조심히 확인해주십시오",
+	"Please enter a prompt": "프롬포트를 입력해주세요",
+	"Please fill in all fields.": "모두 빈칸없이 채워주세요",
+	"Please select a model first.": "",
+	"Please select a reason": "이유를 선택하주세요",
+	"Port": "",
+	"Positive attitude": "긍정적인 자세",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "이전 30일",
+	"Previous 7 days": "이전 7일",
+	"Profile Image": "프로필 이미지",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "프롬프트 (예: 로마 황제에 대해 재미있는 사실을 알려주세요)",
+	"Prompt Content": "프롬프트 내용",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "프롬프트 제안",
+	"Prompt updated successfully": "",
+	"Prompts": "프롬프트",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com에서 \"{{searchValue}}\" 가져오기",
+	"Pull a model from Ollama.com": "Ollama.com에서 모델 가져오기(pull)",
+	"Query Generation Prompt": "",
+	"Query Params": "쿼리 파라미터",
+	"RAG Template": "RAG 템플릿",
+	"Rating": "평가",
+	"Re-rank models by topic similarity": "주제 유사성으로 모델을 재정렬하기",
+	"Read Aloud": "읽어주기",
+	"Record voice": "음성 녹음",
+	"Redirecting you to OpenWebUI Community": "OpenWebUI 커뮤니티로 리디렉션 중",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "스스로를 \"사용자\" 라고 지칭하세요. (예: \"사용자는 영어를 배우고 있습니다\")",
+	"References from": "출처",
+	"Refused when it shouldn't have": "허용되지 않았지만 허용되어야 합니다.",
+	"Regenerate": "재생성",
+	"Release Notes": "릴리스 노트",
+	"Relevance": "관련도",
+	"Remove": "삭제",
+	"Remove Model": "모델 삭제",
+	"Rename": "이름 변경",
+	"Reorder Models": "",
+	"Repeat Last N": "마지막 N 반복",
+	"Request Mode": "요청 모드",
+	"Reranking Model": "Reranking 모델",
+	"Reranking model disabled": "Reranking 모델 비활성화",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking 모델을 \"{{reranking_model}}\"로 설정",
+	"Reset": "초기화",
+	"Reset All Models": "",
+	"Reset Upload Directory": "업로드 디렉토리 초기화",
+	"Reset Vector Storage/Knowledge": "벡터 저장 공간/지식 기반 초기화",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "웹사이트 권환과 같이 응답 알림이 활성화될 수 없습니다. 필요한 접근을 사용하기 위해 브라우져 설정을 확인 부탁드립니다.",
+	"Response splitting": "응답 나누기",
+	"Result": "결과",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "채팅위한 Rich Text Input",
+	"RK": "RK",
+	"Role": "역할",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "실행시키기",
+	"Running": "실행 중",
+	"Save": "저장",
+	"Save & Create": "저장 및 생성",
+	"Save & Update": "저장 및 업데이트",
+	"Save As Copy": "다른 이름으로 저장",
+	"Save Tag": "태그 저장",
+	"Saved": "저장됨",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "브라우저의 저장소에 채팅 로그를 직접 저장하는 것은 더 이상 지원되지 않습니다. 아래 버튼을 클릭하여 채팅 로그를 다운로드하고 삭제하세요. 걱정 마세요. 백엔드를 통해 채팅 로그를 쉽게 다시 가져올 수 있습니다.",
+	"Scroll to bottom when switching between branches": "브랜치 간 전환시 밑으로 스크롤 하세요",
+	"Search": "검색",
+	"Search a model": "모델 검색",
+	"Search Base": "",
+	"Search Chats": "채팅 검색",
+	"Search Collection": "컬렉션검색",
+	"Search Filters": "",
+	"search for tags": "태그 검색",
+	"Search Functions": "함수 검색",
+	"Search Knowledge": "지식 기반 검색",
+	"Search Models": "모델 검색",
+	"Search options": "",
+	"Search Prompts": "프롬프트 검색",
+	"Search Result Count": "검색 결과 수",
+	"Search the web": "",
+	"Search Tools": "검색 도구",
+	"SearchApi API Key": "SearchApi API 키",
+	"SearchApi Engine": "SearchApi 엔진",
+	"Searched {{count}} sites_one": "sites_one {{count}} 검색됨",
+	"Searched {{count}} sites_other": "sites_other {{count}} 검색됨",
+	"Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" 검색 중",
+	"Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\"위한 지식 기반 검색 중",
+	"Searxng Query URL": "Searxng 쿼리 URL",
+	"See readme.md for instructions": "설명은 readme.md를 참조하세요.",
+	"See what's new": "새로운 기능 보기",
+	"Seed": "시드",
+	"Select a base model": "기본 모델 선택",
+	"Select a engine": "엔진 선택",
+	"Select a function": "함수 선택",
+	"Select a group": "",
+	"Select a model": "모델 선택",
+	"Select a pipeline": "파이프라인 선택",
+	"Select a pipeline url": "파이프라인 URL 선택",
+	"Select a tool": "도구 선택",
+	"Select Engine": "엔진 선택",
+	"Select Knowledge": "지식 기반 선택",
+	"Select model": "모델 선택",
+	"Select only one model to call": "음성 기능을 위해서는 모델을 하나만 선택해야 합니다.",
+	"Selected model(s) do not support image inputs": "선택한 모델은 이미지 입력을 지원하지 않습니다.",
+	"Semantic distance to query": "쿼리까지 의미적 거리",
+	"Send": "보내기",
+	"Send a Message": "메시지 보내기",
+	"Send message": "메시지 보내기",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "'stream_options: { include_usage: true }' 요청 보내기 \n지원되는 제공자가 토큰 사용 정보를 응답할 예정입니다",
+	"September": "9월",
+	"Serper API Key": "Serper API 키",
+	"Serply API Key": "Serply API 키",
+	"Serpstack API Key": "Serpstack API 키",
+	"Server connection verified": "서버 연결 확인됨",
+	"Set as default": "기본값으로 설정",
+	"Set CFG Scale": "CFG Scale 설정",
+	"Set Default Model": "기본 모델 설정",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "임베딩 모델 설정 (예: {{model}})",
+	"Set Image Size": "이미지 크기 설정",
+	"Set reranking model (e.g. {{model}})": "reranking 모델 설정 (예: {{model}})",
+	"Set Sampler": "샘플러 설정",
+	"Set Scheduler": "스케쥴러 설정",
+	"Set Steps": "단계 설정",
+	"Set Task Model": "작업 모델 설정",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "음성 설정",
+	"Set whisper model": "자막 생성기 모델 설정",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "설정",
+	"Settings saved successfully!": "설정이 성공적으로 저장되었습니다!",
+	"Share": "공유",
+	"Share Chat": "채팅 공유",
+	"Share to OpenWebUI Community": "OpenWebUI 커뮤니티에 공유",
+	"Show": "보기",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "사용자용 계정 보류 설명창에, 관리자 상세 정보 노출",
+	"Show shortcuts": "단축키 보기",
+	"Show your support!": "당신의 응원을 보내주세요!",
+	"Showcased creativity": "창의성 발휘",
+	"Sign in": "로그인",
+	"Sign in to {{WEBUI_NAME}}": "{{WEBUI_NAME}}로 로그인",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "로그아웃",
+	"Sign up": "가입",
+	"Sign up to {{WEBUI_NAME}}": "{{WEBUI_NAME}}로 가입",
+	"Signing in to {{WEBUI_NAME}}": "{{WEBUI_NAME}}로 가입중",
+	"Source": "출처",
+	"Speech Playback Speed": "음성 재생 속도",
+	"Speech recognition error: {{error}}": "음성 인식 오류: {{error}}",
+	"Speech-to-Text Engine": "음성-텍스트 변환 엔진",
+	"Stop": "정지",
+	"Stop Sequence": "중지 시퀀스",
+	"Stream Chat Response": "스트림 채팅 응답",
+	"STT Model": "STT 모델",
+	"STT Settings": "STT 설정",
+	"Subtitle (e.g. about the Roman Empire)": "자막 (예: 로마 황제)",
+	"Success": "성공",
+	"Successfully updated.": "성공적으로 업데이트되었습니다.",
+	"Suggested": "제안",
+	"Support": "지원",
+	"Support this plugin:": "플러그인 지원",
+	"Sync directory": "디렉토리 연동",
+	"System": "시스템",
+	"System Instructions": "시스템 설명서",
+	"System Prompt": "시스템 프롬프트",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "태그 생성 프롬포트트",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "탭하여 중단",
+	"Tavily API Key": "Tavily API 키",
+	"Tell us more:": "더 알려주세요:",
+	"Temperature": "온도",
+	"Template": "템플릿",
+	"Temporary Chat": "임시 채팅",
+	"Text Splitter": "텍스트 나누기",
+	"Text-to-Speech Engine": "텍스트-음성 변환 엔진",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "피드백 감사합니다!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "이 플러그인 뒤에 있는 개발자는 커뮤니티에서 활동하는 단순한 열정적인 일반인들입니다. 만약 플러그인이 도움 되었다면, 플러그인 개발에 기여를 고려해주세요!",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "평가 리더보드는 Elo 평가 시스템을 기반으로 하고 실시간으로 업데이트됩니다",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "리더보드는 베타테스트중에 있습니다, 평가 기준이 알고리즘 수정과 함께 변할 수 있습니다",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "최대 파일 크기(MB). 만약 파일 크기가 한도를 초과할 시, 파일은 업로드되지 않습니다",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "하나의 채팅에서는 사용가능한 최대 파일 수가 있습니다. 만약 파일 수가 한도를 초과할 시, 파일은 업로드되지 않습니다.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "점수는 0.0(0%)에서 1.0(100%) 사이의 값이어야 합니다.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "테마",
+	"Thinking...": "생각 중...",
+	"This action cannot be undone. Do you wish to continue?": "이 액션은 되돌릴 수 없습니다. 계속 하시겠습니까?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "이렇게 하면 소중한 대화 내용이 백엔드 데이터베이스에 안전하게 저장됩니다. 감사합니다!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "이것은 실험적 기능으로, 예상대로 작동하지 않을 수 있으며 언제든지 변경될 수 있습니다.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "이 행동은 컬렉션에 존재하는 모든 파일을 삭제하고 새로 업로드된 파일들로 대체됩니다",
+	"This response was generated by \"{{model}}\"": "\"{{model}}\"이 생성한 응답입니다",
+	"This will delete": "이것은 다음을 삭제합니다.",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "<strong>{{NAME}}</strong> 와 <strong>모든 내용</strong>을 삭제합니다.",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "지식 기반과 모든 파일 연동을 초기화합니다. 계속 하시겠습니까?",
+	"Thorough explanation": "완전한 설명",
+	"Tika": "티카(Tika)",
+	"Tika Server URL required.": "티카 서버 URL이 필요합니다",
+	"Tiktoken": "틱토큰 (Tiktoken)",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "팁: 각각의 교체 후 채팅 입력에서 탭 키를 눌러 여러 개의 변수 슬롯을 연속적으로 업데이트하세요.",
+	"Title": "제목",
+	"Title (e.g. Tell me a fun fact)": "제목 (예: 재미있는 사실을 알려주세요)",
+	"Title Auto-Generation": "제목 자동 생성",
+	"Title cannot be an empty string.": "제목은 빈 문자열일 수 없습니다.",
+	"Title Generation Prompt": "제목 생성 프롬프트",
+	"TLS": "",
+	"To access the available model names for downloading,": "다운로드 가능한 모델명을 확인하려면,",
+	"To access the GGUF models available for downloading,": "다운로드 가능한 GGUF 모델을 확인하려면,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "WebUI에 접속하려면 관리자에게 문의하십시오. 관리자는 관리자 패널에서 사용자 상태를 관리할 수 있습니다.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "지식 기반을 여기에 첨부하려면. \"지식 기반\" 워크스페이스에 먼저 추가하세요",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "개인정보를 보호하기 위해, 당신의 채팅 로그는 비공개로 유지되고, 오직 당신의 피드백에서 평가, 모델 ID, 태그, 그리고 메타데이타만 공유됩니다",
+	"To select actions here, add them to the \"Functions\" workspace first.": "여기서 행동을 선택하려면, \"함수\" 워크스페이스에 먼저 추가하세요",
+	"To select filters here, add them to the \"Functions\" workspace first.": "여기서 필터를 선택하려면, \"함수\" 워크스페이스에 먼저 추가하세요",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "여기서 도구를 선택하려면, \"도구\" 워크스페이스에 먼저 추가하세요.",
+	"Toast notifications for new updates": "갓 나온 업데이트 알림",
+	"Today": "오늘",
+	"Toggle settings": "설정 전환",
+	"Toggle sidebar": "사이드바 전환",
+	"Token": "토큰",
+	"Tokens To Keep On Context Refresh (num_keep)": "컨텍스트 새로 고침 시 유지할 토큰 수(num_keep)",
+	"Too verbose": "말이 너무 많은",
+	"Tool created successfully": "성공적으로 도구가 생성되었습니다",
+	"Tool deleted successfully": "성공적으로 도구가 삭제되었습니다",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "성공적으로 도구를 가져왔습니다",
+	"Tool Name": "",
+	"Tool updated successfully": "성공적으로 도구가 업데이트되었습니다",
+	"Tools": "도구",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "도구는 임이의 코드를 실행시키는 함수를 불러오는 시스템입니다",
+	"Tools have a function calling system that allows arbitrary code execution": "도구가 임이의 코드를 실행시키는 함수를 가지기",
+	"Tools have a function calling system that allows arbitrary code execution.": "도구가 임이의 코드를 실행시키는 함수를 가지고 있습니다.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "올라마(Ollama)에 접근하는 데 문제가 있나요?",
+	"TTS Model": "TTS 모델",
+	"TTS Settings": "TTS 설정",
+	"TTS Voice": "TTS 음성",
+	"Type": "입력",
+	"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (다운로드) URL 입력",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "앗! {{provider}}에 연결하는 데 문제가 있었습니다.",
+	"UI": "UI",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "고정 해제",
+	"Unravel secrets": "",
+	"Untagged": "태그 해제",
+	"Update": "업데이트",
+	"Update and Copy Link": "링크 업데이트 및 복사",
+	"Update for the latest features and improvements.": "이번 업데이트의 새로운 기능과 개선",
+	"Update password": "비밀번호 업데이트",
+	"Updated": "업데이트됨",
+	"Updated at": "다음에 업데이트됨",
+	"Updated At": "다음에 업데이트됨됨",
+	"Upload": "업로드",
+	"Upload a GGUF model": "GGUF 모델 업로드",
+	"Upload directory": "디렉토리 업로드",
+	"Upload files": "파일 업로드",
+	"Upload Files": "파일 업로드",
+	"Upload Pipeline": "업로드 파이프라인",
+	"Upload Progress": "업로드 진행 상황",
+	"URL": "",
+	"URL Mode": "URL 모드",
+	"Use '#' in the prompt input to load and include your knowledge.": "프롬프트 입력에서 '#'를 사용하여 지식 기반을 불러오고 포함하세요.",
+	"Use Gravatar": "Gravatar 사용",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "초성 사용",
+	"use_mlock (Ollama)": "use_mlock (올라마)",
+	"use_mmap (Ollama)": "use_mmap (올라마)",
+	"user": "사용자",
+	"User": "사용자",
+	"User location successfully retrieved.": "성공적으로 사용자의 위치를 불러왔습니다",
+	"Username": "",
+	"Users": "사용자",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "모든 모델은 기본 아레나 모델을 사용중입니다. 플러스 버튼을 눌러 커스텀 모델을 추가하세요",
+	"Utilize": "활용",
+	"Valid time units:": "유효 시간 단위:",
+	"Valves": "밸브",
+	"Valves updated": "밸브 업데이트됨",
+	"Valves updated successfully": "성공적으로 밸브가 업데이트되었습니다",
+	"variable": "변수",
+	"variable to have them replaced with clipboard content.": "변수를 사용하여 클립보드 내용으로 바꾸세요.",
+	"Version": "버전",
+	"Version {{selectedVersion}} of {{totalVersions}}": "버전 {{totalVersions}}의 {{selectedVersion}}",
+	"Visibility": "",
+	"Voice": "음성",
+	"Voice Input": "음성 입력",
+	"Warning": "경고",
+	"Warning:": "주의:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "주의: 기존 임베딩 모델을 변경 또는 업데이트하는 경우, 모든 문서를 다시 가져와야 합니다.",
+	"Web": "웹",
+	"Web API": "웹 API",
+	"Web Loader Settings": "웹 로더 설정",
+	"Web Search": "웹 검색",
+	"Web Search Engine": "웹 검색 엔진",
+	"Web Search Query Generation": "",
+	"Webhook URL": "웹훅 URL",
+	"WebUI Settings": "WebUI 설정",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "새로운 기능:",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (로컬)",
+	"Why?": "",
+	"Widescreen Mode": "와이드스크린 모드",
+	"Won": "이김",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "워크스페이스",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "프롬프트 제안 작성 (예: 당신은 누구인가요?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "[주제 또는 키워드]에 대한 50단어 요약문 작성.",
+	"Write something...": "아무거나 쓰세요...",
+	"Write your model template content here": "",
+	"Yesterday": "어제",
+	"You": "당신",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "동시에 최대 {{maxCount}} 파일과만 대화할 수 있습니다 ",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "아래 '관리' 버튼으로 메모리를 추가하여 LLM들과의 상호작용을 개인화할 수 있습니다. 이를 통해 더 유용하고 맞춤화된 경험을 제공합니다.",
+	"You cannot upload an empty file.": "빈 파일을 업로드 할 수 없습니다",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "채팅을 보관한 적이 없습니다.",
+	"You have shared this chat": "이 채팅을 공유했습니다.",
+	"You're a helpful assistant.": "당신은 유용한 어시스턴트입니다.",
+	"You're now logged in.": "로그인되었습니다.",
+	"Your account status is currently pending activation.": "현재 계정은 아직 활성화되지 않았습니다.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "당신의 모든 기여는 곧바로 플러그인 개발자에게 갑니다; Open WebUI는 일절 가져가지 않습니다 하지만, 선택한 후원 플랫폼은 수수료를 가져갈 수 있습니다.",
+	"Youtube": "유튜브",
+	"Youtube Loader Settings": "유튜브 로더 설정"
+}
diff --git a/src/lib/i18n/locales/languages.json b/src/lib/i18n/locales/languages.json
new file mode 100644
index 0000000000000000000000000000000000000000..809d375e437bccaba1985708a81b6a5b2b7cf0d8
--- /dev/null
+++ b/src/lib/i18n/locales/languages.json
@@ -0,0 +1,194 @@
+[
+	{
+		"code": "en-US",
+		"title": "English (US)"
+	},
+	{
+		"code": "en-GB",
+		"title": "English (GB)"
+	},
+	{
+		"code": "ar-BH",
+		"title": "Arabic (عربي)"
+	},
+	{
+		"code": "bn-BD",
+		"title": "Bengali (বাংলা)"
+	},
+	{
+		"code": "bg-BG",
+		"title": "Bulgarian (български)"
+	},
+	{
+		"code": "ca-ES",
+		"title": "Catalan (català)"
+	},
+	{
+		"code": "ceb-PH",
+		"title": "Cebuano (Filipino)"
+	},
+	{
+		"code": "cs-CZ",
+		"title": "Czech (čeština)"
+	},
+	{
+		"code": "da-DK",
+		"title": "Danish (Denmark)"
+	},
+	{
+		"code": "de-DE",
+		"title": "German (Deutsch)"
+	},
+	{
+		"code": "es-ES",
+		"title": "Spanish (Español)"
+	},
+	{
+		"code": "eu-ES",
+		"title": "Basque (Euskara)"
+	},
+	{
+		"code": "fa-IR",
+		"title": "Persian (فارسی)"
+	},
+	{
+		"code": "fi-FI",
+		"title": "Finnish (Suomalainen)"
+	},
+	{
+		"code": "fr-CA",
+		"title": "French (Canada)"
+	},
+	{
+		"code": "fr-FR",
+		"title": "French (France)"
+	},
+	{
+		"code": "el-GR",
+		"title": "Greek (Ἑλλάδα)"
+	},
+	{
+		"code": "he-IL",
+		"title": "Hebrew (עברית)"
+	},
+	{
+		"code": "hi-IN",
+		"title": "Hindi (हिंदी)"
+	},
+	{
+		"code": "hr-HR",
+		"title": "Croatian (Hrvatski)"
+	},
+	{
+		"code": "hu-HU",
+		"title": "Hungarian (Magyar)"
+	},
+	{
+		"code": "id-ID",
+		"title": "Indonesian (Bahasa Indonesia)"
+	},
+	{
+		"code": "ie-GA",
+		"title": "Irish (Gaeilge)"
+	},
+	{
+		"code": "it-IT",
+		"title": "Italian (Italiano)"
+	},
+	{
+		"code": "ja-JP",
+		"title": "Japanese (日本語)"
+	},
+	{
+		"code": "ka-GE",
+		"title": "Georgian (ქართული)"
+	},
+	{
+		"code": "ko-KR",
+		"title": "Korean (한국어)"
+	},
+	{
+		"code": "lt-LT",
+		"title": "Lithuanian (Lietuvių)"
+	},
+	{
+		"code": "ms-MY",
+		"title": "Malay (Bahasa Malaysia)"
+	},
+	{
+		"code": "nb-NO",
+		"title": "Norwegian Bokmål (Norway)"
+	},
+	{
+		"code": "nl-NL",
+		"title": "Dutch (Netherlands)"
+	},
+	{
+		"code": "pa-IN",
+		"title": "Punjabi (India)"
+	},
+	{
+		"code": "pl-PL",
+		"title": "Polish (Polski)"
+	},
+	{
+		"code": "pt-BR",
+		"title": "Portuguese (Brazil)"
+	},
+	{
+		"code": "pt-PT",
+		"title": "Portuguese (Portugal)"
+	},
+	{
+		"code": "ro-RO",
+		"title": "Romanian (Romania)"
+	},
+	{
+		"code": "ru-RU",
+		"title": "Russian (Russia)"
+	},
+	{
+		"code": "sv-SE",
+		"title": "Swedish (Svenska)"
+	},
+	{
+		"code": "sr-RS",
+		"title": "Serbian (Српски)"
+	},
+	{
+		"code": "th-TH",
+		"title": "Thailand (ไทย)"
+	},
+	{
+		"code": "tr-TR",
+		"title": "Turkish (Türkçe)"
+	},
+	{
+		"code": "tk-TW",
+		"title": "Turkmen (Türkmençe)"
+	},
+	{
+		"code": "uk-UA",
+		"title": "Ukrainian (Українська)"
+	},
+	{
+		"code": "ur-PK",
+		"title": "Urdu (اردو)"
+	},
+	{
+		"code": "vi-VN",
+		"title": "Vietnamese (Tiếng Việt)"
+	},
+	{
+		"code": "zh-CN",
+		"title": "Chinese (简体中文)"
+	},
+	{
+		"code": "zh-TW",
+		"title": "Chinese (繁體中文)"
+	},
+	{
+		"code": "dg-DG",
+		"title": "Doge (🐶)"
+	}
+]
diff --git a/src/lib/i18n/locales/lt-LT/translation.json b/src/lib/i18n/locales/lt-LT/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..9b7640a08c308fe52a5091a6c967795307f622fe
--- /dev/null
+++ b/src/lib/i18n/locales/lt-LT/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' arba '-1' kad neišteitų iš galiojimo.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(pvz. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(pvz. `sh webui.sh --api`)",
+	"(latest)": "(naujausias)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}} susirašinėjimai",
+	"{{webUIName}} Backend Required": "{{webUIName}} būtinas serveris",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Užduočių modelis naudojamas pokalbių pavadinimų ir paieškos užklausų generavimui.",
+	"a user": "naudotojas",
+	"About": "Apie",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Paskyra",
+	"Account Activation Pending": "Laukiama paskyros patvirtinimo",
+	"Accurate information": "Tiksli informacija",
+	"Actions": "Veiksmai",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktyvūs naudotojai",
+	"Add": "Pridėti",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Pridėti trumpą modelio aprašymą",
+	"Add a tag": "Pridėti žymą",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Pridėti užklausos šabloną",
+	"Add Files": "Pridėti failus",
+	"Add Group": "",
+	"Add Memory": "Pridėti atminį",
+	"Add Model": "Pridėti modelį",
+	"Add Tag": "Pridėti žymą",
+	"Add Tags": "Pridėti žymas",
+	"Add text content": "",
+	"Add User": "Pridėti naudotoją",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Šių nustatymų pakeitimas bus pritakytas visiems naudotojams.",
+	"admin": "Administratorius",
+	"Admin": "Administratorius",
+	"Admin Panel": "Administratorių panelė",
+	"Admin Settings": "Administratorių nustatymai",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratoriai visada turi visus įrankius. Naudotojai turi tuėti prieigą prie dokumentų per modelių nuostatas",
+	"Advanced Parameters": "Pažengę nustatymai",
+	"Advanced Params": "Pažengę nustatymai",
+	"All chats": "",
+	"All Documents": "Visi dokumentai",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Leisti pokalbių ištrynimą",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Leisti nelokalius balsus",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "Leisti naudotojo vietos matymą",
+	"Allow Voice Interruption in Call": "Leisti pertraukimą skambučio metu",
+	"Already have an account?": "Ar jau turite paskyrą?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "assistentas",
+	"and": "ir",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "sukurti naują dalinimosi nuorodą",
+	"API Base URL": "API basės nuoroda",
+	"API Key": "API raktas",
+	"API Key created.": "API raktas sukurtas",
+	"API keys": "API raktai",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Balandis",
+	"Archive": "Archyvai",
+	"Archive All Chats": "Archyvuoti visus pokalbius",
+	"Archived Chats": "Archyvuoti pokalbiai",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Are esate tikri?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Pridėti failą",
+	"Attention to detail": "Dėmesys detalėms",
+	"Attribute for Username": "",
+	"Audio": "Audio įrašas",
+	"August": "Rugpjūtis",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Automatiškai nukopijuoti atsakymą",
+	"Auto-playback response": "Automatinis atsakymo skaitymas",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 bazės nuoroda",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 bazės nuoroda reikalinga.",
+	"Available list": "",
+	"available!": "prieinama!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Atgal",
+	"Bad Response": "Neteisingas atsakymas",
+	"Banners": "Baneriai",
+	"Base Model (From)": "Bazinis modelis",
+	"Batch Size (num_batch)": "Batch dydis",
+	"before": "prieš",
+	"Being lazy": "Būvimas tingiu",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API raktas",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Išvengti SSL patikros puslapiams",
+	"Call": "Skambinti",
+	"Call feature is not supported when using Web STT engine": "Skambučio funkcionalumas neleidžiamas naudojant Web STT variklį",
+	"Camera": "Kamera",
+	"Cancel": "Atšaukti",
+	"Capabilities": "Gebėjimai",
+	"Certificate Path": "",
+	"Change Password": "Keisti slaptažodį",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Pokalbis",
+	"Chat Background Image": "Pokalbio galinė užsklanda",
+	"Chat Bubble UI": "Pokalbio burbulo sąsaja",
+	"Chat Controls": "Pokalbio valdymas",
+	"Chat direction": "Pokalbio linkmė",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Pokalbiai",
+	"Check Again": "Patikrinti iš naujo",
+	"Check for updates": "Patikrinti atnaujinimus",
+	"Checking for updates...": "Ieškoma atnaujinimų...",
+	"Choose a model before saving...": "Pasirinkite modelį prieš išsaugant...",
+	"Chunk Overlap": "Blokų persidengimas",
+	"Chunk Params": "Blokų nustatymai",
+	"Chunk Size": "Blokų dydis",
+	"Ciphers": "",
+	"Citation": "Citata",
+	"Clear memory": "Ištrinti atmintį",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Paspauskite čia dėl pagalbos.",
+	"Click here to": "Paspauskite čia, kad:",
+	"Click here to download user import template file.": "Pasauskite čia norėdami sukurti naudotojo įkėlimo šablono rinkmeną",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Spauskite čia norėdami pasirinkti",
+	"Click here to select a csv file.": "Spauskite čia tam, kad pasirinkti csv failą",
+	"Click here to select a py file.": "Spauskite čia norėdami pasirinkti py failą",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "paspauskite čia.",
+	"Click on the user role button to change a user's role.": "Paspauskite ant naudotojo rolės mygtuko tam, kad pakeisti naudotojo rolę.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Iškarpinės naudojimas neleidžiamas naršyklės.",
+	"Clone": "Klonuoti",
+	"Close": "Uždaryti",
+	"Code execution": "",
+	"Code formatted successfully": "Kodas suformatuotas sėkmingai",
+	"Collection": "Kolekcija",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI bazės nuoroda",
+	"ComfyUI Base URL is required.": "ComfyUI bazės nuoroda privaloma",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Command",
+	"Completions": "",
+	"Concurrent Requests": "Kelios užklausos vienu metu",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Patvrtinti",
+	"Confirm Password": "Patvirtinkite slaptažodį",
+	"Confirm your action": "Patvirtinkite veiksmą",
+	"Connections": "Ryšiai",
+	"Contact Admin for WebUI Access": "Susisiekite su administratoriumi dėl prieigos",
+	"Content": "Turinys",
+	"Content Extraction": "Turinio ištraukimas",
+	"Context Length": "Konteksto ilgis",
+	"Continue Response": "Tęsti atsakymą",
+	"Continue with {{provider}}": "Tęstti su {{tiekėju}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "Valdymas",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Nukopijuota",
+	"Copied shared chat URL to clipboard!": "Nukopijavote pokalbio nuorodą",
+	"Copied to clipboard": "",
+	"Copy": "Kopijuoti",
+	"Copy last code block": "Kopijuoti paskutinį kodo bloką",
+	"Copy last response": "Kopijuoti paskutinį atsakymą",
+	"Copy Link": "Kopijuoti nuorodą",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "La copie dans le presse-papiers a réussi !",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Sukurti modelį",
+	"Create Account": "Créer un compte",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Sukurti naują raktą",
+	"Create new secret key": "Sukurti naują slaptą raktą",
+	"Created at": "Sukurta",
+	"Created At": "Sukurta",
+	"Created by": "Sukurta",
+	"CSV Import": "CSV importavimas",
+	"Current Model": "Dabartinis modelis",
+	"Current Password": "Esamas slaptažodis",
+	"Custom": "Personalizuota",
+	"Dark": "Tamsus",
+	"Database": "Duomenų bazė",
+	"December": "Gruodis",
+	"Default": "Numatytasis",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Numatytasis (SentenceTransformers)",
+	"Default Model": "Numatytasis modelis",
+	"Default model updated": "Numatytasis modelis atnaujintas",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Numatytieji užklausų pasiūlymai",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Numatytoji naudotojo rolė",
+	"Delete": "ištrinti",
+	"Delete a model": "Ištrinti modėlį",
+	"Delete All Chats": "Ištrinti visus pokalbius",
+	"Delete All Models": "",
+	"Delete chat": "Išrinti pokalbį",
+	"Delete Chat": "Ištrinti pokalbį",
+	"Delete chat?": "Ištrinti pokalbį?",
+	"Delete folder?": "",
+	"Delete function?": "Ištrinti funkciją",
+	"Delete prompt?": "Ištrinti užklausą?",
+	"delete this link": "Ištrinti nuorodą",
+	"Delete tool?": "Ištrinti įrankį?",
+	"Delete User": "Ištrinti naudotoją",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} ištrinta",
+	"Deleted {{name}}": "Ištrinta {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Aprašymas",
+	"Didn't fully follow instructions": "Pilnai nesekė instrukcijų",
+	"Disabled": "Išjungta",
+	"Discover a function": "Atrasti funkciją",
+	"Discover a model": "Atrasti modelį",
+	"Discover a prompt": "Atrasti užklausas",
+	"Discover a tool": "Atrasti įrankį",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Atrasti, atsisiųsti arba rasti naujas funkcijas",
+	"Discover, download, and explore custom prompts": "Atrasti ir parsisiųsti užklausas",
+	"Discover, download, and explore custom tools": "Atrasti, atsisiųsti arba rasti naujų įrankių",
+	"Discover, download, and explore model presets": "Atrasti ir parsisiųsti modelių konfigūracija",
+	"Dismissible": "Atemtama",
+	"Display": "",
+	"Display Emoji in Call": "Rodyti emoji pokalbiuose",
+	"Display the username instead of You in the Chat": "Rodyti naudotojo vardą vietoje žodžio Jūs pokalbyje",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Neinstaliuokite funkcijų iš nepatikimų šaltinių",
+	"Do not install tools from sources you do not fully trust.": "Neinstaliuokite įrankių iš nepatikimų šaltinių",
+	"Document": "Dokumentas",
+	"Documentation": "Dokumentacija",
+	"Documents": "Dokumentai",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "neturi jokių išorinių ryšių ir duomenys lieka serveryje.",
+	"Don't have an account?": "Neturite paskyros?",
+	"don't install random functions from sources you don't trust.": "neinstaliuokite funkcijų iš nepatikimų šaltinių",
+	"don't install random tools from sources you don't trust.": "neinstaliuokite įrankių iš nepatikimų šaltinių",
+	"Don't like the style": "Nepatinka stilius",
+	"Done": "Atlikta",
+	"Download": "Parsisiųsti",
+	"Download canceled": "Parsisiuntimas atšauktas",
+	"Download Database": "Parsisiųsti duomenų bazę",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Įkelkite dokumentus čia, kad juos pridėti į pokalbį",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "pvz. '30s', '10m'. Laiko vienetai yra 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Redaguoti",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Koreguoti atminį",
+	"Edit User": "Redaguoti naudotoją",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "El. paštas",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Embedding dydis",
+	"Embedding Model": "Embedding modelis",
+	"Embedding Model Engine": "Embedding modelio variklis",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding modelis nustatytas kaip\"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Leisti dalinimąsi su bendruomene",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Aktyvuoti naujas registracijas",
+	"Enable Web Search": "Leisti paiešką internete",
+	"Enabled": "Leisti",
+	"Engine": "Variklis",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Įsitikinkite, kad CSV failas turi 4 kolonas šiuo eiliškumu: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Įveskite {{role}} žinutę čia",
+	"Enter a detail about yourself for your LLMs to recall": "Įveskite informaciją apie save jūsų modelio atminčiai",
+	"Enter api auth string (e.g. username:password)": "Įveskite API autentifikacijos kodą (pvz. username:password)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Įveskite Bravo Search API raktą",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Įveskite blokų persidengimą",
+	"Enter Chunk Size": "Įveskite blokų dydį",
+	"Enter description": "",
+	"Enter Github Raw URL": "Įveskite GitHub Raw nuorodą",
+	"Enter Google PSE API Key": "Įveskite Google PSE API raktą",
+	"Enter Google PSE Engine Id": "Įveskite Google PSE variklio ID",
+	"Enter Image Size (e.g. 512x512)": "Įveskite paveiksliuko dydį (pvz. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Įveskite kalbos kodus",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Įveskite modelio žymą (pvz. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Įveskite žingsnių kiekį (pvz. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Įveskite rezultatą",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Įveskite Searxng Query nuorodą",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Įveskite Serper API raktą",
+	"Enter Serply API Key": "Įveskite Serply API raktą",
+	"Enter Serpstack API Key": "Įveskite Serpstack API raktą",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Įveskite pabaigos sekvenciją",
+	"Enter system prompt": "Įveskite sistemos užklausą",
+	"Enter Tavily API Key": "Įveskite Tavily API raktą",
+	"Enter Tika Server URL": "Įveskite Tika serverio nuorodą",
+	"Enter Top K": "Įveskite Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Įveskite nuorodą (pvz. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Įveskite nuorododą (pvz. http://localhost:11434",
+	"Enter Your Email": "Įveskite el. pašto adresą",
+	"Enter Your Full Name": "Įveskite vardą bei pavardę",
+	"Enter your message": "Įveskite žinutę",
+	"Enter Your Password": "Įveskite slaptažodį",
+	"Enter Your Role": "Įveskite savo rolę",
+	"Enter Your Username": "",
+	"Error": "Klaida",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Eksperimentinis",
+	"Explore the cosmos": "",
+	"Export": "Eksportuoti",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Eksportuoti visų naudotojų visus pokalbius",
+	"Export chat (.json)": "Eksportuoti pokalbį (.json)",
+	"Export Chats": "Eksportuoti pokalbius",
+	"Export Config to JSON File": "",
+	"Export Functions": "Eksportuoti funkcijas",
+	"Export Models": "Eksportuoti modelius",
+	"Export Presets": "",
+	"Export Prompts": "Eksportuoti užklausas",
+	"Export to CSV": "",
+	"Export Tools": "Eksportuoti įrankius",
+	"External Models": "Išoriniai modeliai",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Nepavyko sukurti API rakto",
+	"Failed to read clipboard contents": "Nepavyko perskaityti kopijuoklės",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Nepavyko atnaujinti nustatymų",
+	"Failed to upload file.": "",
+	"February": "Vasaris",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Galite pridėti specifinių detalių",
+	"File": "Rinkmena",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Rinkmenų rėžimas",
+	"File not found.": "Failas nerastas.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "Rinkmenos",
+	"Filter is now globally disabled": "Filtrai nėra leidžiami globaliai",
+	"Filter is now globally enabled": "Filtrai globaliai leidžiami",
+	"Filters": "Filtrai",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Nepavyko nsutatyti profilio nuotraukos",
+	"Fluidly stream large external response chunks": "Sklandžiai transliuoti ilgus atsakymus",
+	"Focus chat input": "Fokusuoti žinutės įvestį",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Tobulai sekė instrukcijas",
+	"Forge new paths": "",
+	"Form": "Forma",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Dažnumo bauda",
+	"Function": "",
+	"Function created successfully": "Funkcija sukurta sėkmingai",
+	"Function deleted successfully": "Funkcija ištrinta sėkmingai",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Funkcijos šiuo metu neleidžiamos",
+	"Function is now globally enabled": "Funkcijos leidžiamos",
+	"Function Name": "",
+	"Function updated successfully": "Funkcija atnaujinta sėkmingai",
+	"Functions": "Funkcijos",
+	"Functions allow arbitrary code execution": "Funkcijos leidžia nekontroliuojamo kodo vykdymą",
+	"Functions allow arbitrary code execution.": "Funkcijos leidžia nekontroliuojamo kodo vykdymą",
+	"Functions imported successfully": "Funkcijos importuotos sėkmingai",
+	"General": "Bendri",
+	"General Settings": "Bendri nustatymai",
+	"Generate Image": "Generuoti paveikslėlį",
+	"Generating search query": "Generuoti paieškos užklausą",
+	"Generation Info": "Generavimo informacija",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Globalu",
+	"Good Response": "Geras atsakymas",
+	"Google PSE API Key": "Google PSE API raktas",
+	"Google PSE Engine Id": "Google PSE variklio ID",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "valanda:mėnesis:metai",
+	"Haptic Feedback": "",
+	"has no conversations.": "neturi pokalbių",
+	"Hello, {{name}}": "Sveiki, {{name}}",
+	"Help": "Pagalba",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Paslėpti",
+	"Host": "",
+	"How can I help you today?": "Kuo galėčiau Jums padėti ?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hibridinė paieška",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Suprantu veiksmų ir kodo vykdymo rizikas.",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Vaizdų generavimas (eksperimentinis)",
+	"Image Generation Engine": "Vaizdų generavimo variklis",
+	"Image Settings": "Vaizdų nustatymai",
+	"Images": "Vaizdai",
+	"Import Chats": "Importuoti pokalbius",
+	"Import Config from JSON File": "",
+	"Import Functions": "Importuoti funkcijas",
+	"Import Models": "Importuoti modelius",
+	"Import Presets": "",
+	"Import Prompts": "Importuoti užklausas",
+	"Import Tools": "Importuoti įrankius",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Įtraukti `--api-auth` flag when running stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Pridėti `--api` kai vykdomas stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informacija",
+	"Input commands": "Įvesties komandos",
+	"Install from Github URL": "Instaliuoti Github nuorodą",
+	"Instant Auto-Send After Voice Transcription": "Siųsti iškart po balso transkripcijos",
+	"Interface": "Sąsaja",
+	"Invalid file format.": "",
+	"Invalid Tag": "Neteisinga žyma",
+	"January": "Sausis",
+	"Jina API Key": "",
+	"join our Discord for help.": "prisijunkite prie mūsų Discord.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON peržiūra",
+	"July": "liepa",
+	"June": "birželis",
+	"JWT Expiration": "JWT išėjimas iš galiojimo",
+	"JWT Token": "JWT žetonas",
+	"Keep Alive": "Išlaikyti aktyviu",
+	"Key": "",
+	"Keyboard shortcuts": "Klaviatūros trumpiniai",
+	"Knowledge": "Žinios",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Kalba",
+	"Last Active": "Paskutinį kartą aktyvus",
+	"Last Modified": "Paskutinis pakeitimas",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Šviesus",
+	"Listening...": "Klausoma...",
+	"LLMs can make mistakes. Verify important information.": "Dideli kalbos modeliai gali klysti. Patikrinkite atsakymų teisingumą.",
+	"Local": "",
+	"Local Models": "Lokalūs modeliai",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Sukurta OpenWebUI bendruomenės",
+	"Make sure to enclose them with": "Užtikrinktie, kad įtraukiate viduje:",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Tvarkyti",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Tvarkyti procesus",
+	"March": "Kovas",
+	"Max Tokens (num_predict)": "Maksimalus žetonų kiekis (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Daugiausiai trys modeliai gali būti parsisiunčiami vienu metu.",
+	"May": "gegužė",
+	"Memories accessible by LLMs will be shown here.": "Atminitis prieinama kalbos modelio bus rodoma čia.",
+	"Memory": "Atmintis",
+	"Memory added successfully": "Atmintis pridėta sėkmingai",
+	"Memory cleared successfully": "Atmintis ištrinta sėkmingai",
+	"Memory deleted successfully": "Atmintis ištrinta sėkmingai",
+	"Memory updated successfully": "Atmintis atnaujinta sėkmingai",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Žinutės, kurias siunčiate po nuorodos sukūrimo nebus matomos nuorodos turėtojams. Naudotojai su nuoroda matys žinutes iki nuorodos sukūrimo.",
+	"Min P": "Mažiausias p",
+	"Minimum Score": "Minimalus rezultatas",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' modelis sėkmingai atsisiųstas.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modelis '{{modelTag}}' jau atsisiuntimų eilėje.",
+	"Model {{modelId}} not found": "Modelis {{modelId}} nerastas",
+	"Model {{modelName}} is not vision capable": "Modelis {{modelName}} neturi vaizdo gebėjimų",
+	"Model {{name}} is now {{status}}": "Modelis {{name}} dabar {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "Modelis sukurtas sėkmingai",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modelio failų sistemos kelias aptiktas. Reikalingas trumpas modelio pavadinimas atnaujinimui.",
+	"Model Filtering": "",
+	"Model ID": "Modelio ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modelis nepasirinktas",
+	"Model Params": "Modelio parametrai",
+	"Model Permissions": "",
+	"Model updated successfully": "Modelis atnaujintas sėkmingai",
+	"Modelfile Content": "Modelio failo turinys",
+	"Models": "Modeliai",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Daugiau",
+	"Name": "Pavadinimas",
+	"Name your knowledge base": "",
+	"New Chat": "Naujas pokalbis",
+	"New folder": "",
+	"New Password": "Naujas slaptažodis",
+	"No content found": "",
+	"No content to speak": "Nėra turinio kalbėjimui",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Nėra pasirinktų dokumentų",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Rezultatų nerasta",
+	"No search query generated": "Paieškos užklausa nesugeneruota",
+	"No source available": "Šaltinių nerasta",
+	"No users were found.": "",
+	"No valves to update": "Nėra atnaujinamų įeičių",
+	"None": "Nėra",
+	"Not factually correct": "Faktiškai netikslu",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Jei turite minimalų įvertį, paieška gražins tik tą informaciją, kuri viršyje šį įvertį",
+	"Notes": "",
+	"Notifications": "Pranešimai",
+	"November": "lapkritis",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "spalis",
+	"Off": "Išjungta",
+	"Okay, Let's Go!": "Gerai, važiuojam!",
+	"OLED Dark": "OLED tamsus",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API išjungtas",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama versija",
+	"On": "Aktyvuota",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Leistinos tik raidės, skaičiai ir brūkšneliai.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Regis nuoroda nevalidi. Prašau patikrtinkite ir pabandykite iš naujo.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Naudojate nepalaikomą (front-end) web ui rėžimą. Prašau serviruokite WebUI iš back-end",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Atverti naują pokalbį",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Tortue Chat versija per sena. Reikalinga (v{{REQUIRED_VERSION}}) versija.",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "Open AI API nustatymai",
+	"OpenAI API Key is required.": "OpenAI API raktas būtinas.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI API nuoroda ir raktas būtini",
+	"or": "arba",
+	"Organize your users": "",
+	"Other": "Kita",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Slaptažodis",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF dokumentas (.pdf)",
+	"PDF Extract Images (OCR)": "PDF paveikslėlių skaitymas (OCR)",
+	"pending": "laukiama",
+	"Permission denied when accessing media devices": "Leidimas atmestas bandant prisijungti prie medijos įrenginių",
+	"Permission denied when accessing microphone": "Mikrofono leidimas atmestas",
+	"Permission denied when accessing microphone: {{error}}": "Leidimas naudoti mikrofoną atmestas: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalizacija",
+	"Pin": "Smeigtukas",
+	"Pinned": "Įsmeigta",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Procesas ištrintas sėkmingai",
+	"Pipeline downloaded successfully": "Procesas atsisiųstas sėkmingai",
+	"Pipelines": "Procesai",
+	"Pipelines Not Detected": "Procesai neaptikti",
+	"Pipelines Valves": "Procesų įeitys",
+	"Plain text (.txt)": "Grynas tekstas (.txt)",
+	"Playground": "Eksperimentavimo erdvė",
+	"Please carefully review the following warnings:": "Peržiūrėkite šiuos perspėjimus:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Pozityvus elgesys",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Paskutinės 30 dienų",
+	"Previous 7 days": "Paskutinės 7 dienos",
+	"Profile Image": "Profilio nuotrauka",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Užklausa (pvz. supaprastink šį laišką)",
+	"Prompt Content": "Užklausos turinys",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Užklausų pavyzdžiai",
+	"Prompt updated successfully": "",
+	"Prompts": "Užklausos",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Rasti \"{{searchValue}}\" iš Ollama.com",
+	"Pull a model from Ollama.com": "Gauti modelį iš Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Užklausos parametrai",
+	"RAG Template": "RAG šablonas",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Skaityti garsiai",
+	"Record voice": "Įrašyti balsą",
+	"Redirecting you to OpenWebUI Community": "Perkeliam Jus į OpenWebUI bendruomenę",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Vadinkite save Naudotoju (pvz. Naudotojas mokosi prancūzų kalbos)",
+	"References from": "",
+	"Refused when it shouldn't have": "Atmesta kai neturėtų būti atmesta",
+	"Regenerate": "Generuoti iš naujo",
+	"Release Notes": "Naujovės",
+	"Relevance": "",
+	"Remove": "Pašalinti",
+	"Remove Model": "Pašalinti modelį",
+	"Rename": "Pervadinti",
+	"Reorder Models": "",
+	"Repeat Last N": "Pakartoti paskutinius N",
+	"Request Mode": "Užklausos rėžimas",
+	"Reranking Model": "Reranking modelis",
+	"Reranking model disabled": "Reranking modelis neleidžiamas",
+	"Reranking model set to \"{{reranking_model}}\"": "Nustatytas rereanking modelis: \"{{reranking_model}}\"",
+	"Reset": "Atkurti",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Atkurti įkėlimų direktoiją",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Naršyklė neleidžia siųsti pranešimų",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rolė",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "Veikia",
+	"Save": "Išsaugoti",
+	"Save & Create": "Išsaugoti ir sukurti",
+	"Save & Update": "Išsaugoti ir atnaujinti",
+	"Save As Copy": "",
+	"Save Tag": "Išsaugoti žymą",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Pokalbių saugojimas naršyklėje nebegalimas.",
+	"Scroll to bottom when switching between branches": "Slikite link apačios norėdami pakeisti šakas",
+	"Search": "Ieškoti",
+	"Search a model": "Ieškoti modelio",
+	"Search Base": "",
+	"Search Chats": "Ieškoti pokalbiuose",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Ieškoti funkcijų",
+	"Search Knowledge": "",
+	"Search Models": "Ieškoti modelių",
+	"Search options": "",
+	"Search Prompts": "Ieškoti užklausų",
+	"Search Result Count": "Paieškos rezultatų skaičius",
+	"Search the web": "",
+	"Search Tools": "Paieškos įrankiai",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Ieškota {{count}} sites_one",
+	"Searched {{count}} sites_few": "Ieškota {{count}} sites_few",
+	"Searched {{count}} sites_many": "Ieškota {{count}} sites_many",
+	"Searched {{count}} sites_other": "Ieškota {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Ieškoma \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng užklausos URL",
+	"See readme.md for instructions": "Žiūrėti readme.md papildomoms instrukcijoms",
+	"See what's new": "Žiūrėti naujoves",
+	"Seed": "Sėkla",
+	"Select a base model": "Pasirinkite bazinį modelį",
+	"Select a engine": "Pasirinkite variklį",
+	"Select a function": "Pasirinkite funkciją",
+	"Select a group": "",
+	"Select a model": "Pasirinkti modelį",
+	"Select a pipeline": "Pasirinkite procesą",
+	"Select a pipeline url": "Pasirinkite proceso nuorodą",
+	"Select a tool": "Pasirinkite įrankį",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Pasirinkti modelį",
+	"Select only one model to call": "Pasirinkite vieną modelį",
+	"Selected model(s) do not support image inputs": "Pasirinkti modeliai nepalaiko vaizdinių užklausų",
+	"Semantic distance to query": "",
+	"Send": "Siųsti",
+	"Send a Message": "Siųsti žinutę",
+	"Send message": "Siųsti žinutę",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "rugsėjis",
+	"Serper API Key": "Serper API raktas",
+	"Serply API Key": "Serply API raktas",
+	"Serpstack API Key": "Serpstach API raktas",
+	"Server connection verified": "Serverio sujungimas patvirtintas",
+	"Set as default": "Nustatyti numatytąjį",
+	"Set CFG Scale": "",
+	"Set Default Model": "Nustatyti numatytąjį modelį",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Nustatyti embedding modelį",
+	"Set Image Size": "Nustatyti paveikslėlių dydį",
+	"Set reranking model (e.g. {{model}})": "Nustatyti reranking modelį",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Numatyti etapus",
+	"Set Task Model": "Numatyti užduočių modelį",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Numatyti balsą",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Nustatymai",
+	"Settings saved successfully!": "Parametrai sėkmingai išsaugoti!",
+	"Share": "Dalintis",
+	"Share Chat": "Dalintis pokalbiu",
+	"Share to OpenWebUI Community": "Dalintis su OpenWebUI bendruomene",
+	"Show": "Rodyti",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Rodyti administratoriaus duomenis laukiant paskyros patvirtinimo",
+	"Show shortcuts": "Rodyti trumpinius",
+	"Show your support!": "Palaikykite",
+	"Showcased creativity": "Kūrybingų užklausų paroda",
+	"Sign in": "Prisijungti",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Atsijungti",
+	"Sign up": "Sukurti paskyrą",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Šaltinis",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Balso atpažinimo problema: {{error}}",
+	"Speech-to-Text Engine": "Balso atpažinimo modelis",
+	"Stop": "",
+	"Stop Sequence": "Baigt sekvenciją",
+	"Stream Chat Response": "",
+	"STT Model": "STT modelis",
+	"STT Settings": "STT nustatymai",
+	"Subtitle (e.g. about the Roman Empire)": "Subtitras",
+	"Success": "Sėkmingai",
+	"Successfully updated.": "Sėkmingai atnaujinta.",
+	"Suggested": "Siūloma",
+	"Support": "Palaikyti",
+	"Support this plugin:": "Palaikykite šitą modulį",
+	"Sync directory": "",
+	"System": "Sistema",
+	"System Instructions": "",
+	"System Prompt": "Sistemos užklausa",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Paspauskite norėdami pertraukti",
+	"Tavily API Key": "Tavily API raktas",
+	"Tell us more:": "Papasakokite daugiau",
+	"Temperature": "Temperatūra",
+	"Template": "Modelis",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Balso sintezės modelis",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Ačiū už atsiliepimus",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Šis modulis kuriamas savanorių. Palaikykite jų darbus finansiškai arba prisidėdami kodu.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Rezultatas turėtų būti tarp 0.0 (0%) ir 1.0 (100%)",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Mąsto...",
+	"This action cannot be undone. Do you wish to continue?": "Šis veiksmas negali būti atšauktas. Ar norite tęsti?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Tai užtikrina, kad Jūsų pokalbiai saugiai saugojami duomenų bazėje. Ačiū!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Tai eksperimentinė funkcija ir gali veikti nevisada.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Tai ištrins",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Platus paaiškinimas",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Reiklainga Tika serverio nuorodą",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Jei norite pakeisti keletą kintamųjų vieną po kitos, spauskite Tab",
+	"Title": "Pavadinimas",
+	"Title (e.g. Tell me a fun fact)": "Pavadinimas",
+	"Title Auto-Generation": "Automatinis pavadinimų generavimas",
+	"Title cannot be an empty string.": "Pavadinimas negali būti tuščias",
+	"Title Generation Prompt": "Pavadinimo generavimo užklausa",
+	"TLS": "",
+	"To access the available model names for downloading,": "Tam, kad prieiti prie galimų parsisiųsti modelių",
+	"To access the GGUF models available for downloading,": "Tam, kad prieiti prie galimų parsisiųsti GGUF,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Norėdami prieiti prie programos, susisiekite su administratoriumi, kuris Jus patvirtins.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Norėdami pasirinkti veiksmus, pirmiausia pridėkite juos funkcijų nuostatuose",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Norėdami pasirinkti filtrus, pirmiausia pridėkite juos funkcijų nuostatuose",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Norėdami pasirinkti įrankius, pirmiausia pridėkite juos prie įrankių nuostatuose",
+	"Toast notifications for new updates": "",
+	"Today": "Šiandien",
+	"Toggle settings": "Atverti/užverti parametrus",
+	"Toggle sidebar": "Atverti/užverti šoninį meniu",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Žetonų kiekis konteksto atnaujinimui (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Įrankis sukurtas sėkmingai",
+	"Tool deleted successfully": "Įrankis ištrintas sėkmingai",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Įrankis importuotas sėkmingai",
+	"Tool Name": "",
+	"Tool updated successfully": "Įrankis atnaujintas sėkmingai",
+	"Tools": "Įrankiai",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Įrankiai gali naudoti funkcijas ir vykdyti kodą",
+	"Tools have a function calling system that allows arbitrary code execution": "Įrankiai gali naudoti funkcijas ir leisti vykdyti kodą",
+	"Tools have a function calling system that allows arbitrary code execution.": "Įrankiai gali naudoti funkcijas ir leisti vykdyti kodą",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemos prieinant prie Ollama?",
+	"TTS Model": "TTS modelis",
+	"TTS Settings": "TTS parametrai",
+	"TTS Voice": "TTS balsas",
+	"Type": "Tipas",
+	"Type Hugging Face Resolve (Download) URL": "Įveskite Hugging Face Resolve nuorodą",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "O ne! Prisijungiant prie {{provider}} kilo problema.",
+	"UI": "sąsaja",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Atsemigti",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Atnaujinti",
+	"Update and Copy Link": "Atnaujinti ir kopijuoti nuorodą",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Atnaujinti slaptažodį",
+	"Updated": "",
+	"Updated at": "Atnaujinta",
+	"Updated At": "",
+	"Upload": "Atnaujinti",
+	"Upload a GGUF model": "Parsisiųsti GGUF modelį",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Atnaujinti rinkmenas",
+	"Upload Pipeline": "Atnaujinti procesą",
+	"Upload Progress": "Įkėlimo progresas",
+	"URL": "",
+	"URL Mode": "URL režimas",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Naudoti Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Naudotojo inicialai",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "naudotojas",
+	"User": "",
+	"User location successfully retrieved.": "Naudotojo vieta sėkmingai gauta",
+	"Username": "",
+	"Users": "Naudotojai",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Naudoti",
+	"Valid time units:": "Teisingūs laiko vienetai :",
+	"Valves": "Įeitys",
+	"Valves updated": "Įeitys atnaujintos",
+	"Valves updated successfully": "Įeitys atnaujintos sėkmingai",
+	"variable": "kintamasis",
+	"variable to have them replaced with clipboard content.": "kintamoji pakeičiama kopijuoklės turiniu.",
+	"Version": "Versija",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Balsas",
+	"Voice Input": "",
+	"Warning": "Perspėjimas",
+	"Warning:": "Perspėjimas",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Jei pakeisite embedding modelį, turėsite reimportuoti visus dokumentus",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Web krovimo nustatymai",
+	"Web Search": "Web paieška",
+	"Web Search Engine": "Web paieškos variklis",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook nuoroda",
+	"WebUI Settings": "WebUI parametrai",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Kas naujo",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (lokalus)",
+	"Why?": "",
+	"Widescreen Mode": "Plataus ekrano rėžimas",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Nuostatos",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Parašykite užklausą",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Parašyk santrumpą trumpesnę nei 50 žodžių šiam tekstui: [tekstas]",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Vakar",
+	"You": "Jūs",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Galite pagerinti modelių darbą suteikdami jiems atminties funkcionalumą.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Jūs neturite archyvuotų pokalbių",
+	"You have shared this chat": "Pasidalinote šiuo pokalbiu",
+	"You're a helpful assistant.": "Esi asistentas.",
+	"You're now logged in.": "Esate prisijungę.",
+	"Your account status is currently pending activation.": "Jūsų paskyra laukia administratoriaus patvirtinimo.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Jūsų finansinis prisidėjimas tiesiogiai keliaus modulio kūrėjui.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube krovimo nustatymai"
+}
diff --git a/src/lib/i18n/locales/ms-MY/translation.json b/src/lib/i18n/locales/ms-MY/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..5a8c18d62f6c5b89bbfaad3e16f1d2b84f9011a8
--- /dev/null
+++ b/src/lib/i18n/locales/ms-MY/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' untuk tiada tempoh luput.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(contoh `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(contoh `sh webui.sh --api`)",
+	"(latest)": "(terkini)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Perbualan {{user}}",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend diperlukan",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model tugas digunakan semasa melaksanakan tugas seperti menjana tajuk untuk perbualan dan pertanyaan carian web.",
+	"a user": "seorang pengguna",
+	"About": "Mengenai",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Akaun",
+	"Account Activation Pending": "Pengaktifan Akaun belum selesai",
+	"Accurate information": "Informasi tepat",
+	"Actions": "Tindakan",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Pengguna Aktif",
+	"Add": "Tambah",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Tambah penerangan ringkas tentang apa yang model ini boleh lakukan",
+	"Add a tag": "Tambah tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Tambah arahan khusus",
+	"Add Files": "Tambah Fail",
+	"Add Group": "",
+	"Add Memory": "Tambah Memori",
+	"Add Model": "Tambah Model",
+	"Add Tag": "Tambah Tag",
+	"Add Tags": "Tambah Tag",
+	"Add text content": "",
+	"Add User": "Tambah Pengguna",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Melaraskan tetapan ini akan menggunakan perubahan secara universal kepada semua pengguna.",
+	"admin": "pentadbir",
+	"Admin": "Pentadbir",
+	"Admin Panel": "Panel Pentadbir",
+	"Admin Settings": "Tetapan Pentadbir",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Pentadbir mempunyai akses kepada semua alat pada setiap masa; pengguna memerlukan alat yang ditetapkan mengikut model dalam ruang kerja.",
+	"Advanced Parameters": "Parameter Lanjutan",
+	"Advanced Params": "Parameter Lanjutan",
+	"All chats": "",
+	"All Documents": "Semua Dokumen",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Benarkan Penghapusan Perbualan",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Benarkan suara bukan tempatan ",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "Benarkan Lokasi Pengguna",
+	"Allow Voice Interruption in Call": "Benarkan gangguan suara dalam panggilan",
+	"Already have an account?": "Telah mempunyai akaun?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "seorang pembantu",
+	"and": "dan",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "dan cipta pautan kongsi baharu",
+	"API Base URL": "URL Asas API",
+	"API Key": "Kunci API",
+	"API Key created.": "Kunci API dicipta",
+	"API keys": "Kekunci API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "April",
+	"Archive": "Arkib",
+	"Archive All Chats": "Arkibkan Semua Perbualan",
+	"Archived Chats": "Perbualan yang diarkibkan",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Adakah anda pasti",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Kepilkan Fail",
+	"Attention to detail": "Perincian",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "Ogos",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Salin Response secara Automatik ke Papan Klip",
+	"Auto-playback response": "Main semula respons secara automatik",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "URL Asas AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "URL Asas AUTOMATIC1111 diperlukan.",
+	"Available list": "",
+	"available!": "tersedia!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Kembali",
+	"Bad Response": "Maklumbalas Salah",
+	"Banners": "Sepanduk",
+	"Base Model (From)": "Model Asas (Dari)",
+	"Batch Size (num_batch)": "Saiz Kumpulan (num_batch)",
+	"before": "sebelum,",
+	"Being lazy": "Menjadi Malas",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Kunci API Carian Brave",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Pintas pengesahan SSL untuk Laman Web",
+	"Call": "Hubungi",
+	"Call feature is not supported when using Web STT engine": "Ciri panggilan tidak disokong apabila menggunakan enjin Web STT",
+	"Camera": "Kamera",
+	"Cancel": "Batal",
+	"Capabilities": "Keupayaan",
+	"Certificate Path": "",
+	"Change Password": "Tukar Kata Laluan",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Perbualan",
+	"Chat Background Image": "Imej Latar Belakang Perbualan",
+	"Chat Bubble UI": "Antaramuka Buih Perbualan",
+	"Chat Controls": "Kawalan Perbualan",
+	"Chat direction": "Arah Perbualan",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Perbualan",
+	"Check Again": "Semak Kembali",
+	"Check for updates": "Semak kemas kini",
+	"Checking for updates...": "Kemas kini sedang disemak",
+	"Choose a model before saving...": "Pilih model sebelum menyimpan",
+	"Chunk Overlap": "Tindihan 'Çhunk'",
+	"Chunk Params": "Parameter 'Çhunk'",
+	"Chunk Size": "Saiz 'Chunk'",
+	"Ciphers": "",
+	"Citation": "Petikan",
+	"Clear memory": "Kosongkan memori",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klik disini untuk mendapatkan bantuan",
+	"Click here to": "Klik disini untuk",
+	"Click here to download user import template file.": "Klik disini untuk memuat turun fail templat import pengguna",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Klik disini untuk memilih",
+	"Click here to select a csv file.": "Klik disini untuk memilih fail csv",
+	"Click here to select a py file.": "Klik disini untuk memilih fail py",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "klik disini.",
+	"Click on the user role button to change a user's role.": "Klik pada butang peranan pengguna untuk menukar peranan pengguna",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Kebenaran untuk menulis di papan klip ditolak. Sila semak tetapan pelayan web anda untuk memberikan akses yang diperlukan",
+	"Clone": "Klon",
+	"Close": "Tutup",
+	"Code execution": "",
+	"Code formatted successfully": "Kod berjaya diformatkan",
+	"Collection": "Koleksi",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL asas ComfyUI",
+	"ComfyUI Base URL is required.": "URL asas ComfyUI diperlukan",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Arahan",
+	"Completions": "",
+	"Concurrent Requests": "Permintaan Serentak",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Sahkan",
+	"Confirm Password": "Sahkan kata laluan",
+	"Confirm your action": "Sahkan tindakan anda",
+	"Connections": "Sambungan",
+	"Contact Admin for WebUI Access": "Hubungi admin untuk akses WebUI",
+	"Content": "Kandungan",
+	"Content Extraction": "Pengekstrakan Kandungan",
+	"Context Length": "Panjang Konteks",
+	"Continue Response": "Teruskan Respons",
+	"Continue with {{provider}}": "Teruskan dengan {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "Kawalan",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Disalin",
+	"Copied shared chat URL to clipboard!": "Menyalin URL sembang kongsi ke papan klip",
+	"Copied to clipboard": "",
+	"Copy": "Salin",
+	"Copy last code block": "Salin Blok Kod Terakhir",
+	"Copy last response": "Salin Respons Terakhir",
+	"Copy Link": "Salin Pautan",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Menyalin ke papan klip berjaya!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Cipta model",
+	"Create Account": "Cipta Akaun",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Cipta kekunci baharu",
+	"Create new secret key": "Cipta kekunci rahsia baharu",
+	"Created at": "Dicipta di",
+	"Created At": "Dicipta Pada",
+	"Created by": "Dicipta oleh",
+	"CSV Import": "Import CSV",
+	"Current Model": "Model Semasa",
+	"Current Password": "Kata laluan semasa",
+	"Custom": "Tersuai",
+	"Dark": "Gelap",
+	"Database": "Pangkalan Data",
+	"December": "Disember",
+	"Default": "Lalai",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Lalai (SentenceTransformers)",
+	"Default Model": "Model Lalai",
+	"Default model updated": "Model lalai dikemas kini",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Cadangan Gesaan Lalai",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Peranan Pengguna Lalai",
+	"Delete": "Padam",
+	"Delete a model": "Padam Model",
+	"Delete All Chats": "Padam Semua Perbualan",
+	"Delete All Models": "",
+	"Delete chat": "Padam perbualan",
+	"Delete Chat": "Padam Perbualan",
+	"Delete chat?": "Padam perbualan?",
+	"Delete folder?": "",
+	"Delete function?": "Padam fungsi?",
+	"Delete prompt?": "Padam Gesaan?",
+	"delete this link": "Padam pautan ini?",
+	"Delete tool?": "Padam alat?",
+	"Delete User": "Padam Pengguna",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} dipadam",
+	"Deleted {{name}}": "{{name}} dipadam",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Penerangan",
+	"Didn't fully follow instructions": "Tidak mengikut arahan sepenuhnya",
+	"Disabled": "Dilumpuhkan",
+	"Discover a function": "Temui fungsi",
+	"Discover a model": "Temui model",
+	"Discover a prompt": "Temui gesaan",
+	"Discover a tool": "Temui alat",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Temui, muat turun dan teroka fungsi tersuai",
+	"Discover, download, and explore custom prompts": "Temui, muat turun dan teroka gesaan tersuai",
+	"Discover, download, and explore custom tools": "Temui, muat turun dan teroka alat tersuai",
+	"Discover, download, and explore model presets": "Temui, muat turun dan teroka model pratetap",
+	"Dismissible": "Diketepikan",
+	"Display": "",
+	"Display Emoji in Call": "Paparkan Emoji dalam Panggilan",
+	"Display the username instead of You in the Chat": "Paparkan nama pengguna dan bukannya 'Anda' dalam Sembang",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Jangan pasang fungsi daripada sumber yang anda tidak percayai sepenuhnya.",
+	"Do not install tools from sources you do not fully trust.": "Jangan pasang alat daripada sumber yang anda tidak percaya sepenuhnya.",
+	"Document": "Dokumen",
+	"Documentation": "Dokumentasi",
+	"Documents": "Dokumen",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "tidak membuat sebarang sambungan luaran, dan data anda kekal selamat pada pelayan yang dihoskan ditempat anda",
+	"Don't have an account?": "Anda tidak mempunyai akaun?",
+	"don't install random functions from sources you don't trust.": "jangan pasang mana-mana fungsi daripada sumber yang anda tidak percayai.",
+	"don't install random tools from sources you don't trust.": "jangan pasang mana-mana alat daripada sumber yang anda tidak percayai.",
+	"Don't like the style": "Tidak suka gaya ini",
+	"Done": "Selesai",
+	"Download": "Muat Turun",
+	"Download canceled": "Muat Turun dibatalkan",
+	"Download Database": "Muat turun Pangkalan Data",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Letakkan mana-mana fail di sini untuk ditambahkan pada perbualan",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "cth '30s','10m'. Unit masa yang sah ialah 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Edit",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Edit Memori",
+	"Edit User": "Edit Penggunal",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-mel",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Membenamkan Saiz Kelompok",
+	"Embedding Model": "Model Benamkan",
+	"Embedding Model Engine": "Enjin Model Benamkan",
+	"Embedding model set to \"{{embedding_model}}\"": "Model Benamkan ditetapkan kepada \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Benarkan Perkongsian Komuniti",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Benarkan Pendaftaran Baharu",
+	"Enable Web Search": "Benarkan Carian Web",
+	"Enabled": "Dibenarkan",
+	"Engine": "Enjin",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "astikan fail CSV anda mengandungi 4 lajur dalam susunan ini: Nama, E-mel, Kata Laluan, Peranan.",
+	"Enter {{role}} message here": "Masukkan mesej {{role}} di sini",
+	"Enter a detail about yourself for your LLMs to recall": "Masukkan butiran tentang diri anda untuk diingati oleh LLM anda",
+	"Enter api auth string (e.g. username:password)": "Masukkan kekunci auth api ( cth nama pengguna:kata laluan )",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Masukkan Kekunci API carian Brave",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Masukkan Tindihan 'Chunk'",
+	"Enter Chunk Size": "Masukkan Saiz 'Chunk'",
+	"Enter description": "",
+	"Enter Github Raw URL": "Masukkan URL 'Github Raw'",
+	"Enter Google PSE API Key": "Masukkan kunci API Google PSE",
+	"Enter Google PSE Engine Id": "Masukkan Id Enjin Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Masukkan Saiz Imej (cth 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Masukkan kod bahasa",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Masukkan tag model (cth {{ modelTag }})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Masukkan Bilangan Langkah (cth 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Masukkan Skor",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Masukkan URL 'Searxng Query'",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Masukkan Kunci API Serper",
+	"Enter Serply API Key": "Masukkan Kunci API Serply",
+	"Enter Serpstack API Key": "Masukkan Kunci API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Masukkan urutan hentian",
+	"Enter system prompt": "Masukkan gesaan sistem",
+	"Enter Tavily API Key": "Masukkan Kunci API Tavily",
+	"Enter Tika Server URL": "Masukkan URL Pelayan Tika",
+	"Enter Top K": "Masukkan 'Top K'",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Masukkan URL (cth http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Masukkan URL (cth http://localhost:11434)",
+	"Enter Your Email": "Masukkan E-mel Anda",
+	"Enter Your Full Name": "Masukkan Nama Penuh Anda",
+	"Enter your message": "Masukkan mesej anda",
+	"Enter Your Password": "Masukkan Kata Laluan Anda",
+	"Enter Your Role": "Masukkan Peranan Anda",
+	"Enter Your Username": "",
+	"Error": "Ralat",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Percubaan",
+	"Explore the cosmos": "",
+	"Export": "Eksport",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Eksport Semua Perbualan (Semua Pengguna)",
+	"Export chat (.json)": "Eksport perbualan (.json)",
+	"Export Chats": "Eksport Perbualan",
+	"Export Config to JSON File": "",
+	"Export Functions": "Eksport Fungsi",
+	"Export Models": "Eksport Model",
+	"Export Presets": "",
+	"Export Prompts": "Eksport Gesaan",
+	"Export to CSV": "",
+	"Export Tools": "Eksport Alat",
+	"External Models": "Model Luaran",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Gagal mencipta kekunci API",
+	"Failed to read clipboard contents": "Gagal membaca konten papan klip",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Gagal mengemaskini tetapan",
+	"Failed to upload file.": "",
+	"February": "Febuari",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Jangan ragu untuk menambah butiran khusus",
+	"File": "Fail",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Mod Fail",
+	"File not found.": "Fail tidak dijumpai",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "Fail-Fail",
+	"Filter is now globally disabled": "Tapisan kini dilumpuhkan secara global",
+	"Filter is now globally enabled": "Tapisan kini dibenarkan secara global",
+	"Filters": "Tapisan",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Peniruan cap jari dikesan, tidak dapat menggunakan nama pendek sebagai avatar. Lalai kepada imej profail asal",
+	"Fluidly stream large external response chunks": "Strim 'chunks' respons luaran yang besar dengan lancar",
+	"Focus chat input": "Fokus kepada input perbualan",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Mengikut arahan dengan sempurna",
+	"Forge new paths": "",
+	"Form": "Borang",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Penalti Kekerapan",
+	"Function": "",
+	"Function created successfully": "Fungsi berjaya dibuat",
+	"Function deleted successfully": "Fungsi berjaya dipadamkan",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Fungsi kini dilumpuhkan secara global",
+	"Function is now globally enabled": "Fungsi kini dibenarkan secara global",
+	"Function Name": "",
+	"Function updated successfully": "Fungsi berjaya dikemaskini",
+	"Functions": "Fungsi",
+	"Functions allow arbitrary code execution": "Fungsi membenarkan pelaksanaan kod sewenang-wenangnya",
+	"Functions allow arbitrary code execution.": "Fungsi membenarkan pelaksanaan kod sewenang-wenangnya.",
+	"Functions imported successfully": "Fungsi berjaya diimport",
+	"General": "Umum",
+	"General Settings": "Tetapan Umum",
+	"Generate Image": "Jana Imej",
+	"Generating search query": "Jana pertanyaan carian",
+	"Generation Info": "Maklumat Penjanaan",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Global",
+	"Good Response": "Respons Baik",
+	"Google PSE API Key": "Kunci API Google PSE",
+	"Google PSE Engine Id": "ID Enjin Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "tidak mempunyai perbualan.",
+	"Hello, {{name}}": "Hello, {{name}}",
+	"Help": "Bantuan",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Sembunyi",
+	"Host": "",
+	"How can I help you today?": "Bagaimana saya boleh membantu anda hari ini?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Carian Hibrid",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Saya mengakui bahawa saya telah membaca dan saya memahami implikasi tindakan saya. Saya sedar tentang risiko yang berkaitan dengan melaksanakan kod sewenang-wenangnya dan saya telah mengesahkan kebolehpercayaan sumber tersebut.",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Penjanaan Imej (Percubaan)",
+	"Image Generation Engine": "Enjin Penjanaan Imej",
+	"Image Settings": "Tetapan Imej",
+	"Images": "Imej",
+	"Import Chats": "Import Perbualan",
+	"Import Config from JSON File": "",
+	"Import Functions": "Import Fungsi",
+	"Import Models": "Import Model",
+	"Import Presets": "",
+	"Import Prompts": "Import Gesaan",
+	"Import Tools": "Import Alat",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Sertakan bendera `-- api -auth` semasa menjalankan stable-diffusion-webui ",
+	"Include `--api` flag when running stable-diffusion-webui": "Sertakan bendera `-- api ` semasa menjalankan stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Maklumat",
+	"Input commands": "Masukkan Arahan",
+	"Install from Github URL": "Pasang daripada URL Github",
+	"Instant Auto-Send After Voice Transcription": "Hantar Secara Automatik Dengan Segera Selepas Transkripsi Suara",
+	"Interface": "Antaramuka",
+	"Invalid file format.": "",
+	"Invalid Tag": "Tag tidak sah",
+	"January": "Januari",
+	"Jina API Key": "",
+	"join our Discord for help.": "sertai Discord kami untuk mendapatkan bantuan.",
+	"JSON": "JSON",
+	"JSON Preview": "Pratonton JSON",
+	"July": "Julai",
+	"June": "Jun",
+	"JWT Expiration": "Tempoh Tamat JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Kekalkan Hidup",
+	"Key": "",
+	"Keyboard shortcuts": "Pintasan papan kekunci",
+	"Knowledge": "Pengetahuan",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Bahasa",
+	"Last Active": "Dilihat aktif terakhir pada",
+	"Last Modified": "Kemaskini terakhir pada",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Cerah",
+	"Listening...": "Mendengar...",
+	"LLMs can make mistakes. Verify important information.": "LLM boleh membuat kesilapan. Sahkan maklumat penting",
+	"Local": "",
+	"Local Models": "Model Tempatan",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Dicipta oleh Komuniti OpenWebUI",
+	"Make sure to enclose them with": "Pastikan untuk melampirkannya dengan",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Urus",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Urus 'Pipelines'",
+	"March": "Mac",
+	"Max Tokens (num_predict)": "Token Maksimum ( num_predict )",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimum 3 model boleh dimuat turun serentak. Sila cuba sebentar lagi.",
+	"May": "Mei",
+	"Memories accessible by LLMs will be shown here.": "Memori yang boleh diakses oleh LLM akan ditunjukkan di sini.",
+	"Memory": "Memori",
+	"Memory added successfully": "Memori berjaya ditambah",
+	"Memory cleared successfully": "Memori berjaya dikosongkan",
+	"Memory deleted successfully": "Memori berjaya dihapuskan",
+	"Memory updated successfully": "Memori berjaya dikemaskini",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Mesej yang anda hantar selepas membuat pautan anda tidak akan dikongsi. Pengguna dengan URL akan dapat melihat perbualan yang dikongsi.",
+	"Min P": "P Minimum",
+	"Minimum Score": "Skor Minimum",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY HH:mm:ss A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{ modelName }}' telah berjaya dimuat turun.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{ modelTag }}' sudah dalam baris gilir untuk dimuat turun.",
+	"Model {{modelId}} not found": "Model {{ modelId }} tidak dijumpai",
+	"Model {{modelName}} is not vision capable": "Model {{ modelName }} tidak mempunyai keupayaan penglihatan",
+	"Model {{name}} is now {{status}}": "Model {{name}} kini {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "Model berjaya dibuat!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Laluan sistem fail model dikesan. Nama pendek model diperlukan untuk kemas kini, tidak boleh diteruskan.",
+	"Model Filtering": "",
+	"Model ID": "ID Model",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model tidak dipilih",
+	"Model Params": "Model Params",
+	"Model Permissions": "",
+	"Model updated successfully": "Model berjaya dikemas kini",
+	"Modelfile Content": "Kandungan Modelfail",
+	"Models": "Model",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Lagi",
+	"Name": "Nama",
+	"Name your knowledge base": "",
+	"New Chat": "Perbualan Baru",
+	"New folder": "",
+	"New Password": "Kata Laluan Baru",
+	"No content found": "",
+	"No content to speak": "Tiada kandungan untuk bercakap",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Tiada fail dipilih",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Tiada keputusan dijumpai",
+	"No search query generated": "Tiada pertanyaan carian dijana",
+	"No source available": "Tiada sumber tersedia",
+	"No users were found.": "",
+	"No valves to update": "Tiada 'valve' untuk dikemas kini",
+	"None": "Tiada",
+	"Not factually correct": "Tidak tepat secara fakta",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Jika anda menetapkan skor minimum, carian hanya akan mengembalikan dokumen dengan skor lebih besar daripada atau sama dengan skor minimum.",
+	"Notes": "",
+	"Notifications": "Pemberitahuan",
+	"November": "November",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Oktober",
+	"Off": "Mati",
+	"Okay, Let's Go!": "Baiklah, Jom!",
+	"OLED Dark": "OLED Gelap",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama dilumpuhkan",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versi Ollama",
+	"On": "Hidup",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Hanya aksara alfanumerik dan sempang dibenarkan dalam rentetan arahan.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Maaf, didapati URL tidak sah. Sila semak semula dan cuba lagi.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Maaf, Anda menggunakan kaedah yang tidak disokong (bahagian 'frontend' sahaja). Sila sediakan WebUI dari 'backend'.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Buka perbualan baru",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open WebUI version (v{{OPEN_WEBUI_VERSION}}) adalah lebih rendah daripada versi yang diperlukan iaitu (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Tetapan API OpenAI",
+	"OpenAI API Key is required.": "Kekunci API OpenAI diperlukan",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Kekunci OpenAI diperlukan",
+	"or": "atau",
+	"Organize your users": "",
+	"Other": "Lain-lain",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Kata Laluan",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Dokumen PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Imej Ekstrak PDF (OCR)",
+	"pending": "tertunda",
+	"Permission denied when accessing media devices": "Tidak mendapat kebenaran apabila cuba mengakses peranti media",
+	"Permission denied when accessing microphone": "Tidak mendapat kebenaran apabila cuba mengakses mikrofon",
+	"Permission denied when accessing microphone: {{error}}": "Tidak mendapat kebenaran apabila cuba mengakses mikrofon: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalisasi",
+	"Pin": "Pin",
+	"Pinned": "Disemat",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "'Pipeline' berjaya dipadam",
+	"Pipeline downloaded successfully": "'Pipeline' berjaya dimuat turun",
+	"Pipelines": "'Pipeline'",
+	"Pipelines Not Detected": "'Pipeline' tidak ditemui",
+	"Pipelines Valves": "'Pipeline Valves'",
+	"Plain text (.txt)": "Teks biasa (.txt)",
+	"Playground": "Taman Permainan",
+	"Please carefully review the following warnings:": "Sila semak dengan teliti amaran berikut:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Sikap positif",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 hari sebelumnya",
+	"Previous 7 days": "7 hari sebelumnya",
+	"Profile Image": "Imej Profail",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Gesaan (cth Beritahu saya fakta yang menyeronokkan tentang Kesultanan Melaka)",
+	"Prompt Content": "Kandungan Gesaan",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Cadangan Gesaan",
+	"Prompt updated successfully": "",
+	"Prompts": "Gesaan",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Tarik \"{{ searchValue }}\" daripada Ollama.com",
+	"Pull a model from Ollama.com": "Tarik model dari Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "'Query Params'",
+	"RAG Template": "Templat RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Baca dengan lantang",
+	"Record voice": "Rakam suara",
+	"Redirecting you to OpenWebUI Community": "Membawa anda ke Komuniti OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Rujuk diri anda sebagai \"User\" (cth, \"Pengguna sedang belajar bahasa Sepanyol\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Menolak dimana ia tidak sepatutnya",
+	"Regenerate": "Jana semula",
+	"Release Notes": "Nota Keluaran",
+	"Relevance": "",
+	"Remove": "Hapuskan",
+	"Remove Model": "Hapuskan Model",
+	"Rename": "Namakan Semula",
+	"Reorder Models": "",
+	"Repeat Last N": "Ulang N Terakhir",
+	"Request Mode": "Mod Permintaan",
+	"Reranking Model": "Model 'Reranking'",
+	"Reranking model disabled": "Model 'Reranking' dilumpuhkan",
+	"Reranking model set to \"{{reranking_model}}\"": "Model 'Reranking' ditetapkan kepada \"{{reranking_model}}\"",
+	"Reset": "Tetapkan Semula",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Tetapkan Semula Direktori Muat Naik",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Pemberitahuan respons tidak boleh diaktifkan kerana kebenaran tapak web tidak diberi. Sila lawati tetapan pelayar web anda untuk memberikan akses yang diperlukan.",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Peranan",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Jalankan",
+	"Running": "Sedang dijalankan",
+	"Save": "Simpan",
+	"Save & Create": "Simpan & Cipta",
+	"Save & Update": "Simpan & Kemas Kini",
+	"Save As Copy": "",
+	"Save Tag": "Simpan Tag",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Penyimpanan log perbualan terus ke storan pelayan web anda tidak lagi disokong. Sila luangkan sedikit masa untuk memuat turun dan memadam log perbualan anda dengan mengklik butang di bawah. Jangan risau, anda boleh mengimport semula log perbualan anda dengan mudah melalui 'backend'",
+	"Scroll to bottom when switching between branches": "Skrol ke bawah apabila bertukar antara cawangan",
+	"Search": "Carian",
+	"Search a model": "Cari Model",
+	"Search Base": "",
+	"Search Chats": "Cari Perbualan",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Carian Fungsi",
+	"Search Knowledge": "",
+	"Search Models": "Carian Model",
+	"Search options": "",
+	"Search Prompts": "Carian Gesaan",
+	"Search Result Count": "Kiraan Hasil Carian",
+	"Search the web": "",
+	"Search Tools": "Alat Carian",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Mencari {{count}} sites_one",
+	"Searched {{count}} sites_other": "Mencari {{count}} tapak_lain",
+	"Searching \"{{searchQuery}}\"": "encari \"{{ searchQuery }}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL Pertanyaan Searxng",
+	"See readme.md for instructions": "Lihat readme.md untuk arahan",
+	"See what's new": "Lihat apa yang terbaru",
+	"Seed": "Benih",
+	"Select a base model": "Pilih model asas",
+	"Select a engine": "Pilih enjin",
+	"Select a function": "Pilih fungsi",
+	"Select a group": "",
+	"Select a model": "Pilih model",
+	"Select a pipeline": "Pilih 'pipeline'",
+	"Select a pipeline url": "Pilih url 'pipeline'",
+	"Select a tool": "Pilih alat",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Pilih model",
+	"Select only one model to call": "Pilih hanya satu model untuk dipanggil",
+	"Selected model(s) do not support image inputs": "Model dipilih tidak menyokong input imej",
+	"Semantic distance to query": "",
+	"Send": "Hantar",
+	"Send a Message": "Hantar Pesanan",
+	"Send message": "Hantar pesanan",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "September",
+	"Serper API Key": "Kunci API Serper",
+	"Serply API Key": "Kunci API Serply",
+	"Serpstack API Key": "Kunci API Serpstack",
+	"Server connection verified": "Sambungan pelayan disahkan",
+	"Set as default": "Tetapkan sebagai lalai",
+	"Set CFG Scale": "",
+	"Set Default Model": "Tetapkan Model Lalai",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Tetapkan model benamkan (cth {{model}})",
+	"Set Image Size": "Tetapkan saiz imej",
+	"Set reranking model (e.g. {{model}})": "Tetapkan model 'reranking' (cth {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "tapkan Langkah",
+	"Set Task Model": "Tetapkan Model Tugasan",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Tetapan Suara",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Tetapan",
+	"Settings saved successfully!": "Tetapan berjaya disimpan!",
+	"Share": "Kongsi",
+	"Share Chat": "Kongsi Perbualan",
+	"Share to OpenWebUI Community": "Kongsi kepada Komuniti OpenWebUI",
+	"Show": "Tunjukkan",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Tunjukkan Butiran Pentadbir dalam Akaun Menunggu Tindanan",
+	"Show shortcuts": "Tunjukkan pintasan",
+	"Show your support!": "Tunjukkan sokongan anda!",
+	"Showcased creativity": "eativiti yang dipamerkan",
+	"Sign in": "Daftar masuk",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Daftar keluar",
+	"Sign up": "Daftar",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Sumber",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Ralat pengecaman pertuturan: {{error}}",
+	"Speech-to-Text Engine": "Enjin Ucapan-ke-Teks",
+	"Stop": "",
+	"Stop Sequence": "Jujukan Henti",
+	"Stream Chat Response": "",
+	"STT Model": "Model STT",
+	"STT Settings": "Tetapan STT",
+	"Subtitle (e.g. about the Roman Empire)": "Sari kata (cth tentang Kesultanan Melaka)",
+	"Success": "Berjaya",
+	"Successfully updated.": "Berjaya Dikemaskini",
+	"Suggested": "Cadangan",
+	"Support": "Sokongan",
+	"Support this plugin:": "Sokong plugin ini",
+	"Sync directory": "",
+	"System": "Sistem",
+	"System Instructions": "",
+	"System Prompt": "Gesaan Sistem",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Sentuh untuk mengganggu",
+	"Tavily API Key": "Kunci API Tavily",
+	"Tell us more:": "Beritahu kami lebih lanjut",
+	"Temperature": "Suhu",
+	"Template": "Templat",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Enjin Teks-ke-Ucapan",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Terima kasih atas maklum balas anda!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Pembangun di sebalik 'plugin' ini adalah sukarelawan yang bersemangat daripada komuniti. Jika anda mendapati 'plugin' ini membantu, sila pertimbangkan untuk menyumbang kepada pembangunannya.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Skor hendaklah berada diantara 0.0 (0%) dan 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Berfikir...",
+	"This action cannot be undone. Do you wish to continue?": "Tindakan ini tidak boleh diubah semula kepada asal. Adakah anda ingin teruskan",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ini akan memastikan bahawa perbualan berharga anda disimpan dengan selamat ke pangkalan data 'backend' anda. Terima kasih!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "ni adalah ciri percubaan, ia mungkin tidak berfungsi seperti yang diharapkan dan tertakluk kepada perubahan pada bila-bila masa.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Ini akan memadam",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Penjelasan menyeluruh",
+	"Tika": "Tika",
+	"Tika Server URL required.": "URL Pelayan Tika diperlukan.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Petua: Kemas kini berbilang slot pembolehubah secara berturut-turut dengan menekan kekunci tab dalam input perbualan selepas setiap penggantian.",
+	"Title": "Tajuk",
+	"Title (e.g. Tell me a fun fact)": "Tajuk (cth Beritahu saya fakta yang menyeronokkan)",
+	"Title Auto-Generation": "Penjanaan Auto Tajuk",
+	"Title cannot be an empty string.": "Tajuk tidak boleh menjadi rentetan kosong",
+	"Title Generation Prompt": "Gesaan Penjanaan Tajuk",
+	"TLS": "",
+	"To access the available model names for downloading,": "Untuk mengakses nama model yang tersedia untuk dimuat turun,",
+	"To access the GGUF models available for downloading,": "Untuk mengakses model GGUF yang tersedia untuk dimuat turun,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Untuk mengakses WebUI , sila hubungi pentadbir. Pentadbir boleh menguruskan status pengguna daripada Panel Pentadbiran",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Untuk memilih tindakan di sini, tambahkannya pada ruang kerja \"Functions\" dahulu.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Untuk memilih tapisan di sini, tambahkannya pada ruang kerja \"Functions\" dahulu.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Untuk memilih kit alatan di sini, tambahkannya pada ruang kerja \"Tools\" dahulu.",
+	"Toast notifications for new updates": "",
+	"Today": "Hari Ini",
+	"Toggle settings": "Suis Tetapan ",
+	"Toggle sidebar": "Suis Bar Sisi",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Token Untuk Teruskan Dalam Muat Semula Konteks ( num_keep )",
+	"Too verbose": "",
+	"Tool created successfully": "Alat berjaya dibuat",
+	"Tool deleted successfully": "Alat berjaya dipadamkan",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Alat berjaya diimport",
+	"Tool Name": "",
+	"Tool updated successfully": "Alat berjaya dikemas kini",
+	"Tools": "Alatan",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Alatan ialah sistem panggilan fungsi dengan pelaksanaan kod sewenang-wenangnya",
+	"Tools have a function calling system that allows arbitrary code execution": "Alatan mempunyai sistem panggilan fungsi yang membolehkan pelaksanaan kod sewenang-wenangnya",
+	"Tools have a function calling system that allows arbitrary code execution.": "Alatan mempunyai sistem panggilan fungsi yang membolehkan pelaksanaan kod sewenang-wenangnya.",
+	"Top K": "'Top K'",
+	"Top P": "'Top P'",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Masalah mengakses Ollama?",
+	"TTS Model": "Model TTS",
+	"TTS Settings": "Tetapan TTS",
+	"TTS Voice": "Suara TTS",
+	"Type": "jenis",
+	"Type Hugging Face Resolve (Download) URL": "Taip URL 'Hugging Face Resolve (Download)'",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Maaf! Terdapat masalah menyambung ke {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Nyahsematkan",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Kemaskini",
+	"Update and Copy Link": "Kemaskini dan salin pautan",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Kemaskini Kata Laluan",
+	"Updated": "",
+	"Updated at": "Dikemaskini pada",
+	"Updated At": "",
+	"Upload": "Muatnaik",
+	"Upload a GGUF model": "Muatnaik model GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Muatnaik fail",
+	"Upload Pipeline": "Muatnaik 'Pipeline'",
+	"Upload Progress": "Kemajuan Muatnaik",
+	"URL": "",
+	"URL Mode": "Mod URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gunakan Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Gunakan nama pendek",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "se_mmap (Ollama)",
+	"user": "pengguna",
+	"User": "",
+	"User location successfully retrieved.": "Lokasi pengguna berjaya diambil.",
+	"Username": "",
+	"Users": "Pengguna",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Gunakan",
+	"Valid time units:": "Unit masa yang sah:",
+	"Valves": "'Valves'",
+	"Valves updated": "'Valves' dikemaskini",
+	"Valves updated successfully": "'Valves' berjaya dikemaskini",
+	"variable": "pembolehubah",
+	"variable to have them replaced with clipboard content.": "pembolehubah untuk ia digantikan dengan kandungan papan klip.",
+	"Version": "Versi",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Suara",
+	"Voice Input": "",
+	"Warning": "Amaran",
+	"Warning:": "Amaran:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Amaran: Jika anda mengemas kini atau menukar model benam anda, anda perlu mengimport semula semua dokumen.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Tetapan Pemuat Web",
+	"Web Search": "Carian Web",
+	"Web Search Engine": "Enjin Carian Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL 'Webhook'",
+	"WebUI Settings": "Tetapan WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Apakah yang terbaru dalam",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (Local)",
+	"Why?": "",
+	"Widescreen Mode": "Mod Skrin Lebar",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Ruangan Kerja",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Tulis cadangan gesaan (cth Siapakah anda?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Tulis ringkasan dalam 50 patah perkataan yang meringkaskan [topik atau kata kunci].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Semalam",
+	"You": "Anda",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Anda boleh memperibadikan interaksi anda dengan LLM dengan menambahkan memori melalui butang 'Urus' di bawah, menjadikannya lebih membantu dan disesuaikan dengan anda.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Anda tidak mempunyai perbualan yang diarkibkan",
+	"You have shared this chat": "Anda telah berkongsi perbualan ini",
+	"You're a helpful assistant.": "Anda seorang pembantu yang bagus",
+	"You're now logged in.": "Anda kini telah log masuk.",
+	"Your account status is currently pending activation.": "Status akaun anda ialah sedang menunggu pengaktifan.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Seluruh sumbangan anda akan dihantar terus kepada pembangun 'plugin'; Open WebUI tidak mengambil sebarang peratusan keuntungan daripadanya. Walau bagaimanapun, platform pembiayaan yang dipilih mungkin mempunyai caj tersendiri.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Tetapan Pemuat Youtube"
+}
diff --git a/src/lib/i18n/locales/nb-NO/translation.json b/src/lib/i18n/locales/nb-NO/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..4c8a3397fd3d774d1ec1701187f0c162de3fa39e
--- /dev/null
+++ b/src/lib/i18n/locales/nb-NO/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 't', 'd', 'u' eller '-1' for ingen utløp.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(f.eks. `sh webui.sh --api --api-auth brukernavn_passord`)",
+	"(e.g. `sh webui.sh --api`)": "(f.eks. `sh webui.sh --api`)",
+	"(latest)": "(siste)",
+	"{{ models }}": "{{ modeller }}",
+	"{{user}}'s Chats": "{{user}} sine samtaler",
+	"{{webUIName}} Backend Required": "Backend til {{webUIName}} kreves",
+	"*Prompt node ID(s) are required for image generation": "Node-ID-er for ledetekst kreves for generering av bilder",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "En ny versjon (v{{LATEST_VERSION}}) er nå tilgjengelig.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "En oppgavemodell brukes når du utfører oppgaver som å generere titler for samtaler eller utfører søkeforespørsler på nettet",
+	"a user": "en bruker",
+	"About": "Om",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Konto",
+	"Account Activation Pending": "Venter på kontoaktivering",
+	"Accurate information": "Nøyaktig informasjon",
+	"Actions": "Handlinger",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Aktiver denne kommandoen ved å skrive inn \"/{{COMMAND}}\" i chattens inntastingsfelt",
+	"Active Users": "Aktive brukere",
+	"Add": "Legg til",
+	"Add a model ID": "Legg til en modell-ID",
+	"Add a short description about what this model does": "Legg til en kort beskrivelse av hva denne modellen gjør",
+	"Add a tag": "Legg til en tag",
+	"Add Arena Model": "Legg til Arena-modell",
+	"Add Connection": "Legg til tilkobling",
+	"Add Content": "Legg til innhold",
+	"Add content here": "Legg til innhold her",
+	"Add custom prompt": "Legg til egendefinert prompt",
+	"Add Files": "Legg til filer",
+	"Add Group": "",
+	"Add Memory": "Legg til minne",
+	"Add Model": "Legg til modell",
+	"Add Tag": "Legg til etikett",
+	"Add Tags": "Legg til etiketter",
+	"Add text content": "Legg til tekstinnhold",
+	"Add User": "Legg til bruker",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Endring av disse innstillingene vil gjelde for alle brukere på tvers av systemet.",
+	"admin": "administrator",
+	"Admin": "Administrator",
+	"Admin Panel": "Administratorpanel",
+	"Admin Settings": "Administratorinnstillinger",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratorer har alltid tilgang til alle verktøy. Brukere må få tildelt verktøy for hver enkelt modell i arbeidsområdet.",
+	"Advanced Parameters": "Avanserte parametere",
+	"Advanced Params": "Avanserte parametere",
+	"All chats": "Alle chatter",
+	"All Documents": "Alle dokumenter",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Tillat sletting av chatter",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Tillat ikke-lokale stemmer",
+	"Allow Temporary Chat": "Tillat midlertidige chatter",
+	"Allow User Location": "Aktiver stedstjenester",
+	"Allow Voice Interruption in Call": "Muliggjør stemmeavbrytelse i samtaler",
+	"Already have an account?": "Har du allerede en konto?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternativ til top_p, og har som mål å sikre en balanse mellom kvalitet og variasjon. Parameteren p representerer minimumssannsynligheten for at et token skal vurderes, i forhold til sannsynligheten for det mest sannsynlige tokenet. Hvis p for eksempel er 0,05 og det mest sannsynlige tokenet har en sannsynlighet på 0,9, filtreres logits med en verdi på mindre enn 0,045 bort. (Standard: 0,0)",
+	"Amazing": "",
+	"an assistant": "en assistent",
+	"and": "og",
+	"and {{COUNT}} more": "og {{COUNT}} til",
+	"and create a new shared link.": "og opprett en ny delt lenke.",
+	"API Base URL": "Absolutt API-URL",
+	"API Key": "API-nøkkel",
+	"API Key created.": "API-nøkkel opprettet.",
+	"API keys": "API-nøkler",
+	"Application DN": "Applikasjonens DN",
+	"Application DN Password": "Applikasjonens DN-passord",
+	"applies to all users with the \"user\" role": "",
+	"April": "april",
+	"Archive": "Arkiv",
+	"Archive All Chats": "Arkiver alle chatter",
+	"Archived Chats": "Arkiverte chatter",
+	"archived-chat-export": "archived-chat-export",
+	"Are you sure you want to unarchive all archived chats?": "Er du sikker på at du vil oppheve arkiveringen av alle arkiverte chatter?",
+	"Are you sure?": "Er du sikker?",
+	"Arena Models": "Arena-modeller",
+	"Artifacts": "Artifakter",
+	"Ask a question": "Still et spørsmål",
+	"Assistant": "Assistent",
+	"Attach file": "Legg ved fil",
+	"Attention to detail": "Sans for detaljer",
+	"Attribute for Username": "Attributt for brukernavn",
+	"Audio": "Lyd",
+	"August": "august",
+	"Authenticate": "Godkjenn",
+	"Auto-Copy Response to Clipboard": "Respons auto-kopi til utklippstavle",
+	"Auto-playback response": "Automatisk avspilling av svar",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api Autentiseringsstreng",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Grunn-URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Grunn-URL kreves.",
+	"Available list": "Tilgjengelig liste",
+	"available!": "tilgjengelig!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI-tale",
+	"Azure Region": "Azure område",
+	"Back": "Tilbake",
+	"Bad Response": "Dårlig svar",
+	"Banners": "Bannere",
+	"Base Model (From)": "Grunnmodell (Fra)",
+	"Batch Size (num_batch)": "Batchstørrelse (num_batch)",
+	"before": "før",
+	"Being lazy": "Er lat",
+	"Bing Search V7 Endpoint": "Endepunkt for Bing Search V7",
+	"Bing Search V7 Subscription Key": "Abonnementsnøkkel for Bing Search V7",
+	"Brave Search API Key": "API-nøkkel for Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Omgå SSL-verifisering for nettsteder",
+	"Call": "Ring",
+	"Call feature is not supported when using Web STT engine": "Ringefunksjonen støttes ikke når du bruker Web STT-motoren",
+	"Camera": "Kamera",
+	"Cancel": "Avbryt",
+	"Capabilities": "Muligheter",
+	"Certificate Path": "Sertifikatbane",
+	"Change Password": "Endre passord",
+	"Character": "Karakter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Kartlegg ny områder",
+	"Chat": "Chat",
+	"Chat Background Image": "Bakgrunnsbilde for chat",
+	"Chat Bubble UI": "Grensesnitt for chat-boble",
+	"Chat Controls": "Kontrollere i chat",
+	"Chat direction": "Retning på chat",
+	"Chat Overview": "Chatoversikt",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "Auto-generering av chatetiketter",
+	"Chats": "Chatter",
+	"Check Again": "Sjekk på nytt",
+	"Check for updates": "Sjekk for oppdateringer",
+	"Checking for updates...": "Sjekker for oppdateringer ...",
+	"Choose a model before saving...": "Velg en modell før du lagrer ...",
+	"Chunk Overlap": "Chunk-overlapp",
+	"Chunk Params": "Chunk-parametere",
+	"Chunk Size": "Chunk-størrelse",
+	"Ciphers": "Chiffer",
+	"Citation": "Sitering",
+	"Clear memory": "Tøm minnet",
+	"click here": "Klikk her",
+	"Click here for filter guides.": "Klikk her for å få veiledning om filtre",
+	"Click here for help.": "Klikk her for å få hjelp.",
+	"Click here to": "Klikk her for å",
+	"Click here to download user import template file.": "Klikk her for å hente ned malfilen for import av brukere.",
+	"Click here to learn more about faster-whisper and see the available models.": "Klikk her for å lære mer om faster-whisper, og se de tilgjengelige modellene.",
+	"Click here to select": "Klikk her for å velge",
+	"Click here to select a csv file.": "Klikk her for å velge en CSV-fil.",
+	"Click here to select a py file.": "Klikk her for å velge en PY-fil.",
+	"Click here to upload a workflow.json file.": "Klikk her for å laste opp en workflow.json fil.",
+	"click here.": "klikk her.",
+	"Click on the user role button to change a user's role.": "Klikk på knappen Brukerrolle for å endre en brukers rolle.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Skrivetilgang til utklippstavlen avslått. Kontroller nettleserinnstillingene for å gi den nødvendige tilgangen.",
+	"Clone": "Klon",
+	"Close": "Lukk",
+	"Code execution": "Kodekjøring",
+	"Code formatted successfully": "Koden er formatert",
+	"Collection": "Samling",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "Absolutt URL for ComfyUI",
+	"ComfyUI Base URL is required.": "Absolutt URL for ComfyUI kreves.",
+	"ComfyUI Workflow": "ComfyUI-arbeidsflyt",
+	"ComfyUI Workflow Nodes": "ComfyUI-arbeidsflytnoder",
+	"Command": "Kommando",
+	"Completions": "Fullføringer",
+	"Concurrent Requests": "Samtidige forespørsler",
+	"Configure": "Konfigurer",
+	"Configure Models": "",
+	"Confirm": "Bekreft",
+	"Confirm Password": "Bekreft passordet",
+	"Confirm your action": "Bekreft handlingen",
+	"Connections": "Tilkoblinger",
+	"Contact Admin for WebUI Access": "Kontakt administrator for å få tilgang til WebUI",
+	"Content": "Innhold",
+	"Content Extraction": "Uthenting av innhold",
+	"Context Length": "Kontekstlengde",
+	"Continue Response": "Fortsett svar",
+	"Continue with {{provider}}": "Fortsett med {{provider}}",
+	"Continue with Email": "Fortsett med e-post",
+	"Continue with LDAP": "Fortsett med LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Kontrollerer hvordan meldingsteksten deles opp for TTS-forespørsler. 'Punctuation' deler opp i setninger, 'paragraphs' deler opp i avsnitt, og 'none' beholder meldingen som én enkelt streng.",
+	"Controls": "Kontroller",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Styrer balansen mellom sammenheng og mangfold i utdataene. En lavere verdi gir en mer fokusert og sammenhengende tekst. (Standard: 5.0)",
+	"Copied": "Kopiert",
+	"Copied shared chat URL to clipboard!": "Kopierte delt chat-URL til utklippstavlen!",
+	"Copied to clipboard": "Kopier til utklippstaveln",
+	"Copy": "Kopier",
+	"Copy last code block": "Kopier siste kodeblokk",
+	"Copy last response": "Kopier siste svar",
+	"Copy Link": "Kopier lenke",
+	"Copy to clipboard": "Kopier til utklippstavle",
+	"Copying to clipboard was successful!": "Kopiert til utklippstavlen!",
+	"Create": "",
+	"Create a knowledge base": "Opprett en kunnskapsbase",
+	"Create a model": "Opprett en modell",
+	"Create Account": "Opprett konto",
+	"Create Admin Account": "Opprett administratorkonto",
+	"Create Group": "",
+	"Create Knowledge": "Opprett kunnskap",
+	"Create new key": "Lag ny nøkkel",
+	"Create new secret key": "Lag ny hemmelig nøkkel",
+	"Created at": "Opprettet",
+	"Created At": "Opprettet",
+	"Created by": "Opprettet av",
+	"CSV Import": "CSV-import",
+	"Current Model": "Nåværende modell",
+	"Current Password": "Nåværende passord",
+	"Custom": "Tilpasset",
+	"Dark": "Mørk",
+	"Database": "Database",
+	"December": "desember",
+	"Default": "Standard",
+	"Default (Open AI)": "Standard (Open AI)",
+	"Default (SentenceTransformers)": "Standard (SentenceTransformers)",
+	"Default Model": "Standard modell",
+	"Default model updated": "Standard modell oppdatert",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Standard forslag til ledetekster",
+	"Default to 389 or 636 if TLS is enabled": "Velg 389 or 636 som standard hvis TLS er aktivert",
+	"Default to ALL": "Velg ALL som standard",
+	"Default User Role": "Standard brukerrolle",
+	"Delete": "Slett",
+	"Delete a model": "Slett en modell",
+	"Delete All Chats": "Slett alle chatter",
+	"Delete All Models": "",
+	"Delete chat": "Slett chat",
+	"Delete Chat": "Slett chat",
+	"Delete chat?": "Slette chat?",
+	"Delete folder?": "Slette mappe?",
+	"Delete function?": "Slette funksjon?",
+	"Delete prompt?": "Slette ledetekst?",
+	"delete this link": "slett denne lenken",
+	"Delete tool?": "Slette verktøy?",
+	"Delete User": "Slett bruker",
+	"Deleted {{deleteModelTag}}": "Slettet {{deleteModelTag}}",
+	"Deleted {{name}}": "Slettet {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "Beskriv kunnskapsbasen din og målene dine",
+	"Description": "Beskrivelse",
+	"Didn't fully follow instructions": "Fulgte ikke instruksjonene fullstendig",
+	"Disabled": "Deaktivert",
+	"Discover a function": "Oppdag en funksjon",
+	"Discover a model": "Oppdag en modell",
+	"Discover a prompt": "Oppdag en ledetekst",
+	"Discover a tool": "Oppdag et verktøy",
+	"Discover wonders": "Oppdag ",
+	"Discover, download, and explore custom functions": "Oppdag, last ned og utforsk tilpassede funksjoner",
+	"Discover, download, and explore custom prompts": "Oppdag, last ned og utforsk tilpassede ledetekster",
+	"Discover, download, and explore custom tools": "Oppdag, last ned og utforsk tilpassede verktøy",
+	"Discover, download, and explore model presets": "Oppdag, last ned og utforsk forhåndsinnstillinger for modeller",
+	"Dismissible": "Kan lukkes",
+	"Display": "",
+	"Display Emoji in Call": "Vis emoji i samtale",
+	"Display the username instead of You in the Chat": "Vis brukernavnet i stedet for Du i chatten",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "Bli kjent med kunnskap",
+	"Do not install functions from sources you do not fully trust.": "Ikke installer funksjoner fra kilder du ikke stoler på.",
+	"Do not install tools from sources you do not fully trust.": "Ikke installer verktøy fra kilder du ikke stoler på.",
+	"Document": "Dokument",
+	"Documentation": "Dokumentasjon",
+	"Documents": "Dokumenter",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ikke ingen tilkobling til eksterne tjenester. Dataene dine forblir sikkert på den lokale serveren.",
+	"Don't have an account?": "Har du ingen konto?",
+	"don't install random functions from sources you don't trust.": "ikke installer tilfeldige funksjoner fra kilder du ikke stoler på.",
+	"don't install random tools from sources you don't trust.": "ikke installer tilfeldige verktøy fra kilder du ikke stoler på.",
+	"Don't like the style": "Liker ikke stilen",
+	"Done": "Ferdig",
+	"Download": "Last ned",
+	"Download canceled": "Nedlasting avbrutt",
+	"Download Database": "Last ned database",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "Tegne",
+	"Drop any files here to add to the conversation": "Slipp filer her for å legge dem til i samtalen",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "f.eks. '30s','10m'. Gyldige tidsenheter er 's', 'm', 't'.",
+	"e.g. A filter to remove profanity from text": "f.eks. et filter for å fjerne banning fra tekst",
+	"e.g. My Filter": "f.eks. Mitt filter",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "f.eks. mitt_filter",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Rediger",
+	"Edit Arena Model": "Rediger Arena-modell",
+	"Edit Connection": "Rediger tilkobling",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Rediger minne",
+	"Edit User": "Rediger bruker",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-postadresse",
+	"Embark on adventures": "Kom med på eventyr",
+	"Embedding Batch Size": "Batch-størrelse for innbygging",
+	"Embedding Model": "Innbyggingsmodell",
+	"Embedding Model Engine": "Motor for innbygging av modeller",
+	"Embedding model set to \"{{embedding_model}}\"": "Innbyggingsmodell angitt til \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Aktiver deling i fellesskap",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Aktiver Memory Locking (mlock) for å forhindre at modelldata byttes ut av RAM. Dette alternativet låser modellens arbeidssett med sider i RAM-minnet, slik at de ikke byttes ut til disk. Dette kan bidra til å opprettholde ytelsen ved å unngå sidefeil og sikre rask datatilgang.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Aktiver Memory Mapping (mmap) for å laste inn modelldata. Med dette alternativet kan systemet bruke disklagring som en utvidelse av RAM ved å behandle diskfiler som om de befant seg i RAM. Dette kan forbedre modellens ytelse ved å gi raskere datatilgang. Det er imidlertid ikke sikkert at det fungerer som det skal på alle systemer, og det kan kreve mye diskplass.",
+	"Enable Message Rating": "Aktivert vurdering av meldinger",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Aktiver Mirostat-sampling for kontroll av perpleksitet. (Standard: 0, 0 = deaktivert, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Aktiver nye registreringer",
+	"Enable Web Search": "Aktiver websøk",
+	"Enabled": "Aktivert",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Sørg for at CSV-filen din inkluderer 4 kolonner i denne rekkefølgen: Navn, E-post, Passord, Rolle.",
+	"Enter {{role}} message here": "Skriv inn {{role}} melding her",
+	"Enter a detail about yourself for your LLMs to recall": "Skriv inn en detalj om deg selv som språkmodellene dine kan huske",
+	"Enter api auth string (e.g. username:password)": "Skriv inn api-autentiseringsstreng (f.eks. brukernavn:passord)",
+	"Enter Application DN": "Angi applikasjonens DN",
+	"Enter Application DN Password": "Angi applikasjonens DN-passord",
+	"Enter Bing Search V7 Endpoint": "Angi endepunkt for Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Angi abonnementsnøkkel for Bing Search V7",
+	"Enter Brave Search API Key": "Angi API-nøkkel for Brave Search",
+	"Enter certificate path": "Angi sertifikatets bane",
+	"Enter CFG Scale (e.g. 7.0)": "Angi CFG-skala (f.eks. 7,0)",
+	"Enter Chunk Overlap": "Angi Chunk-overlapp",
+	"Enter Chunk Size": "Angi Chunk-størrelse",
+	"Enter description": "Angi beskrivelse",
+	"Enter Github Raw URL": "Angi Github Raw-URL",
+	"Enter Google PSE API Key": "Angi API-nøkkel for Google PSE",
+	"Enter Google PSE Engine Id": "Angi motor-ID for Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Angi bildestørrelse (f.eks. 512x512)",
+	"Enter Jina API Key": "Angi API-nøkkel for Jina",
+	"Enter language codes": "Angi språkkoder",
+	"Enter Model ID": "Angi modellens ID",
+	"Enter model tag (e.g. {{modelTag}})": "Angi modellens etikett (f.eks. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Angi antall steg (f.eks. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Angi Sampler (e.g. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Angi Scheduler (f.eks. Karras)",
+	"Enter Score": "Angi poengsum",
+	"Enter SearchApi API Key": "Angi API-nøkkel for SearchApi",
+	"Enter SearchApi Engine": "Angi motor for SearchApi",
+	"Enter Searxng Query URL": "Angi spørrings-URL for Searxng",
+	"Enter Seed": "Angi Seed",
+	"Enter Serper API Key": "Angi API-nøkkel for Serper",
+	"Enter Serply API Key": "Angi API-nøkkel for Serply",
+	"Enter Serpstack API Key": "Angi API-nøkkel for Serpstack",
+	"Enter server host": "Angi server host",
+	"Enter server label": "Angi server etikett",
+	"Enter server port": "Angi server port",
+	"Enter stop sequence": "Angi stoppsekvens",
+	"Enter system prompt": "Angi systemledetekst",
+	"Enter Tavily API Key": "Angi API-nøkkel for Tavily",
+	"Enter Tika Server URL": "Angi server-URL for Tika",
+	"Enter Top K": "Angi Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Angi URL (f.eks. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Angi URL (f.eks. http://localhost:11434)",
+	"Enter Your Email": "Skriv inn e-postadressen din",
+	"Enter Your Full Name": "Skriv inn det fulle navnet ditt",
+	"Enter your message": "Skriv inn meldingen din",
+	"Enter Your Password": "Skriv inn passordet ditt",
+	"Enter Your Role": "Skriv inn rollen din",
+	"Enter Your Username": "Skriv inn brukernavnet ditt",
+	"Error": "Feil",
+	"ERROR": "FEIL",
+	"Evaluations": "Vurderinger",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Eksempel: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Eksempel: ALL",
+	"Example: ou=users,dc=foo,dc=example": "Eksempel: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Eksempel: sAMAccountName eller uid eller userPrincipalName",
+	"Exclude": "Utelukk",
+	"Experimental": "Eksperimentell",
+	"Explore the cosmos": "Utforsk verdensrommet",
+	"Export": "Eksporter",
+	"Export All Archived Chats": "Eksporter alle arkiverte chatter",
+	"Export All Chats (All Users)": "Eksporter alle chatter (alle brukere)",
+	"Export chat (.json)": "Eksporter chat (.json)",
+	"Export Chats": "Eksporter chatter",
+	"Export Config to JSON File": "Ekporter konfigurasjon til en JSON-fil",
+	"Export Functions": "Eksporter funksjoner",
+	"Export Models": "Eksporter modeller",
+	"Export Presets": "",
+	"Export Prompts": "Eksporter ledetekster",
+	"Export to CSV": "Eksporter til CSV",
+	"Export Tools": "Eksporter verktøy",
+	"External Models": "Eksterne modeller",
+	"Failed to add file.": "Kan ikke legge til filen.",
+	"Failed to create API Key.": "Kan ikke opprette en API-nøkkel.",
+	"Failed to read clipboard contents": "Kan ikke lese innhold på utklippstavlen",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Kan ikke oppdatere innstillinger",
+	"Failed to upload file.": "Kan ikke laste opp filen.",
+	"February": "februar",
+	"Feedback History": "Tilbakemeldingshistorikk",
+	"Feedbacks": "Tilbakemeldinger",
+	"Feel free to add specific details": "Legg gjerne til bestemte detaljer",
+	"File": "Fil",
+	"File added successfully.": "Filen er lagt til.",
+	"File content updated successfully.": "Filens innhold er oppdatert.",
+	"File Mode": "Filmodus",
+	"File not found.": "Finner ikke filen.",
+	"File removed successfully.": "Filen er fjernet.",
+	"File size should not exceed {{maxSize}} MB.": "Filstørrelser kan ikke være på mer enn {{maxSize} MB",
+	"Files": "Filer",
+	"Filter is now globally disabled": "Filteret er nå globalt deaktivert",
+	"Filter is now globally enabled": "Filteret er nå globalt aktivert",
+	"Filters": "Filtre",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeravtrykk-spoofing oppdaget: kan ikke bruke initialer som avatar. Bruker standard profilbilde.",
+	"Fluidly stream large external response chunks": "Flytende strømming av store eksterne svarpakker",
+	"Focus chat input": "Fokuser på chat-inndata",
+	"Folder deleted successfully": "Mappe slettet",
+	"Folder name cannot be empty": "Mappenavn kan ikke være tomt",
+	"Folder name cannot be empty.": "Mappenavn kan ikke være tomt.",
+	"Folder name updated successfully": "Mappenavn oppdatert",
+	"Followed instructions perfectly": "Fulgte instruksjonene perfekt",
+	"Forge new paths": "Glem nye baner",
+	"Form": "Form",
+	"Format your variables using brackets like this:": "Formatér variablene dine med klammer som disse:",
+	"Frequency Penalty": "Frekvensstraff",
+	"Function": "Funksjon",
+	"Function created successfully": "Funksjonen er opprettet",
+	"Function deleted successfully": "Funksjonen er slettet",
+	"Function Description": "Beskrivelse av funksjon",
+	"Function ID": "Funksjonens ID",
+	"Function is now globally disabled": "Funksjonen er nå deaktivert globalt",
+	"Function is now globally enabled": "Funksjonen er nå aktivert globalt",
+	"Function Name": "Funksjonens navn",
+	"Function updated successfully": "Funksjonen er oppdatert",
+	"Functions": "Funksjoner",
+	"Functions allow arbitrary code execution": "Funksjoner tillater vilkårlig kodekjøring",
+	"Functions allow arbitrary code execution.": "Funksjoner tillater vilkårlig kodekjøring.",
+	"Functions imported successfully": "Funksjoner er importert",
+	"General": "Generelt",
+	"General Settings": "Generelle innstillinger",
+	"Generate Image": "Generer bilde",
+	"Generating search query": "Genererer søkespørring",
+	"Generation Info": "Info om generering",
+	"Get started": "Kom i gang",
+	"Get started with {{WEBUI_NAME}}": "Kom i gang med {{WEBUI_NAME}}",
+	"Global": "Globalt",
+	"Good Response": "Godt svar",
+	"Google PSE API Key": "API-nøkkel for Google PSE",
+	"Google PSE Engine Id": "Motor-ID for Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "Grupper",
+	"h:mm a": "t:mm a",
+	"Haptic Feedback": "Haptisk tilbakemelding",
+	"has no conversations.": "har ingen samtaler.",
+	"Hello, {{name}}": "Hei, {{name}}!",
+	"Help": "Hjelp",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Hjelp oss med å skape den beste fellesskapsledertavlen ved å dele tilbakemeldingshistorikken din.",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Skjul",
+	"Host": "Host",
+	"How can I help you today?": "Hva kan jeg hjelpe deg med i dag?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybrid-søk",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Jeg bekrefter at jeg har lest og forstår konsekvensene av mine handlinger. Jeg er klar over risikoen forbundet med å kjøre vilkårlig kode, og jeg har verifisert kildens pålitelighet.",
+	"ID": "ID",
+	"Ignite curiosity": "Vekk nysgjerrigheten",
+	"Image Generation (Experimental)": "Bildegenerering (eksperimentell)",
+	"Image Generation Engine": "Bildegenereringsmotor",
+	"Image Settings": "Bildeinnstillinger",
+	"Images": "Bilder",
+	"Import Chats": "Importer chatter",
+	"Import Config from JSON File": "Importer konfigurasjon fra en JSON-fil",
+	"Import Functions": "Importer funksjoner",
+	"Import Models": "Importer modeller",
+	"Import Presets": "",
+	"Import Prompts": "Importer ledetekster",
+	"Import Tools": "Importer verktøy",
+	"Include": "Inkluder",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Inkluder flagget --api-auth når du kjører stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Inkluder flagget --api når du kjører stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Påvirker hvor raskt algoritmen reagerer på tilbakemeldinger fra den genererte teksten. En lavere læringshastighet vil føre til langsommere justeringer, mens en høyere læringshastighet vil gjøre algoritmen mer responsiv. (Standard: 0,1)",
+	"Info": "Info",
+	"Input commands": "Inntast kommandoer",
+	"Install from Github URL": "Installer fra GitHub-URL",
+	"Instant Auto-Send After Voice Transcription": "Øyeblikkelig automatisk sending etter stemmetranskripsjon",
+	"Interface": "Grensesnitt",
+	"Invalid file format.": "Ugyldig filformat.",
+	"Invalid Tag": "Ugyldig etikett",
+	"January": "januar",
+	"Jina API Key": "API-nøkkel for Jina",
+	"join our Discord for help.": "bli med i Discord-fellesskapet vårt for å få hjelp.",
+	"JSON": "JSON",
+	"JSON Preview": "Forhåndsvisning av JSON",
+	"July": "juli",
+	"June": "juni",
+	"JWT Expiration": "JWT-utløp",
+	"JWT Token": "JWT-token",
+	"Keep Alive": "Hold i live",
+	"Key": "Nøkkel",
+	"Keyboard shortcuts": "Hurtigtaster",
+	"Knowledge": "Kunnskap",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Kunnskap opprettet.",
+	"Knowledge deleted successfully.": "Kunnskap slettet.",
+	"Knowledge reset successfully.": "Tilbakestilling av kunnskap vellykket.",
+	"Knowledge updated successfully": "Kunnskap oppdatert",
+	"Label": "Etikett",
+	"Landing Page Mode": "Modus for startside",
+	"Language": "Språk",
+	"Last Active": "Sist aktiv",
+	"Last Modified": "Sist endret",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP-server oppdatert",
+	"Leaderboard": "Ledertavle",
+	"Leave empty for unlimited": "La stå tomt for ubegrenset",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "La stå tomt for å inkludere alle modeller fra endepunktet \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "La stå tomt for å inkludere alle modeller fra endepunktet \"{{URL}}/api/models\"",
+	"Leave empty to include all models or select specific models": "La stå tomt for å inkludere alle modeller",
+	"Leave empty to use the default prompt, or enter a custom prompt": "La stå tomt for å bruke standard ledetekst, eller angi en tilpasset ledetekst",
+	"Light": "Lys",
+	"Listening...": "Lytter ...",
+	"LLMs can make mistakes. Verify important information.": "Språkmodeller kan gjøre feil. Kontroller viktige opplysninger.",
+	"Local": "Lokal",
+	"Local Models": "Lokale modeller",
+	"Lost": "Tapt",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Laget av OpenWebUI-fellesskapet",
+	"Make sure to enclose them with": "Sørg for å omslutte dem med",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Sørg for å eksportere en workflow.json-fil i API-formatet fra ComfyUI.",
+	"Manage": "Administrer",
+	"Manage Arena Models": "Behandle Arena-modeller",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "Behandle API-tilkoblinger for Ollama",
+	"Manage OpenAI API Connections": "Behandle API-tilkoblinger for OpenAPI",
+	"Manage Pipelines": "Behandle pipelines",
+	"March": "mars",
+	"Max Tokens (num_predict)": "Maks antall tokener (num_predict)",
+	"Max Upload Count": "Maks antall opplastinger",
+	"Max Upload Size": "Maks størrelse på opplasting",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksimalt tre modeller kan lastes ned samtidig. Prøv igjen senere.",
+	"May": "mai",
+	"Memories accessible by LLMs will be shown here.": "Språkmodellers tilgjengelige minner vises her.",
+	"Memory": "Minne",
+	"Memory added successfully": "Minne lagt til",
+	"Memory cleared successfully": "Minne tømt",
+	"Memory deleted successfully": "Minne slettet",
+	"Memory updated successfully": "Minne oppdatert",
+	"Merge Responses": "Flette svar",
+	"Message rating should be enabled to use this feature": "Vurdering av meldinger må være aktivert for å kunne bruke denne funksjonen",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Meldinger du sender etter at du har opprettet lenken, blir ikke delt. Brukere med URL-en vil kunne se den delte chatten.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimum poengsum",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "HH:mm DD MMMM YYYY",
+	"MMMM DD, YYYY hh:mm:ss A": "hh:mm:ss A DD MMMM YYYY",
+	"Model": "Modell",
+	"Model '{{modelName}}' has been successfully downloaded.": "Modellen '{{modelName}}' er lastet ned.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' er allerede i nedlastingskøen.",
+	"Model {{modelId}} not found": "Finner ikke modellen {{modelId}}",
+	"Model {{modelName}} is not vision capable": "Modellen {{modelName}} er ikke egnet til visuelle data",
+	"Model {{name}} is now {{status}}": "Modellen {{name}} er nå {{status}}",
+	"Model accepts image inputs": "Modellen godtar bildeinndata",
+	"Model created successfully!": "Modellen er opprettet!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modellfilsystembane oppdaget. Kan ikke fortsette fordi modellens kortnavn er påkrevd for oppdatering.",
+	"Model Filtering": "",
+	"Model ID": "Modell-ID",
+	"Model IDs": "Modell-ID-er",
+	"Model Name": "Modell",
+	"Model not selected": "Modell ikke valgt",
+	"Model Params": "Modellparametere",
+	"Model Permissions": "",
+	"Model updated successfully": "Modell oppdatert",
+	"Modelfile Content": "Modellfilinnhold",
+	"Models": "Modeller",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "mer",
+	"More": "Mer",
+	"Name": "Navn",
+	"Name your knowledge base": "Gi kunnskapsbasen et navn",
+	"New Chat": "Ny chat",
+	"New folder": "Ny mappe",
+	"New Password": "Nytt passord",
+	"No content found": "Finner ikke noe innhold",
+	"No content to speak": "Mangler innhold for tale",
+	"No distance available": "Ingen avstand tilgjengelig",
+	"No feedbacks found": "Finner ingen tilbakemeldinger",
+	"No file selected": "Ingen fil valgt",
+	"No files found.": "Finner ingen filer",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "Finner ikke noe HTML, CSS- eller JavaScript-innhold.",
+	"No knowledge found": "Finner ingen kunnskaper",
+	"No model IDs": "",
+	"No models found": "Finner ingen modeller",
+	"No models selected": "",
+	"No results found": "Finner ingen resultater",
+	"No search query generated": "Ingen søkespørringer er generert",
+	"No source available": "Ingen kilde tilgjengelig",
+	"No users were found.": "",
+	"No valves to update": "Ingen ventiler å oppdatere",
+	"None": "Ingen",
+	"Not factually correct": "Uriktig informasjon",
+	"Not helpful": "Ikke nyttig",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Merk: Hvis du setter en minimumspoengsum, returnerer søket kun dokumenter med en poengsum som er større enn eller lik minimumspoengsummen.",
+	"Notes": "Notater",
+	"Notifications": "Varsler",
+	"November": "november",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth-ID",
+	"October": "oktober",
+	"Off": "Av",
+	"Okay, Let's Go!": "OK, kjør på!",
+	"OLED Dark": "OLED mørk",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API deaktivert",
+	"Ollama API settings updated": "API-innstillinger for Ollama er oppdatert",
+	"Ollama Version": "Ollama-versjon",
+	"On": "Aktivert",
+	"Only alphanumeric characters and hyphens are allowed": "Bare alfanumeriske tegn og bindestreker er tillatt",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Bare alfanumeriske tegn og bindestreker er tillatt i kommandostrengen.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Bare samlinger kan redigeres, eller lag en ny kunnskapsbase for å kunne redigere / legge til dokumenter.",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oi! Det ser ut som URL-en er ugyldig. Dobbeltsjekk, og prøv igjen.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Oi! Det er fortsatt filer som lastes opp. Vent til opplastingen er ferdig.",
+	"Oops! There was an error in the previous response.": "Oi! Det er en feil i det forrige svaret.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oi! Du bruker en ikke-støttet metode (bare frontend). Du må kjøre WebUI fra backend.",
+	"Open file": "Åpne fil",
+	"Open in full screen": "Åpne i fullskjerm",
+	"Open new chat": "Åpne ny chat",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI bruker faster-whisper internt.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI bruker SpeechT5 og CMU Arctic-høytalerinnbygginger",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open WebUI-versjonen (v{{OPEN_WEBUI_VERSION}}) er lavere enn den påkrevde versjonen (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "API-konfigurasjon for OpenAI",
+	"OpenAI API Key is required.": "API-nøkkel for OpenAI kreves.",
+	"OpenAI API settings updated": "API-innstillinger for OpenAI er oppdatert",
+	"OpenAI URL/Key required.": "URL/nøkkel for OpenAI kreves.",
+	"or": "eller",
+	"Organize your users": "",
+	"Other": "Annet",
+	"OUTPUT": "UTDATA",
+	"Output format": "Format på utdata",
+	"Overview": "Oversikt",
+	"page": "side",
+	"Password": "Passord",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF-dokument (.pdf)",
+	"PDF Extract Images (OCR)": "Uthenting av PDF-bilder (OCR)",
+	"pending": "avventer",
+	"Permission denied when accessing media devices": "Tilgang avslått ved bruk av medieenheter",
+	"Permission denied when accessing microphone": "Tilgang avslått ved bruk av mikrofonen",
+	"Permission denied when accessing microphone: {{error}}": "Tilgang avslått ved bruk av mikrofonen: {{error}}",
+	"Permissions": "",
+	"Personalization": "Tilpassing",
+	"Pin": "Fest",
+	"Pinned": "Festet",
+	"Pioneer insights": "Nyskapende innsikt",
+	"Pipeline deleted successfully": "Pipeline slettet",
+	"Pipeline downloaded successfully": "Pipeline lastet ned",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Ingen pipelines oppdaget",
+	"Pipelines Valves": "Pipeline-ventiler",
+	"Plain text (.txt)": "Ren tekst (.txt)",
+	"Playground": "Lekeplass",
+	"Please carefully review the following warnings:": "Les gjennom følgende advarsler grundig:",
+	"Please enter a prompt": "Angi en ledetekst",
+	"Please fill in all fields.": "Fyll i alle felter",
+	"Please select a model first.": "",
+	"Please select a reason": "Velg en årsak",
+	"Port": "Port",
+	"Positive attitude": "Positiv holdning",
+	"Prefix ID": "Prefiks-ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Prefiks-ID brukes for å unngå konflikter med andre tilkoblinger ved å legge til et prefiks til modell-ID-ene. La det stå tomt for å deaktivere",
+	"Previous 30 days": "Siste 30 dager",
+	"Previous 7 days": "Siste 7 dager",
+	"Profile Image": "Profilbilde",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Ledetekst (f.eks. Fortell meg en morsom fakta om romerriket)",
+	"Prompt Content": "Ledetekstinnhold",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Forslag til ledetekst",
+	"Prompt updated successfully": "",
+	"Prompts": "Ledetekster",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Hent \"{{searchValue}}\" fra Ollama.com",
+	"Pull a model from Ollama.com": "Hent en modell fra Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Spørringsparametere",
+	"RAG Template": "RAG-mal",
+	"Rating": "Vurdering",
+	"Re-rank models by topic similarity": "Ny rangering av modeller etter emnelikhet",
+	"Read Aloud": "Les høyt",
+	"Record voice": "Ta opp tale",
+	"Redirecting you to OpenWebUI Community": "Omdirigerer deg til OpenWebUI-fellesskapet",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Reduserer sannsynligheten for å generere meningsløse svar. En høyere verdi (f.eks. 100) vil gi mer varierte svar, mens en lavere verdi (f.eks. 10) vil være mer konservativ. (Standard: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Omtal deg selv som \"Bruker\" (f.eks. \"Bruker lærer spansk\")",
+	"References from": "Henviser fra",
+	"Refused when it shouldn't have": "Avvist når det ikke burde ha blitt det",
+	"Regenerate": "Generer på nytt",
+	"Release Notes": "Utgivelsesnotater",
+	"Relevance": "Relevans",
+	"Remove": "Fjern",
+	"Remove Model": "Fjern modell",
+	"Rename": "Gi nytt navn",
+	"Reorder Models": "",
+	"Repeat Last N": "Gjenta siste N",
+	"Request Mode": "Forespørselsmodus",
+	"Reranking Model": "Omrangeringsmodell",
+	"Reranking model disabled": "Omrangeringsmodell deaktivert",
+	"Reranking model set to \"{{reranking_model}}\"": "Omrangeringsmodell er angitt til \"{{reranking_model}}\"",
+	"Reset": "Tilbakestill",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Tilbakestill opplastingskatalog",
+	"Reset Vector Storage/Knowledge": "Tilbakestill Vector lagring/kunnskap",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Svar-varsler kan ikke aktiveres fordi tilgang til nettstedet er nektet. Gå til nettleserinnstillingene dine for å gi den nødvendige tilgangen.",
+	"Response splitting": "Oppdeling av svar",
+	"Result": "Resultat",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Rik tekstinndata for chat",
+	"RK": "RK",
+	"Role": "Rolle",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Kjør",
+	"Running": "Kjører",
+	"Save": "Lagre",
+	"Save & Create": "Lagre og opprett",
+	"Save & Update": "Lagre og oppdater",
+	"Save As Copy": "Lagre som kopi",
+	"Save Tag": "Lagre etikett",
+	"Saved": "Lagret",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Lagring av chattelogger direkte til nettleserens lagringsområde støttes ikke lenger. Ta et øyeblikk til å laste ned og slette chatteloggende dine ved å klikke på knappen nedenfor. Ikke bekymre deg, du kan enkelt importere chatteloggene dine til backend på nytt via",
+	"Scroll to bottom when switching between branches": "Bla til bunnen når du bytter mellom grener",
+	"Search": "Søk",
+	"Search a model": "Søk etter en modell",
+	"Search Base": "Søke etter base",
+	"Search Chats": "Søk etter chatter",
+	"Search Collection": "Søk etter samling",
+	"Search Filters": "Søk etter filtre",
+	"search for tags": "søk etter etiketter",
+	"Search Functions": "Søk etter funksjoner",
+	"Search Knowledge": "Søke etter kunnskap",
+	"Search Models": "Søk etter modeller",
+	"Search options": "Søk etter alternativer",
+	"Search Prompts": "Søk etter ledetekster",
+	"Search Result Count": "Antall søkeresultater",
+	"Search the web": "Søk på nettet",
+	"Search Tools": "Søkeverktøy",
+	"SearchApi API Key": "API-nøkkel for SearchApi",
+	"SearchApi Engine": "SearchApi-motor",
+	"Searched {{count}} sites_one": "Søkte i {{count}} sites_one",
+	"Searched {{count}} sites_other": "Søkte i {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Søker etter \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Søker etter kunnskap for \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng forespørsels-URL",
+	"See readme.md for instructions": "Se readme.md for å få instruksjoner",
+	"See what's new": "Se hva som er nytt",
+	"Seed": "Seed",
+	"Select a base model": "Velg en grunnmodell",
+	"Select a engine": "Velg en motor",
+	"Select a function": "Velg en funksjon",
+	"Select a group": "",
+	"Select a model": "Velg en modell",
+	"Select a pipeline": "Velg en pipeline",
+	"Select a pipeline url": "Velg en pipeline-URL",
+	"Select a tool": "Velg et verktøy",
+	"Select Engine": "Velg motor",
+	"Select Knowledge": "Velg kunnskap",
+	"Select model": "Velg modell",
+	"Select only one model to call": "Velg bare én modell som skal kalles",
+	"Selected model(s) do not support image inputs": "Valgte modell(er) støtter ikke bildeinndata",
+	"Semantic distance to query": "Semantisk distanse til spørring",
+	"Send": "Send",
+	"Send a Message": "Send en melding",
+	"Send message": "Send melding",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Sender `stream_options: { include_usage: true }` i forespørselen.\nStøttede leverandører returnerer informasjon i svaret om bruk av token når denne parameteren er angitt.",
+	"September": "september",
+	"Serper API Key": "API-nøkkel for Serper",
+	"Serply API Key": "API-nøkkel for Serply",
+	"Serpstack API Key": "API-nøkkel for Serpstack",
+	"Server connection verified": "Servertilkobling bekreftet",
+	"Set as default": "Angi som standard",
+	"Set CFG Scale": "Angi CFG-skala",
+	"Set Default Model": "Angi standard modell",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Angi innbyggingsmodell (f.eks. {{model}})",
+	"Set Image Size": "Angi bildestørrelse",
+	"Set reranking model (e.g. {{model}})": "Angi modell for omrangering (f.eks. {{model}})",
+	"Set Sampler": "Angi prøvetrekker",
+	"Set Scheduler": "Angi planlegger",
+	"Set Steps": "Angi steg",
+	"Set Task Model": "Angi oppgavemodell",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Angi antall GPU-enheter som brukes til beregning. Dette alternativet styrer hvor mange GPU-enheter (hvis tilgjengelig) som brukes til å behandle innkommende forespørsler. Hvis du øker denne verdien, kan du forbedre ytelsen betydelig for modeller som er optimalisert for GPU-akselerasjon, men det kan også føre til at det brukes mer strøm og GPU-ressurser.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Angi antall arbeidstråder som skal brukes til beregning. Dette alternativet kontrollerer hvor mange tråder som brukes til å behandle innkommende forespørsler samtidig. Hvis du øker denne verdien, kan det forbedre ytelsen under arbeidsbelastninger med høy samtidighet, men det kan også føre til økt forbruk av CPU-ressurser.",
+	"Set Voice": "Angi stemme",
+	"Set whisper model": "Angi whisper-modell",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Angir hvor langt tilbake modellen skal se for å forhindre repetisjon. (Standard: 64, 0 = deaktivert, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Angir hvor sterkt repetisjoner skal straffes. En høyere verdi (f.eks. 1,5) vil straffe gjentakelser hardere, mens en lavere verdi (f.eks. 0,9) vil være mildere. (Standard: 1,1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Angir det tilfeldige tallfrøet som skal brukes til generering. Hvis du setter dette til et bestemt tall, vil modellen generere den samme teksten for den samme ledeteksten (standard: tilfeldig).",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Angir størrelsen på kontekstvinduet som brukes til å generere neste token. (Standard: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Angir hvilke stoppsekvenser som skal brukes. Når dette mønsteret forekommer, stopper LLM genereringen av tekst og returnerer. Du kan angi flere stoppmønstre ved å spesifisere flere separate stoppparametere i en modellfil.",
+	"Settings": "Innstillinger",
+	"Settings saved successfully!": "Innstillinger lagret!",
+	"Share": "Del",
+	"Share Chat": "Del chat",
+	"Share to OpenWebUI Community": "Del med OpenWebUI-fellesskapet",
+	"Show": "Vis",
+	"Show \"What's New\" modal on login": "Vis \"Hva er nytt\"-modal ved innlogging",
+	"Show Admin Details in Account Pending Overlay": "Vis administratordetaljer i ventende kontovisning",
+	"Show shortcuts": "Vis snarveier",
+	"Show your support!": "Vis din støtte!",
+	"Showcased creativity": "Fremhevet kreativitet",
+	"Sign in": "Logg inn",
+	"Sign in to {{WEBUI_NAME}}": "Logg på {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Logg på {{WEBUI_NAME}} med LDAP",
+	"Sign Out": "Logg ut",
+	"Sign up": "Registrer deg",
+	"Sign up to {{WEBUI_NAME}}": "Registrer deg for {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Logger på {{WEBUI_NAME}}",
+	"Source": "Kilde",
+	"Speech Playback Speed": "Hastighet på avspilling av tale",
+	"Speech recognition error: {{error}}": "Feil ved talegjenkjenning: {{error}}",
+	"Speech-to-Text Engine": "Tale-til-tekst-motor",
+	"Stop": "Stopp",
+	"Stop Sequence": "Stoppsekvens",
+	"Stream Chat Response": "Strømme chat-svar",
+	"STT Model": "STT-modell",
+	"STT Settings": "STT-innstillinger",
+	"Subtitle (e.g. about the Roman Empire)": "Undertittel (f.eks. om romerriket)",
+	"Success": "Suksess",
+	"Successfully updated.": "Oppdatert.",
+	"Suggested": "Foreslått",
+	"Support": "Bidra",
+	"Support this plugin:": "Bidra til denne utvidelsen:",
+	"Sync directory": "Synkroniseringsmappe",
+	"System": "System",
+	"System Instructions": "Systeminstruksjoner",
+	"System Prompt": "Systemledetekst",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Ledetekst for genering av etikett",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling brukes til å redusere innvirkningen av mindre sannsynlige tokens fra utdataene. En høyere verdi (f.eks. 2,0) vil redusere effekten mer, mens en verdi på 1,0 deaktiverer denne innstillingen. (standard: 1)",
+	"Tap to interrupt": "Trykk for å avbryte",
+	"Tavily API Key": "API-nøkkel for Tavily",
+	"Tell us more:": "Fortell oss mer:",
+	"Temperature": "Temperatur",
+	"Template": "Mal",
+	"Temporary Chat": "Midlertidig chat",
+	"Text Splitter": "Oppdeling av tekst",
+	"Text-to-Speech Engine": "Tekst-til-tale-motor",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Takk for tilbakemeldingen!",
+	"The Application Account DN you bind with for search": "Applikasjonskontoens DN du binder deg med for søk",
+	"The base to search for users": "Basen for å søke etter brukere",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Batchstørrelsen avgjør hvor mange tekstforespørsler som behandles samtidig. En høyere batchstørrelse kan øke ytelsen og hastigheten til modellen, men det krever også mer minne. (Standard: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Utviklerne bak denne utvidelsen er lidenskapelige frivillige fra fellesskapet. Hvis du finner denne utvidelsen nyttig, vennligst vurder å bidra til utviklingen.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Ledertavlens over evalueringer er basert på Elo-rangeringssystemet, og oppdateres i sanntid.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "LDAP-attributtet som tilsvarer brukernavnet som brukerne bruker for å logge på.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Ledertavlen er for øyeblikket i betaversjon, og vi kommer kanskje til å justere beregningene etter hvert som vi forbedrer algoritmen.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Den maksimale filstørrelsen i MB. Hvis en filstørrelse overskrider denne grensen, blir ikke filen lastet opp.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Maksimalt antall filer som kan brukes samtidig i chatten. Hvis antallet filer overskrider denne grensen, blir de ikke lastet opp.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Poengsummen skal være en verdi mellom 0,0 (0 %) og 1,0 (100 %).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Temperaturen på modellen. Hvis du øker temperaturen, vil modellen svare mer kreativt. (Standard: 0,8)",
+	"Theme": "Tema",
+	"Thinking...": "Tenker ...",
+	"This action cannot be undone. Do you wish to continue?": "Denne handlingen kan ikke angres. Vil du fortsette?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dette sikrer at de verdifulle samtalene dine lagres sikkert i backend-databasen din. Takk!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dette er en eksperimentell funksjon. Det er mulig den ikke fungerer som forventet, og den kan endres når som helst.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Dette alternativet styrer hvor mange tokens som bevares når konteksten oppdateres. Hvis det for eksempel er angitt til 2, beholdes de to siste symbolene i samtalekonteksten. Bevaring av konteksten kan bidra til å opprettholde kontinuiteten i en samtale, men det kan redusere muligheten til å svare på nye emner. (Standard: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Dette alternativet angir det maksimale antallet tokens modellen kan generere i svaret sitt. Hvis du øker denne grensen, kan modellen gi lengre svar, men det kan også øke sannsynligheten for at det genereres uhensiktsmessig eller irrelevant innhold. (Standard: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Dette alternativet sletter alle eksisterende filer i samlingen og erstatter dem med nyopplastede filer.",
+	"This response was generated by \"{{model}}\"": "Dette svaret er generert av \"{{modell}}\"",
+	"This will delete": "Dette sletter",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dette sletter <strong>{{NAME}}</strong> og <strong>alt innholdet</strong>.",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Dette tilbakestiller kunnskapsbasen og synkroniserer alle filer. Vil du fortsette?",
+	"Thorough explanation": "Grundig forklaring",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika server-URL kreves.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Oppdater flere variabelplasser etter hverandre ved å trykke på TAB-tasten i chat-inntastingsfeltet etter hver erstatning.",
+	"Title": "Tittel",
+	"Title (e.g. Tell me a fun fact)": "Tittel (f.eks. Fortell meg noe morsomt)",
+	"Title Auto-Generation": "Automatisk tittelgenerering",
+	"Title cannot be an empty string.": "Tittelen kan ikke være en tom streng.",
+	"Title Generation Prompt": "Ledetekst for tittelgenerering",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Hvis du vil ha tilgang til modellnavn tilgjengelige for nedlasting,",
+	"To access the GGUF models available for downloading,": "Hvis du vil ha tilgang til GGUF-modellene tilgjengelige for nedlasting,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Hvis du vil ha tilgang til WebUI, må du kontakte administrator. Administratorer kan behandle brukeres status fra Admin-panelet.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Hvis du vil legge til kunnskapsbaser her, må du først legge dem til i arbeidsområdet \"Kunnskap\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "For å beskytte personvernet ditt deles bare vurderinger, modell-ID-er, etiketter og metadata fra dine tilbakemeldinger. Chattelogger forblir private og inkluderes ikke.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Hvis du vil velge handlinger her, må du først legge dem til i arbeidsområdet \"Funksjoner\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Hvis du vil velge filtre her, må du først legge dem til i arbeidsområdet \"Funksjoner\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Hvis du vil velge verktøysett her, må du først legge dem til i arbeidsområdet \"Verktøy\".",
+	"Toast notifications for new updates": "Hurtigmelding-notifikasjon for nye oppdateringer",
+	"Today": "I dag",
+	"Toggle settings": "Veksle innstillinger",
+	"Toggle sidebar": "Veksle sidefelt",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens som skal beholdes ved kontekstoppdatering (num_keep)",
+	"Too verbose": "For omfattende",
+	"Tool created successfully": "Verktøy opprettet",
+	"Tool deleted successfully": "Verktøy slettet",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Verktøy importert",
+	"Tool Name": "",
+	"Tool updated successfully": "Verktøy oppdatert",
+	"Tools": "Verktøy",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Verktøy er et funksjonskallsystem med vilkårlig kodekjøring",
+	"Tools have a function calling system that allows arbitrary code execution": "Verktøy inneholder et funksjonskallsystem som tillater vilkårlig kodekjøring",
+	"Tools have a function calling system that allows arbitrary code execution.": "Verktøy inneholder et funksjonskallsystem som tillater vilkårlig kodekjøring.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformatorer",
+	"Trouble accessing Ollama?": "Problemer med å koble til Ollama?",
+	"TTS Model": "TTS-modell",
+	"TTS Settings": "TTS-innstillinger",
+	"TTS Voice": "TTS-stemme",
+	"Type": "Type",
+	"Type Hugging Face Resolve (Download) URL": "Angi nedlastings-Resolve-URL for Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oi! Det oppsto et problem med tilkobling til {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Opphev arkiveringen av alle",
+	"Unarchive All Archived Chats": "Opphev arkiveringen av alle arkiverte chatter",
+	"Unarchive Chat": "Opphev arkivering av chat",
+	"Unlock mysteries": "Lås opp mysterier",
+	"Unpin": "Løsne",
+	"Unravel secrets": "Avslør hemmeligheter",
+	"Untagged": "Ikke merket",
+	"Update": "Oppdater",
+	"Update and Copy Link": "Oppdater og kopier lenke",
+	"Update for the latest features and improvements.": "Oppdater for å få siste funksjoner og forbedringer.",
+	"Update password": "Oppdater passord",
+	"Updated": "Oppdatert",
+	"Updated at": "Oppdatert",
+	"Updated At": "Oppdatert",
+	"Upload": "Last opp",
+	"Upload a GGUF model": "Last opp en GGUF-modell",
+	"Upload directory": "Mappe for opplastinger",
+	"Upload files": "Last opp filer",
+	"Upload Files": "Last opp filer",
+	"Upload Pipeline": "Last opp pipeline",
+	"Upload Progress": "Opplastingsfremdrift",
+	"URL": "URL",
+	"URL Mode": "URL-modus",
+	"Use '#' in the prompt input to load and include your knowledge.": "Bruk # i ledetekstinndata for å laste inn og inkludere dine kunnskaper.",
+	"Use Gravatar": "Bruk Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Bruk initialer",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "bruker",
+	"User": "Bruker",
+	"User location successfully retrieved.": "Brukerens lokasjon hentet",
+	"Username": "Brukernavn",
+	"Users": "Brukere",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Bruker standard Arena-modellen med alle modeller. Klikk på plussknappen for å legge til egne modeller.",
+	"Utilize": "Bruk",
+	"Valid time units:": "Gyldige tidsenheter:",
+	"Valves": "Ventiler",
+	"Valves updated": "Ventiler oppdatert",
+	"Valves updated successfully": "Ventilene er oppdatert",
+	"variable": "variabel",
+	"variable to have them replaced with clipboard content.": "variabel for å erstatte dem med utklippstavleinnhold.",
+	"Version": "Versjon",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Version {{selectedVersion}} av {{totalVersions}}",
+	"Visibility": "",
+	"Voice": "Stemme",
+	"Voice Input": "Taleinndata",
+	"Warning": "Advarsel",
+	"Warning:": "Advarsel!",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Advarsel: Hvis du oppdaterer eller endrer innbyggingsmodellen din, må du importere alle dokumenter på nytt.",
+	"Web": "Web",
+	"Web API": "Web-API",
+	"Web Loader Settings": "Web-lasterinnstillinger",
+	"Web Search": "Nettsøk",
+	"Web Search Engine": "Nettsøkmotor",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "Innstillinger for WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI vil rette forespørsler til \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI vil rette forespørsler til \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Hva prøver du å oppnå?",
+	"What are you working on?": "Hva jobber du på nå?",
+	"What’s New in": "Hva er nytt i",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Hvis denne modusen er aktivert, svarer modellen på alle chattemeldinger i sanntid, og genererer et svar så snart brukeren sender en melding. Denne modusen er nyttig for live chat-applikasjoner, men kan påvirke ytelsen på tregere maskinvare.",
+	"wherever you are": "uansett hvor du er",
+	"Whisper (Local)": "Whisper (Lokal)",
+	"Why?": "",
+	"Widescreen Mode": "Bredskjermmodus",
+	"Won": "Vant",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Fungerer sammen med top-k. En høyere verdi (f.eks. 0,95) vil føre til mer mangfoldig tekst, mens en lavere verdi (f.eks. 0,5) vil generere mer fokusert og konservativ tekst. (Standard: 0,9)",
+	"Workspace": "Arbeidsområde",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Skriv inn et ledetekstforslag (f.eks. Hvem er du?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv inn et sammendrag på 50 ord som oppsummerer [emne eller nøkkelord].",
+	"Write something...": "Skriv inn noe...",
+	"Write your model template content here": "Skriv inn modellens malinnhold her",
+	"Yesterday": "I går",
+	"You": "Du",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Du kan bare chatte med maksimalt {{maxCount}} fil(er) om gangen.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan tilpasse interaksjonene dine med språkmodeller ved å legge til minner gjennom Administrer-knappen nedenfor, slik at de blir mer til nyttige og tilpasset deg.",
+	"You cannot upload an empty file.": "Du kan ikke laste opp en tom fil.",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Du har ingen arkiverte samtaler.",
+	"You have shared this chat": "Du har delt denne chatten",
+	"You're a helpful assistant.": "Du er en nyttig assistent.",
+	"You're now logged in.": "Du er nå logget inn.",
+	"Your account status is currently pending activation.": "Status på kontoen er for øyeblikket ventende på aktivering.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Hele beløpet går uavkortet til utvikleren av tillegget. Open WebUI mottar ikke deler av beløpet. Den valgte betalingsplattformen kan ha gebyrer.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Innstillinger for YouTube-laster"
+}
diff --git a/src/lib/i18n/locales/nl-NL/translation.json b/src/lib/i18n/locales/nl-NL/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..e8147b6014484e6a2acfc675c4b3e7419a143d08
--- /dev/null
+++ b/src/lib/i18n/locales/nl-NL/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w', of '-1' for geen vervaldatum.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(bv. `sh webui.sh --api --api-auth gebruikersnaam_wachtwoord`)",
+	"(e.g. `sh webui.sh --api`)": "(bv. `sh webui.sh --api`)",
+	"(latest)": "(nieuwste)",
+	"{{ models }}": "{{ modellen }}",
+	"{{user}}'s Chats": "{{user}}'s chats",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend Verplicht",
+	"*Prompt node ID(s) are required for image generation": "*Prompt node ID('s) zijn vereist voor het genereren van afbeeldingen",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Een nieuwe versie (v{{LATEST_VERSION}}) is nu beschikbaar",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Een taakmodel wordt gebruikt bij het uitvoeren van taken zoals het genereren van titels voor chats en zoekopdrachten op het internet",
+	"a user": "een gebruiker",
+	"About": "Over",
+	"Access": "Toegang",
+	"Access Control": "Toegangsbeheer",
+	"Accessible to all users": "Toegankelijk voor alle gebruikers",
+	"Account": "Account",
+	"Account Activation Pending": "Accountactivatie in afwachting",
+	"Accurate information": "Accurate informatie",
+	"Actions": "Acties",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Activeer dit commando door \"/{{COMMAND}}\" in de chat te typen",
+	"Active Users": "Actieve gebruikers",
+	"Add": "Toevoegen",
+	"Add a model ID": "Voeg een model-ID toe",
+	"Add a short description about what this model does": "Voeg een korte beschrijving toe over wat dit model doet",
+	"Add a tag": "Voeg een tag toe",
+	"Add Arena Model": "Voeg arenamodel toe",
+	"Add Connection": "Voeg verbinding toe",
+	"Add Content": "Voeg content toe",
+	"Add content here": "Voeg hier content toe",
+	"Add custom prompt": "Voeg een aangepaste prompt toe",
+	"Add Files": "Voege bestanden toe",
+	"Add Group": "Voeg groep toe",
+	"Add Memory": "Voeg geheugen toe",
+	"Add Model": "Voeg model toe",
+	"Add Tag": "Voeg tag toe",
+	"Add Tags": "Voeg tags toe",
+	"Add text content": "Voeg tekstinhoud toe",
+	"Add User": "Voeg gebruiker toe",
+	"Add User Group": "Voeg gebruikersgroep toe",
+	"Adjusting these settings will apply changes universally to all users.": "Het aanpassen van deze instellingen zal universeel worden toegepast op alle gebruikers.",
+	"admin": "beheerder",
+	"Admin": "Beheerder",
+	"Admin Panel": "Beheerderspaneel",
+	"Admin Settings": "Beheerdersinstellingen",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Beheerders hebben altijd toegang tot alle gereedschappen; gebruikers moeten gereedschap toegewezen krijgen per model in de werkruimte.",
+	"Advanced Parameters": "Geavanceerde parameters",
+	"Advanced Params": "Geavanceerde params",
+	"All chats": "Alle chats",
+	"All Documents": "Alle documenten",
+	"All models deleted successfully": "Alle modellen zijn succesvol verwijderd",
+	"Allow Chat Delete": "Sta chatverwijdering toe",
+	"Allow Chat Deletion": "Sta chatverwijdering toe",
+	"Allow Chat Edit": "Sta chatwijziging toe",
+	"Allow File Upload": "Sta bestandenupload toe",
+	"Allow non-local voices": "Niet-lokale stemmen toestaan",
+	"Allow Temporary Chat": "Tijdelijke chat toestaan",
+	"Allow User Location": "Gebruikerslocatie toestaan",
+	"Allow Voice Interruption in Call": "Stemonderbreking tijdens gesprek toestaan",
+	"Already have an account?": "Heb je al een account?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternatief voor de top_p, en streeft naar een evenwicht tussen kwaliteit en variatie. De parameter p vertegenwoordigt de minimumwaarschijnlijkheid dat een token in aanmerking wordt genomen, in verhouding tot de waarschijnlijkheid van het meest waarschijnlijke token. Bijvoorbeeld, met p=0.05 en de meest waarschijnlijke token met een waarschijnlijkheid van 0.9, worden logits met een waarde kleiner dan 0.045 uitgefilterd. (Standaard: 0,0)",
+	"Amazing": "Geweldig",
+	"an assistant": "een assistent",
+	"and": "en",
+	"and {{COUNT}} more": "en {{COUNT}} meer",
+	"and create a new shared link.": "en maak een nieuwe gedeelde link.",
+	"API Base URL": "API Base URL",
+	"API Key": "API-sleutel",
+	"API Key created.": "API-sleutel aangemaakt.",
+	"API keys": "API-sleutels",
+	"Application DN": "Applicatie DN",
+	"Application DN Password": "Applicatie",
+	"applies to all users with the \"user\" role": "wordt op alle gebruikers met de \"gebruikersrol\" toegepast",
+	"April": "April",
+	"Archive": "Archief",
+	"Archive All Chats": "Archiveer alle chats",
+	"Archived Chats": "Chatrecord",
+	"archived-chat-export": "gearchiveerde-chat-export",
+	"Are you sure you want to unarchive all archived chats?": "Weet je zeker dat je alle gearchiveerde chats wil onarchiveren?",
+	"Are you sure?": "Weet je het zeker?",
+	"Arena Models": "Arenamodellen",
+	"Artifacts": "Artefacten",
+	"Ask a question": "Stel een vraag",
+	"Assistant": "Assistent",
+	"Attach file": "Voeg een bestand toe",
+	"Attention to detail": "Attention to detail",
+	"Attribute for Username": "Attribuut voor gebruikersnaam",
+	"Audio": "Audio",
+	"August": "Augustus",
+	"Authenticate": "Authenticeer",
+	"Auto-Copy Response to Clipboard": "Antwoord automatisch kopiëren naar klembord",
+	"Auto-playback response": "Automatisch afspelen van antwoord",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "Automatic1111 Api Auth String",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Basis-URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Basis-URL is verplicht",
+	"Available list": "Beschikbare lijst",
+	"available!": "beschikbaar!",
+	"Awful": "Verschrikkelijk",
+	"Azure AI Speech": "Azure AI-spraak",
+	"Azure Region": "Azure regio",
+	"Back": "Terug",
+	"Bad Response": "Ongeldig antwoord",
+	"Banners": "Banners",
+	"Base Model (From)": "Basismodel (Vanaf)",
+	"Batch Size (num_batch)": "Batchgrootte (num_batch)",
+	"before": "voor",
+	"Being lazy": "Lui zijn",
+	"Bing Search V7 Endpoint": "Bing Search V7 Endpoint",
+	"Bing Search V7 Subscription Key": "Bing Search V7 Subscription Key",
+	"Brave Search API Key": "Brave Search API-sleutel",
+	"By {{name}}": "Op {{name}}",
+	"Bypass SSL verification for Websites": "SSL-verificatie omzeilen voor websites",
+	"Call": "Oproep",
+	"Call feature is not supported when using Web STT engine": "Belfunctie wordt niet ondersteund bij gebruik van de Web STT engine",
+	"Camera": "Camera",
+	"Cancel": "Annuleren",
+	"Capabilities": "Mogelijkheden",
+	"Certificate Path": "Certificaatpad",
+	"Change Password": "Wijzig Wachtwoord",
+	"Character": "Karakter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Verken nieuwe grenzen",
+	"Chat": "Chat",
+	"Chat Background Image": "Chatachtergrond",
+	"Chat Bubble UI": "Chatbubble-UI",
+	"Chat Controls": "Chatbesturing",
+	"Chat direction": "Chatrichting",
+	"Chat Overview": "Chatoverzicht",
+	"Chat Permissions": "Chattoestemmingen",
+	"Chat Tags Auto-Generation": "Chatlabels automatisch genereren",
+	"Chats": "Chats",
+	"Check Again": "Controleer Opnieuw",
+	"Check for updates": "Controleer op updates",
+	"Checking for updates...": "Controleren op updates...",
+	"Choose a model before saving...": "Kies een model voordat je opslaat...",
+	"Chunk Overlap": "Chunkoverlap",
+	"Chunk Params": "Chunkparams",
+	"Chunk Size": "Chunkgrootte",
+	"Ciphers": "Versleutelingen",
+	"Citation": "Citaat",
+	"Clear memory": "Geheugen wissen",
+	"click here": "klik hier",
+	"Click here for filter guides.": "Klik hier voor filterhulp",
+	"Click here for help.": "Klik hier voor hulp.",
+	"Click here to": "Klik hier om",
+	"Click here to download user import template file.": "Klik hier om het sjabloonbestand voor gebruikersimport te downloaden.",
+	"Click here to learn more about faster-whisper and see the available models.": "Klik hier om meer te leren over faster-whisper en de beschikbare modellen te bekijken.",
+	"Click here to select": "Klik hier om te selecteren",
+	"Click here to select a csv file.": "Klik hier om een csv file te selecteren.",
+	"Click here to select a py file.": "Klik hier om een py-bestand te selecteren.",
+	"Click here to upload a workflow.json file.": "Klik hier om een workflow.json-bestand te uploaden.",
+	"click here.": "klik hier.",
+	"Click on the user role button to change a user's role.": "Klik op de gebruikersrol knop om de rol van een gebruiker te wijzigen.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Klembord schrijftoestemming geweigerd. Kijk je browserinstellingen na om de benodigde toestemming te geven.",
+	"Clone": "Kloon",
+	"Close": "Sluiten",
+	"Code execution": "Code uitvoeren",
+	"Code formatted successfully": "Code succesvol geformateerd",
+	"Collection": "Verzameling",
+	"Color": "Kleur",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL is required.",
+	"ComfyUI Workflow": "ComfyUI workflow",
+	"ComfyUI Workflow Nodes": "ComfyUI workflowknopen",
+	"Command": "Commando",
+	"Completions": "Voltooiingen",
+	"Concurrent Requests": "Gelijktijdige verzoeken",
+	"Configure": "Configureer",
+	"Configure Models": "Configureer modellen",
+	"Confirm": "Bevestigen",
+	"Confirm Password": "Bevestig wachtwoord",
+	"Confirm your action": "Bevestig uw actie",
+	"Connections": "Verbindingen",
+	"Contact Admin for WebUI Access": "Neem contact op met de beheerder voor WebUI-toegang",
+	"Content": "Inhoud",
+	"Content Extraction": "Inhoudsextractie",
+	"Context Length": "Contextlengte",
+	"Continue Response": "Doorgaan met antwoord",
+	"Continue with {{provider}}": "Ga verder met {{provider}}",
+	"Continue with Email": "Ga door met E-mail",
+	"Continue with LDAP": "Ga door met LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Bepaal hoe berichttekst wordt opgesplitst voor TTS-verzoeken. 'Leestekens' splitst op in zinnen, 'alinea's' splitst op in paragrafen en 'geen' houdt het bericht als een enkele string.",
+	"Controls": "Besturingselementen",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Regelt de balans tussen coherentie en diversiteit van de uitvoer. Een lagere waarde resulteert in meer gerichte en coherente tekst. (Standaard: 5.0)",
+	"Copied": "Gekopieerd",
+	"Copied shared chat URL to clipboard!": "URL van gedeelde gesprekspagina gekopieerd naar klembord!",
+	"Copied to clipboard": "Gekopieerd naar klembord",
+	"Copy": "Kopieer",
+	"Copy last code block": "Kopieer laatste codeblok",
+	"Copy last response": "Kopieer laatste antwoord",
+	"Copy Link": "Kopieer link",
+	"Copy to clipboard": "Kopier naar klembord",
+	"Copying to clipboard was successful!": "Kopiëren naar klembord was succesvol!",
+	"Create": "Aanmaken",
+	"Create a knowledge base": "Maak een kennisbasis aan",
+	"Create a model": "Een model maken",
+	"Create Account": "Maak account",
+	"Create Admin Account": "Maak admin-account",
+	"Create Group": "Maak groep",
+	"Create Knowledge": "Creër kennis",
+	"Create new key": "Maak nieuwe sleutel",
+	"Create new secret key": "Maak nieuwe geheime sleutel",
+	"Created at": "Gemaakt op",
+	"Created At": "Gemaakt op",
+	"Created by": "Gemaakt door",
+	"CSV Import": "CSV import",
+	"Current Model": "Huidig model",
+	"Current Password": "Huidig wachtwoord",
+	"Custom": "Aangepast",
+	"Dark": "Donker",
+	"Database": "Database",
+	"December": "December",
+	"Default": "Standaard",
+	"Default (Open AI)": "Standaard (Open AI)",
+	"Default (SentenceTransformers)": "Standaard (SentenceTransformers)",
+	"Default Model": "Standaardmodel",
+	"Default model updated": "Standaardmodel bijgewerkt",
+	"Default Models": "Standaardmodellen",
+	"Default permissions": "Standaardrechten",
+	"Default permissions updated successfully": "Standaardrechten succesvol bijgewerkt",
+	"Default Prompt Suggestions": "Standaard Prompt Suggesties",
+	"Default to 389 or 636 if TLS is enabled": "Standaard 389 of 636 als TLS is ingeschakeld",
+	"Default to ALL": "Standaar op ALL",
+	"Default User Role": "Standaard gebruikersrol",
+	"Delete": "Verwijderen",
+	"Delete a model": "Verwijder een model",
+	"Delete All Chats": "Verwijder alle chats",
+	"Delete All Models": "Verwijder alle modellen",
+	"Delete chat": "Verwijder chat",
+	"Delete Chat": "Verwijder chat",
+	"Delete chat?": "Verwijder chat?",
+	"Delete folder?": "Verwijder map?",
+	"Delete function?": "Verwijder functie?",
+	"Delete prompt?": "Verwijder prompt?",
+	"delete this link": "verwijder deze link",
+	"Delete tool?": "Verwijder tool?",
+	"Delete User": "Verwijder gebruiker",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} is verwijderd",
+	"Deleted {{name}}": "{{name}} verwijderd",
+	"Deleted User": "Gebruiker verwijderd",
+	"Describe your knowledge base and objectives": "Beschrijf je kennisbasis en doelstellingen",
+	"Description": "Beschrijving",
+	"Didn't fully follow instructions": "Heeft niet alle instructies gevolgt",
+	"Disabled": "Uitgeschakeld",
+	"Discover a function": "Ontdek een functie",
+	"Discover a model": "Ontdek een model",
+	"Discover a prompt": "Ontdek een prompt",
+	"Discover a tool": "Ontdek een tool",
+	"Discover wonders": "Ontdek wonderen",
+	"Discover, download, and explore custom functions": "Ontdek, download en verken aangepaste functies",
+	"Discover, download, and explore custom prompts": "Ontdek, download en verken aangepaste prompts",
+	"Discover, download, and explore custom tools": "Ontdek, download en verken aangepaste gereedschappen",
+	"Discover, download, and explore model presets": "Ontdek, download en verken model presets",
+	"Dismissible": "Afwijsbaar",
+	"Display": "Toon",
+	"Display Emoji in Call": "Emoji tonen tijdens gesprek",
+	"Display the username instead of You in the Chat": "Toon de gebruikersnaam in plaats van Jij in de Chat",
+	"Displays citations in the response": "Toon citaten in het antwoord",
+	"Dive into knowledge": "Duik in kennis",
+	"Do not install functions from sources you do not fully trust.": "Installeer geen functies vanuit bronnen die je niet volledig vertrouwt",
+	"Do not install tools from sources you do not fully trust.": "Installeer geen tools vanuit bronnen die je niet volledig vertrouwt.",
+	"Document": "Document",
+	"Documentation": "Documentatie",
+	"Documents": "Documenten",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "maakt geen externe verbindingen, en je gegevens blijven veilig op je lokaal gehoste server.",
+	"Don't have an account?": "Heb je geen account?",
+	"don't install random functions from sources you don't trust.": "installeer geen willekeurige functies van bronnen die je niet vertrouwd",
+	"don't install random tools from sources you don't trust.": "installeer geen willekeurige gereedschappen van bronnen die je niet vertrouwd",
+	"Don't like the style": "Vind je de stijl niet mooi?",
+	"Done": "Voltooid",
+	"Download": "Download",
+	"Download canceled": "Download geannuleerd",
+	"Download Database": "Download database",
+	"Drag and drop a file to upload or select a file to view": "Sleep een bestand om te uploaden of selecteer een bestand om te bekijken",
+	"Draw": "Teken",
+	"Drop any files here to add to the conversation": "Sleep hier bestanden om toe te voegen aan het gesprek",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "bijv. '30s', '10m'. Geldige tijdseenheden zijn 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "bijv. Een filter om gevloek uit tekst te verwijderen",
+	"e.g. My Filter": "bijv. Mijn filter",
+	"e.g. My Tools": "bijv. Mijn gereedschappen",
+	"e.g. my_filter": "bijv. mijn_filter",
+	"e.g. my_tools": "bijv. mijn_gereedschappen",
+	"e.g. Tools for performing various operations": "Gereedschappen om verschillende bewerkingen uit te voeren",
+	"Edit": "Wijzig",
+	"Edit Arena Model": "Bewerk arenamodel",
+	"Edit Connection": "Bewerk connectie",
+	"Edit Default Permissions": "Standaardrechten bewerken",
+	"Edit Memory": "Bewerk geheugen",
+	"Edit User": "Wijzig gebruiker",
+	"Edit User Group": "Bewerk gebruikergroep",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "E-mail",
+	"Embark on adventures": "Ga op avonturen",
+	"Embedding Batch Size": "Embedding batchgrootte",
+	"Embedding Model": "Embedding Model",
+	"Embedding Model Engine": "Embedding Model Engine",
+	"Embedding model set to \"{{embedding_model}}\"": "Embedding model ingesteld op \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Schakel API-sleutel authenticatie in",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Delen via de community inschakelen",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Schakel Memory Locking (mlock) in om te voorkomen dat modelgegevens uit het RAM worden verwisseld. Deze optie vergrendelt de werkset pagina's van het model in het RAM, zodat ze niet naar de schijf worden uitgewisseld. Dit kan helpen om de prestaties op peil te houden door paginafouten te voorkomen en snelle gegevenstoegang te garanderen.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Schakel Memory Mapping (mmap) in om modelgegevens te laden. Deze optie laat het systeem schijfopslag gebruiken als een uitbreiding van RAM door schijfbestanden te behandelen alsof ze in RAM zitten. Dit kan de prestaties van het model verbeteren door snellere gegevenstoegang mogelijk te maken. Het is echter mogelijk dat deze optie niet op alle systemen correct werkt en een aanzienlijke hoeveelheid schijfruimte in beslag kan nemen.",
+	"Enable Message Rating": "Schakel berichtbeoordeling in",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Mirostat-sampling inschakelen voor het regelen van de perplexiteit. (Standaard: 0, 0 = uitgeschakeld, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Schakel nieuwe registraties in",
+	"Enable Web Search": "Zoeken op het web inschakelen",
+	"Enabled": "Ingeschakeld",
+	"Engine": "Engine",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Zorg ervoor dat uw CSV-bestand de volgende vier kolommen in deze volgorde bevat: Naam, E-mail, Wachtwoord, Rol.",
+	"Enter {{role}} message here": "Voeg {{role}} bericht hier toe",
+	"Enter a detail about yourself for your LLMs to recall": "Voer een detail over jezelf in zodat LLM's het kunnen onthouden",
+	"Enter api auth string (e.g. username:password)": "Voer api auth string in (bv. gebruikersnaam:wachtwoord)",
+	"Enter Application DN": "Voer applicatie-DN in",
+	"Enter Application DN Password": "Voer applicatie-DN wachtwoord in",
+	"Enter Bing Search V7 Endpoint": "Voer Bing Search V7 Endpoint in",
+	"Enter Bing Search V7 Subscription Key": "Voer Bing Search V7 abonnementscode in",
+	"Enter Brave Search API Key": "Voer de Brave Search API-sleutel in",
+	"Enter certificate path": "Voer certificaatpad in",
+	"Enter CFG Scale (e.g. 7.0)": "Voer CFG schaal in (bv. 7.0)",
+	"Enter Chunk Overlap": "Voeg Chunk Overlap toe",
+	"Enter Chunk Size": "Voeg Chunk Size toe",
+	"Enter description": "Voer beschrijving in",
+	"Enter Github Raw URL": "Voer de Github Raw-URL in",
+	"Enter Google PSE API Key": "Voer de Google PSE API-sleutel in",
+	"Enter Google PSE Engine Id": "Voer Google PSE Engine-ID in",
+	"Enter Image Size (e.g. 512x512)": "Voeg afbeelding formaat toe (Bijv. 512x512)",
+	"Enter Jina API Key": "Voer Jina API-sleutel in",
+	"Enter language codes": "Voeg taal codes toe",
+	"Enter Model ID": "Voer model-ID in",
+	"Enter model tag (e.g. {{modelTag}})": "Voeg model-tag toe (Bijv. {{modelTag}})",
+	"Enter Mojeek Search API Key": "Voer Mojeek Search API-sleutel in",
+	"Enter Number of Steps (e.g. 50)": "Voeg aantal stappen toe (Bijv. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Voer Sampler in (bv. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Voer Scheduler in (bv. Karras)",
+	"Enter Score": "Voeg score toe",
+	"Enter SearchApi API Key": "Voer SearchApi API-sleutel in",
+	"Enter SearchApi Engine": "Voer SearchApi-Engine in",
+	"Enter Searxng Query URL": "Voer de URL van de Searxng-query in",
+	"Enter Seed": "Voer Seed in",
+	"Enter Serper API Key": "Voer de Serper API-sleutel in",
+	"Enter Serply API Key": "Voer Serply API-sleutel in",
+	"Enter Serpstack API Key": "Voer de Serpstack API-sleutel in",
+	"Enter server host": "Voer serverhost in",
+	"Enter server label": "Voer serverlabel in",
+	"Enter server port": "Voer serverpoort in",
+	"Enter stop sequence": "Voer stopsequentie in",
+	"Enter system prompt": "Voer systeem prompt in",
+	"Enter Tavily API Key": "Voer Tavily API-sleutel in",
+	"Enter Tika Server URL": "Voer Tika Server URL in",
+	"Enter Top K": "Voeg Top K toe",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Voer URL in (Bijv. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Voer URL in (Bijv. http://localhost:11434)",
+	"Enter Your Email": "Voer je Email in",
+	"Enter Your Full Name": "Voer je Volledige Naam in",
+	"Enter your message": "Voer je bericht in",
+	"Enter Your Password": "Voer je Wachtwoord in",
+	"Enter Your Role": "Voer je Rol in",
+	"Enter Your Username": "Voer je gebruikersnaam in",
+	"Error": "Fout",
+	"ERROR": "ERROR",
+	"Evaluations": "Beoordelingen",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Voorbeeld: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Voorbeeld: ALL",
+	"Example: ou=users,dc=foo,dc=example": "Voorbeeld: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Voorbeeld: sAMAccountName or uid or userPrincipalName",
+	"Exclude": "Sluit uit",
+	"Experimental": "Experimenteel",
+	"Explore the cosmos": "Ontdek de kosmos",
+	"Export": "Exporteren",
+	"Export All Archived Chats": "Exporteer alle gearchiveerde chats",
+	"Export All Chats (All Users)": "Exporteer alle chats (Alle gebruikers)",
+	"Export chat (.json)": "Exporteer chat (.json)",
+	"Export Chats": "Exporteer chats",
+	"Export Config to JSON File": "Exporteer configuratie naar JSON-bestand",
+	"Export Functions": "Exporteer functies",
+	"Export Models": "Modellen exporteren",
+	"Export Presets": "Exporteer voorinstellingen",
+	"Export Prompts": "Exporteer Prompts",
+	"Export to CSV": "Exporteer naar CSV",
+	"Export Tools": "Exporteer gereedschappen",
+	"External Models": "Externe modules",
+	"Failed to add file.": "Het is niet gelukt om het bestand toe te voegen.",
+	"Failed to create API Key.": "Kan API Key niet aanmaken.",
+	"Failed to read clipboard contents": "Kan klembord inhoud niet lezen",
+	"Failed to save models configuration": "Het is niet gelukt om de modelconfiguratie op te slaan",
+	"Failed to update settings": "Instellingen konden niet worden bijgewerkt.",
+	"Failed to upload file.": "Bestand kon niet worden geüpload.",
+	"February": "Februari",
+	"Feedback History": "Feedback geschiedenis",
+	"Feedbacks": "Feedback",
+	"Feel free to add specific details": "Voeg specifieke details toe",
+	"File": "Bestand",
+	"File added successfully.": "Bestand succesvol toegevoegd.",
+	"File content updated successfully.": "Bestandsinhoud succesvol bijgewerkt.",
+	"File Mode": "Bestandsmodus",
+	"File not found.": "Bestand niet gevonden.",
+	"File removed successfully.": "Bestand succesvol verwijderd.",
+	"File size should not exceed {{maxSize}} MB.": "Bestandsgrootte mag niet groter zijn dan {{maxSize}} MB.",
+	"Files": "Bestanden",
+	"Filter is now globally disabled": "Filter is nu globaal uitgeschakeld",
+	"Filter is now globally enabled": "Filter is nu globaal ingeschakeld",
+	"Filters": "Filters",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Vingerafdruk spoofing gedetecteerd: kan initialen niet gebruiken als avatar. Standaardprofielafbeelding wordt gebruikt.",
+	"Fluidly stream large external response chunks": "Stream grote externe responsbrokken vloeiend",
+	"Focus chat input": "Focus chat input",
+	"Folder deleted successfully": "Map succesvol verwijderd",
+	"Folder name cannot be empty": "Mapnaam kan niet leeg zijn",
+	"Folder name cannot be empty.": "Mapnaam kan niet leeg zijn",
+	"Folder name updated successfully": "Mapnaam succesvol aangepast",
+	"Followed instructions perfectly": "Volgde instructies perfect",
+	"Forge new paths": "Smeed nieuwe paden",
+	"Form": "Formulier",
+	"Format your variables using brackets like this:": "Formateer je variabelen met haken zoals dit:",
+	"Frequency Penalty": "Frequentiestraf",
+	"Function": "Functie",
+	"Function created successfully": "Functie succesvol aangemaakt",
+	"Function deleted successfully": "Functie succesvol verwijderd",
+	"Function Description": "Functiebeschrijving",
+	"Function ID": "Functie-ID",
+	"Function is now globally disabled": "Functie is nu globaal uitgeschakeld",
+	"Function is now globally enabled": "Functie is nu globaal ingeschakeld",
+	"Function Name": "Functienaam",
+	"Function updated successfully": "Functienaam succesvol aangepast",
+	"Functions": "Functies",
+	"Functions allow arbitrary code execution": "Functies staan willekeurige code-uitvoering toe",
+	"Functions allow arbitrary code execution.": "Functies staan willekeurige code-uitvoering toe",
+	"Functions imported successfully": "Functies succesvol geïmporteerd",
+	"General": "Algemeen",
+	"General Settings": "Algemene instellingen",
+	"Generate Image": "Genereer afbeelding",
+	"Generating search query": "Zoekopdracht genereren",
+	"Generation Info": "Generatie Info",
+	"Get started": "Begin",
+	"Get started with {{WEBUI_NAME}}": "Begin met {{WEBUI_NAME}}",
+	"Global": "Globaal",
+	"Good Response": "Goed antwoord",
+	"Google PSE API Key": "Google PSE API-sleutel",
+	"Google PSE Engine Id": "Google PSE-engine-ID",
+	"Group created successfully": "Groep succesvol aangemaakt",
+	"Group deleted successfully": "Groep succesvol verwijderd",
+	"Group Description": "Groepsbeschrijving",
+	"Group Name": "Groepsnaam",
+	"Group updated successfully": "Groep succesvol bijgewerkt",
+	"Groups": "Groepen",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Haptische feedback",
+	"has no conversations.": "heeft geen gesprekken.",
+	"Hello, {{name}}": "Hallo, {{name}}",
+	"Help": "Help",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Help ons het beste community leaderboard te maken door je feedbackgeschiedenis te delen!",
+	"Hex Color": "Hex-kleur",
+	"Hex Color - Leave empty for default color": "Hex-kleur - laat leeg voor standaardkleur",
+	"Hide": "Verberg",
+	"Host": "Host",
+	"How can I help you today?": "Hoe kan ik je vandaag helpen?",
+	"How would you rate this response?": "Hoe zou je dit antwoord beoordelen?",
+	"Hybrid Search": "Hybride Zoeken",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Ik bevestig dat ik de implicaties van mijn actie heb gelezen en begrepen. Ik ben me bewust van de risico's die gepaard gaan met het uitvoeren van willekeurige code en ik heb de betrouwbaarheid van de bron gecontroleerd.",
+	"ID": "ID",
+	"Ignite curiosity": "Wakker nieuwsgierigheid aan",
+	"Image Generation (Experimental)": "Afbeeldingsgeneratie (Experimenteel)",
+	"Image Generation Engine": "Afbeeldingsgeneratie Engine",
+	"Image Settings": "Afbeeldingsinstellingen",
+	"Images": "Afbeeldingen",
+	"Import Chats": "Importeer Chats",
+	"Import Config from JSON File": "Importeer configuratie vanuit JSON-bestand",
+	"Import Functions": "Importeer Functies",
+	"Import Models": "Modellen importeren",
+	"Import Presets": "Importeer voorinstellingen",
+	"Import Prompts": "Importeer Prompts",
+	"Import Tools": "Importeer Gereedschappen",
+	"Include": "Voeg toe",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Voeg '--api-auth` toe bij het uitvoeren van stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Voeg `--api` vlag toe bij het uitvoeren van stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Beïnvloedt hoe snel het algoritme reageert op feedback van de gegenereerde tekst. Een lagere leersnelheid resulteert in langzamere aanpassingen, terwijl een hogere leersnelheid het algoritme gevoeliger maakt. (Standaard: 0,1)",
+	"Info": "Info",
+	"Input commands": "Voer commando's in",
+	"Install from Github URL": "Installeren vanaf Github-URL",
+	"Instant Auto-Send After Voice Transcription": "Direct automatisch verzenden na spraaktranscriptie",
+	"Interface": "Interface",
+	"Invalid file format.": "Ongeldig bestandsformaat",
+	"Invalid Tag": "Ongeldige Tag",
+	"January": "Januari",
+	"Jina API Key": "Jina API-sleutel",
+	"join our Discord for help.": "join onze Discord voor hulp.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON-voorbeeld",
+	"July": "Juli",
+	"June": "Juni",
+	"JWT Expiration": "JWT Expiration",
+	"JWT Token": "JWT Token",
+	"Keep Alive": "Houd Actief",
+	"Key": "Sleutel",
+	"Keyboard shortcuts": "Toetsenbord snelkoppelingen",
+	"Knowledge": "Kennis",
+	"Knowledge Access": "Kennistoegang",
+	"Knowledge created successfully.": "Kennis succesvol aangemaakt",
+	"Knowledge deleted successfully.": "Kennis succesvol verwijderd",
+	"Knowledge reset successfully.": "Kennis succesvol gereset",
+	"Knowledge updated successfully": "Kennis succesvol bijgewerkt",
+	"Label": "Label",
+	"Landing Page Mode": "Landingspaginamodus",
+	"Language": "Taal",
+	"Last Active": "Laatst Actief",
+	"Last Modified": "Laatst aangepast",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP-server bijgewerkt",
+	"Leaderboard": "Klassement",
+	"Leave empty for unlimited": "Laat leeg voor ongelimiteerd",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Laat leeg om alle modellen van het \"{{URL}}/api/tags\" endpoint toe te voegen",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Laat leeg om alle modellen van het \"{{URL}}/models\" endpoint toe te voegen",
+	"Leave empty to include all models or select specific models": "Laat leeg om alle modellen mee te nemen, of selecteer specifieke modellen",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Laat leeg om de standaard prompt te gebruiken, of selecteer een aangepaste prompt",
+	"Light": "Licht",
+	"Listening...": "Aan het luisteren...",
+	"LLMs can make mistakes. Verify important information.": "LLMs kunnen fouten maken. Verifieer belangrijke informatie.",
+	"Local": "Lokaal",
+	"Local Models": "Lokale modellen",
+	"Lost": "Verloren",
+	"LTR": "LNR",
+	"Made by OpenWebUI Community": "Gemaakt door OpenWebUI Community",
+	"Make sure to enclose them with": "Zorg ervoor dat je ze omringt met",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Zorg ervoor dat je een workflow.json-bestand als API-formaat exporteert vanuit ComfyUI.",
+	"Manage": "Beheren",
+	"Manage Arena Models": "Beheer srenamodellen",
+	"Manage Ollama": "Beheer Ollama",
+	"Manage Ollama API Connections": "Beheer Ollama API-verbindingen",
+	"Manage OpenAI API Connections": "Beheer OpenAI API-verbindingen",
+	"Manage Pipelines": "Pijplijnen beheren",
+	"March": "Maart",
+	"Max Tokens (num_predict)": "Max Tokens (num_predict)",
+	"Max Upload Count": "Maximale Uploadhoeveelheid",
+	"Max Upload Size": "Maximale Uploadgrootte",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maximaal 3 modellen kunnen tegelijkertijd worden gedownload. Probeer het later opnieuw.",
+	"May": "Mei",
+	"Memories accessible by LLMs will be shown here.": "Geheugen toegankelijk voor LLMs wordt hier getoond.",
+	"Memory": "Geheugen",
+	"Memory added successfully": "Geheugen succesvol toegevoegd",
+	"Memory cleared successfully": "Geheugen succesvol vrijgemaakt",
+	"Memory deleted successfully": "Geheugen succesvol verwijderd",
+	"Memory updated successfully": "Geheugen succesvol bijgewerkt",
+	"Merge Responses": "Voeg antwoorden samen",
+	"Message rating should be enabled to use this feature": "Berichtbeoordeling moet ingeschakeld zijn om deze functie te gebruiken",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Berichten die je verzendt nadat je jouw link hebt gemaakt, worden niet gedeeld. Gebruikers met de URL kunnen de gedeelde chat bekijken.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimale score",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Model",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' is succesvol gedownload.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' staat al in de wachtrij voor downloaden.",
+	"Model {{modelId}} not found": "Model {{modelId}} niet gevonden",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} is niet geschikt voor visie",
+	"Model {{name}} is now {{status}}": "Model {{name}} is nu {{status}}",
+	"Model accepts image inputs": "Model accepteerd afbeeldingsinvoer",
+	"Model created successfully!": "Model succesvol gecreëerd",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model filesystem path gedetecteerd. Model shortname is vereist voor update, kan niet doorgaan.",
+	"Model Filtering": "Modelfiltratie",
+	"Model ID": "Model-ID",
+	"Model IDs": "Model-IDs",
+	"Model Name": "Modelnaam",
+	"Model not selected": "Model niet geselecteerd",
+	"Model Params": "Modelparams",
+	"Model Permissions": "Modeltoestemmingen",
+	"Model updated successfully": "Model succesvol bijgewerkt",
+	"Modelfile Content": "Modelfile Inhoud",
+	"Models": "Modellen",
+	"Models Access": "Modellentoegang",
+	"Models configuration saved successfully": "Modellenconfiguratie succeslvol opgeslagen",
+	"Mojeek Search API Key": "Mojeek Search API-sleutel",
+	"more": "Meer",
+	"More": "Meer",
+	"Name": "Naam",
+	"Name your knowledge base": "Geef je kennisbasis een naam",
+	"New Chat": "Nieuwe Chat",
+	"New folder": "Nieuwe map",
+	"New Password": "Nieuw Wachtwoord",
+	"No content found": "Geen content gevonden",
+	"No content to speak": "Geen inhoud om over te spreken",
+	"No distance available": "Geen afstand beschikbaar",
+	"No feedbacks found": "Geen feedback gevonden",
+	"No file selected": "Geen bestand geselecteerd",
+	"No files found.": "Geen bestanden gevonden",
+	"No groups with access, add a group to grant access": "Geen groepen met toegang, voeg een groep toe om toegang te geven",
+	"No HTML, CSS, or JavaScript content found.": "Geen HTML, CSS, of JavaScript inhoud gevonden",
+	"No knowledge found": "Geen kennis gevonden",
+	"No model IDs": "Geen model-ID's",
+	"No models found": "Geen modellen gevonden",
+	"No models selected": "Geen modellen geselecteerd",
+	"No results found": "Geen resultaten gevonden",
+	"No search query generated": "Geen zoekopdracht gegenereerd",
+	"No source available": "Geen bron beschikbaar",
+	"No users were found.": "Geen gebruikers gevonden",
+	"No valves to update": "Geen kleppen om bij te werken",
+	"None": "Geen",
+	"Not factually correct": "Niet feitelijk juist",
+	"Not helpful": "Niet nuttig",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Opmerking: Als je een minimumscore instelt, levert de zoekopdracht alleen documenten op met een score groter dan of gelijk aan de minimumscore.",
+	"Notes": "Aantekeningen",
+	"Notifications": "Notificaties",
+	"November": "November",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Oktober",
+	"Off": "Uit",
+	"Okay, Let's Go!": "Oké, laten we gaan!",
+	"OLED Dark": "OLED Donker",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API uitgeschakeld",
+	"Ollama API settings updated": "Ollama API-instellingen bijgewerkt",
+	"Ollama Version": "Ollama Versie",
+	"On": "Aan",
+	"Only alphanumeric characters and hyphens are allowed": "Alleen alfanumerieke tekens en koppeltekens zijn toegestaan",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Alleen alfanumerieke karakters en streepjes zijn toegestaan in de commando string.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Alleen verzamelinge kunnen gewijzigd worden, maak een nieuwe kennisbank aan om bestanden aan te passen/toe te voegen",
+	"Only select users and groups with permission can access": "Alleen geselecteerde gebruikers en groepen met toestemming hebben toegang",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oeps! Het lijkt erop dat de URL ongeldig is. Controleer het nogmaals en probeer opnieuw.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Oeps! Er zijn nog bestanden aan het uploaden. Wacht tot het uploaden voltooid is.",
+	"Oops! There was an error in the previous response.": "Oeps! Er was een fout in de vorige reactie.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oeps! Je gebruikt een niet-ondersteunde methode (alleen frontend). Serveer de WebUI vanuit de backend.",
+	"Open file": "Open bestand",
+	"Open in full screen": "Open in volledig scherm",
+	"Open new chat": "Open nieuwe chat",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI gebruikt faster-whisper intern",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI gebruikt SpeechT5 en CMU Arctic spreker-embeddings",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open WebUI versie (v{{OPEN_WEBUI_VERSION}}) is kleiner dan de benodigde versie (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API-configuratie",
+	"OpenAI API Key is required.": "OpenAI API-sleutel is verplicht",
+	"OpenAI API settings updated": "OpenAI API-sleutel bijgewerkt",
+	"OpenAI URL/Key required.": "OpenAI URL/Sleutel vereist.",
+	"or": "of",
+	"Organize your users": "Orden je gebruikers",
+	"Other": "Andere",
+	"OUTPUT": "UITVOER",
+	"Output format": "Uitvoerformaat",
+	"Overview": "Overzicht",
+	"page": "pagina",
+	"Password": "Wachtwoord",
+	"Paste Large Text as File": "Plak grote tekst als bestand",
+	"PDF document (.pdf)": "PDF document (.pdf)",
+	"PDF Extract Images (OCR)": "PDF extraheer afbeeldingen (OCR)",
+	"pending": "wachtend",
+	"Permission denied when accessing media devices": "Toegang geweigerd bij het toegang krijgen tot media-apparaten",
+	"Permission denied when accessing microphone": "Toegang geweigerd bij het toegang krijgen tot de microfoon",
+	"Permission denied when accessing microphone: {{error}}": "Toestemming geweigerd bij toegang tot microfoon: {{error}}",
+	"Permissions": "Toestemmingen",
+	"Personalization": "Personalisatie",
+	"Pin": "Zet vast",
+	"Pinned": "Vastgezet",
+	"Pioneer insights": "Verken inzichten",
+	"Pipeline deleted successfully": "Pijpleiding succesvol verwijderd",
+	"Pipeline downloaded successfully": "Pijpleiding succesvol gedownload",
+	"Pipelines": "Pijpleidingen",
+	"Pipelines Not Detected": "Pijpleiding niet gedetecteerd",
+	"Pipelines Valves": "Pijpleidingen Kleppen",
+	"Plain text (.txt)": "Platte tekst (.txt)",
+	"Playground": "Speeltuin",
+	"Please carefully review the following warnings:": "Beoordeel de volgende waarschuwingen nauwkeurig:",
+	"Please enter a prompt": "Voer een prompt in",
+	"Please fill in all fields.": "Voer alle velden in",
+	"Please select a model first.": "",
+	"Please select a reason": "Voer een reden in",
+	"Port": "Poort",
+	"Positive attitude": "Positieve positie",
+	"Prefix ID": "Voorvoegsel-ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Voorvoegsel-ID wordt gebruikt om conflicten met andere verbindingen te vermijden door een voorvoegsel aan het model-ID toe te voegen - laat leeg om uit te schakelen",
+	"Previous 30 days": "Afgelopen 30 dagen",
+	"Previous 7 days": "Afgelopen 7 dagen",
+	"Profile Image": "Profielafbeelding",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (bv. Vertel me een leuke gebeurtenis over het Romeinse Rijk)",
+	"Prompt Content": "Promptinhoud",
+	"Prompt created successfully": "Prompt succesvol aangemaakt",
+	"Prompt suggestions": "Promptsuggesties",
+	"Prompt updated successfully": "Prompt succesvol bijgewerkt",
+	"Prompts": "Prompts",
+	"Prompts Access": "Prompttoegang",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Haal \"{{searchValue}}\" uit Ollama.com",
+	"Pull a model from Ollama.com": "Haal een model van Ollama.com",
+	"Query Generation Prompt": "Vraaggeneratieprompt",
+	"Query Params": "Vraagparameters",
+	"RAG Template": "RAG-sjabloon",
+	"Rating": "Beoordeling",
+	"Re-rank models by topic similarity": "Herrangschik modellen op basis van onderwerpsovereenkomst",
+	"Read Aloud": "Voorlezen",
+	"Record voice": "Neem stem op",
+	"Redirecting you to OpenWebUI Community": "Je wordt doorgestuurd naar OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Vermindert de kans op het genereren van onzin. Een hogere waarde (bijv. 100) zal meer diverse antwoorden geven, terwijl een lagere waarde (bijv. 10) conservatiever zal zijn. (Standaard: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Refereer naar jezelf als \"user\" (bv. \"User is Spaans aan het leren\"",
+	"References from": "Referenties van",
+	"Refused when it shouldn't have": "Geweigerd terwijl het niet had moeten",
+	"Regenerate": "Regenereren",
+	"Release Notes": "Release-opmerkingen",
+	"Relevance": "Relevantie",
+	"Remove": "Verwijderen",
+	"Remove Model": "Verwijder model",
+	"Rename": "Hernoemen",
+	"Reorder Models": "Herschik modellen",
+	"Repeat Last N": "Herhaal Laatste N",
+	"Request Mode": "Request Modus",
+	"Reranking Model": "Reranking Model",
+	"Reranking model disabled": "Reranking model uitgeschakeld",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking model ingesteld op \"{{reranking_model}}\"",
+	"Reset": "Herstellen",
+	"Reset All Models": "Herstel alle modellen",
+	"Reset Upload Directory": "Herstel Uploadmap",
+	"Reset Vector Storage/Knowledge": "Herstel Vectoropslag/-kennis",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Antwoordmeldingen kunnen niet worden geactiveerd omdat de rechten voor de website zijn geweigerd. Ga naar de instellingen van uw browser om de benodigde toegang te verlenen.",
+	"Response splitting": "Antwoord splitsing",
+	"Result": "Resultaat",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Rijke tekstinvoer voor chatten",
+	"RK": "RK",
+	"Role": "Rol",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RNL",
+	"Run": "Uitvoeren",
+	"Running": "Aan het uitvoeren",
+	"Save": "Opslaan",
+	"Save & Create": "Opslaan & Creëren",
+	"Save & Update": "Opslaan & Bijwerken",
+	"Save As Copy": "Bewaar als kopie",
+	"Save Tag": "Bewaar Tag",
+	"Saved": "Opgeslagen",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Chat logs direct opslaan in de opslag van je browser wordt niet langer ondersteund. Neem even de tijd om je chat logs te downloaden en te verwijderen door op de knop hieronder te klikken. Maak je geen zorgen, je kunt je chat logs eenvoudig opnieuw importeren naar de backend via",
+	"Scroll to bottom when switching between branches": "Scroll naar onderen bij het wisselen tussen takken",
+	"Search": "Zoeken",
+	"Search a model": "Zoek een model",
+	"Search Base": "Zoeken naar basis",
+	"Search Chats": "Chats zoeken",
+	"Search Collection": "Zoek naar verzamelingen",
+	"Search Filters": "Zoek naar filters",
+	"search for tags": "Zoek naar tags",
+	"Search Functions": "Zoek naar functie",
+	"Search Knowledge": "Zoek naar Kennis",
+	"Search Models": "Modellen zoeken",
+	"Search options": "Opties zoeken",
+	"Search Prompts": "Prompts zoeken",
+	"Search Result Count": "Aantal zoekresultaten",
+	"Search the web": "Zoek op het internet",
+	"Search Tools": "Zoek gereedschappen",
+	"SearchApi API Key": "SearchApi API-sleutel",
+	"SearchApi Engine": "SearchApi Engine",
+	"Searched {{count}} sites_one": "Gezocht op {{count}} sites_one",
+	"Searched {{count}} sites_other": "Gezocht op {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" aan het zoeken.",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Zoek kennis bij \"{{searchQuery}}\"",
+	"Searxng Query URL": "Searxng Query URL",
+	"See readme.md for instructions": "Zie readme.md voor instructies",
+	"See what's new": "Zie wat er nieuw is",
+	"Seed": "Seed",
+	"Select a base model": "Selecteer een basismodel",
+	"Select a engine": "Selecteer een engine",
+	"Select a function": "Selecteer een functie",
+	"Select a group": "Selecteer een groep",
+	"Select a model": "Selecteer een model",
+	"Select a pipeline": "Selecteer een pijplijn",
+	"Select a pipeline url": "Selecteer een pijplijn-URL",
+	"Select a tool": "Selecteer een tool",
+	"Select Engine": "Selecteer Engine",
+	"Select Knowledge": "Selecteer kennis",
+	"Select model": "Selecteer een model",
+	"Select only one model to call": "Selecteer maar één model om aan te roepen",
+	"Selected model(s) do not support image inputs": "Geselecteerde modellen ondersteunen geen beeldinvoer",
+	"Semantic distance to query": "Semantische afstand tot query",
+	"Send": "Verzenden",
+	"Send a Message": "Stuur een bericht",
+	"Send message": "Stuur bericht",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Stuurt `stream_options: { include_usage: true }` in het verzoek. \nOndersteunde providers zullen informatie over tokengebruik in het antwoord terugsturen als dit aan staat.",
+	"September": "September",
+	"Serper API Key": "Serper API-sleutel",
+	"Serply API Key": "Serply API-sleutel",
+	"Serpstack API Key": "Serpstack API-sleutel",
+	"Server connection verified": "Server verbinding geverifieerd",
+	"Set as default": "Stel in als standaard",
+	"Set CFG Scale": "Stel CFG-schaal in",
+	"Set Default Model": "Stel Standaardmodel in",
+	"Set embedding model": "Stel embedding-model in",
+	"Set embedding model (e.g. {{model}})": "Stel embedding-model in (bv. {{model}})",
+	"Set Image Size": "Stel afbeeldingsgrootte in",
+	"Set reranking model (e.g. {{model}})": "Stel reranking-model in (bv. {{model}})",
+	"Set Sampler": "Stel Sampler in",
+	"Set Scheduler": "Stel planner in",
+	"Set Steps": "Stel stappen in",
+	"Set Task Model": "Taakmodel instellen",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Stel het aantal GPU apparaten in dat gebruikt wordt voor berekeningen. Deze optie bepaalt hoeveel GPU-apparaten (indien beschikbaar) worden gebruikt om binnenkomende aanvragen te verwerken. Het verhogen van deze waarde kan de prestaties aanzienlijk verbeteren voor modellen die geoptimaliseerd zijn voor GPU-versnelling, maar kan ook meer stroom en GPU-bronnen verbruiken.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Stel het aantal threads in dat wordt gebruikt voor berekeningen. Deze optie bepaalt hoeveel threads worden gebruikt om gelijktijdig binnenkomende verzoeken te verwerken. Het verhogen van deze waarde kan de prestaties verbeteren onder hoge concurrency werklasten, maar kan ook meer CPU-bronnen verbruiken.",
+	"Set Voice": "Stel stem in",
+	"Set whisper model": "Stel Whisper-model in",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Stelt in hoe ver het model terug moet kijken om herhaling te voorkomen. (Standaard: 64, 0 = uitgeschakeld, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Stelt in hoe sterk herhalingen bestraft moeten worden. Een hogere waarde (bijv. 1,5) zal herhalingen sterker bestraffen, terwijl een lagere waarde (bijv. 0,9) milder zal zijn. (Standaard: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Stelt de willekeurigheid in om te gebruiken voor het genereren. Als je dit op een specifiek getal instelt, genereert het model dezelfde tekst voor dezelfde prompt. (Standaard: willekeurig)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Stelt de grootte in van het contextvenster dat wordt gebruikt om het volgende token te genereren. (Standaard: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Stelt de te gebruiken stopsequentie in. Als dit patroon wordt gevonden, stopt de LLM met het genereren van tekst en keert terug. Er kunnen meerdere stoppatronen worden ingesteld door meerdere afzonderlijke stopparameters op te geven in een modelbestand.",
+	"Settings": "Instellingen",
+	"Settings saved successfully!": "Instellingen succesvol opgeslagen!",
+	"Share": "Delen",
+	"Share Chat": "Deel chat",
+	"Share to OpenWebUI Community": "Deel naar OpenWebUI-community",
+	"Show": "Toon",
+	"Show \"What's New\" modal on login": "Toon \"Wat is nieuw\" bij inloggen",
+	"Show Admin Details in Account Pending Overlay": "Admin-details weergeven in overlay in afwachting van account",
+	"Show shortcuts": "Toon snelkoppelingen",
+	"Show your support!": "Toon je steun",
+	"Showcased creativity": "Toonde creativiteit",
+	"Sign in": "Inloggen",
+	"Sign in to {{WEBUI_NAME}}": "Log in bij {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Log in bij {{WEBUI_NAME}} met LDAP",
+	"Sign Out": "Uitloggen",
+	"Sign up": "Registreren",
+	"Sign up to {{WEBUI_NAME}}": "Meld je aan bij {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Aan het inloggen bij {{WEBUI_NAME}}",
+	"Source": "Bron",
+	"Speech Playback Speed": "Afspeelsnelheid spraak",
+	"Speech recognition error: {{error}}": "Spraakherkenning fout: {{error}}",
+	"Speech-to-Text Engine": "Spraak-naar-tekst Engine",
+	"Stop": "Stop",
+	"Stop Sequence": "Stopsequentie",
+	"Stream Chat Response": "Stream chat-antwoord",
+	"STT Model": "STT Model",
+	"STT Settings": "STT Instellingen",
+	"Subtitle (e.g. about the Roman Empire)": "Ondertitel (bijv. over de Romeinse Empire)",
+	"Success": "Succes",
+	"Successfully updated.": "Succesvol bijgewerkt.",
+	"Suggested": "Suggestie",
+	"Support": "Ondersteuning",
+	"Support this plugin:": "ondersteun deze plugin",
+	"Sync directory": "Synchroniseer map",
+	"System": "Systeem",
+	"System Instructions": "Systeem instructies",
+	"System Prompt": "Systeem prompt",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Prompt voor taggeneratie",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling wordt gebruikt om de impact van minder waarschijnlijke tokens uit de uitvoer te verminderen. Een hogere waarde (bv. 2,0) zal de impact meer verminderen, terwijl een waarde van 1,0 deze instelling uitschakelt. (standaard: 1)",
+	"Tap to interrupt": "Tik om te onderbreken",
+	"Tavily API Key": "Tavily API-sleutel",
+	"Tell us more:": "Vertel ons meer:",
+	"Temperature": "Temperatuur",
+	"Template": "Template",
+	"Temporary Chat": "Tijdelijke chat",
+	"Text Splitter": "Tekst splitser",
+	"Text-to-Speech Engine": "Tekst-naar-Spraak Engine",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Bedankt voor je feedback!",
+	"The Application Account DN you bind with for search": "Het applicatieaccount DN waarmee je zoekt",
+	"The base to search for users": "De basis om gebruikers te zoeken",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "De batchgrootte bepaalt hoeveel tekstverzoeken tegelijk worden verwerkt. Een hogere batchgrootte kan de prestaties en snelheid van het model verhogen, maar vereist ook meer geheugen.  (Standaard: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "De ontwikkelaars achter deze plugin zijn gepassioneerde vrijwilligers uit de gemeenschap. Als je deze plugin nuttig vindt, overweeg dan om bij te dragen aan de ontwikkeling ervan.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Het beoordelingsklassement is gebaseerd op het Elo-classificatiesysteem en wordt in realtime bijgewerkt.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "Het LDAP-attribuut dat de gebruikersnaam koppelt die gebruikers gebruiken om in te loggen.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Het leaderboard is momenteel in bèta en we kunnen de ratingberekeningen aanpassen naarmate we het algoritme verfijnen.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "De maximale bestandsgrootte in MB. Als het bestand groter is dan deze limiet, wordt het bestand niet geüpload.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Het maximum aantal bestanden dat in één keer kan worden gebruikt in de chat. Als het aantal bestanden deze limiet overschrijdt, worden de bestanden niet geüpload.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Het score moet een waarde zijn tussen 0.0 (0%) en 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "De temperatuur van het model. Als je de temperatuur verhoogt, zal het model creatiever antwoorden. (Standaard: 0,8)",
+	"Theme": "Thema",
+	"Thinking...": "Aan het denken...",
+	"This action cannot be undone. Do you wish to continue?": "Deze actie kan niet ongedaan worden gemaakt. Wilt u doorgaan?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Dit zorgt ervoor dat je waardevolle gesprekken veilig worden opgeslagen in je backend database. Dank je wel!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Dit is een experimentele functie, het kan functioneren zoals verwacht en kan op elk moment veranderen.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Deze optie bepaalt hoeveel tokens bewaard blijven bij het verversen van de context. Als deze bijvoorbeeld op 2 staat, worden de laatste 2 tekens van de context van het gesprek bewaard. Het behouden van de context kan helpen om de continuïteit van een gesprek te behouden, maar het kan de mogelijkheid om te reageren op nieuwe onderwerpen verminderen. (Standaard: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Deze optie stelt het maximum aantal tokens in dat het model kan genereren in zijn antwoord. Door deze limiet te verhogen, kan het model langere antwoorden geven, maar het kan ook de kans vergroten dat er onbehulpzame of irrelevante inhoud wordt gegenereerd.  (Standaard: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Deze optie verwijdert alle bestaande bestanden in de collectie en vervangt ze door nieuw geüploade bestanden.",
+	"This response was generated by \"{{model}}\"": "Dit antwoord is gegenereerd door  \"{{model}}\"",
+	"This will delete": "Dit zal verwijderen",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dit zal <strong>{{NAME}}</strong> verwijderen en <strong>al zijn inhoud</strong>.",
+	"This will delete all models including custom models": "Dit zal alle modellen, ook aangepaste modellen, verwijderen",
+	"This will delete all models including custom models and cannot be undone.": "Dit zal alle modellen, ook aangepaste modellen, verwijderen en kan niet ontdaan worden",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Dit zal de kennisdatabase resetten en alle bestanden synchroniseren. Wilt u doorgaan?",
+	"Thorough explanation": "Gevorderde uitleg",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Tika Server-URL vereist",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tip: Werk meerdere variabele slots achtereenvolgens bij door op de tab-toets te drukken in de chat input na elke vervanging.",
+	"Title": "Titel",
+	"Title (e.g. Tell me a fun fact)": "Titel (bv. Vertel me een leuke gebeurtenis)",
+	"Title Auto-Generation": "Titel Auto-Generatie",
+	"Title cannot be an empty string.": "Titel kan niet leeg zijn.",
+	"Title Generation Prompt": "Titel Generatie Prompt",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Om de beschikbare modelnamen voor downloaden te openen,",
+	"To access the GGUF models available for downloading,": "Om toegang te krijgen tot de GGUF-modellen die beschikbaar zijn voor downloaden,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Om toegang te krijgen tot de WebUI, neem contact op met de administrator. Beheerders kunnen de gebruikersstatussen beheren vanuit het Beheerderspaneel.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Om hier een kennisbron bij te voegen, voeg ze eerst aan de \"Kennis\" werkplaats toe.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Om je privacy te beschermen, worden alleen beoordelingen, model-ID's, tags en metadata van je feedback gedeeld - je chatlogs blijven privé en worden niet opgenomen.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Om hier acties te selecteren, voeg ze eerst aan de \"Functies\" Werkplaats toe.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Om hier filters te selecteren, voeg ze eerst aan de \"Functies\" Werkplaats toe.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Om hier gereedschapssets te selecteren, voeg ze eerst aan de \"Gereedschappen\" Werkplaats toe.",
+	"Toast notifications for new updates": "Toon notificaties voor nieuwe updates",
+	"Today": "Vandaag",
+	"Toggle settings": "Wissel instellingen",
+	"Toggle sidebar": "Wissel sidebar",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Te bewaren tokens bij contextverversing (num_keep)",
+	"Too verbose": "Te langdradig",
+	"Tool created successfully": "Gereedschap succesvol aangemaakt",
+	"Tool deleted successfully": "Gereedschap succesvol verwijderd",
+	"Tool Description": "Gereedschapbeschrijving",
+	"Tool ID": "Gereedschaps-ID",
+	"Tool imported successfully": "Gereedschap succesvol geïmporteerd",
+	"Tool Name": "Gereedschapsnaam",
+	"Tool updated successfully": "Gereedschap succesvol bijgewerkt",
+	"Tools": "Gereedschappen",
+	"Tools Access": "Gereedschaptoegang",
+	"Tools are a function calling system with arbitrary code execution": "Gereedschappen zijn een systeem voor het aanroepen van functies met willekeurige code-uitvoering",
+	"Tools have a function calling system that allows arbitrary code execution": "Gereedschappen hebben een systeem voor het aanroepen van functies waarmee willekeurige code kan worden uitgevoerd",
+	"Tools have a function calling system that allows arbitrary code execution.": "Gereedschappen hebben een systeem voor het aanroepen van functies waarmee willekeurige code kan worden uitgevoerd",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemen met toegang tot Ollama?",
+	"TTS Model": "TTS Model",
+	"TTS Settings": "TTS instellingen",
+	"TTS Voice": "TTS Stem",
+	"Type": "Type",
+	"Type Hugging Face Resolve (Download) URL": "Type Hugging Face Resolve (Download) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Er was een probleem met verbinden met {{provider}}.",
+	"UI": "UI",
+	"Unarchive All": "Onarchiveer alles",
+	"Unarchive All Archived Chats": "Onarchiveer alle gearchiveerde chats",
+	"Unarchive Chat": "Onarchiveer chat",
+	"Unlock mysteries": "Ontsleutel mysteries",
+	"Unpin": "Losmaken",
+	"Unravel secrets": "Ontrafel geheimen",
+	"Untagged": "Ongemarkeerd",
+	"Update": "Bijwerken",
+	"Update and Copy Link": "Bijwerken en kopieer link",
+	"Update for the latest features and improvements.": "Bijwerken voor de nieuwste functies en verbeteringen",
+	"Update password": "Wijzig wachtwoord",
+	"Updated": "Bijgewerkt",
+	"Updated at": "Bijgewerkt om",
+	"Updated At": "Bijgewerkt om",
+	"Upload": "Uploaden",
+	"Upload a GGUF model": "Upload een GGUF-model",
+	"Upload directory": "Upload map",
+	"Upload files": "Bestanden uploaden",
+	"Upload Files": "Bestanden uploaden",
+	"Upload Pipeline": "Upload Pijpleiding",
+	"Upload Progress": "Upload Voortgang",
+	"URL": "URL",
+	"URL Mode": "URL-modus",
+	"Use '#' in the prompt input to load and include your knowledge.": "Gebruik '#' in de promptinvoer om je kennis te laden en op te nemen.",
+	"Use Gravatar": "Gebruik Gravatar",
+	"Use groups to group your users and assign permissions.": "Gebruik groepen om gebruikers te groeperen en rechten aan te wijzen",
+	"Use Initials": "Gebruik initialen",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "user",
+	"User": "User",
+	"User location successfully retrieved.": "Gebruikerslocatie succesvol opgehaald",
+	"Username": "Gebruikersnaam",
+	"Users": "Gebruikers",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Het standaard arena-model gebruiken met alle modellen. Klik op de plusknop om aangepaste modellen toe te voegen.",
+	"Utilize": "Utilize",
+	"Valid time units:": "Geldige tijdseenheden:",
+	"Valves": "Kleppen",
+	"Valves updated": "Kleppen bijgewerkt",
+	"Valves updated successfully": "Kleppen succesvol bijgewerkt",
+	"variable": "variabele",
+	"variable to have them replaced with clipboard content.": "variabele om ze te laten vervangen door klembord inhoud.",
+	"Version": "Versie",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Versie {{selectedVersion}} van {{totalVersions}}",
+	"Visibility": "Zichtbaarheid",
+	"Voice": "Stem",
+	"Voice Input": "Steminvoer",
+	"Warning": "Waarschuwing",
+	"Warning:": "Waarschuwing",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Waarschuwing: Door dit in te schakelen kunnen gebruikers willekeurige code uploaden naar de server.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Warning: Als je de embedding model bijwerkt of wijzigt, moet je alle documenten opnieuw importeren.",
+	"Web": "Web",
+	"Web API": "Web-API",
+	"Web Loader Settings": "Web Loader instellingen",
+	"Web Search": "Zoeken op het web",
+	"Web Search Engine": "Zoekmachine op het web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI Instellingen",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI zal verzoeken doen aan \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI zal verzoeken doen aan \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Wat probeer je te bereiken?",
+	"What are you working on?": "Waar werk je aan?",
+	"What’s New in": "Wat is nieuw in",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Als dit is ingeschakeld, reageert het model op elk chatbericht in real-time, waarbij een reactie wordt gegenereerd zodra de gebruiker een bericht stuurt. Deze modus is handig voor live chat-toepassingen, maar kan de prestaties op langzamere hardware beïnvloeden.",
+	"wherever you are": "waar je ook bent",
+	"Whisper (Local)": "Whisper (Lokaal)",
+	"Why?": "Waarom?",
+	"Widescreen Mode": "Breedschermmodus",
+	"Won": "Gewonnen",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Werkt samen met top-k. Een hogere waarde (bijv. 0,95) zal leiden tot meer diverse tekst, terwijl een lagere waarde (bijv. 0,5) meer gerichte en conservatieve tekst zal genereren. (Standaard: 0,9)",
+	"Workspace": "Werkruimte",
+	"Workspace Permissions": "Werkruimtemachtigingen",
+	"Write a prompt suggestion (e.g. Who are you?)": "Schrijf een prompt suggestie (bijv. Wie ben je?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Schrijf een samenvatting in 50 woorden die [onderwerp of trefwoord] samenvat.",
+	"Write something...": "Schrijf iets...",
+	"Write your model template content here": "Schrijf je modelsjablooninhoud hier",
+	"Yesterday": "Gisteren",
+	"You": "Jij",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Je kunt slechts met maximaal {{maxCount}} bestand(en) tegelijk chatten",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Je kunt je interacties met LLM's personaliseren door herinneringen toe te voegen via de 'Beheer'-knop hieronder, waardoor ze nuttiger en voor jou op maat gemaakt worden.",
+	"You cannot upload an empty file.": "Je kunt een leeg bestand niet uploaden.",
+	"You do not have permission to upload files.": "Je hebt geen toestemming om bestanden up te loaden",
+	"You have no archived conversations.": "Je hebt geen gearchiveerde gesprekken.",
+	"You have shared this chat": "Je hebt dit gesprek gedeeld",
+	"You're a helpful assistant.": "Je bent een behulpzame assistent.",
+	"You're now logged in.": "Je bent nu ingelogd.",
+	"Your account status is currently pending activation.": "Je accountstatus wacht nu op activatie",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Je volledige bijdrage gaat direct naar de ontwikkelaar van de plugin; Open WebUI neemt hier geen deel van. Het gekozen financieringsplatform kan echter wel zijn eigen kosten hebben.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube-laderinstellingen"
+}
diff --git a/src/lib/i18n/locales/pa-IN/translation.json b/src/lib/i18n/locales/pa-IN/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..99e420c8a06e900fc6aea4f97469601d20746c05
--- /dev/null
+++ b/src/lib/i18n/locales/pa-IN/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'ਸ', 'ਮ', 'ਘੰ', 'ਦ', 'ਹਫ਼ਤਾ' ਜਾਂ '-1' ਬਿਨਾ ਮਿਆਦ ਦੇ।",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(ਉਦਾਹਰਣ ਦੇ ਤੌਰ ਤੇ `sh webui.sh --api`)",
+	"(latest)": "(ਤਾਜ਼ਾ)",
+	"{{ models }}": "{{ ਮਾਡਲ }}",
+	"{{user}}'s Chats": "{{user}} ਦੀਆਂ ਗੱਲਾਂ",
+	"{{webUIName}} Backend Required": "{{webUIName}} ਬੈਕਐਂਡ ਲੋੜੀਂਦਾ ਹੈ",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "ਚੈਟਾਂ ਅਤੇ ਵੈੱਬ ਖੋਜ ਪੁੱਛਗਿੱਛਾਂ ਵਾਸਤੇ ਸਿਰਲੇਖ ਤਿਆਰ ਕਰਨ ਵਰਗੇ ਕਾਰਜ ਾਂ ਨੂੰ ਕਰਦੇ ਸਮੇਂ ਇੱਕ ਕਾਰਜ ਮਾਡਲ ਦੀ ਵਰਤੋਂ ਕੀਤੀ ਜਾਂਦੀ ਹੈ",
+	"a user": "ਇੱਕ ਉਪਭੋਗਤਾ",
+	"About": "ਬਾਰੇ",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "ਖਾਤਾ",
+	"Account Activation Pending": "",
+	"Accurate information": "ਸਹੀ ਜਾਣਕਾਰੀ",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "ਸ਼ਾਮਲ ਕਰੋ",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "ਇਸ ਬਾਰੇ ਇੱਕ ਸੰਖੇਪ ਵੇਰਵਾ ਸ਼ਾਮਲ ਕਰੋ ਕਿ ਇਹ ਮਾਡਲ ਕੀ ਕਰਦਾ ਹੈ",
+	"Add a tag": "ਇੱਕ ਟੈਗ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "ਕਸਟਮ ਪ੍ਰੰਪਟ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add Files": "ਫਾਈਲਾਂ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add Group": "",
+	"Add Memory": "ਮਿਹਾਨ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add Model": "ਮਾਡਲ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add Tag": "",
+	"Add Tags": "ਟੈਗ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add text content": "",
+	"Add User": "ਉਪਭੋਗਤਾ ਸ਼ਾਮਲ ਕਰੋ",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "ਇਹ ਸੈਟਿੰਗਾਂ ਨੂੰ ਠੀਕ ਕਰਨ ਨਾਲ ਸਾਰੇ ਉਪਭੋਗਤਾਵਾਂ ਲਈ ਬਦਲਾਅ ਲਾਗੂ ਹੋਣਗੇ।",
+	"admin": "ਪ੍ਰਬੰਧਕ",
+	"Admin": "",
+	"Admin Panel": "ਪ੍ਰਬੰਧਕ ਪੈਨਲ",
+	"Admin Settings": "ਪ੍ਰਬੰਧਕ ਸੈਟਿੰਗਾਂ",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "ਉੱਚ ਸਤਰ ਦੇ ਪੈਰਾਮੀਟਰ",
+	"Advanced Params": "ਐਡਵਾਂਸਡ ਪਰਮਜ਼",
+	"All chats": "",
+	"All Documents": "ਸਾਰੇ ਡਾਕੂਮੈਂਟ",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "ਗੱਲਬਾਤ ਮਿਟਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "ਪਹਿਲਾਂ ਹੀ ਖਾਤਾ ਹੈ?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "ਇੱਕ ਸਹਾਇਕ",
+	"and": "ਅਤੇ",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "ਅਤੇ ਇੱਕ ਨਵਾਂ ਸਾਂਝਾ ਲਿੰਕ ਬਣਾਓ।",
+	"API Base URL": "API ਬੇਸ URL",
+	"API Key": "API ਕੁੰਜੀ",
+	"API Key created.": "API ਕੁੰਜੀ ਬਣਾਈ ਗਈ।",
+	"API keys": "API ਕੁੰਜੀਆਂ",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "ਅਪ੍ਰੈਲ",
+	"Archive": "ਆਰਕਾਈਵ",
+	"Archive All Chats": "ਸਾਰੀਆਂ ਚੈਟਾਂ ਨੂੰ ਆਰਕਾਈਵ ਕਰੋ",
+	"Archived Chats": "ਆਰਕਾਈਵ ਕੀਤੀਆਂ ਗੱਲਾਂ",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "ਕੀ ਤੁਸੀਂ ਯਕੀਨਨ ਹੋ?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "ਫਾਈਲ ਜੋੜੋ",
+	"Attention to detail": "ਵੇਰਵੇ 'ਤੇ ਧਿਆਨ",
+	"Attribute for Username": "",
+	"Audio": "ਆਡੀਓ",
+	"August": "ਅਗਸਤ",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "ਜਵਾਬ ਆਟੋ ਕਾਪੀ ਕਲਿੱਪਬੋਰਡ 'ਤੇ",
+	"Auto-playback response": "ਆਟੋ-ਪਲੇਬੈਕ ਜਵਾਬ",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 ਬੇਸ URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 ਬੇਸ URL ਦੀ ਲੋੜ ਹੈ।",
+	"Available list": "",
+	"available!": "ਉਪਲਬਧ ਹੈ!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "ਵਾਪਸ",
+	"Bad Response": "ਖਰਾਬ ਜਵਾਬ",
+	"Banners": "ਬੈਨਰ",
+	"Base Model (From)": "ਬੇਸ ਮਾਡਲ (ਤੋਂ)",
+	"Batch Size (num_batch)": "",
+	"before": "ਪਹਿਲਾਂ",
+	"Being lazy": "ਆਲਸੀ ਹੋਣਾ",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "ਬਹਾਦਰ ਖੋਜ API ਕੁੰਜੀ",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "ਵੈਬਸਾਈਟਾਂ ਲਈ SSL ਪ੍ਰਮਾਣਿਕਤਾ ਨੂੰ ਬਾਈਪਾਸ ਕਰੋ",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "ਰੱਦ ਕਰੋ",
+	"Capabilities": "ਸਮਰੱਥਾਵਾਂ",
+	"Certificate Path": "",
+	"Change Password": "ਪਾਸਵਰਡ ਬਦਲੋ",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "ਗੱਲਬਾਤ",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "ਗੱਲਬਾਤ ਬਬਲ UI",
+	"Chat Controls": "",
+	"Chat direction": "ਗੱਲਬਾਤ ਡਿਰੈਕਟਨ",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "ਗੱਲਾਂ",
+	"Check Again": "ਮੁੜ ਜਾਂਚ ਕਰੋ",
+	"Check for updates": "ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ",
+	"Checking for updates...": "ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਰਿਹਾ ਹੈ...",
+	"Choose a model before saving...": "ਸੰਭਾਲਣ ਤੋਂ ਪਹਿਲਾਂ ਇੱਕ ਮਾਡਲ ਚੁਣੋ...",
+	"Chunk Overlap": "ਚੰਕ ਓਵਰਲੈਪ",
+	"Chunk Params": "ਚੰਕ ਪੈਰਾਮੀਟਰ",
+	"Chunk Size": "ਚੰਕ ਆਕਾਰ",
+	"Ciphers": "",
+	"Citation": "ਹਵਾਲਾ",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "ਮਦਦ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।",
+	"Click here to": "ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "ਚੁਣਨ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ",
+	"Click here to select a csv file.": "CSV ਫਾਈਲ ਚੁਣਨ ਲਈ ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "ਇੱਥੇ ਕਲਿੱਕ ਕਰੋ।",
+	"Click on the user role button to change a user's role.": "ਉਪਭੋਗਤਾ ਦੀ ਭੂਮਿਕਾ ਬਦਲਣ ਲਈ ਉਪਭੋਗਤਾ ਭੂਮਿਕਾ ਬਟਨ 'ਤੇ ਕਲਿੱਕ ਕਰੋ।",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "ਕਲੋਨ",
+	"Close": "ਬੰਦ ਕਰੋ",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "ਸੰਗ੍ਰਹਿ",
+	"Color": "",
+	"ComfyUI": "ਕੰਫੀਯੂਆਈ",
+	"ComfyUI Base URL": "ਕੰਫੀਯੂਆਈ ਬੇਸ URL",
+	"ComfyUI Base URL is required.": "ਕੰਫੀਯੂਆਈ ਬੇਸ URL ਦੀ ਲੋੜ ਹੈ।",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "ਕਮਾਂਡ",
+	"Completions": "",
+	"Concurrent Requests": "ਸਮਕਾਲੀ ਬੇਨਤੀਆਂ",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "ਪਾਸਵਰਡ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ",
+	"Confirm your action": "",
+	"Connections": "ਕਨੈਕਸ਼ਨ",
+	"Contact Admin for WebUI Access": "",
+	"Content": "ਸਮੱਗਰੀ",
+	"Content Extraction": "",
+	"Context Length": "ਸੰਦਰਭ ਲੰਬਾਈ",
+	"Continue Response": "ਜਵਾਬ ਜਾਰੀ ਰੱਖੋ",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "ਸਾਂਝੇ ਕੀਤੇ ਗੱਲਬਾਤ URL ਨੂੰ ਕਲਿੱਪਬੋਰਡ 'ਤੇ ਕਾਪੀ ਕਰ ਦਿੱਤਾ!",
+	"Copied to clipboard": "",
+	"Copy": "ਕਾਪੀ ਕਰੋ",
+	"Copy last code block": "ਆਖਰੀ ਕੋਡ ਬਲਾਕ ਨੂੰ ਕਾਪੀ ਕਰੋ",
+	"Copy last response": "ਆਖਰੀ ਜਵਾਬ ਨੂੰ ਕਾਪੀ ਕਰੋ",
+	"Copy Link": "ਲਿੰਕ ਕਾਪੀ ਕਰੋ",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "ਕਲਿੱਪਬੋਰਡ 'ਤੇ ਕਾਪੀ ਕਰਨਾ ਸਫਲ ਰਿਹਾ!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "ਇੱਕ ਮਾਡਲ ਬਣਾਓ",
+	"Create Account": "ਖਾਤਾ ਬਣਾਓ",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "ਨਵੀਂ ਕੁੰਜੀ ਬਣਾਓ",
+	"Create new secret key": "ਨਵੀਂ ਗੁਪਤ ਕੁੰਜੀ ਬਣਾਓ",
+	"Created at": "ਤੇ ਬਣਾਇਆ ਗਿਆ",
+	"Created At": "ਤੇ ਬਣਾਇਆ ਗਿਆ",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "ਮੌਜੂਦਾ ਮਾਡਲ",
+	"Current Password": "ਮੌਜੂਦਾ ਪਾਸਵਰਡ",
+	"Custom": "ਕਸਟਮ",
+	"Dark": "ਗੂੜ੍ਹਾ",
+	"Database": "ਡਾਟਾਬੇਸ",
+	"December": "ਦਸੰਬਰ",
+	"Default": "ਮੂਲ",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "ਮੂਲ (ਸੈਂਟੈਂਸਟ੍ਰਾਂਸਫਾਰਮਰਸ)",
+	"Default Model": "ਡਿਫਾਲਟ ਮਾਡਲ",
+	"Default model updated": "ਮੂਲ ਮਾਡਲ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "ਮੂਲ ਪ੍ਰੰਪਟ ਸੁਝਾਅ",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "ਮੂਲ ਉਪਭੋਗਤਾ ਭੂਮਿਕਾ",
+	"Delete": "ਮਿਟਾਓ",
+	"Delete a model": "ਇੱਕ ਮਾਡਲ ਮਿਟਾਓ",
+	"Delete All Chats": "ਸਾਰੀਆਂ ਚੈਟਾਂ ਨੂੰ ਮਿਟਾਓ",
+	"Delete All Models": "",
+	"Delete chat": "ਗੱਲਬਾਤ ਮਿਟਾਓ",
+	"Delete Chat": "ਗੱਲਬਾਤ ਮਿਟਾਓ",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "ਇਸ ਲਿੰਕ ਨੂੰ ਮਿਟਾਓ",
+	"Delete tool?": "",
+	"Delete User": "ਉਪਭੋਗਤਾ ਮਿਟਾਓ",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} ਮਿਟਾਇਆ ਗਿਆ",
+	"Deleted {{name}}": "ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "ਵਰਣਨਾ",
+	"Didn't fully follow instructions": "ਹਦਾਇਤਾਂ ਨੂੰ ਪੂਰੀ ਤਰ੍ਹਾਂ ਫਾਲੋ ਨਹੀਂ ਕੀਤਾ",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "ਇੱਕ ਮਾਡਲ ਲੱਭੋ",
+	"Discover a prompt": "ਇੱਕ ਪ੍ਰੰਪਟ ਖੋਜੋ",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "ਕਸਟਮ ਪ੍ਰੰਪਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "ਮਾਡਲ ਪ੍ਰੀਸੈਟਾਂ ਨੂੰ ਖੋਜੋ, ਡਾਊਨਲੋਡ ਕਰੋ ਅਤੇ ਪੜਚੋਲ ਕਰੋ",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "ਗੱਲਬਾਤ 'ਚ ਤੁਹਾਡੇ ਸਥਾਨ 'ਤੇ ਉਪਭੋਗਤਾ ਨਾਮ ਦਿਖਾਓ",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "ਡਾਕੂਮੈਂਟ",
+	"Documentation": "",
+	"Documents": "ਡਾਕੂਮੈਂਟ",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ਕੋਈ ਬਾਹਰੀ ਕਨੈਕਸ਼ਨ ਨਹੀਂ ਬਣਾਉਂਦਾ, ਅਤੇ ਤੁਹਾਡਾ ਡਾਟਾ ਤੁਹਾਡੇ ਸਥਾਨਕ ਸਰਵਰ 'ਤੇ ਸੁਰੱਖਿਅਤ ਰਹਿੰਦਾ ਹੈ।",
+	"Don't have an account?": "ਖਾਤਾ ਨਹੀਂ ਹੈ?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "ਸਟਾਈਲ ਪਸੰਦ ਨਹੀਂ ਹੈ",
+	"Done": "",
+	"Download": "ਡਾਊਨਲੋਡ",
+	"Download canceled": "ਡਾਊਨਲੋਡ ਰੱਦ ਕੀਤਾ ਗਿਆ",
+	"Download Database": "ਡਾਟਾਬੇਸ ਡਾਊਨਲੋਡ ਕਰੋ",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "ਗੱਲਬਾਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਕੋਈ ਵੀ ਫਾਈਲ ਇੱਥੇ ਛੱਡੋ",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "ਉਦਾਹਰਣ ਲਈ '30ਸ','10ਮਿ'. ਸਹੀ ਸਮਾਂ ਇਕਾਈਆਂ ਹਨ 'ਸ', 'ਮ', 'ਘੰ'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "ਸੰਪਾਦਨ ਕਰੋ",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "ਉਪਭੋਗਤਾ ਸੰਪਾਦਨ ਕਰੋ",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "ਈਮੇਲ",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "ਐਮਬੈੱਡਿੰਗ ਮਾਡਲ",
+	"Embedding Model Engine": "ਐਮਬੈੱਡਿੰਗ ਮਾਡਲ ਇੰਜਣ",
+	"Embedding model set to \"{{embedding_model}}\"": "ਐਮਬੈੱਡਿੰਗ ਮਾਡਲ ਨੂੰ \"{{embedding_model}}\" 'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "ਕਮਿਊਨਿਟੀ ਸ਼ੇਅਰਿੰਗ ਨੂੰ ਸਮਰੱਥ ਕਰੋ",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "ਨਵੇਂ ਸਾਈਨ ਅਪ ਯੋਗ ਕਰੋ",
+	"Enable Web Search": "ਵੈੱਬ ਖੋਜ ਨੂੰ ਸਮਰੱਥ ਕਰੋ",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "ਸੁਨਿਸ਼ਚਿਤ ਕਰੋ ਕਿ ਤੁਹਾਡੀ CSV ਫਾਈਲ ਵਿੱਚ ਇਸ ਕ੍ਰਮ ਵਿੱਚ 4 ਕਾਲਮ ਹਨ: ਨਾਮ, ਈਮੇਲ, ਪਾਸਵਰਡ, ਭੂਮਿਕਾ।",
+	"Enter {{role}} message here": "{{role}} ਸੁਨੇਹਾ ਇੱਥੇ ਦਰਜ ਕਰੋ",
+	"Enter a detail about yourself for your LLMs to recall": "ਤੁਹਾਡੇ LLMs ਨੂੰ ਸੁਨੇਹਾ ਕਰਨ ਲਈ ਸੁਨੇਹਾ ਇੱਥੇ ਦਰਜ ਕਰੋ",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "ਬਹਾਦਰ ਖੋਜ API ਕੁੰਜੀ ਦਾਖਲ ਕਰੋ",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "ਚੰਕ ਓਵਰਲੈਪ ਦਰਜ ਕਰੋ",
+	"Enter Chunk Size": "ਚੰਕ ਆਕਾਰ ਦਰਜ ਕਰੋ",
+	"Enter description": "",
+	"Enter Github Raw URL": "Github ਕੱਚਾ URL ਦਾਖਲ ਕਰੋ",
+	"Enter Google PSE API Key": "Google PSE API ਕੁੰਜੀ ਦਾਖਲ ਕਰੋ",
+	"Enter Google PSE Engine Id": "Google PSE ਇੰਜਣ ID ਦਾਖਲ ਕਰੋ",
+	"Enter Image Size (e.g. 512x512)": "ਚਿੱਤਰ ਆਕਾਰ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "ਭਾਸ਼ਾ ਕੋਡ ਦਰਜ ਕਰੋ",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "ਮਾਡਲ ਟੈਗ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "ਕਦਮਾਂ ਦੀ ਗਿਣਤੀ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "ਸਕੋਰ ਦਰਜ ਕਰੋ",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Searxng Query URL ਦਾਖਲ ਕਰੋ",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper API ਕੁੰਜੀ ਦਾਖਲ ਕਰੋ",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Serpstack API ਕੁੰਜੀ ਦਾਖਲ ਕਰੋ",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "ਰੋਕਣ ਦਾ ਕ੍ਰਮ ਦਰਜ ਕਰੋ",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "ਸਿਖਰ K ਦਰਜ ਕਰੋ",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "URL ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "URL ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ http://localhost:11434)",
+	"Enter Your Email": "ਆਪਣੀ ਈਮੇਲ ਦਰਜ ਕਰੋ",
+	"Enter Your Full Name": "ਆਪਣਾ ਪੂਰਾ ਨਾਮ ਦਰਜ ਕਰੋ",
+	"Enter your message": "",
+	"Enter Your Password": "ਆਪਣਾ ਪਾਸਵਰਡ ਦਰਜ ਕਰੋ",
+	"Enter Your Role": "ਆਪਣੀ ਭੂਮਿਕਾ ਦਰਜ ਕਰੋ",
+	"Enter Your Username": "",
+	"Error": "ਗਲਤੀ",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "ਪਰਮਾਣੂਕ੍ਰਿਤ",
+	"Explore the cosmos": "",
+	"Export": "ਨਿਰਯਾਤ",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "ਸਾਰੀਆਂ ਗੱਲਾਂ ਨਿਰਯਾਤ ਕਰੋ (ਸਾਰੇ ਉਪਭੋਗਤਾ)",
+	"Export chat (.json)": "",
+	"Export Chats": "ਗੱਲਾਂ ਨਿਰਯਾਤ ਕਰੋ",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "ਨਿਰਯਾਤ ਮਾਡਲ",
+	"Export Presets": "",
+	"Export Prompts": "ਪ੍ਰੰਪਟ ਨਿਰਯਾਤ ਕਰੋ",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "API ਕੁੰਜੀ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ।",
+	"Failed to read clipboard contents": "ਕਲਿੱਪਬੋਰਡ ਸਮੱਗਰੀ ਪੜ੍ਹਣ ਵਿੱਚ ਅਸਫਲ",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "ਫਰਵਰੀ",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "ਖੁੱਲ੍ਹੇ ਦਿਲ ਨਾਲ ਖਾਸ ਵੇਰਵੇ ਸ਼ਾਮਲ ਕਰੋ",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "ਫਾਈਲ ਮੋਡ",
+	"File not found.": "ਫਾਈਲ ਨਹੀਂ ਮਿਲੀ।",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "ਫਿੰਗਰਪ੍ਰਿੰਟ ਸਪੂਫਿੰਗ ਪਾਈ ਗਈ: ਅਵਤਾਰ ਵਜੋਂ ਸ਼ੁਰੂਆਤੀ ਅੱਖਰ ਵਰਤਣ ਵਿੱਚ ਅਸਮਰੱਥ। ਮੂਲ ਪ੍ਰੋਫਾਈਲ ਚਿੱਤਰ 'ਤੇ ਡਿਫਾਲਟ।",
+	"Fluidly stream large external response chunks": "ਵੱਡੇ ਬਾਹਰੀ ਜਵਾਬ ਚੰਕਾਂ ਨੂੰ ਸਹੀ ਢੰਗ ਨਾਲ ਸਟ੍ਰੀਮ ਕਰੋ",
+	"Focus chat input": "ਗੱਲਬਾਤ ਇਨਪੁਟ 'ਤੇ ਧਿਆਨ ਦਿਓ",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "ਹਦਾਇਤਾਂ ਨੂੰ ਬਿਲਕੁਲ ਫਾਲੋ ਕੀਤਾ",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "ਬਾਰੰਬਾਰਤਾ ਜੁਰਮਾਨਾ",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "ਆਮ",
+	"General Settings": "ਆਮ ਸੈਟਿੰਗਾਂ",
+	"Generate Image": "",
+	"Generating search query": "ਖੋਜ ਪੁੱਛਗਿੱਛ ਤਿਆਰ ਕਰਨਾ",
+	"Generation Info": "ਜਨਰੇਸ਼ਨ ਜਾਣਕਾਰੀ",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "ਵਧੀਆ ਜਵਾਬ",
+	"Google PSE API Key": "Google PSE API ਕੁੰਜੀ",
+	"Google PSE Engine Id": "ਗੂਗਲ PSE ਇੰਜਣ ID",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "ਹ:ਮਿੰਟ ਪੂਃ",
+	"Haptic Feedback": "",
+	"has no conversations.": "ਕੋਈ ਗੱਲਬਾਤ ਨਹੀਂ ਹੈ।",
+	"Hello, {{name}}": "ਸਤ ਸ੍ਰੀ ਅਕਾਲ, {{name}}",
+	"Help": "ਮਦਦ",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "ਲੁਕਾਓ",
+	"Host": "",
+	"How can I help you today?": "ਮੈਂ ਅੱਜ ਤੁਹਾਡੀ ਕਿਵੇਂ ਮਦਦ ਕਰ ਸਕਦਾ ਹਾਂ?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "ਹਾਈਬ੍ਰਿਡ ਖੋਜ",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "ਚਿੱਤਰ ਜਨਰੇਸ਼ਨ (ਪਰਮਾਣੂਕ੍ਰਿਤ)",
+	"Image Generation Engine": "ਚਿੱਤਰ ਜਨਰੇਸ਼ਨ ਇੰਜਣ",
+	"Image Settings": "ਚਿੱਤਰ ਸੈਟਿੰਗਾਂ",
+	"Images": "ਚਿੱਤਰ",
+	"Import Chats": "ਗੱਲਾਂ ਆਯਾਤ ਕਰੋ",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "ਮਾਡਲ ਆਯਾਤ ਕਰੋ",
+	"Import Presets": "",
+	"Import Prompts": "ਪ੍ਰੰਪਟ ਆਯਾਤ ਕਰੋ",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "ਸਟੇਬਲ-ਡਿਫਿਊਸ਼ਨ-ਵੈਬਯੂਆਈ ਚਲਾਉਣ ਸਮੇਂ `--api` ਝੰਡਾ ਸ਼ਾਮਲ ਕਰੋ",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "ਜਾਣਕਾਰੀ",
+	"Input commands": "ਇਨਪੁਟ ਕਮਾਂਡਾਂ",
+	"Install from Github URL": "Github URL ਤੋਂ ਇੰਸਟਾਲ ਕਰੋ",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "ਇੰਟਰਫੇਸ",
+	"Invalid file format.": "",
+	"Invalid Tag": "ਗਲਤ ਟੈਗ",
+	"January": "ਜਨਵਰੀ",
+	"Jina API Key": "",
+	"join our Discord for help.": "ਮਦਦ ਲਈ ਸਾਡੇ ਡਿਸਕੋਰਡ ਵਿੱਚ ਸ਼ਾਮਲ ਹੋਵੋ।",
+	"JSON": "JSON",
+	"JSON Preview": "JSON ਪੂਰਵ-ਦਰਸ਼ਨ",
+	"July": "ਜੁਲਾਈ",
+	"June": "ਜੂਨ",
+	"JWT Expiration": "JWT ਮਿਆਦ ਖਤਮ",
+	"JWT Token": "JWT ਟੋਕਨ",
+	"Keep Alive": "ਜੀਵਿਤ ਰੱਖੋ",
+	"Key": "",
+	"Keyboard shortcuts": "ਕੀਬੋਰਡ ਸ਼ਾਰਟਕਟ",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "ਭਾਸ਼ਾ",
+	"Last Active": "ਆਖਰੀ ਸਰਗਰਮ",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "ਹਲਕਾ",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLMs ਗਲਤੀਆਂ ਕਰ ਸਕਦੇ ਹਨ। ਮਹੱਤਵਪੂਰਨ ਜਾਣਕਾਰੀ ਦੀ ਪੁਸ਼ਟੀ ਕਰੋ।",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "ਓਪਨਵੈਬਯੂਆਈ ਕਮਿਊਨਿਟੀ ਦੁਆਰਾ ਬਣਾਇਆ ਗਿਆ",
+	"Make sure to enclose them with": "ਸੁਨਿਸ਼ਚਿਤ ਕਰੋ ਕਿ ਉਨ੍ਹਾਂ ਨੂੰ ਘੇਰੋ",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "ਪਾਈਪਲਾਈਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ",
+	"March": "ਮਾਰਚ",
+	"Max Tokens (num_predict)": "ਮੈਕਸ ਟੋਕਨ (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "ਇੱਕ ਸਮੇਂ ਵਿੱਚ ਵੱਧ ਤੋਂ ਵੱਧ 3 ਮਾਡਲ ਡਾਊਨਲੋਡ ਕੀਤੇ ਜਾ ਸਕਦੇ ਹਨ। ਕਿਰਪਾ ਕਰਕੇ ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
+	"May": "ਮਈ",
+	"Memories accessible by LLMs will be shown here.": "LLMs ਲਈ ਸਮਰੱਥ ਕਾਰਨ ਇੱਕ ਸੂਚਨਾ ਨੂੰ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ।",
+	"Memory": "ਮੀਮਰ",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "ਤੁਹਾਡਾ ਲਿੰਕ ਬਣਾਉਣ ਤੋਂ ਬਾਅਦ ਤੁਹਾਡੇ ਵੱਲੋਂ ਭੇਜੇ ਗਏ ਸੁਨੇਹੇ ਸਾਂਝੇ ਨਹੀਂ ਕੀਤੇ ਜਾਣਗੇ। URL ਵਾਲੇ ਉਪਭੋਗਤਾ ਸਾਂਝੀ ਚੈਟ ਨੂੰ ਵੇਖ ਸਕਣਗੇ।",
+	"Min P": "",
+	"Minimum Score": "ਘੱਟੋ-ਘੱਟ ਸਕੋਰ",
+	"Mirostat": "ਮਿਰੋਸਟੈਟ",
+	"Mirostat Eta": "ਮਿਰੋਸਟੈਟ ਈਟਾ",
+	"Mirostat Tau": "ਮਿਰੋਸਟੈਟ ਟਾਉ",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "ਮਾਡਲ '{{modelName}}' ਸਫਲਤਾਪੂਰਵਕ ਡਾਊਨਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ।",
+	"Model '{{modelTag}}' is already in queue for downloading.": "ਮਾਡਲ '{{modelTag}}' ਪਹਿਲਾਂ ਹੀ ਡਾਊਨਲੋਡ ਲਈ ਕਤਾਰ ਵਿੱਚ ਹੈ।",
+	"Model {{modelId}} not found": "ਮਾਡਲ {{modelId}} ਨਹੀਂ ਮਿਲਿਆ",
+	"Model {{modelName}} is not vision capable": "ਮਾਡਲ {{modelName}} ਦ੍ਰਿਸ਼ਟੀ ਸਮਰੱਥ ਨਹੀਂ ਹੈ",
+	"Model {{name}} is now {{status}}": "ਮਾਡਲ {{name}} ਹੁਣ {{status}} ਹੈ",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "ਮਾਡਲ ਫਾਈਲਸਿਸਟਮ ਪੱਥ ਪਾਇਆ ਗਿਆ। ਅੱਪਡੇਟ ਲਈ ਮਾਡਲ ਸ਼ੌਰਟਨੇਮ ਦੀ ਲੋੜ ਹੈ, ਜਾਰੀ ਨਹੀਂ ਰੱਖ ਸਕਦੇ।",
+	"Model Filtering": "",
+	"Model ID": "ਮਾਡਲ ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "ਮਾਡਲ ਚੁਣਿਆ ਨਹੀਂ ਗਿਆ",
+	"Model Params": "ਮਾਡਲ ਪਰਮਜ਼",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "ਮਾਡਲਫਾਈਲ ਸਮੱਗਰੀ",
+	"Models": "ਮਾਡਲ",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "ਹੋਰ",
+	"Name": "ਨਾਮ",
+	"Name your knowledge base": "",
+	"New Chat": "ਨਵੀਂ ਗੱਲਬਾਤ",
+	"New folder": "",
+	"New Password": "ਨਵਾਂ ਪਾਸਵਰਡ",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "ਕੋਈ ਨਤੀਜੇ ਨਹੀਂ ਮਿਲੇ",
+	"No search query generated": "ਕੋਈ ਖੋਜ ਪੁੱਛਗਿੱਛ ਤਿਆਰ ਨਹੀਂ ਕੀਤੀ ਗਈ",
+	"No source available": "ਕੋਈ ਸਰੋਤ ਉਪਲਬਧ ਨਹੀਂ",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "ਕੋਈ ਨਹੀਂ",
+	"Not factually correct": "ਤੱਥਕ ਰੂਪ ਵਿੱਚ ਸਹੀ ਨਹੀਂ",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "ਨੋਟ: ਜੇ ਤੁਸੀਂ ਘੱਟੋ-ਘੱਟ ਸਕੋਰ ਸੈੱਟ ਕਰਦੇ ਹੋ, ਤਾਂ ਖੋਜ ਸਿਰਫ਼ ਉਹੀ ਡਾਕੂਮੈਂਟ ਵਾਪਸ ਕਰੇਗੀ ਜਿਨ੍ਹਾਂ ਦਾ ਸਕੋਰ ਘੱਟੋ-ਘੱਟ ਸਕੋਰ ਦੇ ਬਰਾਬਰ ਜਾਂ ਵੱਧ ਹੋਵੇ।",
+	"Notes": "",
+	"Notifications": "ਸੂਚਨਾਵਾਂ",
+	"November": "ਨਵੰਬਰ",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (ਓਲਾਮਾ)",
+	"OAuth ID": "",
+	"October": "ਅਕਤੂਬਰ",
+	"Off": "ਬੰਦ",
+	"Okay, Let's Go!": "ਠੀਕ ਹੈ, ਚੱਲੋ ਚੱਲੀਏ!",
+	"OLED Dark": "OLED ਗੂੜ੍ਹਾ",
+	"Ollama": "ਓਲਾਮਾ",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API ਅਸਮਰੱਥ",
+	"Ollama API settings updated": "",
+	"Ollama Version": "ਓਲਾਮਾ ਵਰਜਨ",
+	"On": "ਚਾਲੂ",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "ਕਮਾਂਡ ਸਤਰ ਵਿੱਚ ਸਿਰਫ਼ ਅਲਫ਼ਾਨਯੂਮੈਰਿਕ ਅੱਖਰ ਅਤੇ ਹਾਈਫਨ ਦੀ ਆਗਿਆ ਹੈ।",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "ਓਹੋ! ਲੱਗਦਾ ਹੈ ਕਿ URL ਗਲਤ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਜਾਂਚ ਕਰੋ ਅਤੇ ਮੁੜ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "ਓਹੋ! ਤੁਸੀਂ ਇੱਕ ਅਣਸਮਰਥਿਤ ਢੰਗ ਵਰਤ ਰਹੇ ਹੋ (ਸਿਰਫ਼ ਫਰੰਟਐਂਡ)। ਕਿਰਪਾ ਕਰਕੇ ਵੈਬਯੂਆਈ ਨੂੰ ਬੈਕਐਂਡ ਤੋਂ ਸਰਵ ਕਰੋ।",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "ਨਵੀਂ ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "ਓਪਨਏਆਈ",
+	"OpenAI API": "ਓਪਨਏਆਈ API",
+	"OpenAI API Config": "ਓਪਨਏਆਈ API ਕਨਫਿਗ",
+	"OpenAI API Key is required.": "ਓਪਨਏਆਈ API ਕੁੰਜੀ ਦੀ ਲੋੜ ਹੈ।",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "ਓਪਨਏਆਈ URL/ਕੁੰਜੀ ਦੀ ਲੋੜ ਹੈ।",
+	"or": "ਜਾਂ",
+	"Organize your users": "",
+	"Other": "ਹੋਰ",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "ਪਾਸਵਰਡ",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF ਡਾਕੂਮੈਂਟ (.pdf)",
+	"PDF Extract Images (OCR)": "PDF ਚਿੱਤਰ ਕੱਢੋ (OCR)",
+	"pending": "ਬਕਾਇਆ",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਤੱਕ ਪਹੁੰਚਣ ਸਮੇਂ ਆਗਿਆ ਰੱਦ ਕੀਤੀ ਗਈ: {{error}}",
+	"Permissions": "",
+	"Personalization": "ਪਰਸੋਨਲਿਸ਼ਮ",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "ਪਾਈਪਲਾਈਨਾਂ",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "ਪਾਈਪਲਾਈਨਾਂ ਵਾਲਵ",
+	"Plain text (.txt)": "ਸਧਾਰਨ ਪਾਠ (.txt)",
+	"Playground": "ਖੇਡ ਦਾ ਮੈਦਾਨ",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "ਸਕਾਰਾਤਮਕ ਰਵੱਈਆ",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "ਪਿਛਲੇ 30 ਦਿਨ",
+	"Previous 7 days": "ਪਿਛਲੇ 7 ਦਿਨ",
+	"Profile Image": "ਪ੍ਰੋਫਾਈਲ ਚਿੱਤਰ",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "ਪ੍ਰੰਪਟ (ਉਦਾਹਰਣ ਲਈ ਮੈਨੂੰ ਰੋਮਨ ਸਾਮਰਾਜ ਬਾਰੇ ਇੱਕ ਮਜ਼ੇਦਾਰ ਤੱਥ ਦੱਸੋ)",
+	"Prompt Content": "ਪ੍ਰੰਪਟ ਸਮੱਗਰੀ",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "ਪ੍ਰੰਪਟ ਸੁਝਾਅ",
+	"Prompt updated successfully": "",
+	"Prompts": "ਪ੍ਰੰਪਟ",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "ਓਲਾਮਾ.ਕਾਮ ਤੋਂ \"{{searchValue}}\" ਖਿੱਚੋ",
+	"Pull a model from Ollama.com": "ਓਲਾਮਾ.ਕਾਮ ਤੋਂ ਇੱਕ ਮਾਡਲ ਖਿੱਚੋ",
+	"Query Generation Prompt": "",
+	"Query Params": "ਪ੍ਰਸ਼ਨ ਪੈਰਾਮੀਟਰ",
+	"RAG Template": "RAG ਟੈਮਪਲੇਟ",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "ਜੋਰ ਨਾਲ ਪੜ੍ਹੋ",
+	"Record voice": "ਆਵਾਜ਼ ਰਿਕਾਰਡ ਕਰੋ",
+	"Redirecting you to OpenWebUI Community": "ਤੁਹਾਨੂੰ ਓਪਨਵੈਬਯੂਆਈ ਕਮਿਊਨਿਟੀ ਵੱਲ ਰੀਡਾਇਰੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "ਜਦੋਂ ਇਹ ਨਹੀਂ ਹੋਣਾ ਚਾਹੀਦਾ ਸੀ ਤਾਂ ਇਨਕਾਰ ਕੀਤਾ",
+	"Regenerate": "ਮੁੜ ਬਣਾਓ",
+	"Release Notes": "ਰਿਲੀਜ਼ ਨੋਟਸ",
+	"Relevance": "",
+	"Remove": "ਹਟਾਓ",
+	"Remove Model": "ਮਾਡਲ ਹਟਾਓ",
+	"Rename": "ਨਾਮ ਬਦਲੋ",
+	"Reorder Models": "",
+	"Repeat Last N": "ਆਖਰੀ N ਨੂੰ ਦੁਹਰਾਓ",
+	"Request Mode": "ਬੇਨਤੀ ਮੋਡ",
+	"Reranking Model": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ",
+	"Reranking model disabled": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ ਅਯੋਗ ਕੀਤਾ ਗਿਆ",
+	"Reranking model set to \"{{reranking_model}}\"": "ਮਾਡਲ ਮੁੜ ਰੈਂਕਿੰਗ ਨੂੰ \"{{reranking_model}}\" 'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "ਭੂਮਿਕਾ",
+	"Rosé Pine": "ਰੋਜ਼ ਪਾਈਨ",
+	"Rosé Pine Dawn": "ਰੋਜ਼ ਪਾਈਨ ਡਾਨ",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "",
+	"Save": "ਸੰਭਾਲੋ",
+	"Save & Create": "ਸੰਭਾਲੋ ਅਤੇ ਬਣਾਓ",
+	"Save & Update": "ਸੰਭਾਲੋ ਅਤੇ ਅੱਪਡੇਟ ਕਰੋ",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਦੇ ਸਟੋਰੇਜ ਵਿੱਚ ਸਿੱਧੇ ਗੱਲਬਾਤ ਲੌਗ ਸੰਭਾਲਣਾ ਹੁਣ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਹੇਠਾਂ ਦਿੱਤੇ ਬਟਨ 'ਤੇ ਕਲਿੱਕ ਕਰਕੇ ਆਪਣੇ ਗੱਲਬਾਤ ਲੌਗ ਡਾਊਨਲੋਡ ਅਤੇ ਮਿਟਾਉਣ ਲਈ ਕੁਝ ਸਮਾਂ ਲਓ। ਚਿੰਤਾ ਨਾ ਕਰੋ, ਤੁਸੀਂ ਆਪਣੇ ਗੱਲਬਾਤ ਲੌਗ ਨੂੰ ਬੈਕਐਂਡ ਵਿੱਚ ਆਸਾਨੀ ਨਾਲ ਮੁੜ ਆਯਾਤ ਕਰ ਸਕਦੇ ਹੋ",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "ਖੋਜ",
+	"Search a model": "ਇੱਕ ਮਾਡਲ ਖੋਜੋ",
+	"Search Base": "",
+	"Search Chats": "ਖੋਜ ਚੈਟਾਂ",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "ਖੋਜ ਮਾਡਲ",
+	"Search options": "",
+	"Search Prompts": "ਪ੍ਰੰਪਟ ਖੋਜੋ",
+	"Search Result Count": "ਖੋਜ ਨਤੀਜੇ ਦੀ ਗਿਣਤੀ",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "ਖੋਜਿਆ {{count}} sites_one",
+	"Searched {{count}} sites_other": "ਖੋਜਿਆ {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng Query URL",
+	"See readme.md for instructions": "ਹਦਾਇਤਾਂ ਲਈ readme.md ਵੇਖੋ",
+	"See what's new": "ਨਵਾਂ ਕੀ ਹੈ ਵੇਖੋ",
+	"Seed": "ਬੀਜ",
+	"Select a base model": "ਆਧਾਰ ਮਾਡਲ ਚੁਣੋ",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "ਇੱਕ ਮਾਡਲ ਚੁਣੋ",
+	"Select a pipeline": "ਪਾਈਪਲਾਈਨ ਚੁਣੋ",
+	"Select a pipeline url": "ਪਾਈਪਲਾਈਨ URL ਚੁਣੋ",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "ਮਾਡਲ ਚੁਣੋ",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "ਚੁਣੇ ਗਏ ਮਾਡਲ(ਆਂ) ਚਿੱਤਰ ਇਨਪੁੱਟਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੇ",
+	"Semantic distance to query": "",
+	"Send": "ਭੇਜੋ",
+	"Send a Message": "ਇੱਕ ਸੁਨੇਹਾ ਭੇਜੋ",
+	"Send message": "ਸੁਨੇਹਾ ਭੇਜੋ",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "ਸਤੰਬਰ",
+	"Serper API Key": "Serper API ਕੁੰਜੀ",
+	"Serply API Key": "",
+	"Serpstack API Key": "Serpstack API ਕੁੰਜੀ",
+	"Server connection verified": "ਸਰਵਰ ਕਨੈਕਸ਼ਨ ਦੀ ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ",
+	"Set as default": "ਮੂਲ ਵਜੋਂ ਸੈੱਟ ਕਰੋ",
+	"Set CFG Scale": "",
+	"Set Default Model": "ਮੂਲ ਮਾਡਲ ਸੈੱਟ ਕਰੋ",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ਐਮਬੈੱਡਿੰਗ ਮਾਡਲ ਸੈੱਟ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ {{model}})",
+	"Set Image Size": "ਚਿੱਤਰ ਆਕਾਰ ਸੈੱਟ ਕਰੋ",
+	"Set reranking model (e.g. {{model}})": "ਮੁੜ ਰੈਂਕਿੰਗ ਮਾਡਲ ਸੈੱਟ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "ਕਦਮ ਸੈੱਟ ਕਰੋ",
+	"Set Task Model": "ਟਾਸਕ ਮਾਡਲ ਸੈੱਟ ਕਰੋ",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "ਆਵਾਜ਼ ਸੈੱਟ ਕਰੋ",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "ਸੈਟਿੰਗਾਂ",
+	"Settings saved successfully!": "ਸੈਟਿੰਗਾਂ ਸਫਲਤਾਪੂਰਵਕ ਸੰਭਾਲੀਆਂ ਗਈਆਂ!",
+	"Share": "ਸਾਂਝਾ ਕਰੋ",
+	"Share Chat": "ਗੱਲਬਾਤ ਸਾਂਝੀ ਕਰੋ",
+	"Share to OpenWebUI Community": "ਓਪਨਵੈਬਯੂਆਈ ਕਮਿਊਨਿਟੀ ਨਾਲ ਸਾਂਝਾ ਕਰੋ",
+	"Show": "ਦਿਖਾਓ",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "ਸ਼ਾਰਟਕਟ ਦਿਖਾਓ",
+	"Show your support!": "",
+	"Showcased creativity": "ਸਿਰਜਣਾਤਮਕਤਾ ਦਿਖਾਈ",
+	"Sign in": "ਸਾਈਨ ਇਨ ਕਰੋ",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "ਸਾਈਨ ਆਊਟ ਕਰੋ",
+	"Sign up": "ਰਜਿਸਟਰ ਕਰੋ",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "ਸਰੋਤ",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "ਬੋਲ ਪਛਾਣ ਗਲਤੀ: {{error}}",
+	"Speech-to-Text Engine": "ਬੋਲ-ਤੋਂ-ਪਾਠ ਇੰਜਣ",
+	"Stop": "",
+	"Stop Sequence": "ਰੋਕੋ ਕ੍ਰਮ",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT ਸੈਟਿੰਗਾਂ",
+	"Subtitle (e.g. about the Roman Empire)": "ਉਪਸਿਰਲੇਖ (ਉਦਾਹਰਣ ਲਈ ਰੋਮਨ ਸਾਮਰਾਜ ਬਾਰੇ)",
+	"Success": "ਸਫਲਤਾ",
+	"Successfully updated.": "ਸਫਲਤਾਪੂਰਵਕ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ।",
+	"Suggested": "ਸੁਝਾਇਆ ਗਿਆ",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "ਸਿਸਟਮ",
+	"System Instructions": "",
+	"System Prompt": "ਸਿਸਟਮ ਪ੍ਰੰਪਟ",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "ਸਾਨੂੰ ਹੋਰ ਦੱਸੋ:",
+	"Temperature": "ਤਾਪਮਾਨ",
+	"Template": "ਟੈਮਪਲੇਟ",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "ਪਾਠ-ਤੋਂ-ਬੋਲ ਇੰਜਣ",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "ਤੁਹਾਡੇ ਫੀਡਬੈਕ ਲਈ ਧੰਨਵਾਦ!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "ਸਕੋਰ 0.0 (0%) ਅਤੇ 1.0 (100%) ਦੇ ਵਿਚਕਾਰ ਹੋਣਾ ਚਾਹੀਦਾ ਹੈ।",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "ਥੀਮ",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "ਇਹ ਯਕੀਨੀ ਬਣਾਉਂਦਾ ਹੈ ਕਿ ਤੁਹਾਡੀਆਂ ਕੀਮਤੀ ਗੱਲਾਂ ਤੁਹਾਡੇ ਬੈਕਐਂਡ ਡਾਟਾਬੇਸ ਵਿੱਚ ਸੁਰੱਖਿਅਤ ਤੌਰ 'ਤੇ ਸੰਭਾਲੀਆਂ ਗਈਆਂ ਹਨ। ਧੰਨਵਾਦ!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "ਵਿਸਥਾਰ ਨਾਲ ਵਿਆਖਿਆ",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "ਸਲਾਹ: ਹਰ ਬਦਲਾਅ ਦੇ ਬਾਅਦ ਗੱਲਬਾਤ ਇਨਪੁਟ ਵਿੱਚ ਟੈਬ ਕੀ ਦਬਾ ਕੇ ਲਗਾਤਾਰ ਕਈ ਵੈਰੀਏਬਲ ਸਲਾਟਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ।",
+	"Title": "ਸਿਰਲੇਖ",
+	"Title (e.g. Tell me a fun fact)": "ਸਿਰਲੇਖ (ਉਦਾਹਰਣ ਲਈ ਮੈਨੂੰ ਇੱਕ ਮਜ਼ੇਦਾਰ ਤੱਥ ਦੱਸੋ)",
+	"Title Auto-Generation": "ਸਿਰਲੇਖ ਆਟੋ-ਜਨਰੇਸ਼ਨ",
+	"Title cannot be an empty string.": "ਸਿਰਲੇਖ ਖਾਲੀ ਸਤਰ ਨਹੀਂ ਹੋ ਸਕਦਾ।",
+	"Title Generation Prompt": "ਸਿਰਲੇਖ ਜਨਰੇਸ਼ਨ ਪ੍ਰੰਪਟ",
+	"TLS": "",
+	"To access the available model names for downloading,": "ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਉਪਲਬਧ ਮਾਡਲ ਨਾਮਾਂ ਤੱਕ ਪਹੁੰਚਣ ਲਈ,",
+	"To access the GGUF models available for downloading,": "ਡਾਊਨਲੋਡ ਕਰਨ ਲਈ ਉਪਲਬਧ GGUF ਮਾਡਲਾਂ ਤੱਕ ਪਹੁੰਚਣ ਲਈ,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "ਅੱਜ",
+	"Toggle settings": "ਸੈਟਿੰਗਾਂ ਟੌਗਲ ਕਰੋ",
+	"Toggle sidebar": "ਸਾਈਡਬਾਰ ਟੌਗਲ ਕਰੋ",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "ਸਿਖਰ K",
+	"Top P": "ਸਿਖਰ P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "ਓਲਾਮਾ ਤੱਕ ਪਹੁੰਚਣ ਵਿੱਚ ਮੁਸ਼ਕਲ?",
+	"TTS Model": "",
+	"TTS Settings": "TTS ਸੈਟਿੰਗਾਂ",
+	"TTS Voice": "",
+	"Type": "ਕਿਸਮ",
+	"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (ਡਾਊਨਲੋਡ) URL ਟਾਈਪ ਕਰੋ",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "ਓਹੋ! {{provider}} ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ।",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "ਅੱਪਡੇਟ ਕਰੋ ਅਤੇ ਲਿੰਕ ਕਾਪੀ ਕਰੋ",
+	"Update for the latest features and improvements.": "",
+	"Update password": "ਪਾਸਵਰਡ ਅੱਪਡੇਟ ਕਰੋ",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "ਇੱਕ GGUF ਮਾਡਲ ਅਪਲੋਡ ਕਰੋ",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "ਫਾਇਲਾਂ ਅੱਪਲੋਡ ਕਰੋ",
+	"Upload Pipeline": "",
+	"Upload Progress": "ਅਪਲੋਡ ਪ੍ਰਗਤੀ",
+	"URL": "",
+	"URL Mode": "URL ਮੋਡ",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "ਗ੍ਰਾਵਾਟਾਰ ਵਰਤੋ",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "ਸ਼ੁਰੂਆਤੀ ਅੱਖਰ ਵਰਤੋ",
+	"use_mlock (Ollama)": "use_mlock (ਓਲਾਮਾ)",
+	"use_mmap (Ollama)": "use_mmap (ਓਲਾਮਾ)",
+	"user": "ਉਪਭੋਗਤਾ",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "ਉਪਭੋਗਤਾ",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "ਵਰਤੋਂ",
+	"Valid time units:": "ਵੈਧ ਸਮਾਂ ਇਕਾਈਆਂ:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "ਵੈਰੀਏਬਲ",
+	"variable to have them replaced with clipboard content.": "ਕਲਿੱਪਬੋਰਡ ਸਮੱਗਰੀ ਨਾਲ ਬਦਲਣ ਲਈ ਵੈਰੀਏਬਲ।",
+	"Version": "ਵਰਜਨ",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "ਚੇਤਾਵਨੀ",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "ਚੇਤਾਵਨੀ: ਜੇ ਤੁਸੀਂ ਆਪਣਾ ਐਮਬੈੱਡਿੰਗ ਮਾਡਲ ਅੱਪਡੇਟ ਜਾਂ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਸਾਰੇ ਡਾਕੂਮੈਂਟ ਮੁੜ ਆਯਾਤ ਕਰਨ ਦੀ ਲੋੜ ਹੋਵੇਗੀ।",
+	"Web": "ਵੈਬ",
+	"Web API": "",
+	"Web Loader Settings": "ਵੈਬ ਲੋਡਰ ਸੈਟਿੰਗਾਂ",
+	"Web Search": "ਵੈੱਬ ਖੋਜ",
+	"Web Search Engine": "ਵੈੱਬ ਖੋਜ ਇੰਜਣ",
+	"Web Search Query Generation": "",
+	"Webhook URL": "ਵੈਬਹੁੱਕ URL",
+	"WebUI Settings": "ਵੈਬਯੂਆਈ ਸੈਟਿੰਗਾਂ",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "ਨਵਾਂ ਕੀ ਹੈ",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "ਕਾਰਜਸਥਲ",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "ਇੱਕ ਪ੍ਰੰਪਟ ਸੁਝਾਅ ਲਿਖੋ (ਉਦਾਹਰਣ ਲਈ ਤੁਸੀਂ ਕੌਣ ਹੋ?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "50 ਸ਼ਬਦਾਂ ਵਿੱਚ ਇੱਕ ਸੰਖੇਪ ਲਿਖੋ ਜੋ [ਵਿਸ਼ਾ ਜਾਂ ਕੁੰਜੀ ਸ਼ਬਦ] ਨੂੰ ਸੰਖੇਪ ਕਰਦਾ ਹੈ।",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "ਕੱਲ੍ਹ",
+	"You": "ਤੁਸੀਂ",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "ਤੁਹਾਡੇ ਕੋਲ ਕੋਈ ਆਰਕਾਈਵ ਕੀਤੀਆਂ ਗੱਲਾਂ ਨਹੀਂ ਹਨ।",
+	"You have shared this chat": "ਤੁਸੀਂ ਇਹ ਗੱਲਬਾਤ ਸਾਂਝੀ ਕੀਤੀ ਹੈ",
+	"You're a helpful assistant.": "ਤੁਸੀਂ ਇੱਕ ਮਦਦਗਾਰ ਸਹਾਇਕ ਹੋ।",
+	"You're now logged in.": "ਤੁਸੀਂ ਹੁਣ ਲੌਗ ਇਨ ਹੋ ਗਏ ਹੋ।",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "ਯੂਟਿਊਬ",
+	"Youtube Loader Settings": "ਯੂਟਿਊਬ ਲੋਡਰ ਸੈਟਿੰਗਾਂ"
+}
diff --git a/src/lib/i18n/locales/pl-PL/translation.json b/src/lib/i18n/locales/pl-PL/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..756b9460a9044bd7c046a1233fcd5104685ca64c
--- /dev/null
+++ b/src/lib/i18n/locales/pl-PL/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' lub '-1' dla bez wygaśnięcia.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(np. `sh webui.sh --api`)",
+	"(latest)": "(najnowszy)",
+	"{{ models }}": "{{ modele }}",
+	"{{user}}'s Chats": "{{user}} - czaty",
+	"{{webUIName}} Backend Required": "Backend {{webUIName}} wymagane",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Model zadań jest używany podczas wykonywania zadań, takich jak generowanie tytułów czatów i zapytań wyszukiwania w Internecie",
+	"a user": "użytkownik",
+	"About": "O nas",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Konto",
+	"Account Activation Pending": "",
+	"Accurate information": "Dokładna informacja",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "Dodaj",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Dodaj krótki opis działania tego modelu",
+	"Add a tag": "Dodaj tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Dodaj własne polecenie",
+	"Add Files": "Dodaj pliki",
+	"Add Group": "",
+	"Add Memory": "Dodaj pamięć",
+	"Add Model": "Dodaj model",
+	"Add Tag": "",
+	"Add Tags": "Dodaj tagi",
+	"Add text content": "",
+	"Add User": "Dodaj użytkownika",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Dostosowanie tych ustawień spowoduje zastosowanie zmian uniwersalnie do wszystkich użytkowników.",
+	"admin": "admin",
+	"Admin": "",
+	"Admin Panel": "Panel administracyjny",
+	"Admin Settings": "Ustawienia administratora",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Zaawansowane parametry",
+	"Advanced Params": "Zaawansowane parametry",
+	"All chats": "",
+	"All Documents": "Wszystkie dokumenty",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Pozwól na usuwanie czatu",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Masz już konto?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "asystent",
+	"and": "i",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "i utwórz nowy udostępniony link",
+	"API Base URL": "Podstawowy adres URL interfejsu API",
+	"API Key": "Klucz API",
+	"API Key created.": "Klucz API utworzony.",
+	"API keys": "Klucze API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Kwiecień",
+	"Archive": "Archiwum",
+	"Archive All Chats": "Archiwizuj wszystkie czaty",
+	"Archived Chats": "Zarchiwizowane czaty",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Jesteś pewien?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Dołącz plik",
+	"Attention to detail": "Dbałość o szczegóły",
+	"Attribute for Username": "",
+	"Audio": "Dźwięk",
+	"August": "Sierpień",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Automatyczne kopiowanie odpowiedzi do schowka",
+	"Auto-playback response": "Odtwarzanie automatyczne odpowiedzi",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "Podstawowy adres URL AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Podstawowy adres URL AUTOMATIC1111 jest wymagany.",
+	"Available list": "",
+	"available!": "dostępny!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Wstecz",
+	"Bad Response": "Zła odpowiedź",
+	"Banners": "Banery",
+	"Base Model (From)": "Model podstawowy (od)",
+	"Batch Size (num_batch)": "",
+	"before": "przed",
+	"Being lazy": "Jest leniwy",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Klucz API wyszukiwania Brave",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Pomiń weryfikację SSL dla stron webowych",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Anuluj",
+	"Capabilities": "Możliwości",
+	"Certificate Path": "",
+	"Change Password": "Zmień hasło",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Czat",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Bąbelki czatu",
+	"Chat Controls": "",
+	"Chat direction": "Kierunek czatu",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Czaty",
+	"Check Again": "Sprawdź ponownie",
+	"Check for updates": "Sprawdź aktualizacje",
+	"Checking for updates...": "Sprawdzanie aktualizacji...",
+	"Choose a model before saving...": "Wybierz model przed zapisaniem...",
+	"Chunk Overlap": "Zachodzenie bloku",
+	"Chunk Params": "Parametry bloku",
+	"Chunk Size": "Rozmiar bloku",
+	"Ciphers": "",
+	"Citation": "Cytat",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Kliknij tutaj, aby uzyskać pomoc.",
+	"Click here to": "Kliknij tutaj, żeby",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Kliknij tutaj, aby wybrać",
+	"Click here to select a csv file.": "Kliknij tutaj, żeby wybrać plik CSV",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "kliknij tutaj.",
+	"Click on the user role button to change a user's role.": "Kliknij przycisk roli użytkownika, aby zmienić rolę użytkownika.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Klon",
+	"Close": "Zamknij",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Kolekcja",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "Bazowy URL ComfyUI",
+	"ComfyUI Base URL is required.": "Bazowy URL ComfyUI jest wymagany.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Polecenie",
+	"Completions": "",
+	"Concurrent Requests": "Równoczesne żądania",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Potwierdź hasło",
+	"Confirm your action": "",
+	"Connections": "Połączenia",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Zawartość",
+	"Content Extraction": "",
+	"Context Length": "Długość kontekstu",
+	"Continue Response": "Kontynuuj odpowiedź",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Skopiowano URL czatu do schowka!",
+	"Copied to clipboard": "",
+	"Copy": "Kopiuj",
+	"Copy last code block": "Skopiuj ostatni blok kodu",
+	"Copy last response": "Skopiuj ostatnią odpowiedź",
+	"Copy Link": "Kopiuj link",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Kopiowanie do schowka zakończone powodzeniem!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Tworzenie modelu",
+	"Create Account": "Utwórz konto",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Utwórz nowy klucz",
+	"Create new secret key": "Utwórz nowy klucz bezpieczeństwa",
+	"Created at": "Utworzono o",
+	"Created At": "Utworzono o",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Bieżący model",
+	"Current Password": "Bieżące hasło",
+	"Custom": "Niestandardowy",
+	"Dark": "Ciemny",
+	"Database": "Baza danych",
+	"December": "Grudzień",
+	"Default": "Domyślny",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Domyślny (SentenceTransformers)",
+	"Default Model": "Model domyślny",
+	"Default model updated": "Domyślny model zaktualizowany",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Domyślne sugestie promptów",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Domyślna rola użytkownika",
+	"Delete": "Usuń",
+	"Delete a model": "Usuń model",
+	"Delete All Chats": "Usuń wszystkie czaty",
+	"Delete All Models": "",
+	"Delete chat": "Usuń czat",
+	"Delete Chat": "Usuń czat",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "usuń ten link",
+	"Delete tool?": "",
+	"Delete User": "Usuń użytkownika",
+	"Deleted {{deleteModelTag}}": "Usunięto {{deleteModelTag}}",
+	"Deleted {{name}}": "Usunięto {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Opis",
+	"Didn't fully follow instructions": "Nie postępował zgodnie z instrukcjami",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Odkryj model",
+	"Discover a prompt": "Odkryj prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Odkryj, pobierz i eksploruj niestandardowe prompty",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Odkryj, pobierz i eksploruj ustawienia modeli",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Wyświetl nazwę użytkownika zamiast Ty w czacie",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Dokument",
+	"Documentation": "",
+	"Documents": "Dokumenty",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "nie nawiązuje żadnych zewnętrznych połączeń, a Twoje dane pozostają bezpiecznie na Twoim lokalnie hostowanym serwerze.",
+	"Don't have an account?": "Nie masz konta?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Nie podobał mi się styl",
+	"Done": "",
+	"Download": "Pobieranie",
+	"Download canceled": "Pobieranie przerwane",
+	"Download Database": "Pobierz bazę danych",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Upuść pliki tutaj, aby dodać do rozmowy",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "np. '30s', '10m'. Poprawne jednostki czasu to 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Edytuj",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Edytuj użytkownika",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Model osadzania",
+	"Embedding Model Engine": "Silnik modelu osadzania",
+	"Embedding model set to \"{{embedding_model}}\"": "Model osadzania ustawiono na \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Włączanie udostępniania społecznościowego",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Włącz nowe rejestracje",
+	"Enable Web Search": "Włączanie wyszukiwania w Internecie",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Upewnij się, że twój plik CSV zawiera 4 kolumny w następującym porządku: Nazwa, Email, Hasło, Rola.",
+	"Enter {{role}} message here": "Wprowadź wiadomość {{role}} tutaj",
+	"Enter a detail about yourself for your LLMs to recall": "Wprowadź szczegóły o sobie, aby LLMs mogli pamiętać",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Wprowadź klucz API Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Wprowadź zakchodzenie bloku",
+	"Enter Chunk Size": "Wprowadź rozmiar bloku",
+	"Enter description": "",
+	"Enter Github Raw URL": "Wprowadź nieprzetworzony adres URL usługi Github",
+	"Enter Google PSE API Key": "Wprowadź klucz API Google PSE",
+	"Enter Google PSE Engine Id": "Wprowadź identyfikator aparatu Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Wprowadź rozmiar obrazu (np. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Wprowadź kody języków",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Wprowadź tag modelu (np. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Wprowadź liczbę kroków (np. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Wprowadź wynik",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Wprowadź adres URL zapytania Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Wprowadź klucz API Serper",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Wprowadź klucz API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Wprowadź sekwencję zatrzymania",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Wprowadź Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Wprowadź adres URL (np. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Wprowadź adres URL (np. http://localhost:11434/)",
+	"Enter Your Email": "Wprowadź swój adres email",
+	"Enter Your Full Name": "Wprowadź swoje imię i nazwisko",
+	"Enter your message": "",
+	"Enter Your Password": "Wprowadź swoje hasło",
+	"Enter Your Role": "Wprowadź swoją rolę",
+	"Enter Your Username": "",
+	"Error": "Błąd",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Eksperymentalne",
+	"Explore the cosmos": "",
+	"Export": "Eksport",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Eksportuj wszystkie czaty (wszyscy użytkownicy)",
+	"Export chat (.json)": "",
+	"Export Chats": "Eksportuj czaty",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Eksportuj modele",
+	"Export Presets": "",
+	"Export Prompts": "Eksportuj prompty",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Nie udało się utworzyć klucza API.",
+	"Failed to read clipboard contents": "Nie udało się odczytać zawartości schowka",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "Luty",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Podaj inne szczegóły",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Tryb pliku",
+	"File not found.": "Plik nie został znaleziony.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Wykryto podszywanie się pod odcisk palca: Nie można używać inicjałów jako awatara. Przechodzenie do domyślnego obrazu profilowego.",
+	"Fluidly stream large external response chunks": "Płynnie przesyłaj strumieniowo duże fragmenty odpowiedzi zewnętrznych",
+	"Focus chat input": "Skoncentruj na czacie",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Postępował z idealnie według instrukcji",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Kara za częstotliwość",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Ogólne",
+	"General Settings": "Ogólne ustawienia",
+	"Generate Image": "",
+	"Generating search query": "Generowanie zapytania",
+	"Generation Info": "Informacja o generacji",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Dobra odpowiedź",
+	"Google PSE API Key": "Klucz API Google PSE",
+	"Google PSE Engine Id": "Identyfikator silnika Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "nie ma rozmów.",
+	"Hello, {{name}}": "Witaj, {{name}}",
+	"Help": "Pomoc",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Ukryj",
+	"Host": "",
+	"How can I help you today?": "Jak mogę Ci dzisiaj pomóc?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Szukanie hybrydowe",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generowanie obrazu (eksperymentalne)",
+	"Image Generation Engine": "Silnik generowania obrazu",
+	"Image Settings": "Ustawienia obrazu",
+	"Images": "Obrazy",
+	"Import Chats": "Importuj czaty",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Importowanie modeli",
+	"Import Presets": "",
+	"Import Prompts": "Importuj prompty",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Dołącz flagę `--api` podczas uruchamiania stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informacji",
+	"Input commands": "Wprowadź komendy",
+	"Install from Github URL": "Instalowanie z adresu URL usługi Github",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Interfejs",
+	"Invalid file format.": "",
+	"Invalid Tag": "Nieprawidłowy tag",
+	"January": "Styczeń",
+	"Jina API Key": "",
+	"join our Discord for help.": "Dołącz do naszego Discorda po pomoc.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON (wersja zapoznawcza)",
+	"July": "Lipiec",
+	"June": "Czerwiec",
+	"JWT Expiration": "Wygaśnięcie JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Zachowaj łączność",
+	"Key": "",
+	"Keyboard shortcuts": "Skróty klawiszowe",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Język",
+	"Last Active": "Ostatnio aktywny",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Jasny",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "LLMy mogą popełniać błędy. Zweryfikuj ważne informacje.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Stworzone przez społeczność OpenWebUI",
+	"Make sure to enclose them with": "Upewnij się, że są one zamknięte w",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Zarządzanie potokami",
+	"March": "Marzec",
+	"Max Tokens (num_predict)": "Maksymalna liczba żetonów (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maksymalnie 3 modele można pobierać jednocześnie. Spróbuj ponownie później.",
+	"May": "Maj",
+	"Memories accessible by LLMs will be shown here.": "Pamięci używane przez LLM będą tutaj widoczne.",
+	"Memory": "Pamięć",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Wiadomości wysyłane po utworzeniu linku nie będą udostępniane. Użytkownicy z adresem URL będą mogli wyświetlić udostępniony czat.",
+	"Min P": "",
+	"Minimum Score": "Minimalny wynik",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Model '{{modelName}}' został pomyślnie pobrany.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Model '{{modelTag}}' jest już w kolejce do pobrania.",
+	"Model {{modelId}} not found": "Model {{modelId}} nie został znaleziony",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} nie jest w stanie zobaczyć",
+	"Model {{name}} is now {{status}}": "Model {{name}} to teraz {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Wykryto ścieżkę systemu plików modelu. Wymagana jest krótka nazwa modelu do aktualizacji, nie można kontynuować.",
+	"Model Filtering": "",
+	"Model ID": "Identyfikator modelu",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Model nie został wybrany",
+	"Model Params": "Parametry modelu",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Zawartość pliku modelu",
+	"Models": "Modele",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Więcej",
+	"Name": "Nazwa",
+	"Name your knowledge base": "",
+	"New Chat": "Nowy czat",
+	"New folder": "",
+	"New Password": "Nowe hasło",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Nie znaleziono rezultatów",
+	"No search query generated": "Nie wygenerowano zapytania wyszukiwania",
+	"No source available": "Źródło nie dostępne",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Żaden",
+	"Not factually correct": "Nie zgodne z faktami",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Uwaga: Jeśli ustawisz minimalny wynik, szukanie zwróci jedynie dokumenty z wynikiem większym lub równym minimalnemu.",
+	"Notes": "",
+	"Notifications": "Powiadomienia",
+	"November": "Listopad",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Październik",
+	"Off": "Wyłączony",
+	"Okay, Let's Go!": "Okej, zaczynamy!",
+	"OLED Dark": "Ciemny OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Interfejs API Ollama wyłączony",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Wersja Ollama",
+	"On": "Włączony",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "W poleceniu dozwolone są tylko znaki alfanumeryczne i myślniki.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ups! Wygląda na to, że URL jest nieprawidłowy. Sprawdź jeszcze raz i spróbuj ponownie.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ups! Używasz nieobsługiwanej metody (tylko interfejs front-end). Proszę obsłużyć interfejs WebUI z poziomu backendu.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Otwórz nowy czat",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "Konfiguracja OpenAI API",
+	"OpenAI API Key is required.": "Klucz API OpenAI jest wymagany.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Klucz OpenAI jest wymagany.",
+	"or": "lub",
+	"Organize your users": "",
+	"Other": "Inne",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Hasło",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Dokument PDF (.pdf)",
+	"PDF Extract Images (OCR)": "PDF Wyodrębnij obrazy (OCR)",
+	"pending": "oczekujące",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Odmowa dostępu do mikrofonu: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalizacja",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Rurociągów",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Rurociągi Zawory",
+	"Plain text (.txt)": "Zwykły tekst (.txt)",
+	"Playground": "Plac zabaw",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Pozytywne podejście",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Poprzednie 30 dni",
+	"Previous 7 days": "Poprzednie 7 dni",
+	"Profile Image": "Obrazek profilowy",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (np. powiedz mi zabawny fakt o Imperium Rzymskim",
+	"Prompt Content": "Zawartość prompta",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Sugestie prompta",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompty",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Pobierz \"{{searchValue}}\" z Ollama.com",
+	"Pull a model from Ollama.com": "Pobierz model z Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parametry zapytania",
+	"RAG Template": "Szablon RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Czytaj na głos",
+	"Record voice": "Nagraj głos",
+	"Redirecting you to OpenWebUI Community": "Przekierowujemy Cię do społeczności OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "Odmówił, kiedy nie powinien",
+	"Regenerate": "Generuj ponownie",
+	"Release Notes": "Notatki wydania",
+	"Relevance": "",
+	"Remove": "Usuń",
+	"Remove Model": "Usuń model",
+	"Rename": "ZMień nazwę",
+	"Reorder Models": "",
+	"Repeat Last N": "Powtórz ostatnie N",
+	"Request Mode": "Tryb żądania",
+	"Reranking Model": "Zmiana rankingu modelu",
+	"Reranking model disabled": "Zmiana rankingu modelu zablokowana",
+	"Reranking model set to \"{{reranking_model}}\"": "Zmiana rankingu modelu ustawiona na \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rola",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RLT",
+	"Run": "",
+	"Running": "",
+	"Save": "Zapisz",
+	"Save & Create": "Zapisz i utwórz",
+	"Save & Update": "Zapisz i zaktualizuj",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Bezpośrednie zapisywanie dzienników czatu w pamięci przeglądarki nie jest już obsługiwane. Prosimy o pobranie i usunięcie dzienników czatu, klikając poniższy przycisk. Nie martw się, możesz łatwo ponownie zaimportować dzienniki czatu do backendu za pomocą",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Szukaj",
+	"Search a model": "Szukaj modelu",
+	"Search Base": "",
+	"Search Chats": "Szukaj w czatach",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Szukaj modeli",
+	"Search options": "",
+	"Search Prompts": "Szukaj promptów",
+	"Search Result Count": "Liczba wyników wyszukiwania",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Wyszukiwano {{count}} sites_one",
+	"Searched {{count}} sites_few": "Wyszukiwano {{count}} sites_few",
+	"Searched {{count}} sites_many": "Wyszukiwano {{count}} sites_many",
+	"Searched {{count}} sites_other": "Wyszukiwano {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Adres URL zapytania Searxng",
+	"See readme.md for instructions": "Zajrzyj do readme.md po instrukcje",
+	"See what's new": "Zobacz co nowego",
+	"Seed": "Seed",
+	"Select a base model": "Wybieranie modelu bazowego",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Wybierz model",
+	"Select a pipeline": "Wybieranie potoku",
+	"Select a pipeline url": "Wybieranie adresu URL potoku",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Wybierz model",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "Wybrane modele nie obsługują danych wejściowych obrazu",
+	"Semantic distance to query": "",
+	"Send": "Wyślij",
+	"Send a Message": "Wyślij Wiadomość",
+	"Send message": "Wyślij wiadomość",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Wrzesień",
+	"Serper API Key": "Klucz API Serper",
+	"Serply API Key": "",
+	"Serpstack API Key": "Klucz API Serpstack",
+	"Server connection verified": "Połączenie z serwerem zweryfikowane",
+	"Set as default": "Ustaw jako domyślne",
+	"Set CFG Scale": "",
+	"Set Default Model": "Ustaw domyślny model",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Ustaw model osadzania (e.g. {{model}})",
+	"Set Image Size": "Ustaw rozmiar obrazu",
+	"Set reranking model (e.g. {{model}})": "Ustaw zmianę rankingu modelu (e.g. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Ustaw kroki",
+	"Set Task Model": "Ustawianie modelu zadań",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Ustaw głos",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Ustawienia",
+	"Settings saved successfully!": "Ustawienia zapisane pomyślnie!",
+	"Share": "Udostępnij",
+	"Share Chat": "Udostępnij czat",
+	"Share to OpenWebUI Community": "Dziel się z społecznością OpenWebUI",
+	"Show": "Pokaż",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Pokaż skróty",
+	"Show your support!": "",
+	"Showcased creativity": "Pokaz kreatywności",
+	"Sign in": "Zaloguj się",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Wyloguj się",
+	"Sign up": "Zarejestruj się",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Źródło",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Błąd rozpoznawania mowy: {{error}}",
+	"Speech-to-Text Engine": "Silnik mowy na tekst",
+	"Stop": "",
+	"Stop Sequence": "Zatrzymaj sekwencję",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "Ustawienia STT",
+	"Subtitle (e.g. about the Roman Empire)": "Podtytuł (np. o Imperium Rzymskim)",
+	"Success": "Sukces",
+	"Successfully updated.": "Pomyślnie zaktualizowano.",
+	"Suggested": "Sugerowane",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "System",
+	"System Instructions": "",
+	"System Prompt": "Prompt systemowy",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Powiedz nam więcej",
+	"Temperature": "Temperatura",
+	"Template": "Szablon",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Silnik tekstu na mowę",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Dzięki za informację zwrotną!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Wynik powinien być wartością pomiędzy 0.0 (0%) a 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Motyw",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "To zapewnia, że Twoje cenne rozmowy są bezpiecznie zapisywane w bazie danych backendowej. Dziękujemy!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Dokładne wyjaśnienie",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Porada: Aktualizuj wiele zmiennych kolejno, naciskając klawisz tabulatora w polu wprowadzania czatu po każdej zmianie.",
+	"Title": "Tytuł",
+	"Title (e.g. Tell me a fun fact)": "Tytuł (np. Powiedz mi jakiś zabawny fakt)",
+	"Title Auto-Generation": "Automatyczne generowanie tytułu",
+	"Title cannot be an empty string.": "Tytuł nie może być pusty",
+	"Title Generation Prompt": "Prompt generowania tytułu",
+	"TLS": "",
+	"To access the available model names for downloading,": "Aby uzyskać dostęp do dostępnych nazw modeli do pobrania,",
+	"To access the GGUF models available for downloading,": "Aby uzyskać dostęp do dostępnych modeli GGUF do pobrania,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Dzisiaj",
+	"Toggle settings": "Przełącz ustawienia",
+	"Toggle sidebar": "Przełącz panel boczny",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Najlepsze K",
+	"Top P": "Najlepsze P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemy z dostępem do Ollama?",
+	"TTS Model": "",
+	"TTS Settings": "Ustawienia TTS",
+	"TTS Voice": "",
+	"Type": "Typ",
+	"Type Hugging Face Resolve (Download) URL": "Wprowadź adres URL do pobrania z Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "O nie! Wystąpił problem z połączeniem z {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Uaktualnij i skopiuj link",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Aktualizacja hasła",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Prześlij model GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Prześlij pliki",
+	"Upload Pipeline": "",
+	"Upload Progress": "Postęp przesyłania",
+	"URL": "",
+	"URL Mode": "Tryb adresu URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Użyj Gravatara",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Użyj inicjałów",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "użytkownik",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Użytkownicy",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Wykorzystaj",
+	"Valid time units:": "Poprawne jednostki czasu:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "zmienna",
+	"variable to have them replaced with clipboard content.": "zmienna która zostanie zastąpiona zawartością schowka.",
+	"Version": "Wersja",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Ostrzeżenie",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Uwaga: Jeśli uaktualnisz lub zmienisz model osadzania, będziesz musiał ponownie zaimportować wszystkie dokumenty.",
+	"Web": "Sieć",
+	"Web API": "",
+	"Web Loader Settings": "Ustawienia pobierania z sieci",
+	"Web Search": "Wyszukiwarka w Internecie",
+	"Web Search Engine": "Wyszukiwarka internetowa",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL webhook",
+	"WebUI Settings": "Ustawienia interfejsu WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Co nowego w",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Obszar roboczy",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Napisz sugestię do polecenia (np. Kim jesteś?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Napisz podsumowanie w 50 słowach, które podsumowuje [temat lub słowo kluczowe].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Wczoraj",
+	"You": "Ty",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Nie masz zarchiwizowanych rozmów.",
+	"You have shared this chat": "Udostępniłeś ten czat",
+	"You're a helpful assistant.": "Jesteś pomocnym asystentem.",
+	"You're now logged in.": "Jesteś teraz zalogowany.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Ustawienia pobierania z Youtube"
+}
diff --git a/src/lib/i18n/locales/pt-BR/translation.json b/src/lib/i18n/locales/pt-BR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..1c26471a1be3f8cb6dbda54433a77839e6f1f111
--- /dev/null
+++ b/src/lib/i18n/locales/pt-BR/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ou '-1' para sem expiração.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(por exemplo, `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(por exemplo, `sh webui.sh --api`)",
+	"(latest)": "(último)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Chats de {{user}}",
+	"{{webUIName}} Backend Required": "Backend {{webUIName}} necessário",
+	"*Prompt node ID(s) are required for image generation": "*Prompt node ID(s) são obrigatórios para gerar imagens",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Um nova versão (v{{LATEST_VERSION}}) está disponível.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Um modelo de tarefa é usado ao realizar tarefas como gerar títulos para chats e consultas de pesquisa na web",
+	"a user": "um usuário",
+	"About": "Sobre",
+	"Access": "Acesso",
+	"Access Control": "Controle de Acesso",
+	"Accessible to all users": "Accessível para todos os usuários",
+	"Account": "Conta",
+	"Account Activation Pending": "Ativação da Conta Pendente",
+	"Accurate information": "Informação precisa",
+	"Actions": "Ações",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Ativar esse comando no chat digitando \"/{{COMMAND}}\"",
+	"Active Users": "Usuários Ativos",
+	"Add": "Adicionar",
+	"Add a model ID": "Adicione um ID de modelo",
+	"Add a short description about what this model does": "Adicione uma descrição curta sobre o que este modelo faz",
+	"Add a tag": "Adicionar uma tag",
+	"Add Arena Model": "Adicionar Modelo Arena",
+	"Add Connection": "Adicionar Conexao",
+	"Add Content": "Adicionar Conteúdo",
+	"Add content here": "Adicionar conteúdo aqui",
+	"Add custom prompt": "Adicionar prompt personalizado",
+	"Add Files": "Adicionar Arquivos",
+	"Add Group": "Adicionar Grupo",
+	"Add Memory": "Adicionar Memória",
+	"Add Model": "Adicionar Modelo",
+	"Add Tag": "Adicionar Tag",
+	"Add Tags": "Adicionar Tags",
+	"Add text content": "Adicionar conteúdo de texto",
+	"Add User": "Adicionar Usuário",
+	"Add User Group": "Adicionar grupo de usuários",
+	"Adjusting these settings will apply changes universally to all users.": "Ajustar essas configurações aplicará mudanças para todos os usuários.",
+	"admin": "admin",
+	"Admin": "Admin",
+	"Admin Panel": "Painel do Admin",
+	"Admin Settings": "Configurações do Admin",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Os admins têm acesso a todas as ferramentas o tempo todo; os usuários precisam de ferramentas atribuídas, por modelo, no workspace.",
+	"Advanced Parameters": "Parâmetros Avançados",
+	"Advanced Params": "Parâmetros Avançados",
+	"All chats": "Todos os chats",
+	"All Documents": "Todos os Documentos",
+	"All models deleted successfully": "Todos os modelos foram excluídos com sucesso",
+	"Allow Chat Delete": "Permitir Exclusão de Chats",
+	"Allow Chat Deletion": "Permitir Exclusão de Chats",
+	"Allow Chat Edit": "Permitir Edição de Chats",
+	"Allow File Upload": "Permitir Envio de arquivos",
+	"Allow non-local voices": "Permitir vozes não locais",
+	"Allow Temporary Chat": "Permitir Conversa Temporária",
+	"Allow User Location": "Permitir Localização do Usuário",
+	"Allow Voice Interruption in Call": "Permitir Interrupção de Voz na Chamada",
+	"Already have an account?": "Já tem uma conta?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Alternativa ao 'top_p', e visa garantir um equilíbrio entre qualidade e variedade. O parâmetro 'p' representa a probabilidade mínima para que um token seja considerado, em relação à probabilidade do token mais provável. Por exemplo, com 'p=0.05' e o token mais provável com probabilidade de '0.9', as predições com valor inferior a '0.045' são filtrados. (Default: 0.0)",
+	"Amazing": "Incrível",
+	"an assistant": "um assistente",
+	"and": "e",
+	"and {{COUNT}} more": "e mais {{COUNT}}",
+	"and create a new shared link.": "e criar um novo link compartilhado.",
+	"API Base URL": "URL Base da API",
+	"API Key": "Chave API",
+	"API Key created.": "Chave API criada.",
+	"API keys": "Chaves API",
+	"Application DN": "DN da Aplicação",
+	"Application DN Password": "Senha da aplicação DN",
+	"applies to all users with the \"user\" role": "Aplicar para todos com permissão de \"usuário\"",
+	"April": "Abril",
+	"Archive": "Arquivar",
+	"Archive All Chats": "Arquivar Todos os Chats",
+	"Archived Chats": "Chats Arquivados",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "Você tem certeza que deseja desarquivar todos os chats arquivados?",
+	"Are you sure?": "Você tem certeza?",
+	"Arena Models": "Arena de Modelos",
+	"Artifacts": "Artefatos",
+	"Ask a question": "Faça uma pergunta",
+	"Assistant": "Assistente",
+	"Attach file": "Anexar arquivo",
+	"Attention to detail": "Atenção aos detalhes",
+	"Attribute for Username": "Atribuir para nome de usuário",
+	"Audio": "Áudio",
+	"August": "Agosto",
+	"Authenticate": "Autenticar",
+	"Auto-Copy Response to Clipboard": "Cópia Automática da Resposta para a Área de Transferência",
+	"Auto-playback response": "Resposta de reprodução automática",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "String de Autenticação da API AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "URL Base AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "URL Base AUTOMATIC1111 é necessária.",
+	"Available list": "Lista disponível",
+	"available!": "disponível!",
+	"Awful": "Horrível",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Voltar",
+	"Bad Response": "Resposta Ruim",
+	"Banners": "Banners",
+	"Base Model (From)": "Modelo Base (De)",
+	"Batch Size (num_batch)": "Tamanho do Lote (num_batch)",
+	"before": "antes",
+	"Being lazy": "Sendo preguiçoso",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Chave API do Brave Search",
+	"By {{name}}": "Por {{name}}",
+	"Bypass SSL verification for Websites": "Ignorar verificação SSL para Sites",
+	"Call": "Chamada",
+	"Call feature is not supported when using Web STT engine": "O recurso de chamada não é suportado ao usar o mecanismo Web STT",
+	"Camera": "Câmera",
+	"Cancel": "Cancelar",
+	"Capabilities": "Capacidades",
+	"Certificate Path": "Caminho do Certificado",
+	"Change Password": "Mudar Senha",
+	"Character": "Caracter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Trace novas fronteiras",
+	"Chat": "Chat",
+	"Chat Background Image": "Imagem de Fundo do Chat",
+	"Chat Bubble UI": "Interface de Bolha de Chat",
+	"Chat Controls": "Controles de Chat",
+	"Chat direction": "Direção do Chat",
+	"Chat Overview": "Visão Geral do Chat",
+	"Chat Permissions": "Permissões de Chat",
+	"Chat Tags Auto-Generation": "Tags de Chat Geradas Automaticamente",
+	"Chats": "Chats",
+	"Check Again": "Verificar Novamente",
+	"Check for updates": "Verificar atualizações",
+	"Checking for updates...": "Verificando atualizações...",
+	"Choose a model before saving...": "Escolha um modelo antes de salvar...",
+	"Chunk Overlap": "Sobreposição de Chunk",
+	"Chunk Params": "Parâmetros de Chunk",
+	"Chunk Size": "Tamanho do Chunk",
+	"Ciphers": "Cifras",
+	"Citation": "Citação",
+	"Clear memory": "Limpar memória",
+	"click here": "Clique aqui",
+	"Click here for filter guides.": "Clique aqui para obter instruções de filtros.",
+	"Click here for help.": "Clique aqui para obter ajuda.",
+	"Click here to": "Clique aqui para",
+	"Click here to download user import template file.": "Clique aqui para baixar o arquivo de modelo de importação de usuários.",
+	"Click here to learn more about faster-whisper and see the available models.": "Clique aqui para aprender mais sobre Whisper e ver os modelos disponíveis.",
+	"Click here to select": "Clique aqui para enviar",
+	"Click here to select a csv file.": "Clique aqui para enviar um arquivo csv.",
+	"Click here to select a py file.": "Clique aqui para enviar um arquivo python.",
+	"Click here to upload a workflow.json file.": "Clique aqui para enviar um arquivo workflow.json.",
+	"click here.": "clique aqui.",
+	"Click on the user role button to change a user's role.": "Clique no botão de função do usuário para alterar a função de um usuário.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permissão de escrita na área de transferência negada. Verifique as configurações do seu navegador para conceder o acesso necessário.",
+	"Clone": "Clonar",
+	"Close": "Fechar",
+	"Code execution": "Execução de código",
+	"Code formatted successfully": "Código formatado com sucesso",
+	"Collection": "Coleção",
+	"Color": "Cor",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL Base do ComfyUI",
+	"ComfyUI Base URL is required.": "URL Base do ComfyUI é necessária.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Comando",
+	"Completions": "Conclusões",
+	"Concurrent Requests": "Solicitações Concomitantes",
+	"Configure": "Configurar",
+	"Configure Models": "",
+	"Confirm": "Confirmar",
+	"Confirm Password": "Confirmar Senha",
+	"Confirm your action": "Confirme sua ação",
+	"Connections": "Conexões",
+	"Contact Admin for WebUI Access": "Contate o Admin para Acesso ao WebUI",
+	"Content": "Conteúdo",
+	"Content Extraction": "Extração de Conteúdo",
+	"Context Length": "Tamanho de Contexto",
+	"Continue Response": "Continuar Resposta",
+	"Continue with {{provider}}": "Continuar com {{provider}}",
+	"Continue with Email": "Continuar com Email",
+	"Continue with LDAP": "Continuar com LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Controlar como o texto do mensagem é dividido para solicitações TTS. 'Pontuação' dividida em frases, 'parágrafos' divide em parágrafos e 'não' mantém a mensagem como uma cadeia de caracteres.",
+	"Controls": "Controles",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Controlar o equilibrio entre a coerencia e a diversidade da saída. Um valor mais baixo fará com que o texto seja mais focado e coerente. (Padrão: 5.0)",
+	"Copied": "Copiado",
+	"Copied shared chat URL to clipboard!": "URL de chat compartilhado copiado para a área de transferência!",
+	"Copied to clipboard": "Copiado para a área de transferência",
+	"Copy": "Copiar",
+	"Copy last code block": "Copiar último bloco de código",
+	"Copy last response": "Copiar última resposta",
+	"Copy Link": "Copiar Link",
+	"Copy to clipboard": "Copiar para a área de transferência",
+	"Copying to clipboard was successful!": "Cópia para a área de transferência bem-sucedida!",
+	"Create": "Criar",
+	"Create a knowledge base": "Criar uma base de conhecimento",
+	"Create a model": "Criar um modelo",
+	"Create Account": "Criar Conta",
+	"Create Admin Account": "Criar Conta de Admin",
+	"Create Group": "Criar Grupo",
+	"Create Knowledge": "Criar Conhecimento",
+	"Create new key": "Criar nova chave",
+	"Create new secret key": "Criar nova chave secreta",
+	"Created at": "Criado em",
+	"Created At": "Criado Em",
+	"Created by": "Criado por",
+	"CSV Import": "Importação CSV",
+	"Current Model": "Modelo Atual",
+	"Current Password": "Senha Atual",
+	"Custom": "Personalizado",
+	"Dark": "Escuro",
+	"Database": "Banco de Dados",
+	"December": "Dezembro",
+	"Default": "Padrão",
+	"Default (Open AI)": "Padrão (Open AI)",
+	"Default (SentenceTransformers)": "Padrão (SentenceTransformers)",
+	"Default Model": "Modelo Padrão",
+	"Default model updated": "Modelo padrão atualizado",
+	"Default Models": "",
+	"Default permissions": "Permissões padrão",
+	"Default permissions updated successfully": "Permissões padrão atualizadas com sucesso",
+	"Default Prompt Suggestions": "Sugestões de Prompt Padrão",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "Padrão para TODOS",
+	"Default User Role": "Padrão para novos usuários",
+	"Delete": "Excluir",
+	"Delete a model": "Excluir um modelo",
+	"Delete All Chats": "Excluir Todos os Chats",
+	"Delete All Models": "Excluir Todos os Modelos",
+	"Delete chat": "Excluir chat",
+	"Delete Chat": "Excluir Chat",
+	"Delete chat?": "Excluir chat?",
+	"Delete folder?": "Excluir pasta?",
+	"Delete function?": "Excluir função?",
+	"Delete prompt?": "Excluir prompt?",
+	"delete this link": "Excluir este link",
+	"Delete tool?": "Excluir ferramenta?",
+	"Delete User": "Excluir Usuário",
+	"Deleted {{deleteModelTag}}": "Excluído {{deleteModelTag}}",
+	"Deleted {{name}}": "Excluído {{name}}",
+	"Deleted User": "Usuário Excluído",
+	"Describe your knowledge base and objectives": "Descreva sua base de conhecimento e objetivos",
+	"Description": "Descrição",
+	"Didn't fully follow instructions": "Não seguiu completamente as instruções",
+	"Disabled": "Desativado",
+	"Discover a function": "Descubra uma função",
+	"Discover a model": "Descubra um modelo",
+	"Discover a prompt": "Descubra um prompt",
+	"Discover a tool": "Descubra uma ferramenta",
+	"Discover wonders": "Descobrir maravilhas",
+	"Discover, download, and explore custom functions": "Descubra, baixe e explore funções personalizadas",
+	"Discover, download, and explore custom prompts": "Descubra, baixe e explore prompts personalizados",
+	"Discover, download, and explore custom tools": "Descubra, baixe e explore ferramentas personalizadas",
+	"Discover, download, and explore model presets": "Descubra, baixe e explore predefinições de modelos",
+	"Dismissible": "Descartável",
+	"Display": "Exibir",
+	"Display Emoji in Call": "Exibir Emoji na Chamada",
+	"Display the username instead of You in the Chat": "Exibir o nome de usuário em vez de Você no Chat",
+	"Displays citations in the response": "Exibir citações na resposta",
+	"Dive into knowledge": "Explorar base de conhecimento",
+	"Do not install functions from sources you do not fully trust.": "Não instale funções de fontes que você não confia totalmente.",
+	"Do not install tools from sources you do not fully trust.": "Não instale ferramentas de fontes que você não confia totalmente.",
+	"Document": "Documento",
+	"Documentation": "Documentação",
+	"Documents": "Documentos",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "não faz nenhuma conexão externa, e seus dados permanecem seguros no seu servidor local.",
+	"Don't have an account?": "Não tem uma conta?",
+	"don't install random functions from sources you don't trust.": "não instale funções aleatórias de fontes que você não confia.",
+	"don't install random tools from sources you don't trust.": "não instale ferramentas aleatórias de fontes que você não confia.",
+	"Don't like the style": "Não gosta do estilo",
+	"Done": "Concluído",
+	"Download": "Baixar",
+	"Download canceled": "Download cancelado",
+	"Download Database": "Baixar Banco de Dados",
+	"Drag and drop a file to upload or select a file to view": "Arraste e solte um arquivo para enviar ou selecione um arquivo para visualizar",
+	"Draw": "Empate",
+	"Drop any files here to add to the conversation": "Solte qualquer arquivo aqui para adicionar à conversa",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "por exemplo, '30s', '10m'. Unidades de tempo válidas são 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "Exemplo: Um filtro para remover palavrões do texto",
+	"e.g. My Filter": "Exemplo: Meu Filtro",
+	"e.g. My Tools": "Exemplo: Minhas Ferramentas",
+	"e.g. my_filter": "Exemplo: my_filter",
+	"e.g. my_tools": "Exemplo: my_tools",
+	"e.g. Tools for performing various operations": "Exemplo: Ferramentas para executar operações diversas",
+	"Edit": "Editar",
+	"Edit Arena Model": "Editar Arena de Modelos",
+	"Edit Connection": "Editar Conexão",
+	"Edit Default Permissions": "Editar Permissões Padrão",
+	"Edit Memory": "Editar Memória",
+	"Edit User": "Editar Usuário",
+	"Edit User Group": "Editar Grupo de Usuários",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "Embarque em aventuras",
+	"Embedding Batch Size": "Tamanho do Lote de Embedding",
+	"Embedding Model": "Modelo de Embedding",
+	"Embedding Model Engine": "Motor do Modelo de Embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Modelo de embedding definido para \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Ativar Autenticação por API Key",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Ativar Compartilhamento com a Comunidade",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Habilite o bloqueio de memória (mlock) para evitar que os dados do modelo sejam transferidos da RAM para a área de troca (swap). Essa opção bloqueia o conjunto de páginas em uso pelo modelo na RAM, garantindo que elas não sejam transferidas para o disco. Isso pode ajudar a manter o desempenho, evitando falhas de página e garantindo acesso rápido aos dados.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Habilite o mapeamento de memória (mmap) para carregar dados do modelo. Esta opção permite que o sistema use o armazenamento em disco como uma extensão da RAM, tratando os arquivos do disco como se estivessem na RAM. Isso pode melhorar o desempenho do modelo, permitindo acesso mais rápido aos dados. No entanto, pode não funcionar corretamente com todos os sistemas e consumir uma quantidade significativa de espaço em disco.",
+	"Enable Message Rating": "Ativar Avaliação de Mensagens",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Habilite a amostragem Mirostat para controlar a perplexidade. (Padrão: 0, 0 = Desativado, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Ativar Novos Cadastros",
+	"Enable Web Search": "Ativar Pesquisa na Web",
+	"Enabled": "Ativado",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Certifique-se de que seu arquivo CSV inclua 4 colunas nesta ordem: Nome, Email, Senha, Função.",
+	"Enter {{role}} message here": "Digite a mensagem de {{role}} aqui",
+	"Enter a detail about yourself for your LLMs to recall": "Digite um detalhe sobre você para seus LLMs lembrarem",
+	"Enter api auth string (e.g. username:password)": "Digite a string de autenticação da API (por exemplo, username:password)",
+	"Enter Application DN": "Digite o DN da Aplicação",
+	"Enter Application DN Password": "Digite a Senha do DN da Aplicação",
+	"Enter Bing Search V7 Endpoint": "Digite o Endpoint do Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Digite a Chave de Assinatura do Bing Search V7",
+	"Enter Brave Search API Key": "Digite a Chave API do Brave Search",
+	"Enter certificate path": "Digite o caminho do certificado",
+	"Enter CFG Scale (e.g. 7.0)": "Digite a escala de CFG (por exemplo, 7.0)",
+	"Enter Chunk Overlap": "Digite a Sobreposição de Chunk",
+	"Enter Chunk Size": "Digite o Tamanho do Chunk",
+	"Enter description": "Digite a descrição",
+	"Enter Github Raw URL": "Digite a URL bruta do Github",
+	"Enter Google PSE API Key": "Digite a Chave API do Google PSE",
+	"Enter Google PSE Engine Id": "Digite o ID do Motor do Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Digite o Tamanho da Imagem (por exemplo, 512x512)",
+	"Enter Jina API Key": "Digite a Chave API Jina",
+	"Enter language codes": "Digite os códigos de idioma",
+	"Enter Model ID": "Digite o ID do modelo",
+	"Enter model tag (e.g. {{modelTag}})": "Digite a tag do modelo (por exemplo, {{modelTag}})",
+	"Enter Mojeek Search API Key": "Digite a Chave API do Mojeek Search",
+	"Enter Number of Steps (e.g. 50)": "Digite o Número de Passos (por exemplo, 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Digite o Sampler (por exemplo, Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Digite o Agendador (por exemplo, Karras)",
+	"Enter Score": "Digite a Pontuação",
+	"Enter SearchApi API Key": "Digite a Chave API do SearchApi",
+	"Enter SearchApi Engine": "Digite o Motor do SearchApi",
+	"Enter Searxng Query URL": "Digite a URL de Consulta do Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Digite a Chave API do Serper",
+	"Enter Serply API Key": "Digite a Chave API do Serply",
+	"Enter Serpstack API Key": "Digite a Chave API do Serpstack",
+	"Enter server host": "Digite o host do servidor",
+	"Enter server label": "Digite o label do servidor",
+	"Enter server port": "Digite a porta do servidor",
+	"Enter stop sequence": "Digite a sequência de parada",
+	"Enter system prompt": "Digite o prompt do sistema",
+	"Enter Tavily API Key": "Digite a Chave API do Tavily",
+	"Enter Tika Server URL": "Digite a URL do Servidor Tika",
+	"Enter Top K": "Digite o Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Digite a URL (por exemplo, http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Digite a URL (por exemplo, http://localhost:11434)",
+	"Enter Your Email": "Digite Seu Email",
+	"Enter Your Full Name": "Digite Seu Nome Completo",
+	"Enter your message": "Digite sua mensagem",
+	"Enter Your Password": "Digite Sua Senha",
+	"Enter Your Role": "Digite Sua Função",
+	"Enter Your Username": "Digite seu usuário",
+	"Error": "Erro",
+	"ERROR": "",
+	"Evaluations": "Avaliações",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Exemplo: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Exemplo: ALL",
+	"Example: ou=users,dc=foo,dc=example": "Exemplo: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Exemplo: sAMAccountName ou uid ou userPrincipalName",
+	"Exclude": "Excluir",
+	"Experimental": "Experimental",
+	"Explore the cosmos": "Explorar o cosmos",
+	"Export": "Exportar",
+	"Export All Archived Chats": "Exportar todos os chats arquivados",
+	"Export All Chats (All Users)": "Exportar Todos os Chats (Todos os Usuários)",
+	"Export chat (.json)": "Exportar chat (.json)",
+	"Export Chats": "Exportar Chats",
+	"Export Config to JSON File": "Exportar Configuração para Arquivo JSON",
+	"Export Functions": "Exportar Funções",
+	"Export Models": "Exportar Modelos",
+	"Export Presets": "Exportar Presets",
+	"Export Prompts": "Exportar Prompts",
+	"Export to CSV": "Exportar para CSV",
+	"Export Tools": "Exportar Ferramentas",
+	"External Models": "Modelos Externos",
+	"Failed to add file.": "Falha ao adicionar arquivo.",
+	"Failed to create API Key.": "Falha ao criar a Chave API.",
+	"Failed to read clipboard contents": "Falha ao ler o conteúdo da área de transferência",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Falha ao atualizar as configurações",
+	"Failed to upload file.": "Falha ao carregar o arquivo.",
+	"February": "Fevereiro",
+	"Feedback History": "Histórico de comentários",
+	"Feedbacks": "Comentários",
+	"Feel free to add specific details": "Sinta-se à vontade para adicionar detalhes específicos",
+	"File": "Arquivo",
+	"File added successfully.": "Arquivo adicionado com sucesso.",
+	"File content updated successfully.": "Arquivo de conteúdo atualizado com sucesso.",
+	"File Mode": "Modo de Arquivo",
+	"File not found.": "Arquivo não encontrado.",
+	"File removed successfully.": "Arquivo removido com sucesso.",
+	"File size should not exceed {{maxSize}} MB.": "Arquivo não pode exceder {{maxSize}} MB.",
+	"Files": "Arquivos",
+	"Filter is now globally disabled": "O filtro está agora desativado globalmente",
+	"Filter is now globally enabled": "O filtro está agora ativado globalmente",
+	"Filters": "Filtros",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Falsificação de impressão digital detectada: Não foi possível usar as iniciais como avatar. Usando a imagem de perfil padrão.",
+	"Fluidly stream large external response chunks": "Transmitir fluentemente grandes blocos de respostas externas",
+	"Focus chat input": "Focar entrada de chat",
+	"Folder deleted successfully": "Pasta excluída com sucesso",
+	"Folder name cannot be empty": "Nome da pasta não pode estar vazio",
+	"Folder name cannot be empty.": "Nome da pasta não pode estar vazio.",
+	"Folder name updated successfully": "Nome da pasta atualizado com sucesso",
+	"Followed instructions perfectly": "Seguiu as instruções perfeitamente",
+	"Forge new paths": "Trilhar novos caminhos",
+	"Form": "Formulário",
+	"Format your variables using brackets like this:": "Formate suas variáveis usando colchetes como este:",
+	"Frequency Penalty": "Penalização por Frequência",
+	"Function": "Função",
+	"Function created successfully": "Função criada com sucesso",
+	"Function deleted successfully": "Função excluída com sucesso",
+	"Function Description": "Descrição da Função",
+	"Function ID": "ID da Função",
+	"Function is now globally disabled": "A função está agora desativada globalmente",
+	"Function is now globally enabled": "A função está agora ativada globalmente",
+	"Function Name": "Nome da Função",
+	"Function updated successfully": "Função atualizada com sucesso",
+	"Functions": "Funções",
+	"Functions allow arbitrary code execution": "Funções permitem a execução arbitrária de código",
+	"Functions allow arbitrary code execution.": "Funções permitem a execução arbitrária de código.",
+	"Functions imported successfully": "Funções importadas com sucesso",
+	"General": "Geral",
+	"General Settings": "Configurações Gerais",
+	"Generate Image": "Gerar Imagem",
+	"Generating search query": "Gerando consulta de pesquisa",
+	"Generation Info": "Informações de Geração",
+	"Get started": "Iniciar",
+	"Get started with {{WEBUI_NAME}}": "Iniciar com {{WEBUI_NAME}}",
+	"Global": "Global",
+	"Good Response": "Boa Resposta",
+	"Google PSE API Key": "Chave API do Google PSE",
+	"Google PSE Engine Id": "ID do Motor do Google PSE",
+	"Group created successfully": "Grupo criado com sucesso",
+	"Group deleted successfully": "Grupo excluído com sucesso",
+	"Group Description": "Descrição do Grupo",
+	"Group Name": "Nome do Grupo",
+	"Group updated successfully": "Grupo atualizado com sucesso",
+	"Groups": "Grupos",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "não tem conversas.",
+	"Hello, {{name}}": "Olá, {{name}}",
+	"Help": "Ajuda",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Ajude-nos a criar o melhor ranking da comunidade compartilhando sua historia de comentários!",
+	"Hex Color": "Cor hexadecimal",
+	"Hex Color - Leave empty for default color": "Cor Hexadecimal - Deixe em branco para a cor padrão",
+	"Hide": "Ocultar",
+	"Host": "Servidor",
+	"How can I help you today?": "Como posso ajudar você hoje?",
+	"How would you rate this response?": "Como você avalia essa resposta?",
+	"Hybrid Search": "Pesquisa Híbrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Eu reconheço que li e entendi as implicações da minha ação. Estou ciente dos riscos associados à execução de código arbitrário e verifiquei a confiabilidade da fonte.",
+	"ID": "",
+	"Ignite curiosity": "Desperte a curiosidade",
+	"Image Generation (Experimental)": "Geração de Imagem (Experimental)",
+	"Image Generation Engine": "Motor de Geração de Imagem",
+	"Image Settings": "Configurações de Imagem",
+	"Images": "Imagens",
+	"Import Chats": "Importar Chats",
+	"Import Config from JSON File": "Importar Configurações de JSON",
+	"Import Functions": "Importar Funções",
+	"Import Models": "Importar Modelos",
+	"Import Presets": "Importar Presets",
+	"Import Prompts": "Importar Prompts",
+	"Import Tools": "Importar Ferramentas",
+	"Include": "Incluir",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Incluir a flag `--api-auth` ao executar stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Incluir a flag `--api` ao executar stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Define a rapidez com que o algoritmo responde ao feedback do texto gerado. Uma taxa de aprendizado menor resultará em ajustes mais lentos, enquanto uma taxa maior tornará o algoritmo mais responsivo. (Padrão: 0,1)",
+	"Info": "Informação",
+	"Input commands": "Comandos de entrada",
+	"Install from Github URL": "Instalar da URL do Github",
+	"Instant Auto-Send After Voice Transcription": "Envio Automático Instantâneo Após Transcrição de Voz",
+	"Interface": "Interface",
+	"Invalid file format.": "Formato de arquivo inválido.",
+	"Invalid Tag": "Tag Inválida",
+	"January": "Janeiro",
+	"Jina API Key": "",
+	"join our Discord for help.": "junte-se ao nosso Discord para ajudar.",
+	"JSON": "JSON",
+	"JSON Preview": "Pré-visualização JSON",
+	"July": "Julho",
+	"June": "Junho",
+	"JWT Expiration": "Expiração do JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Manter Vivo",
+	"Key": "Chave",
+	"Keyboard shortcuts": "Atalhos de Teclado",
+	"Knowledge": "Conhecimento",
+	"Knowledge Access": "Acesso ao Conhecimento",
+	"Knowledge created successfully.": "Conhecimento criado com sucesso.",
+	"Knowledge deleted successfully.": "Conhecimento excluído com sucesso.",
+	"Knowledge reset successfully.": "Conhecimento resetado com sucesso.",
+	"Knowledge updated successfully": "Conhecimento atualizado com sucesso",
+	"Label": "Rótulo",
+	"Landing Page Mode": "Modo Landing Page",
+	"Language": "Idioma",
+	"Last Active": "Última Atividade",
+	"Last Modified": "Última Modificação",
+	"LDAP": "",
+	"LDAP server updated": "Servidor LDAP atualizado",
+	"Leaderboard": "Tabela de classificação",
+	"Leave empty for unlimited": "Deixe vazio para ilimitado",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Deixe vazio para incluir todos os modelos do endpoint \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Deixe vazio para incluir todos os modelos do endpoint \"{{URL}}/models\"",
+	"Leave empty to include all models or select specific models": "Deixe vazio para incluir todos os modelos ou selecione modelos especificos",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Deixe vazio para usar o prompt padrão, ou insira um prompt personalizado",
+	"Light": "Claro",
+	"Listening...": "Escutando...",
+	"LLMs can make mistakes. Verify important information.": "LLMs podem cometer erros. Verifique informações importantes.",
+	"Local": "",
+	"Local Models": "Modelos Locais",
+	"Lost": "Perdeu",
+	"LTR": "Esquerda para Direita",
+	"Made by OpenWebUI Community": "Feito pela Comunidade OpenWebUI",
+	"Make sure to enclose them with": "Certifique-se de encerrá-los com",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Certifique-se de exportar um arquivo workflow.json como o formato API do ComfyUI.",
+	"Manage": "Gerenciar",
+	"Manage Arena Models": "Gerenciar Arena de Modelos",
+	"Manage Ollama": "Gerenciar Ollama",
+	"Manage Ollama API Connections": "Gerenciar Conexões Ollama API",
+	"Manage OpenAI API Connections": "Gerenciar Conexões OpenAI API",
+	"Manage Pipelines": "Gerenciar Pipelines",
+	"March": "Março",
+	"Max Tokens (num_predict)": "Máximo de Tokens (num_predict)",
+	"Max Upload Count": "Quantidade máxima de anexos",
+	"Max Upload Size": "Tamanho máximo do arquivo",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Máximo de 3 modelos podem ser baixados simultaneamente. Por favor, tente novamente mais tarde.",
+	"May": "Maio",
+	"Memories accessible by LLMs will be shown here.": "Memórias acessíveis por LLMs serão mostradas aqui.",
+	"Memory": "Memória",
+	"Memory added successfully": "Memória adicionada com sucesso",
+	"Memory cleared successfully": "Memória limpa com sucesso",
+	"Memory deleted successfully": "Memória excluída com sucesso",
+	"Memory updated successfully": "Memória atualizada com sucesso",
+	"Merge Responses": "Mesclar respostas",
+	"Message rating should be enabled to use this feature": "Mensagem de avaliação deve estar habilitada para usar esta função",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Mensagens enviadas após criar seu link não serão compartilhadas. Usuários com o URL poderão visualizar o chat compartilhado.",
+	"Min P": "",
+	"Minimum Score": "Pontuação Mínima",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Modelo",
+	"Model '{{modelName}}' has been successfully downloaded.": "Modelo '{{modelName}}' foi baixado com sucesso.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modelo '{{modelTag}}' já está na fila para download.",
+	"Model {{modelId}} not found": "Modelo {{modelId}} não encontrado",
+	"Model {{modelName}} is not vision capable": "Modelo {{modelName}} não é capaz de visão",
+	"Model {{name}} is now {{status}}": "Modelo {{name}} está agora {{status}}",
+	"Model accepts image inputs": "Modelo aceita entradas de imagens",
+	"Model created successfully!": "Modelo criado com sucesso!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Caminho do sistema de arquivos do modelo detectado. Nome curto do modelo é necessário para atualização, não é possível continuar.",
+	"Model Filtering": "Filtrando modelo",
+	"Model ID": "ID do Modelo",
+	"Model IDs": "IDs do modelo",
+	"Model Name": "Nome do Modelo",
+	"Model not selected": "Modelo não selecionado",
+	"Model Params": "Parâmetros do Modelo",
+	"Model Permissions": "Permissões do Modelo",
+	"Model updated successfully": "Modelo atualizado com sucesso",
+	"Modelfile Content": "Conteúdo do Arquivo do Modelo",
+	"Models": "Modelos",
+	"Models Access": "Acesso aos Modelos",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "Chave de API Mojeel Search",
+	"more": "mais",
+	"More": "Mais",
+	"Name": "Nome",
+	"Name your knowledge base": "Nome da sua base de conhecimento",
+	"New Chat": "Novo Chat",
+	"New folder": "Nova pasta",
+	"New Password": "Nova Senha",
+	"No content found": "Nenhum conteúdo encontrado",
+	"No content to speak": "Sem conteúdo para falar",
+	"No distance available": "Sem distância disponível",
+	"No feedbacks found": "Comentários não encontrados",
+	"No file selected": "Nenhum arquivo selecionado",
+	"No files found.": "Nenhum arquivo encontrado.",
+	"No groups with access, add a group to grant access": "Nenhum grupo com acesso, adicione um grupo para dar acesso",
+	"No HTML, CSS, or JavaScript content found.": "Nenhum conteúdo HTML, CSS ou JavaScript encontrado.",
+	"No knowledge found": "Nenhum conhecimento encontrado",
+	"No model IDs": "Nenhum ID de modelo",
+	"No models found": "Nenhum modelo encontrado",
+	"No models selected": "",
+	"No results found": "Nenhum resultado encontrado",
+	"No search query generated": "Nenhuma consulta de pesquisa gerada",
+	"No source available": "Nenhuma fonte disponível",
+	"No users were found.": "Nenhum usuário foi encontrado.",
+	"No valves to update": "Nenhuma válvula para atualizar",
+	"None": "Nenhum",
+	"Not factually correct": "Não está factualmente correto",
+	"Not helpful": "Não é útil",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Se você definir uma pontuação mínima, a pesquisa retornará apenas documentos com pontuação igual ou superior à pontuação mínima.",
+	"Notes": "Notas",
+	"Notifications": "Notificações",
+	"November": "Novembro",
+	"num_gpu (Ollama)": "Número de GPUs (Ollama)",
+	"num_thread (Ollama)": "Número de Threads (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Outubro",
+	"Off": "Desligado",
+	"Okay, Let's Go!": "Ok, Vamos Lá!",
+	"OLED Dark": "OLED Escuro",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama desativada",
+	"Ollama API settings updated": "Configurações da API Ollama atualizadas",
+	"Ollama Version": "Versão Ollama",
+	"On": "Ligado",
+	"Only alphanumeric characters and hyphens are allowed": "Somente caracteres alfanuméricos e hífens são permitidos",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Apenas caracteres alfanuméricos e hífens são permitidos na string de comando.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Somente coleções podem ser editadas. Crie uma nova base de conhecimento para editar/adicionar documentos.",
+	"Only select users and groups with permission can access": "Somente usuários e grupos selecionados com permissão podem acessar.",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ops! Parece que a URL é inválida. Por favor, verifique novamente e tente de novo.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Ops! Existem arquivos a serem carregados. Por favor, aguarde que o carregamento tenha concluído.",
+	"Oops! There was an error in the previous response.": "Ops! Houve um erro na resposta anterior.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ops! Você está usando um método não suportado (somente frontend). Por favor, sirva a WebUI a partir do backend.",
+	"Open file": "Abrir arquivo",
+	"Open in full screen": "Abrir em tela cheia",
+	"Open new chat": "Abrir novo chat",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI usa faster-whisper internamente.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "A Open WebUI usa os embeddings de voz do SpeechT5 e do CMU Arctic.",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "A versão do Open WebUI (v{{OPEN_WEBUI_VERSION}}) é inferior à versão necessária (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Configuração da API OpenAI",
+	"OpenAI API Key is required.": "Chave API OpenAI é necessária.",
+	"OpenAI API settings updated": "Configurações OpenAI atualizadas",
+	"OpenAI URL/Key required.": "URL/Chave OpenAI necessária.",
+	"or": "ou",
+	"Organize your users": "Organizar seus usuários",
+	"Other": "Outro",
+	"OUTPUT": "SAÍDA",
+	"Output format": "Formato de saída",
+	"Overview": "Visão Geral",
+	"page": "página",
+	"Password": "Senha",
+	"Paste Large Text as File": "Cole Textos Longos como Arquivo",
+	"PDF document (.pdf)": "Documento PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Extrair Imagens do PDF (OCR)",
+	"pending": "pendente",
+	"Permission denied when accessing media devices": "Permissão negada ao acessar dispositivos de mídia",
+	"Permission denied when accessing microphone": "Permissão negada ao acessar o microfone",
+	"Permission denied when accessing microphone: {{error}}": "Permissão negada ao acessar o microfone: {{error}}",
+	"Permissions": "Permissões",
+	"Personalization": "Personalização",
+	"Pin": "Fixar",
+	"Pinned": "Fixado",
+	"Pioneer insights": "Insights pioneiros",
+	"Pipeline deleted successfully": "Pipeline excluído com sucesso",
+	"Pipeline downloaded successfully": "Pipeline baixado com sucesso",
+	"Pipelines": "Pipelines",
+	"Pipelines Not Detected": "Pipelines Não Detectados",
+	"Pipelines Valves": "Válvulas de Pipelines",
+	"Plain text (.txt)": "Texto simples (.txt)",
+	"Playground": "Playground",
+	"Please carefully review the following warnings:": "Por favor, revise cuidadosamente os seguintes avisos:",
+	"Please enter a prompt": "Por favor, digite um prompt",
+	"Please fill in all fields.": "Por favor, preencha todos os campos.",
+	"Please select a model first.": "",
+	"Please select a reason": "Por favor, seleccione uma razão",
+	"Port": "Porta",
+	"Positive attitude": "Atitude positiva",
+	"Prefix ID": "Prefixo ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "O ID de prefixo é utilizado para evitar conflitos com outras conexões, adicionando um prefixo aos IDs dos modelos - deixe em branco para desativar.",
+	"Previous 30 days": "Últimos 30 dias",
+	"Previous 7 days": "Últimos 7 dias",
+	"Profile Image": "Imagem de Perfil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (por exemplo, Diga-me um fato divertido sobre o Império Romano)",
+	"Prompt Content": "Conteúdo do Prompt",
+	"Prompt created successfully": "Prompt criado com sucesso",
+	"Prompt suggestions": "Sugestões de Prompt",
+	"Prompt updated successfully": "Prompt atualizado com sucesso",
+	"Prompts": "Prompts",
+	"Prompts Access": "Acessar prompts",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Obter \"{{searchValue}}\" de Ollama.com",
+	"Pull a model from Ollama.com": "Obter um modelo de Ollama.com",
+	"Query Generation Prompt": "Prompt de Geração de Consulta",
+	"Query Params": "Parâmetros de Consulta",
+	"RAG Template": "Modelo RAG",
+	"Rating": "Avaliação",
+	"Re-rank models by topic similarity": "Reclassificação de modelos por similaridade de tópico",
+	"Read Aloud": "Ler em Voz Alta",
+	"Record voice": "Gravar voz",
+	"Redirecting you to OpenWebUI Community": "Redirecionando você para a Comunidade OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Reduz a probabilidade de gerar absurdos. Um valor mais alto (por exemplo, 100) dará respostas mais diversas, enquanto um valor mais baixo (por exemplo, 10) será mais conservador. (Padrão: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Refira-se como \"Usuário\" (por exemplo, \"Usuário está aprendendo espanhol\")",
+	"References from": "Referências de",
+	"Refused when it shouldn't have": "Recusado quando não deveria",
+	"Regenerate": "Gerar novamente",
+	"Release Notes": "Notas de Lançamento",
+	"Relevance": "Relevância",
+	"Remove": "Remover",
+	"Remove Model": "Remover Modelo",
+	"Rename": "Renomear",
+	"Reorder Models": "",
+	"Repeat Last N": "Repetir Último N",
+	"Request Mode": "Modo de Solicitação",
+	"Reranking Model": "Modelo de Reclassificação",
+	"Reranking model disabled": "Modelo de Reclassificação desativado",
+	"Reranking model set to \"{{reranking_model}}\"": "Modelo de Reclassificação definido como \"{{reranking_model}}\"",
+	"Reset": "Redefinir",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Redefinir Diretório de Upload",
+	"Reset Vector Storage/Knowledge": "Redefinir Armazenamento de Vetores/Conhecimento",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Notificações de resposta não podem ser ativadas pois as permissões do site foram negadas. Por favor, visite as configurações do seu navegador para conceder o acesso necessário.",
+	"Response splitting": "Divisão da Resposta",
+	"Result": "Resultado",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Entrada de rich text para bate-papo",
+	"RK": "",
+	"Role": "Função",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "Direita para Esquerda",
+	"Run": "Executar",
+	"Running": "Executando",
+	"Save": "Salvar",
+	"Save & Create": "Salvar e Criar",
+	"Save & Update": "Salvar e Atualizar",
+	"Save As Copy": "Salvar Como Cópia",
+	"Save Tag": "Salvar Tag",
+	"Saved": "Armazenado",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Salvar registros de chat diretamente no armazenamento do seu navegador não é mais suportado. Por favor, reserve um momento para baixar e excluir seus registros de chat clicando no botão abaixo. Não se preocupe, você pode facilmente reimportar seus registros de chat para o backend através de",
+	"Scroll to bottom when switching between branches": "Rolar para baixo quando se troca entre modelos",
+	"Search": "Pesquisar",
+	"Search a model": "Pesquisar um modelo",
+	"Search Base": "Pesquisar base",
+	"Search Chats": "Pesquisar Chats",
+	"Search Collection": "Pesquisar Coleção",
+	"Search Filters": "Pesquisar Filtros",
+	"search for tags": "Pesquisar por tags",
+	"Search Functions": "Pesquisar Funções",
+	"Search Knowledge": "Pesquisar Conhecimento",
+	"Search Models": "Pesquisar Modelos",
+	"Search options": "Opções de pesquisa",
+	"Search Prompts": "Prompts de Pesquisa",
+	"Search Result Count": "Contagem de Resultados da Pesquisa",
+	"Search the web": "Pesquisar web",
+	"Search Tools": "Pesquisar Ferramentas",
+	"SearchApi API Key": "Chave API SearchApi",
+	"SearchApi Engine": "Motor SearchApi",
+	"Searched {{count}} sites_one": "Pesquisou {{count}} sites_one",
+	"Searched {{count}} sites_many": "Pesquisou {{count}} sites_many",
+	"Searched {{count}} sites_other": "Pesquisou {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Pesquisando \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Buscando conhecimento para \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL da Consulta Searxng",
+	"See readme.md for instructions": "Veja readme.md para instruções",
+	"See what's new": "Veja o que há de novo",
+	"Seed": "Seed",
+	"Select a base model": "Selecione um modelo base",
+	"Select a engine": "Selecione um motor",
+	"Select a function": "Selecione uma função",
+	"Select a group": "Selecionar grupo",
+	"Select a model": "Selecione um modelo",
+	"Select a pipeline": "Selecione um pipeline",
+	"Select a pipeline url": "Selecione uma URL de pipeline",
+	"Select a tool": "Selecione uma ferramenta",
+	"Select Engine": "Selecionar Motor",
+	"Select Knowledge": "Selecionar Conhecimento",
+	"Select model": "Selecionar modelo",
+	"Select only one model to call": "Selecione apenas um modelo para chamar",
+	"Selected model(s) do not support image inputs": "Modelo(s) selecionado(s) não suportam entradas de imagem",
+	"Semantic distance to query": "Distância semântica para consulta",
+	"Send": "Enviar",
+	"Send a Message": "Enviar uma Mensagem",
+	"Send message": "Enviar mensagem",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Envia `stream_options: { include_usage: true }` na solicitação. Provedores compatíveis retornarão informações sobre o uso de tokens na resposta quando configurado.",
+	"September": "Setembro",
+	"Serper API Key": "Chave da API Serper",
+	"Serply API Key": "Chave da API Serply",
+	"Serpstack API Key": "Chave da API Serpstack",
+	"Server connection verified": "Conexão com o servidor verificada",
+	"Set as default": "Definir como padrão",
+	"Set CFG Scale": "Definir escala CFG",
+	"Set Default Model": "Definir Modelo Padrão",
+	"Set embedding model": "Definir modelo de embedding",
+	"Set embedding model (e.g. {{model}})": "Definir modelo de embedding (por exemplo, {{model}})",
+	"Set Image Size": "Definir Tamanho da Imagem",
+	"Set reranking model (e.g. {{model}})": "Definir modelo de reclassificação (por exemplo, {{model}})",
+	"Set Sampler": "Definir Sampler",
+	"Set Scheduler": "Definir Agendador",
+	"Set Steps": "Definir Etapas",
+	"Set Task Model": "Definir Modelo de Tarefa",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Defina o número de dispositivos GPU usados para computação. Esta opção controla quantos dispositivos GPU (se disponíveis) são usados para processar as solicitações recebidas. Aumentar esse valor pode melhorar significativamente o desempenho para modelos otimizados para aceleração de GPU, mas também pode consumir mais energia e recursos da GPU.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Defina o número de threads de trabalho usadas para computação. Esta opção controla quantos threads são usados para processar as solicitações recebidas de forma simultânea. Aumentar esse valor pode melhorar o desempenho em cargas de trabalho de alta concorrência, mas também pode consumir mais recursos da CPU.",
+	"Set Voice": "Definir Voz",
+	"Set whisper model": "Definir modelo Whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Define a distância de retrocesso que o modelo deve olhar para evitar repetições. (Padrão: 64, 0 = desativado, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Define com que intensidade as repetições serão penalizadas. Um valor mais alto (por exemplo, 1,5) penalizará as repetições mais fortemente, enquanto um valor mais baixo (por exemplo, 0,9) será mais permissivo. (Padrão: 1,1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Define a semente do número aleatório a ser usada para a geração. Definir isso como um número específico fará com que o modelo gere o mesmo texto para o mesmo prompt. (Padrão: aleatório)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Define o tamanho da janela de contexto usada para gerar o próximo token. (Padrão: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Define as sequências de parada a serem usadas. Quando esse padrão for encontrado, o modelo de linguagem (LLM) parará de gerar texto e retornará. Vários padrões de parada podem ser definidos especificando parâmetros de parada separados em um arquivo de modelo.",
+	"Settings": "Configurações",
+	"Settings saved successfully!": "Configurações salvas com sucesso!",
+	"Share": "Compartilhar",
+	"Share Chat": "Compartilhar Chat",
+	"Share to OpenWebUI Community": "Compartilhar com a Comunidade OpenWebUI",
+	"Show": "Mostrar",
+	"Show \"What's New\" modal on login": "Mostrar \"O que há de Novo\" no login",
+	"Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na Sobreposição de Conta Pendentes",
+	"Show shortcuts": "Mostrar atalhos",
+	"Show your support!": "Mostre seu apoio!",
+	"Showcased creativity": "Criatividade exibida",
+	"Sign in": "Entrar",
+	"Sign in to {{WEBUI_NAME}}": "Faça login em {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Faça login em {{WEBUI_NAME}} com LDAP",
+	"Sign Out": "Sair",
+	"Sign up": "Inscrever-se",
+	"Sign up to {{WEBUI_NAME}}": "Inscreva-se em {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Fazendo login em {{WEBUI_NAME}}",
+	"Source": "Fonte",
+	"Speech Playback Speed": "Velocidade de reprodução de fala",
+	"Speech recognition error: {{error}}": "Erro de reconhecimento de fala: {{error}}",
+	"Speech-to-Text Engine": "Motor de Transcrição de Fala",
+	"Stop": "Parar",
+	"Stop Sequence": "Sequência de Parada",
+	"Stream Chat Response": "Stream Resposta do Chat",
+	"STT Model": "Modelo STT",
+	"STT Settings": "Configurações STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtítulo (por exemplo, sobre o Império Romano)",
+	"Success": "Sucesso",
+	"Successfully updated.": "Atualizado com sucesso.",
+	"Suggested": "Sugerido",
+	"Support": "Suporte",
+	"Support this plugin:": "Apoie este plugin:",
+	"Sync directory": "",
+	"System": "Sistema",
+	"System Instructions": "Instruções do sistema",
+	"System Prompt": "Prompt do Sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Prompt para geração de Tags",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "A amostragem *tail free* é usada para reduzir o impacto de tokens menos prováveis na saída. Um valor mais alto (por exemplo, 2,0) reduzirá mais o impacto, enquanto um valor de 1,0 desativa essa configuração. (Padrão: 1)",
+	"Tap to interrupt": "Toque para interromper",
+	"Tavily API Key": "Chave da API Tavily",
+	"Tell us more:": "Conte-nos mais:",
+	"Temperature": "Temperatura",
+	"Template": "Template",
+	"Temporary Chat": "Chat temporário",
+	"Text Splitter": "Divisor de Texto",
+	"Text-to-Speech Engine": "Motor de Texto para Fala",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Obrigado pelo seu comentário!",
+	"The Application Account DN you bind with for search": "O DN (Distinguished Name) da Conta de Aplicação com a qual você se conecta para pesquisa.",
+	"The base to search for users": "Base para pesquisar usuários.",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "O tamanho do lote (batch size) determina quantas solicitações de texto são processadas juntas de uma vez. Um tamanho de lote maior pode aumentar o desempenho e a velocidade do modelo, mas também requer mais memória. (Padrão: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Os desenvolvedores por trás deste plugin são voluntários apaixonados da comunidade. Se você achar este plugin útil, considere contribuir para o seu desenvolvimento.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "A evolução do ranking de avaliação é baseada no sistema Elo e será atualizada em tempo real.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "O atributo LDAP que mapeia para o nome de usuário que os usuários usam para fazer login.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "O ranking atual está em beta, e podemos ajustar as contas de avaliação como refinamos o algoritmo.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Máximo tamanho de arquivo em MB. Se o tamanho do arquivo exceder este limite, o arquivo não será enviado.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "O número máximo de arquivos que podem ser utilizados a cada vez em chat. Se o número de arquivos exceder este limite, os arquivos não serão enviados.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "A pontuação deve ser um valor entre 0.0 (0%) e 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Temperatura do modelo. Aumentar a temperatura fará com que o modelo responda de forma mais criativa. (Padrão: 0,8)",
+	"Theme": "Tema",
+	"Thinking...": "Pensando...",
+	"This action cannot be undone. Do you wish to continue?": "Esta ação não pode ser desfeita. Você deseja continuar?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Isso garante que suas conversas valiosas sejam salvas com segurança no banco de dados do backend. Obrigado!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Esta é uma funcionalidade experimental, pode não funcionar como esperado e está sujeita a alterações a qualquer momento.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Esta opção controla quantos tokens são preservados ao atualizar o contexto. Por exemplo, se definido como 2, os últimos 2 tokens do contexto da conversa serão mantidos. Preservar o contexto pode ajudar a manter a continuidade da conversa, mas pode reduzir a capacidade de responder a novos tópicos. (Padrão: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Esta opção define o número máximo de tokens que o modelo pode gerar em sua resposta. Aumentar esse limite permite que o modelo forneça respostas mais longas, mas também pode aumentar a probabilidade de gerar conteúdo irrelevante ou não útil. (Padrão: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Essa opção deletará todos os arquivos existentes na coleção e todos eles serão substituídos.",
+	"This response was generated by \"{{model}}\"": "Esta resposta foi gerada por \"{{model}}\"",
+	"This will delete": "Isso vai excluir",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Esta ação excluirá <strong>{{NAME}}</strong> e <strong>todos seus conteúdos</strong>.",
+	"This will delete all models including custom models": "Isto vai excluir todos os modelos, incluindo personalizados",
+	"This will delete all models including custom models and cannot be undone.": "Isto vai excluir todos os modelos, incluindo personalizados e não pode ser desfeito.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Esta ação resetará a base de conhecimento e sincronizará todos os arquivos. Deseja continuar?",
+	"Thorough explanation": "Explicação detalhada",
+	"Tika": "Tika",
+	"Tika Server URL required.": "URL do servidor Tika necessária.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Dica: Atualize vários slots de variáveis consecutivamente pressionando a tecla Tab na entrada de chat após cada substituição.",
+	"Title": "Título",
+	"Title (e.g. Tell me a fun fact)": "Título (por exemplo, Conte-me um fato divertido)",
+	"Title Auto-Generation": "Geração Automática de Título",
+	"Title cannot be an empty string.": "O Título não pode ser uma string vazia.",
+	"Title Generation Prompt": "Prompt de Geração de Título",
+	"TLS": "",
+	"To access the available model names for downloading,": "Para acessar os nomes de modelos disponíveis para download,",
+	"To access the GGUF models available for downloading,": "Para acessar os modelos GGUF disponíveis para download,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para acessar a WebUI, entre em contato com o administrador. Os administradores podem gerenciar os status dos usuários no Painel de Administração.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Para anexar a base de conhecimento aqui, adicione-os ao espaço de trabalho \"Conhecimento\" primeiro.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Para proteger sua privacidade, apenas classificações, IDs de modelo, tags e metadados são compartilhados a partir de seus comentários – seus registros de bate-papo permanecem privados e não são incluídos.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Para selecionar ações aqui, adicione-os ao espaço de trabalho \"Ações\" primeiro.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Para selecionar filtros aqui, adicione-os ao espaço de trabalho \"Funções\" primeiro.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Para selecionar kits de ferramentas aqui, adicione-os ao espaço de trabalho \"Ferramentas\" primeiro.",
+	"Toast notifications for new updates": "Notificações de alerta para novas atualizações",
+	"Today": "Hoje",
+	"Toggle settings": "Alternar configurações",
+	"Toggle sidebar": "Alternar barra lateral",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens a Manter na Atualização do Contexto (num_keep)",
+	"Too verbose": "Muito detalhado",
+	"Tool created successfully": "Ferramenta criada com sucesso",
+	"Tool deleted successfully": "Ferramenta excluída com sucesso",
+	"Tool Description": "Descrição da ferramenta",
+	"Tool ID": "ID da ferramenta",
+	"Tool imported successfully": "Ferramenta importada com sucesso",
+	"Tool Name": "Nome da ferramenta",
+	"Tool updated successfully": "Ferramenta atualizada com sucesso",
+	"Tools": "Ferramentas",
+	"Tools Access": "Acesso as Ferramentas",
+	"Tools are a function calling system with arbitrary code execution": "Ferramentas são um sistema de chamada de funções com execução de código arbitrário",
+	"Tools have a function calling system that allows arbitrary code execution": "Ferramentas possuem um sistema de chamada de funções que permite a execução de código arbitrário",
+	"Tools have a function calling system that allows arbitrary code execution.": "Ferramentas possuem um sistema de chamada de funções que permite a execução de código arbitrário.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemas para acessar o Ollama?",
+	"TTS Model": "Modelo TTS",
+	"TTS Settings": "Configurações TTS",
+	"TTS Voice": "Voz TTS",
+	"Type": "Tipo",
+	"Type Hugging Face Resolve (Download) URL": "Digite o URL de download do Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ops! Houve um problema ao conectar-se ao {{provider}}.",
+	"UI": "Interface",
+	"Unarchive All": "Desarquivar tudo",
+	"Unarchive All Archived Chats": "Desarquivar Todos os Chats Arquivados",
+	"Unarchive Chat": "Desarquivar Chat",
+	"Unlock mysteries": "Desvendar mistérios",
+	"Unpin": "Desfixar",
+	"Unravel secrets": "Desvendar segredos",
+	"Untagged": "Sem tag",
+	"Update": "Atualizar",
+	"Update and Copy Link": "Atualizar e Copiar Link",
+	"Update for the latest features and improvements.": "Atualizar para as novas funcionalidades e melhorias.",
+	"Update password": "Atualizar senha",
+	"Updated": "Atualizado",
+	"Updated at": "Atualizado em",
+	"Updated At": "Atualizado Em",
+	"Upload": "Fazer upload",
+	"Upload a GGUF model": "Fazer upload de um modelo GGUF",
+	"Upload directory": "Carregar diretório",
+	"Upload files": "Carregar arquivos",
+	"Upload Files": "Fazer upload de Arquivos",
+	"Upload Pipeline": "Fazer upload de Pipeline",
+	"Upload Progress": "Progresso do Upload",
+	"URL": "",
+	"URL Mode": "Modo URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Usar '#' no prompt para carregar e incluir seus conhecimentos.",
+	"Use Gravatar": "Usar Gravatar",
+	"Use groups to group your users and assign permissions.": "Use grupos para agrupar seus usuários e atribuir permissões.",
+	"Use Initials": "Usar Iniciais",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "usuário",
+	"User": "Usuário",
+	"User location successfully retrieved.": "Localização do usuário recuperada com sucesso.",
+	"Username": "Nome do Usuário",
+	"Users": "Usuários",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Usando a arena de modelos padrão para todos os modelos. Clique no botão mais para adicionar modelos personalizados.",
+	"Utilize": "Utilizar",
+	"Valid time units:": "Unidades de tempo válidas:",
+	"Valves": "Válvulas",
+	"Valves updated": "Válvulas atualizadas",
+	"Valves updated successfully": "Válvulas atualizadas com sucesso",
+	"variable": "variável",
+	"variable to have them replaced with clipboard content.": "variável para ser substituída pelo conteúdo da área de transferência.",
+	"Version": "Versão",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Versão {{selectedVersion}} de {{totalVersions}}",
+	"Visibility": "Visibilidade",
+	"Voice": "Voz",
+	"Voice Input": "Entrada de voz",
+	"Warning": "Aviso",
+	"Warning:": "Aviso:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Aviso: Habilitar isso permitirá que os usuários façam upload de código arbitrário no servidor.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Aviso: Se você atualizar ou alterar seu modelo de incorporação, será necessário reimportar todos os documentos.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Configurações do Carregador Web",
+	"Web Search": "Pesquisa na Web",
+	"Web Search Engine": "Mecanismo de Busca na Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL do Webhook",
+	"WebUI Settings": "Configurações da WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "A WebUI fará requisições para \"{{url}}/api/chat\".",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "A WebUI fará requisições para \"{{url}}/chat/completions\".",
+	"What are you trying to achieve?": "O que está tentando alcançar?",
+	"What are you working on?": "No que está trabalhando?",
+	"What’s New in": "O que há de novo em",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Quando habilitado, o modelo responderá a cada mensagem de chat em tempo real, gerando uma resposta assim que o usuário enviar uma mensagem. Este modo é útil para aplicativos de chat ao vivo, mas pode impactar o desempenho em hardware mais lento.",
+	"wherever you are": "onde quer que você esteja.",
+	"Whisper (Local)": "Whisper (Local)",
+	"Why?": "Por que",
+	"Widescreen Mode": "Modo Tela Cheia",
+	"Won": "Ganhou",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Funciona em conjunto com o top-k. Um valor mais alto (por exemplo, 0,95) levará a um texto mais diversificado, enquanto um valor mais baixo (por exemplo, 0,5) gerará um texto mais focado e conservador. (Padrão: 0,9)",
+	"Workspace": "Espaço de Trabalho",
+	"Workspace Permissions": "Permissões do espaço de trabalho",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escreva uma sugestão de prompt (por exemplo, Quem é você?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Escreva um resumo em 50 palavras que resuma [tópico ou palavra-chave].",
+	"Write something...": "Escreva algo...",
+	"Write your model template content here": "Escreva o conteúdo do template do modelo aqui.",
+	"Yesterday": "Ontem",
+	"You": "Você",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Você só pode conversar com no máximo {{maxCount}} arquivo(s) de cada vez.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Você pode personalizar suas interações com LLMs adicionando memórias através do botão 'Gerenciar' abaixo, tornando-as mais úteis e adaptadas a você.",
+	"You cannot upload an empty file.": "Você não pode carregar um arquivo vazio.",
+	"You do not have permission to upload files.": "Você não tem permissão para fazer upload de arquivos.",
+	"You have no archived conversations.": "Você não tem conversas arquivadas.",
+	"You have shared this chat": "Você compartilhou este chat",
+	"You're a helpful assistant.": "Você é um assistente útil.",
+	"You're now logged in.": "Você agora está logado.",
+	"Your account status is currently pending activation.": "O status da sua conta está atualmente aguardando ativação.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Toda a sua contribuição irá diretamente para o desenvolvedor do plugin; o Open WebUI não retém nenhuma porcentagem. No entanto, a plataforma de financiamento escolhida pode ter suas próprias taxas.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Configurações do Carregador Youtube"
+}
diff --git a/src/lib/i18n/locales/pt-PT/translation.json b/src/lib/i18n/locales/pt-PT/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..ff2ccae36dd90b0ed728c8f1081792a05e30bf48
--- /dev/null
+++ b/src/lib/i18n/locales/pt-PT/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ou '-1' para nenhuma expiração.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(por exemplo, `sh webui.sh --api`)",
+	"(latest)": "(mais recente)",
+	"{{ models }}": "{{ modelos }}",
+	"{{user}}'s Chats": "{{user}}'s Chats",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend Necessário",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Um modelo de tarefa é usado ao executar tarefas como gerar títulos para bate-papos e consultas de pesquisa na Web",
+	"a user": "um utilizador",
+	"About": "Acerca de",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Conta",
+	"Account Activation Pending": "Ativação da Conta Pendente",
+	"Accurate information": "Informações precisas",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Utilizadores Ativos",
+	"Add": "Adicionar",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Adicione uma breve descrição sobre o que este modelo faz",
+	"Add a tag": "Adicionar uma tag",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Adicionar um prompt curto",
+	"Add Files": "Adicionar Ficheiros",
+	"Add Group": "",
+	"Add Memory": "Adicionar memória",
+	"Add Model": "Adicionar modelo",
+	"Add Tag": "",
+	"Add Tags": "adicionar tags",
+	"Add text content": "",
+	"Add User": "Adicionar Utilizador",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ajustar essas configurações aplicará alterações universalmente a todos os utilizadores.",
+	"admin": "administrador",
+	"Admin": "Admin",
+	"Admin Panel": "Painel do Administrador",
+	"Admin Settings": "Configurações do Administrador",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Parâmetros Avançados",
+	"Advanced Params": "Params Avançados",
+	"All chats": "",
+	"All Documents": "Todos os Documentos",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Permitir Exclusão de Conversa",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Permitir vozes não locais",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Já tem uma conta?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "um assistente",
+	"and": "e",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "e criar um novo link partilhado.",
+	"API Base URL": "URL Base da API",
+	"API Key": "Chave da API",
+	"API Key created.": "Chave da API criada.",
+	"API keys": "Chaves da API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Abril",
+	"Archive": "Arquivo",
+	"Archive All Chats": "Arquivar todos os chats",
+	"Archived Chats": "Conversas arquivadas",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Tem a certeza?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Anexar ficheiro",
+	"Attention to detail": "Detalhado",
+	"Attribute for Username": "",
+	"Audio": "Áudio",
+	"August": "Agosto",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Cópia Automática da Resposta para a Área de Transferência",
+	"Auto-playback response": "Reprodução automática da resposta",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "URL Base do AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "O URL Base do AUTOMATIC1111 é obrigatório.",
+	"Available list": "",
+	"available!": "disponível!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Voltar",
+	"Bad Response": "Resposta má",
+	"Banners": "Estandartes",
+	"Base Model (From)": "Modelo Base (De)",
+	"Batch Size (num_batch)": "",
+	"before": "antes",
+	"Being lazy": "Ser preguiçoso",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Chave da API de Pesquisa Brave",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Ignorar verificação SSL para sites",
+	"Call": "Chamar",
+	"Call feature is not supported when using Web STT engine": "A funcionalide de Chamar não é suportada quando usa um motor Web STT",
+	"Camera": "Camera",
+	"Cancel": "Cancelar",
+	"Capabilities": "Capacidades",
+	"Certificate Path": "",
+	"Change Password": "Alterar Senha",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Conversa",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Bolha UI da Conversa",
+	"Chat Controls": "",
+	"Chat direction": "Direção da Conversa",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Conversas",
+	"Check Again": "Verifique novamente",
+	"Check for updates": "Verificar atualizações",
+	"Checking for updates...": "Verificando atualizações...",
+	"Choose a model before saving...": "Escolha um modelo antes de guardar...",
+	"Chunk Overlap": "Sobreposição de Fragmento",
+	"Chunk Params": "Parâmetros de Fragmento",
+	"Chunk Size": "Tamanho do Fragmento",
+	"Ciphers": "",
+	"Citation": "Citação",
+	"Clear memory": "Limpar memória",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Clique aqui para obter ajuda.",
+	"Click here to": "Clique aqui para",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Clique aqui para selecionar",
+	"Click here to select a csv file.": "Clique aqui para selecionar um ficheiro csv.",
+	"Click here to select a py file.": "Clique aqui para selecionar um ficheiro py",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "clique aqui.",
+	"Click on the user role button to change a user's role.": "Clique no botão de função do utilizador para alterar a função de um utilizador.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Clonar",
+	"Close": "Fechar",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Coleção",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL Base do ComfyUI",
+	"ComfyUI Base URL is required.": "O URL Base do ComfyUI é obrigatório.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Comando",
+	"Completions": "",
+	"Concurrent Requests": "Solicitações simultâneas",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Confirmar Senha",
+	"Confirm your action": "",
+	"Connections": "Conexões",
+	"Contact Admin for WebUI Access": "Contatar Admin para acesso ao WebUI",
+	"Content": "Conteúdo",
+	"Content Extraction": "",
+	"Context Length": "Comprimento do Contexto",
+	"Continue Response": "Continuar resposta",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "URL de Conversa partilhado copiada com sucesso!",
+	"Copied to clipboard": "",
+	"Copy": "Copiar",
+	"Copy last code block": "Copiar último bloco de código",
+	"Copy last response": "Copiar última resposta",
+	"Copy Link": "Copiar link",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Cópia para a área de transferência bem-sucedida!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Criar um modelo",
+	"Create Account": "Criar Conta",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Criar nova chave",
+	"Create new secret key": "Criar nova chave secreta",
+	"Created at": "Criado em",
+	"Created At": "Criado em",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Modelo Atual",
+	"Current Password": "Senha Atual",
+	"Custom": "Personalizado",
+	"Dark": "Escuro",
+	"Database": "Base de dados",
+	"December": "Dezembro",
+	"Default": "Padrão",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Padrão (SentenceTransformers)",
+	"Default Model": "Modelo padrão",
+	"Default model updated": "Modelo padrão atualizado",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Sugestões de Prompt Padrão",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Função de Utilizador Padrão",
+	"Delete": "Apagar",
+	"Delete a model": "Apagar um modelo",
+	"Delete All Chats": "Apagar todas as conversas",
+	"Delete All Models": "",
+	"Delete chat": "Apagar conversa",
+	"Delete Chat": "Apagar Conversa",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "apagar este link",
+	"Delete tool?": "",
+	"Delete User": "Apagar Utilizador",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} apagado",
+	"Deleted {{name}}": "Apagado {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Descrição",
+	"Didn't fully follow instructions": "Não seguiu instruções com precisão",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Descubra um modelo",
+	"Discover a prompt": "Descobrir um prompt",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Descubra, descarregue e explore prompts personalizados",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Descubra, descarregue e explore predefinições de modelo",
+	"Dismissible": "Dispensável",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Exibir o nome de utilizador em vez de Você na Conversa",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Documento",
+	"Documentation": "Documentação",
+	"Documents": "Documentos",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "não faz conexões externas e os seus dados permanecem seguros no seu servidor alojado localmente.",
+	"Don't have an account?": "Não tem uma conta?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Não gosta do estilo",
+	"Done": "",
+	"Download": "Descarregar",
+	"Download canceled": "Download cancelado",
+	"Download Database": "Descarregar Base de Dados",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Largue os ficheiros aqui para adicionar à conversa",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "por exemplo, '30s', '10m'. Unidades de tempo válidas são 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Editar",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Editar Utilizador",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "E-mail",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Tamanho do Lote do Embedding",
+	"Embedding Model": "Modelo de Embedding",
+	"Embedding Model Engine": "Motor de Modelo de Embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Modelo de Embedding definido como \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Active a Partilha da Comunidade",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Ativar Novas Inscrições",
+	"Enable Web Search": "Ativar pesquisa na Web",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Confirme que o seu ficheiro CSV inclui 4 colunas nesta ordem: Nome, E-mail, Senha, Função.",
+	"Enter {{role}} message here": "Escreva a mensagem de {{role}} aqui",
+	"Enter a detail about yourself for your LLMs to recall": "Escreva um detalhe sobre você para que os seus LLMs possam lembrar-se",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Escreva a chave da API do Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Escreva a Sobreposição de Fragmento",
+	"Enter Chunk Size": "Escreva o Tamanho do Fragmento",
+	"Enter description": "",
+	"Enter Github Raw URL": "Escreva o URL cru do Github",
+	"Enter Google PSE API Key": "Escreva a chave da API PSE do Google",
+	"Enter Google PSE Engine Id": "Escreva o ID do mecanismo PSE do Google",
+	"Enter Image Size (e.g. 512x512)": "Escreva o Tamanho da Imagem (por exemplo, 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Escreva os códigos de idioma",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Escreva a tag do modelo (por exemplo, {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Escreva o Número de Etapas (por exemplo, 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Escreva a Pontuação",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Escreva o URL da Pesquisa Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Escreva a chave da API Serper",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Escreva a chave da API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Escreva a sequência de paragem",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Escreva o Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Escreva o URL (por exemplo, http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Escreva o URL (por exemplo, http://localhost:11434)",
+	"Enter Your Email": "Escreva o seu E-mail",
+	"Enter Your Full Name": "Escreva o seu Nome Completo",
+	"Enter your message": "",
+	"Enter Your Password": "Escreva a sua Senha",
+	"Enter Your Role": "Escreva a sua Função",
+	"Enter Your Username": "",
+	"Error": "Erro",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Experimental",
+	"Explore the cosmos": "",
+	"Export": "Exportar",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exportar Todas as Conversas (Todos os Utilizadores)",
+	"Export chat (.json)": "Exportar Conversa (.json)",
+	"Export Chats": "Exportar Conversas",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Modelos de Exportação",
+	"Export Presets": "",
+	"Export Prompts": "Exportar Prompts",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "Modelos Externos",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Falha ao criar a Chave da API.",
+	"Failed to read clipboard contents": "Falha ao ler o conteúdo da área de transferência",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Falha ao atualizar as definições",
+	"Failed to upload file.": "",
+	"February": "Fevereiro",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Sinta-se à vontade para adicionar detalhes específicos",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Modo de Ficheiro",
+	"File not found.": "Ficheiro não encontrado.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Detectada falsificação da impressão digital: Não é possível usar iniciais como avatar. A usar a imagem de perfil padrão.",
+	"Fluidly stream large external response chunks": "Transmita com fluidez grandes blocos de resposta externa",
+	"Focus chat input": "Focar na conversa",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Seguiu instruções perfeitamente",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Penalidade de Frequência",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Geral",
+	"General Settings": "Configurações Gerais",
+	"Generate Image": "Gerar imagem",
+	"Generating search query": "A gerar a consulta da pesquisa",
+	"Generation Info": "Informações de Geração",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Boa Resposta",
+	"Google PSE API Key": "Chave da API PSE do Google",
+	"Google PSE Engine Id": "ID do mecanismo PSE do Google",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "não possui conversas.",
+	"Hello, {{name}}": "Olá, {{name}}",
+	"Help": "Ajuda",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Ocultar",
+	"Host": "",
+	"How can I help you today?": "Como posso ajudá-lo hoje?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Pesquisa Híbrida",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Geração de Imagens (Experimental)",
+	"Image Generation Engine": "Mecanismo de Geração de Imagens",
+	"Image Settings": "Configurações da Imagem",
+	"Images": "Imagens",
+	"Import Chats": "Importar Conversas",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Importar Modelos",
+	"Import Presets": "",
+	"Import Prompts": "Importar Prompts",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Inclua a flag `--api` ao executar stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informação",
+	"Input commands": "Comandos de entrada",
+	"Install from Github URL": "Instalar a partir do URL do Github",
+	"Instant Auto-Send After Voice Transcription": "Enviar automaticamente depois da transcrição da voz",
+	"Interface": "Interface",
+	"Invalid file format.": "",
+	"Invalid Tag": "Etiqueta Inválida",
+	"January": "Janeiro",
+	"Jina API Key": "",
+	"join our Discord for help.": "junte-se ao nosso Discord para obter ajuda.",
+	"JSON": "JSON",
+	"JSON Preview": "Pré-visualização JSON",
+	"July": "Julho",
+	"June": "Junho",
+	"JWT Expiration": "Expiração JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Manter Vivo",
+	"Key": "",
+	"Keyboard shortcuts": "Atalhos de teclado",
+	"Knowledge": "Conhecimento",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Idioma",
+	"Last Active": "Último Ativo",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Claro",
+	"Listening...": "A escutar...",
+	"LLMs can make mistakes. Verify important information.": "LLMs podem cometer erros. Verifique informações importantes.",
+	"Local": "",
+	"Local Models": "Modelos Locais",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Feito pela Comunidade OpenWebUI",
+	"Make sure to enclose them with": "Certifique-se de colocá-los entre",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Gerir",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Gerir pipelines",
+	"March": "Março",
+	"Max Tokens (num_predict)": "Máx Tokens (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "O máximo de 3 modelos podem ser descarregados simultaneamente. Tente novamente mais tarde.",
+	"May": "Maio",
+	"Memories accessible by LLMs will be shown here.": "Memórias acessíveis por LLMs serão mostradas aqui.",
+	"Memory": "Memória",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Mensagens que você enviar após criar o seu link não serão partilhadas. Os utilizadores com o URL poderão visualizar a conversa partilhada.",
+	"Min P": "",
+	"Minimum Score": "Mínimo de Pontuação",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD/MM/YYYY",
+	"MMMM DD, YYYY HH:mm": "DD/MM/YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "O modelo '{{modelName}}' foi descarregado com sucesso.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "O modelo '{{modelTag}}' já está na fila para descarregar.",
+	"Model {{modelId}} not found": "Modelo {{modelId}} não foi encontrado",
+	"Model {{modelName}} is not vision capable": "O modelo {{modelName}} não é capaz de visão",
+	"Model {{name}} is now {{status}}": "Modelo {{name}} agora é {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Dtectado caminho do sistema de ficheiros do modelo. É necessário o nome curto do modelo para atualização, não é possível continuar.",
+	"Model Filtering": "",
+	"Model ID": "ID do modelo",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modelo não selecionado",
+	"Model Params": "Params Modelo",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Conteúdo do Ficheiro do Modelo",
+	"Models": "Modelos",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Mais",
+	"Name": "Nome",
+	"Name your knowledge base": "",
+	"New Chat": "Nova Conversa",
+	"New folder": "",
+	"New Password": "Nova Senha",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Não foram encontrados resultados",
+	"No search query generated": "Não foi gerada nenhuma consulta de pesquisa",
+	"No source available": "Nenhuma fonte disponível",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Nenhum",
+	"Not factually correct": "Não é correto em termos factuais",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Nota: Se você definir uma pontuação mínima, a pesquisa só retornará documentos com uma pontuação maior ou igual à pontuação mínima.",
+	"Notes": "",
+	"Notifications": "Notificações da Área de Trabalho",
+	"November": "Novembro",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Outubro",
+	"Off": "Desligado",
+	"Okay, Let's Go!": "Ok, Vamos Lá!",
+	"OLED Dark": "OLED Escuro",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API do Ollama desativada",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versão do Ollama",
+	"On": "Ligado",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Apenas caracteres alfanuméricos e hífens são permitidos na string de comando.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Epá! Parece que o URL é inválido. Verifique novamente e tente outra vez.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Epá! Você está a usar um método não suportado (somente frontend). Por favor, sirva o WebUI a partir do backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Abrir nova conversa",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Configuração da API OpenAI",
+	"OpenAI API Key is required.": "A Chave da API OpenAI é obrigatória.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "URL/Chave da API OpenAI é necessária.",
+	"or": "ou",
+	"Organize your users": "",
+	"Other": "Outro",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Senha",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Documento PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Extrair Imagens de PDF (OCR)",
+	"pending": "pendente",
+	"Permission denied when accessing media devices": "A permissão foi negada ao aceder aos dispositivos de media",
+	"Permission denied when accessing microphone": "A permissão foi negada ao aceder ao microfone",
+	"Permission denied when accessing microphone: {{error}}": "A permissão foi negada ao aceder o microfone: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalização",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Condutas",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Válvulas de Condutas",
+	"Plain text (.txt)": "Texto sem formatação (.txt)",
+	"Playground": "Recreio",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Atitude Positiva",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Últimos 30 dias",
+	"Previous 7 days": "Últimos 7 dias",
+	"Profile Image": "Imagem de Perfil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (ex.: Dê-me um facto divertido sobre o Império Romano)",
+	"Prompt Content": "Conteúdo do Prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Sugestões de Prompt",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompts",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Puxar \"{{searchValue}}\" do Ollama.com",
+	"Pull a model from Ollama.com": "Puxar um modelo do Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parâmetros de Consulta",
+	"RAG Template": "Modelo RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Ler em Voz Alta",
+	"Record voice": "Gravar voz",
+	"Redirecting you to OpenWebUI Community": "Redirecionando-o para a Comunidade OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Refera-se a si próprio como \"User\" (por exemplo, \"User está a aprender Espanhol\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Recusado quando não deveria",
+	"Regenerate": "Regenerar",
+	"Release Notes": "Notas de Lançamento",
+	"Relevance": "",
+	"Remove": "Remover",
+	"Remove Model": "Remover Modelo",
+	"Rename": "Renomear",
+	"Reorder Models": "",
+	"Repeat Last N": "Repetir Últimos N",
+	"Request Mode": "Modo de Pedido",
+	"Reranking Model": "Modelo de Reranking",
+	"Reranking model disabled": "Modelo de Reranking desativado",
+	"Reranking model set to \"{{reranking_model}}\"": "Modelo de Reranking definido como \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Limpar Pasta de Carregamento",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Função",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "A correr",
+	"Save": "Guardar",
+	"Save & Create": "Guardar e Criar",
+	"Save & Update": "Guardar e Atualizar",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Guardar o registo das conversas diretamente no armazenamento do seu navegador já não é suportado. Reserve um momento para descarregar e eliminar os seus registos de conversas clicando no botão abaixo. Não se preocupe, você pode facilmente reimportar os seus registos de conversas para o backend através de",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Pesquisar",
+	"Search a model": "Pesquisar um modelo",
+	"Search Base": "",
+	"Search Chats": "Pesquisar Conversas",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Modelos de pesquisa",
+	"Search options": "",
+	"Search Prompts": "Pesquisar Prompts",
+	"Search Result Count": "Contagem de resultados da pesquisa",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Pesquisado {{count}} sites_one",
+	"Searched {{count}} sites_many": "Pesquisado {{count}} sites_many",
+	"Searched {{count}} sites_other": "Pesquisado {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL de consulta Searxng",
+	"See readme.md for instructions": "Consulte readme.md para obter instruções",
+	"See what's new": "Veja o que há de novo",
+	"Seed": "Semente",
+	"Select a base model": "Selecione um modelo base",
+	"Select a engine": "Selecione um motor",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Selecione um modelo",
+	"Select a pipeline": "Selecione um pipeline",
+	"Select a pipeline url": "Selecione um URL de pipeline",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Selecione o modelo",
+	"Select only one model to call": "Selecione apenas um modelo para a chamada",
+	"Selected model(s) do not support image inputs": "O(s) modelo(s) selecionado(s) não suporta(m) entradas de imagem",
+	"Semantic distance to query": "",
+	"Send": "Enviar",
+	"Send a Message": "Enviar uma Mensagem",
+	"Send message": "Enviar mensagem",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Setembro",
+	"Serper API Key": "Chave API Serper",
+	"Serply API Key": "",
+	"Serpstack API Key": "Chave da API Serpstack",
+	"Server connection verified": "Conexão com o servidor verificada",
+	"Set as default": "Definir como padrão",
+	"Set CFG Scale": "",
+	"Set Default Model": "Definir Modelo Padrão",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Definir modelo de vetorização (ex.: {{model}})",
+	"Set Image Size": "Definir Tamanho da Imagem",
+	"Set reranking model (e.g. {{model}})": "Definir modelo de reranking (ex.: {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Definir Etapas",
+	"Set Task Model": "Definir modelo de tarefa",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Definir Voz",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Configurações",
+	"Settings saved successfully!": "Configurações guardadas com sucesso!",
+	"Share": "Partilhar",
+	"Share Chat": "Partilhar Conversa",
+	"Share to OpenWebUI Community": "Partilhar com a Comunidade OpenWebUI",
+	"Show": "Mostrar",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Mostrar Detalhes do Administrador na sobreposição de Conta Pendente",
+	"Show shortcuts": "Mostrar atalhos",
+	"Show your support!": "",
+	"Showcased creativity": "Criatividade Exibida",
+	"Sign in": "Entrar",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Sair",
+	"Sign up": "Inscrever-se",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Fonte",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Erro de reconhecimento de fala: {{error}}",
+	"Speech-to-Text Engine": "Motor de Fala para Texto",
+	"Stop": "",
+	"Stop Sequence": "Sequência de Paragem",
+	"Stream Chat Response": "",
+	"STT Model": "Modelo STT",
+	"STT Settings": "Configurações STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtítulo (ex.: sobre o Império Romano)",
+	"Success": "Sucesso",
+	"Successfully updated.": "Atualizado com sucesso.",
+	"Suggested": "Sugerido",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Sistema",
+	"System Instructions": "",
+	"System Prompt": "Prompt do Sistema",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Diga-nos mais:",
+	"Temperature": "Temperatura",
+	"Template": "Modelo",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Motor de Texto para Fala",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Obrigado pelo seu feedback!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "A pontuação deve ser um valor entre 0.0 (0%) e 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "A pensar...",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Isto garante que suas conversas valiosas sejam guardadas com segurança na sua base de dados de backend. Obrigado!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Isto é um recurso experimental, pode não funcionar conforme o esperado e está sujeito a alterações a qualquer momento.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Explicação Minuciosa",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Dica: Atualize vários slots de variáveis consecutivamente pressionando a tecla Tab na entrada da conversa após cada substituição.",
+	"Title": "Título",
+	"Title (e.g. Tell me a fun fact)": "Título (ex.: Diz-me um facto divertido)",
+	"Title Auto-Generation": "Geração Automática de Título",
+	"Title cannot be an empty string.": "Título não pode ser uma string vazia.",
+	"Title Generation Prompt": "Prompt de Geração de Título",
+	"TLS": "",
+	"To access the available model names for downloading,": "Para aceder aos nomes de modelo disponíveis para descarregar,",
+	"To access the GGUF models available for downloading,": "Para aceder aos modelos GGUF disponíveis para descarregar,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Para aceder ao WebUI, entre em contato com o administrador. Os administradores podem gerir o status dos utilizadores no Painel de Administração.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Hoje",
+	"Toggle settings": "Alternar configurações",
+	"Toggle sidebar": "Alternar barra lateral",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problemas a aceder ao Ollama?",
+	"TTS Model": "Modelo TTS",
+	"TTS Settings": "Configurações TTS",
+	"TTS Voice": "Voz TTS",
+	"Type": "Tipo",
+	"Type Hugging Face Resolve (Download) URL": "Escreva o URL do Hugging Face Resolve (Descarregar)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! Houve um problema ao conectar a {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Atualizar e Copiar Link",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Atualizar senha",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Carregar um modelo GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Carregar ficheiros",
+	"Upload Pipeline": "Carregar Pipeline",
+	"Upload Progress": "Progresso do Carregamento",
+	"URL": "",
+	"URL Mode": "Modo de URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Usar Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Usar Iniciais",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "utilizador",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Utilizadores",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Utilizar",
+	"Valid time units:": "Unidades de tempo válidas:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "variável",
+	"variable to have them replaced with clipboard content.": "variável para que sejam substituídos pelo conteúdo da área de transferência.",
+	"Version": "Versão",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Aviso",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Aviso: Se você atualizar ou alterar o seu modelo de vetorização, você tem de reimportar todos os documentos.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Configurações do Carregador da Web",
+	"Web Search": "Pesquisa na Web",
+	"Web Search Engine": "Motor de Pesquisa Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL do Webhook",
+	"WebUI Settings": "Configurações WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "O que há de novo em",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (Local)",
+	"Why?": "",
+	"Widescreen Mode": "Modo Widescreen",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Espaço de Trabalho",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Escreva uma sugestão de prompt (por exemplo, Quem és tu?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Escreva um resumo em 50 palavras que resuma [tópico ou palavra-chave].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Ontem",
+	"You": "Você",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Você pode personalizar as suas interações com LLMs adicionando memórias através do botão ‘Gerir’ abaixo, tornando-as mais úteis e personalizadas para você.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Você não tem conversas arquivadas.",
+	"You have shared this chat": "Você partilhou esta conversa",
+	"You're a helpful assistant.": "Você é um assistente útil.",
+	"You're now logged in.": "Você agora está conectado.",
+	"Your account status is currently pending activation.": "O status da sua conta está atualmente com a ativação pendente.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Configurações do Carregador do Youtube"
+}
diff --git a/src/lib/i18n/locales/ro-RO/translation.json b/src/lib/i18n/locales/ro-RO/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..e9d5267f6fe23d086dc536ac4628cd07348590f6
--- /dev/null
+++ b/src/lib/i18n/locales/ro-RO/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' sau '-1' fără expirare.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(de ex. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(de ex. `sh webui.sh --api`)",
+	"(latest)": "(ultimul)",
+	"{{ models }}": "{{ modele }}",
+	"{{user}}'s Chats": "Conversațiile lui {{user}}",
+	"{{webUIName}} Backend Required": "Este necesar backend-ul {{webUIName}}",
+	"*Prompt node ID(s) are required for image generation": "*Sunt necesare ID-urile nodurilor de solicitare pentru generarea imaginii*",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "O nouă versiune (v{{LATEST_VERSION}}) este acum disponibilă.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Un model de sarcină este utilizat pentru realizarea unor sarcini precum generarea de titluri pentru conversații și interogări de căutare pe web",
+	"a user": "un utilizator",
+	"About": "Despre",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Cont",
+	"Account Activation Pending": "Activarea contului în așteptare",
+	"Accurate information": "Informații precise",
+	"Actions": "Acțiuni",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Utilizatori activi",
+	"Add": "Adaugă",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Adaugă o scurtă descriere despre ce face acest model",
+	"Add a tag": "Adaugă o etichetă",
+	"Add Arena Model": "Adaugă Modelul Arena",
+	"Add Connection": "",
+	"Add Content": "Adăugați conținut",
+	"Add content here": "Adăugați conținut aici",
+	"Add custom prompt": "Adaugă prompt personalizat",
+	"Add Files": "Adaugă Fișiere",
+	"Add Group": "",
+	"Add Memory": "Adaugă Memorie",
+	"Add Model": "Adaugă Model",
+	"Add Tag": "Adaugă Etichetă",
+	"Add Tags": "Adaugă Etichete",
+	"Add text content": "Adăugați conținut textual",
+	"Add User": "Adaugă Utilizator",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Ajustarea acestor setări va aplica modificările universal pentru toți utilizatorii.",
+	"admin": "administrator",
+	"Admin": "Administrator",
+	"Admin Panel": "Panoul de Administrare",
+	"Admin Settings": "Setări de Administrator",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratorii au acces la toate instrumentele în orice moment; utilizatorii au nevoie de instrumente asignate pe model în spațiul de lucru.",
+	"Advanced Parameters": "Parametri Avansați",
+	"Advanced Params": "Parametri Avansați",
+	"All chats": "Toate conversațiile",
+	"All Documents": "Toate Documentele",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Permite Ștergerea Conversațiilor",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Permite voci non-locale",
+	"Allow Temporary Chat": "Permite Chat Temporar",
+	"Allow User Location": "Permite Localizarea Utilizatorului",
+	"Allow Voice Interruption in Call": "Permite Întreruperea Vocii în Apel",
+	"Already have an account?": "Deja ai un cont?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "un asistent",
+	"and": "și",
+	"and {{COUNT}} more": "și {{COUNT}} mai multe",
+	"and create a new shared link.": "și creează un nou link partajat.",
+	"API Base URL": "URL Bază API",
+	"API Key": "Cheie API",
+	"API Key created.": "Cheie API creată.",
+	"API keys": "Chei API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Aprilie",
+	"Archive": "Arhivează",
+	"Archive All Chats": "Arhivează Toate Conversațiile",
+	"Archived Chats": "Conversații Arhivate",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Ești sigur?",
+	"Arena Models": "Arena Models",
+	"Artifacts": "Artefacte",
+	"Ask a question": "Pune o întrebare",
+	"Assistant": "Asistent",
+	"Attach file": "Atașează fișier",
+	"Attention to detail": "Atenție la detalii",
+	"Attribute for Username": "",
+	"Audio": "Audio",
+	"August": "August",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Copiere Automată a Răspunsului în Clipboard",
+	"Auto-playback response": "Redare automată a răspunsului",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111 este un proiect popular pentru interfața grafică a utilizatorului a modelelor de difuzie stabilă. Aceasta oferă o interfață web pentru a genera imagini folosind AI și este utilizată pe scară largă pentru a experimenta cu generarea de artă AI.",
+	"AUTOMATIC1111 Api Auth String": "Șir de Autentificare API AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "URL Bază AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Este necesar URL-ul Bază AUTOMATIC1111.",
+	"Available list": "Listă disponibilă",
+	"available!": "disponibil!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI Speech este un serviciu care face parte din suita de servicii cognitive oferite de Microsoft Azure. Acesta permite integrarea capabilităților de recunoaștere vocală, generare a vorbirii și transcriere automată în aplicații. Serviciul oferă dezvoltatorilor posibilitatea de a crea aplicații care pot converti vorbirea în text, genera vorbire naturală din text sau traduce între limbi. Azure AI Speech este util în diverse scenarii, cum ar fi asistenți vocali, aplicații de servicii pentru clienți sau instrumente de accesibilitate, facilitând o interacțiune mai naturală între utilizatori și tehnologie.",
+	"Azure Region": "Regiune Azure",
+	"Back": "Înapoi",
+	"Bad Response": "Răspuns Greșit",
+	"Banners": "Bannere",
+	"Base Model (From)": "Model de Bază (De la)",
+	"Batch Size (num_batch)": "Dimensiune Lot (num_batch)",
+	"before": "înainte",
+	"Being lazy": "Fiind leneș",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Cheie API Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Ocolește verificarea SSL pentru site-uri web",
+	"Call": "Apel",
+	"Call feature is not supported when using Web STT engine": "Funcția de apel nu este suportată când se utilizează motorul Web STT",
+	"Camera": "Cameră",
+	"Cancel": "Anulează",
+	"Capabilities": "Capabilități",
+	"Certificate Path": "",
+	"Change Password": "Schimbă Parola",
+	"Character": "Caracter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Conversație",
+	"Chat Background Image": "Imagine de Fundal pentru Conversație",
+	"Chat Bubble UI": "Interfață cu Bule de Conversație",
+	"Chat Controls": "Controale pentru Conversație",
+	"Chat direction": "Direcția conversației",
+	"Chat Overview": "Prezentare generală a conversației",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "Generare automată a etichetelor de conversație",
+	"Chats": "Conversații",
+	"Check Again": "Verifică din Nou",
+	"Check for updates": "Verifică actualizări",
+	"Checking for updates...": "Se verifică actualizările...",
+	"Choose a model before saving...": "Alege un model înainte de a salva...",
+	"Chunk Overlap": "Suprapunere Bloc",
+	"Chunk Params": "Parametri Bloc",
+	"Chunk Size": "Dimensiune Bloc",
+	"Ciphers": "",
+	"Citation": "Citație",
+	"Clear memory": "Șterge memoria",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Apasă aici pentru ajutor.",
+	"Click here to": "Apasă aici pentru",
+	"Click here to download user import template file.": "Apasă aici pentru a descărca fișierul șablon de import utilizator.",
+	"Click here to learn more about faster-whisper and see the available models.": "Faceți clic aici pentru a afla mai multe despre faster-whisper și pentru a vedea modelele disponibile.",
+	"Click here to select": "Apasă aici pentru a selecta",
+	"Click here to select a csv file.": "Apasă aici pentru a selecta un fișier csv.",
+	"Click here to select a py file.": "Apasă aici pentru a selecta un fișier py.",
+	"Click here to upload a workflow.json file.": "Faceți clic aici pentru a încărca un fișier workflow.json.",
+	"click here.": "apasă aici.",
+	"Click on the user role button to change a user's role.": "Apasă pe butonul rolului utilizatorului pentru a schimba rolul unui utilizator.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Permisiunea de scriere în clipboard a fost refuzată. Vă rugăm să verificați setările browserului pentru a acorda accesul necesar.",
+	"Clone": "Clonează",
+	"Close": "Închide",
+	"Code execution": "Executarea codului",
+	"Code formatted successfully": "Cod formatat cu succes",
+	"Collection": "Colecție",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL De Bază ComfyUI",
+	"ComfyUI Base URL is required.": "Este necesar URL-ul De Bază ComfyUI.",
+	"ComfyUI Workflow": "Flux de lucru ComfyUI",
+	"ComfyUI Workflow Nodes": "Noduri de flux de lucru ComfyUI",
+	"Command": "Comandă",
+	"Completions": "Completări",
+	"Concurrent Requests": "Cereri Concurente",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Confirmă",
+	"Confirm Password": "Confirmă Parola",
+	"Confirm your action": "Confirmă acțiunea ta",
+	"Connections": "Conexiuni",
+	"Contact Admin for WebUI Access": "Contactează administratorul pentru acces WebUI",
+	"Content": "Conținut",
+	"Content Extraction": "Extragere Conținut",
+	"Context Length": "Lungime Context",
+	"Continue Response": "Continuă Răspunsul",
+	"Continue with {{provider}}": "Continuă cu {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Controlează modul în care textul mesajului este divizat pentru cererile TTS. 'Punctuation' împarte în propoziții, 'paragraphs' împarte în paragrafe, iar 'none' menține mesajul ca un șir unic.",
+	"Controls": "Controale",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Copiat",
+	"Copied shared chat URL to clipboard!": "URL-ul conversației partajate a fost copiat în clipboard!",
+	"Copied to clipboard": "Copiat în clipboard",
+	"Copy": "Copiază",
+	"Copy last code block": "Copiază ultimul bloc de cod",
+	"Copy last response": "Copiază ultimul răspuns",
+	"Copy Link": "Copiază Link",
+	"Copy to clipboard": "Copiază în clipboard",
+	"Copying to clipboard was successful!": "Copierea în clipboard a fost realizată cu succes!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Creează un model",
+	"Create Account": "Creează Cont",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "Creează cunoștințe",
+	"Create new key": "Creează cheie nouă",
+	"Create new secret key": "Creează cheie secretă nouă",
+	"Created at": "Creat la",
+	"Created At": "Creat La",
+	"Created by": "Creat de",
+	"CSV Import": "Import CSV",
+	"Current Model": "Model Curent",
+	"Current Password": "Parola Curentă",
+	"Custom": "Personalizat",
+	"Dark": "Întunecat",
+	"Database": "Bază de Date",
+	"December": "Decembrie",
+	"Default": "Implicit",
+	"Default (Open AI)": "Implicit (Open AI)",
+	"Default (SentenceTransformers)": "Implicit (SentenceTransformers)",
+	"Default Model": "Model Implicit",
+	"Default model updated": "Modelul implicit a fost actualizat",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Sugestii de Prompt Implicite",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Rolul Implicit al Utilizatorului",
+	"Delete": "Șterge",
+	"Delete a model": "Șterge un model",
+	"Delete All Chats": "Șterge Toate Conversațiile",
+	"Delete All Models": "",
+	"Delete chat": "Șterge conversația",
+	"Delete Chat": "Șterge Conversația",
+	"Delete chat?": "Șterge conversația?",
+	"Delete folder?": "Ștergeți folderul?",
+	"Delete function?": "Șterge funcția?",
+	"Delete prompt?": "Șterge promptul?",
+	"delete this link": "șterge acest link",
+	"Delete tool?": "Șterge instrumentul?",
+	"Delete User": "Șterge Utilizatorul",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} șters",
+	"Deleted {{name}}": "{{name}} șters",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Descriere",
+	"Didn't fully follow instructions": "Nu a urmat complet instrucțiunile",
+	"Disabled": "Dezactivat",
+	"Discover a function": "Descoperă o funcție",
+	"Discover a model": "Descoperă un model",
+	"Discover a prompt": "Descoperă un prompt",
+	"Discover a tool": "Descoperă un instrument",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Descoperă, descarcă și explorează funcții personalizate",
+	"Discover, download, and explore custom prompts": "Descoperă, descarcă și explorează prompturi personalizate",
+	"Discover, download, and explore custom tools": "Descoperă, descarcă și explorează instrumente personalizate",
+	"Discover, download, and explore model presets": "Descoperă, descarcă și explorează presetări de model",
+	"Dismissible": "Ignorabil",
+	"Display": "",
+	"Display Emoji in Call": "Afișează Emoji în Apel",
+	"Display the username instead of You in the Chat": "Afișează numele utilizatorului în loc de Tu în Conversație",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Nu instalați funcții din surse în care nu aveți încredere completă.",
+	"Do not install tools from sources you do not fully trust.": "Nu instalați instrumente din surse în care nu aveți încredere completă.",
+	"Document": "Document",
+	"Documentation": "Documentație",
+	"Documents": "Documente",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "nu face nicio conexiune externă, iar datele tale rămân în siguranță pe serverul găzduit local.",
+	"Don't have an account?": "Nu ai un cont?",
+	"don't install random functions from sources you don't trust.": "nu instala funcții aleatorii din surse în care nu ai încredere.",
+	"don't install random tools from sources you don't trust.": "nu instala instrumente aleatorii din surse în care nu ai încredere.",
+	"Don't like the style": "Nu îți place stilul",
+	"Done": "Gata",
+	"Download": "Descarcă",
+	"Download canceled": "Descărcare anulată",
+	"Download Database": "Descarcă Baza de Date",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "Desenează",
+	"Drop any files here to add to the conversation": "Plasează orice fișiere aici pentru a le adăuga la conversație",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "de ex. '30s', '10m'. Unitățile de timp valide sunt 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Editează",
+	"Edit Arena Model": "Editați Modelul Arena",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Editează Memorie",
+	"Edit User": "Editează Utilizator",
+	"Edit User Group": "",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Dimensiune Lot de Încapsulare",
+	"Embedding Model": "Model de Încapsulare",
+	"Embedding Model Engine": "Motor de Model de Încapsulare",
+	"Embedding model set to \"{{embedding_model}}\"": "Modelul de încapsulare setat la \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Activează Partajarea Comunitară",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Activează Evaluarea Mesajelor",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Activează Înscrierile Noi",
+	"Enable Web Search": "Activează Căutarea pe Web",
+	"Enabled": "Activat",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Asigurați-vă că fișierul CSV include 4 coloane în această ordine: Nume, Email, Parolă, Rol.",
+	"Enter {{role}} message here": "Introduceți mesajul pentru {{role}} aici",
+	"Enter a detail about yourself for your LLMs to recall": "Introduceți un detaliu despre dvs. pe care LLM-urile să-l rețină",
+	"Enter api auth string (e.g. username:password)": "Introduceți șirul de autentificare API (de ex. username:password)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Introduceți Cheia API Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "Introduceți Scara CFG (de ex. 7.0)",
+	"Enter Chunk Overlap": "Introduceți Suprapunerea Blocului",
+	"Enter Chunk Size": "Introduceți Dimensiunea Blocului",
+	"Enter description": "Introduceți descrierea",
+	"Enter Github Raw URL": "Introduceți URL-ul Raw de pe Github",
+	"Enter Google PSE API Key": "Introduceți Cheia API Google PSE",
+	"Enter Google PSE Engine Id": "Introduceți ID-ul Motorului Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Introduceți Dimensiunea Imaginii (de ex. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Introduceți codurile limbilor",
+	"Enter Model ID": "Introdu codul modelului",
+	"Enter model tag (e.g. {{modelTag}})": "Introduceți eticheta modelului (de ex. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Introduceți Numărul de Pași (de ex. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Introduce Sampler (de exemplu, Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Introduceți Programatorul (de exemplu, Karras)",
+	"Enter Score": "Introduceți Scorul",
+	"Enter SearchApi API Key": "Introduceți cheia API SearchApi",
+	"Enter SearchApi Engine": "Introduceți motorul SearchApi",
+	"Enter Searxng Query URL": "Introduceți URL-ul Interogării Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Introduceți Cheia API Serper",
+	"Enter Serply API Key": "Introduceți Cheia API Serply",
+	"Enter Serpstack API Key": "Introduceți Cheia API Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Introduceți secvența de oprire",
+	"Enter system prompt": "Introduceți promptul de sistem",
+	"Enter Tavily API Key": "Introduceți Cheia API Tavily",
+	"Enter Tika Server URL": "Introduceți URL-ul Serverului Tika",
+	"Enter Top K": "Introduceți Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introduceți URL-ul (de ex. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Introduceți URL-ul (de ex. http://localhost:11434)",
+	"Enter Your Email": "Introduceți Email-ul Dvs.",
+	"Enter Your Full Name": "Introduceți Numele Dvs. Complet",
+	"Enter your message": "Introduceți mesajul dvs.",
+	"Enter Your Password": "Introduceți Parola Dvs.",
+	"Enter Your Role": "Introduceți Rolul Dvs.",
+	"Enter Your Username": "",
+	"Error": "Eroare",
+	"ERROR": "EROARE",
+	"Evaluations": "Evaluări",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "Exclude",
+	"Experimental": "Experimental",
+	"Explore the cosmos": "",
+	"Export": "Exportă",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exportă Toate Conversațiile (Toți Utilizatorii)",
+	"Export chat (.json)": "Exportă conversația (.json)",
+	"Export Chats": "Exportă Conversațiile",
+	"Export Config to JSON File": "Exportă Configurația în Fișier JSON",
+	"Export Functions": "Exportă Funcțiile",
+	"Export Models": "Exportă Modelele",
+	"Export Presets": "",
+	"Export Prompts": "Exportă Prompturile",
+	"Export to CSV": "",
+	"Export Tools": "Exportă Instrumentele",
+	"External Models": "Modele Externe",
+	"Failed to add file.": "Eșec la adăugarea fișierului.",
+	"Failed to create API Key.": "Crearea cheii API a eșuat.",
+	"Failed to read clipboard contents": "Citirea conținutului clipboard-ului a eșuat",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Actualizarea setărilor a eșuat",
+	"Failed to upload file.": "Încărcarea fișierului a eșuat.",
+	"February": "Februarie",
+	"Feedback History": "Istoricul feedback-ului",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Adăugați detalii specifice fără nicio ezitare",
+	"File": "Fișier",
+	"File added successfully.": "Fișierul a fost adăugat cu succes.",
+	"File content updated successfully.": "Conținutul fișierului a fost actualizat cu succes.",
+	"File Mode": "Mod Fișier",
+	"File not found.": "Fișierul nu a fost găsit.",
+	"File removed successfully.": "Fișierul a fost eliminat cu succes.",
+	"File size should not exceed {{maxSize}} MB.": "Dimensiunea fișierului nu ar trebui să depășească {{maxSize}} MB.",
+	"Files": "Fișiere",
+	"Filter is now globally disabled": "Filtrul este acum dezactivat global",
+	"Filter is now globally enabled": "Filtrul este acum activat global",
+	"Filters": "Filtre",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Detectată falsificarea amprentelor: Nu se pot folosi inițialele ca avatar. Se utilizează imaginea de profil implicită.",
+	"Fluidly stream large external response chunks": "Transmite fluent blocuri mari de răspuns extern",
+	"Focus chat input": "Focalizează câmpul de intrare pentru conversație",
+	"Folder deleted successfully": "Folder șters cu succes",
+	"Folder name cannot be empty": "Numele folderului nu poate fi gol",
+	"Folder name cannot be empty.": "Numele folderului nu poate fi gol.",
+	"Folder name updated successfully": "Numele folderului a fost actualizat cu succes",
+	"Followed instructions perfectly": "A urmat instrucțiunile perfect",
+	"Forge new paths": "",
+	"Form": "Formular",
+	"Format your variables using brackets like this:": "Formatează variabilele folosind acolade așa:",
+	"Frequency Penalty": "Penalizare de Frecvență",
+	"Function": "Funcție",
+	"Function created successfully": "Funcția a fost creată cu succes",
+	"Function deleted successfully": "Funcția a fost ștearsă cu succes",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Funcția este acum dezactivată global",
+	"Function is now globally enabled": "Funcția este acum activată global",
+	"Function Name": "",
+	"Function updated successfully": "Funcția a fost actualizată cu succes",
+	"Functions": "Funcții",
+	"Functions allow arbitrary code execution": "Funcțiile permit executarea arbitrară a codului",
+	"Functions allow arbitrary code execution.": "Funcțiile permit executarea arbitrară a codului.",
+	"Functions imported successfully": "Funcțiile au fost importate cu succes",
+	"General": "General",
+	"General Settings": "Setări Generale",
+	"Generate Image": "Generează Imagine",
+	"Generating search query": "Se generează interogarea de căutare",
+	"Generation Info": "Informații Generare",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Global",
+	"Good Response": "Răspuns Bun",
+	"Google PSE API Key": "Cheie API Google PSE",
+	"Google PSE Engine Id": "ID Motor Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Feedback haptic",
+	"has no conversations.": "nu are conversații.",
+	"Hello, {{name}}": "Salut, {{name}}",
+	"Help": "Ajutor",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Ajută-ne să creăm cel mai bun clasament al comunității împărtășind istoricul tău de feedback!",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Ascunde",
+	"Host": "",
+	"How can I help you today?": "Cum te pot ajuta astăzi?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Căutare Hibridă",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Recunosc că am citit și înțeleg implicațiile acțiunii mele. Sunt conștient de riscurile asociate cu executarea codului arbitrar și am verificat fiabilitatea sursei.",
+	"ID": "ID",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Generare Imagine (Experimental)",
+	"Image Generation Engine": "Motor de Generare a Imaginilor",
+	"Image Settings": "Setări Imagine",
+	"Images": "Imagini",
+	"Import Chats": "Importă Conversațiile",
+	"Import Config from JSON File": "Importarea configurației dintr-un fișier JSON",
+	"Import Functions": "Importă Funcțiile",
+	"Import Models": "Importă Modelele",
+	"Import Presets": "",
+	"Import Prompts": "Importă Prompturile",
+	"Import Tools": "Importă Instrumentele",
+	"Include": "Include",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Includeți flag-ul `--api-auth` când rulați stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Includeți flag-ul `--api` când rulați stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Informații",
+	"Input commands": "Comenzi de intrare",
+	"Install from Github URL": "Instalează de la URL-ul Github",
+	"Instant Auto-Send After Voice Transcription": "Trimitere Automată Instantanee După Transcrierea Vocii",
+	"Interface": "Interfață",
+	"Invalid file format.": "Format de fișier invalid.",
+	"Invalid Tag": "Etichetă Invalidă",
+	"January": "Ianuarie",
+	"Jina API Key": "",
+	"join our Discord for help.": "alătură-te Discord-ului nostru pentru ajutor.",
+	"JSON": "JSON",
+	"JSON Preview": "Previzualizare JSON",
+	"July": "Iulie",
+	"June": "Iunie",
+	"JWT Expiration": "Expirarea JWT",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Menține Activ",
+	"Key": "",
+	"Keyboard shortcuts": "Scurtături de la Tastatură",
+	"Knowledge": "Cunoștințe",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "Cunoașterea a fost creată cu succes.",
+	"Knowledge deleted successfully.": "Cunoștințele au fost șterse cu succes.",
+	"Knowledge reset successfully.": "Resetarea cunoștințelor a fost efectuată cu succes.",
+	"Knowledge updated successfully": "Cunoașterea a fost actualizată cu succes",
+	"Label": "",
+	"Landing Page Mode": "Modul Pagină de Aterizare",
+	"Language": "Limbă",
+	"Last Active": "Ultima Activitate",
+	"Last Modified": "Ultima Modificare",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "Tabel de clasament",
+	"Leave empty for unlimited": "Lăsați gol pentru nelimitat",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "Lăsați gol pentru a include toate modelele sau selectați modele specifice",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Lăsați gol pentru a utiliza promptul implicit sau introduceți un prompt personalizat",
+	"Light": "Luminos",
+	"Listening...": "Ascult...",
+	"LLMs can make mistakes. Verify important information.": "LLM-urile pot face greșeli. Verificați informațiile importante.",
+	"Local": "",
+	"Local Models": "Modele Locale",
+	"Lost": "Pierdut",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Realizat de Comunitatea OpenWebUI",
+	"Make sure to enclose them with": "Asigurați-vă că le închideți cu",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Asigură-te că exporți un fișier {{workflow.json}} în format API din {{ComfyUI}}.",
+	"Manage": "Gestionează",
+	"Manage Arena Models": "Gestionați Modelele Arena",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Gestionează Conductele",
+	"March": "Martie",
+	"Max Tokens (num_predict)": "Număr Maxim de Tokeni (num_predict)",
+	"Max Upload Count": "Număr maxim de încărcări",
+	"Max Upload Size": "Dimensiune Maximă de Încărcare",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Maxim 3 modele pot fi descărcate simultan. Vă rugăm să încercați din nou mai târziu.",
+	"May": "Mai",
+	"Memories accessible by LLMs will be shown here.": "Memoriile accesibile de LLM-uri vor fi afișate aici.",
+	"Memory": "Memorie",
+	"Memory added successfully": "Memoria a fost adăugată cu succes",
+	"Memory cleared successfully": "Memoria a fost ștearsă cu succes",
+	"Memory deleted successfully": "Memoria a fost ștearsă cu succes",
+	"Memory updated successfully": "Memoria a fost actualizată cu succes",
+	"Merge Responses": "Combină răspunsurile",
+	"Message rating should be enabled to use this feature": "Evaluarea mesajelor ar trebui să fie activată pentru a utiliza această funcționalitate.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Mesajele pe care le trimiteți după crearea link-ului dvs. nu vor fi partajate. Utilizatorii cu URL-ul vor putea vizualiza conversația partajată.",
+	"Min P": "",
+	"Minimum Score": "Scor Minim",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Model",
+	"Model '{{modelName}}' has been successfully downloaded.": "Modelul '{{modelName}}' a fost descărcat cu succes.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modelul '{{modelTag}}' este deja în coada de descărcare.",
+	"Model {{modelId}} not found": "Modelul {{modelId}} nu a fost găsit",
+	"Model {{modelName}} is not vision capable": "Modelul {{modelName}} nu are capacități de viziune",
+	"Model {{name}} is now {{status}}": "Modelul {{name}} este acum {{status}}",
+	"Model accepts image inputs": "Modelul acceptă imagini ca intrări.",
+	"Model created successfully!": "Modelul a fost creat cu succes!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Calea sistemului de fișiere al modelului detectată. Este necesar numele scurt al modelului pentru actualizare, nu se poate continua.",
+	"Model Filtering": "",
+	"Model ID": "ID Model",
+	"Model IDs": "",
+	"Model Name": "Nume model",
+	"Model not selected": "Modelul nu a fost selectat",
+	"Model Params": "Parametri Model",
+	"Model Permissions": "",
+	"Model updated successfully": "Modelul a fost actualizat cu succes",
+	"Modelfile Content": "Conținutul Fișierului Model",
+	"Models": "Modele",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "mai mult",
+	"More": "Mai multe",
+	"Name": "Nume",
+	"Name your knowledge base": "",
+	"New Chat": "Conversație Nouă",
+	"New folder": "Folder nou",
+	"New Password": "Parolă Nouă",
+	"No content found": "Nu a fost găsit niciun conținut",
+	"No content to speak": "Nu există conținut de vorbit",
+	"No distance available": "Nicio distanță disponibilă",
+	"No feedbacks found": "Niciun feedback găsit",
+	"No file selected": "Nu a fost selectat niciun fișier",
+	"No files found.": "Nu au fost găsite fișiere.",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "Niciun conținut HTML, CSS sau JavaScript găsit.",
+	"No knowledge found": "Nu au fost găsite informații.",
+	"No model IDs": "",
+	"No models found": "Nu s-au găsit modele",
+	"No models selected": "",
+	"No results found": "Nu au fost găsite rezultate",
+	"No search query generated": "Nu a fost generată nicio interogare de căutare",
+	"No source available": "Nicio sursă disponibilă",
+	"No users were found.": "",
+	"No valves to update": "Nu există valve de actualizat",
+	"None": "Niciunul",
+	"Not factually correct": "Nu este corect din punct de vedere factual",
+	"Not helpful": "Nu este de ajutor",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Notă: Dacă setați un scor minim, căutarea va returna doar documente cu un scor mai mare sau egal cu scorul minim.",
+	"Notes": "Note",
+	"Notifications": "Notificări",
+	"November": "Noiembrie",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "ID OAuth",
+	"October": "Octombrie",
+	"Off": "Dezactivat",
+	"Okay, Let's Go!": "Ok, Să Începem!",
+	"OLED Dark": "Întunecat OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "API Ollama",
+	"Ollama API disabled": "API Ollama dezactivat",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Versiune Ollama",
+	"On": "Activat",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Doar caracterele alfanumerice și cratimele sunt permise în șirul de comandă.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Doar colecțiile pot fi editate, creați o nouă bază de cunoștințe pentru a edita/adăuga documente.",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Oops! Se pare că URL-ul este invalid. Vă rugăm să verificați din nou și să încercați din nou.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Ups! Încă mai există fișiere care se încarcă. Vă rugăm să așteptați până se finalizează încărcarea.",
+	"Oops! There was an error in the previous response.": "Ups! A apărut o eroare în răspunsul anterior.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Oops! Utilizați o metodă nesuportată (doar frontend). Vă rugăm să serviți WebUI din backend.",
+	"Open file": "Deschide fișierul",
+	"Open in full screen": "Deschide în ecran complet",
+	"Open new chat": "Deschide conversație nouă",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI folosește faster-whisper intern.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Versiunea Open WebUI (v{{OPEN_WEBUI_VERSION}}) este mai mică decât versiunea necesară (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Configurația API OpenAI",
+	"OpenAI API Key is required.": "Este necesară cheia API OpenAI.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Este necesar URL-ul/Cheia OpenAI.",
+	"or": "sau",
+	"Organize your users": "",
+	"Other": "Altele",
+	"OUTPUT": "Output rezultatat",
+	"Output format": "Formatul de ieșire",
+	"Overview": "Privire de ansamblu",
+	"page": "pagina",
+	"Password": "Parolă",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Document PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Extrage Imagini PDF (OCR)",
+	"pending": "în așteptare",
+	"Permission denied when accessing media devices": "Permisiunea refuzată la accesarea dispozitivelor media",
+	"Permission denied when accessing microphone": "Permisiunea refuzată la accesarea microfonului",
+	"Permission denied when accessing microphone: {{error}}": "Permisiunea refuzată la accesarea microfonului: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalizare",
+	"Pin": "Fixează",
+	"Pinned": "Fixat",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Conducta a fost ștearsă cu succes",
+	"Pipeline downloaded successfully": "Conducta a fost descărcată cu succes",
+	"Pipelines": "Conducte",
+	"Pipelines Not Detected": "Conducte Nedetectate",
+	"Pipelines Valves": "Valvele Conductelor",
+	"Plain text (.txt)": "Text simplu (.txt)",
+	"Playground": "Teren de Joacă",
+	"Please carefully review the following warnings:": "Vă rugăm să revizuiți cu atenție următoarele avertismente:",
+	"Please enter a prompt": "Te rog să introduci un mesaj",
+	"Please fill in all fields.": "Vă rugăm să completați toate câmpurile.",
+	"Please select a model first.": "",
+	"Please select a reason": "Vă rugăm să selectați un motiv",
+	"Port": "",
+	"Positive attitude": "Atitudine pozitivă",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Ultimele 30 de zile",
+	"Previous 7 days": "Ultimele 7 zile",
+	"Profile Image": "Imagine de Profil",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (de ex. Spune-mi un fapt amuzant despre Imperiul Roman)",
+	"Prompt Content": "Conținut Prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Sugestii de Prompt",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompturi",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Extrage \"{{searchValue}}\" de pe Ollama.com",
+	"Pull a model from Ollama.com": "Extrage un model de pe Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Parametri Interogare",
+	"RAG Template": "Șablon RAG",
+	"Rating": "Evaluare",
+	"Re-rank models by topic similarity": "Reordonează modelele în funcție de similaritatea tematică",
+	"Read Aloud": "Citește cu Voce Tare",
+	"Record voice": "Înregistrează vocea",
+	"Redirecting you to OpenWebUI Community": "Vă redirecționăm către Comunitatea OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referiți-vă la dvs. ca \"Utilizator\" (de ex., \"Utilizatorul învață spaniolă\")",
+	"References from": "Referințe din",
+	"Refused when it shouldn't have": "Refuzat când nu ar fi trebuit",
+	"Regenerate": "Regenerare",
+	"Release Notes": "Note de Lansare",
+	"Relevance": "Relevanță",
+	"Remove": "Înlătură",
+	"Remove Model": "Înlătură Modelul",
+	"Rename": "Redenumește",
+	"Reorder Models": "",
+	"Repeat Last N": "Repetă Ultimele N",
+	"Request Mode": "Mod de Cerere",
+	"Reranking Model": "Model de Rearanjare",
+	"Reranking model disabled": "Modelul de Rearanjare este dezactivat",
+	"Reranking model set to \"{{reranking_model}}\"": "Modelul de Rearanjare setat la \"{{reranking_model}}\"",
+	"Reset": "Resetează",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Resetează Directorul de Încărcare",
+	"Reset Vector Storage/Knowledge": "Resetarea Stocării/Vectoului de Cunoștințe",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Notificările de răspuns nu pot fi activate deoarece permisiunile site-ului au fost refuzate. Vă rugăm să vizitați setările browserului pentru a acorda accesul necesar.",
+	"Response splitting": "Împărțirea răspunsurilor",
+	"Result": "Rezultat",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Introducere text îmbogățit pentru chat",
+	"RK": "RK",
+	"Role": "Rol",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Execută",
+	"Running": "Rulează",
+	"Save": "Salvează",
+	"Save & Create": "Salvează & Creează",
+	"Save & Update": "Salvează & Actualizează",
+	"Save As Copy": "Salvează ca Copie",
+	"Save Tag": "Salvează Eticheta",
+	"Saved": "Salvat",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Salvarea jurnalelor de conversație direct în stocarea browserului dvs. nu mai este suportată. Vă rugăm să luați un moment pentru a descărca și a șterge jurnalele de conversație făcând clic pe butonul de mai jos. Nu vă faceți griji, puteți reimporta ușor jurnalele de conversație în backend prin",
+	"Scroll to bottom when switching between branches": "Derulați până jos când comutați între ramuri.",
+	"Search": "Caută",
+	"Search a model": "Caută un model",
+	"Search Base": "",
+	"Search Chats": "Caută în Conversații",
+	"Search Collection": "Căutare Colecție",
+	"Search Filters": "",
+	"search for tags": "caută etichete",
+	"Search Functions": "Caută Funcții",
+	"Search Knowledge": "Căutare Cunoștințe",
+	"Search Models": "Caută Modele",
+	"Search options": "",
+	"Search Prompts": "Caută Prompturi",
+	"Search Result Count": "Număr Rezultate Căutare",
+	"Search the web": "",
+	"Search Tools": "Caută Instrumente",
+	"SearchApi API Key": "Cheie API pentru SearchApi",
+	"SearchApi Engine": "Motorul SearchApi",
+	"Searched {{count}} sites_one": "{{count}} site căutat",
+	"Searched {{count}} sites_few": "",
+	"Searched {{count}} sites_other": "{{count}} alte site-uri căutate",
+	"Searching \"{{searchQuery}}\"": "Căutare \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Căutare cunoștințe pentru \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL Interogare Searxng",
+	"See readme.md for instructions": "Consultați readme.md pentru instrucțiuni",
+	"See what's new": "Vezi ce e nou",
+	"Seed": "",
+	"Select a base model": "Selectează un model de bază",
+	"Select a engine": "Selectează un motor",
+	"Select a function": "Selectează o funcție",
+	"Select a group": "",
+	"Select a model": "Selectează un model",
+	"Select a pipeline": "Selectează o conductă",
+	"Select a pipeline url": "Selectează un URL de conductă",
+	"Select a tool": "Selectează un instrument",
+	"Select Engine": "Selectează motorul",
+	"Select Knowledge": "Selectarea cunoștințelor (Knowledge Selection) este un proces esențial în multiple domenii, incluzând inteligența artificială și învățarea automată. Aceasta presupune alegerea corectă a informațiilor sau datelor relevante dintr-un set mai mare pentru a le utiliza în analize, modele sau sisteme specifice. De exemplu, în învățarea automată, selectarea caracteristicilor este un aspect al selectării cunoștințelor și implică alegerea celor mai relevante date de intrare care contribuie la îmbunătățirea preciziei modelului.",
+	"Select model": "Selectează model",
+	"Select only one model to call": "Selectează doar un singur model pentru apel",
+	"Selected model(s) do not support image inputs": "Modelul(e) selectat(e) nu suportă intrări de imagine",
+	"Semantic distance to query": "Distanța semantică față de interogare",
+	"Send": "Trimite",
+	"Send a Message": "Trimite un Mesaj",
+	"Send message": "Trimite mesajul",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Trimite `stream_options: { include_usage: true }` în cerere. Furnizorii care suportă această opțiune vor returna informații despre utilizarea token-urilor în răspuns când este setată.",
+	"September": "Septembrie",
+	"Serper API Key": "Cheie API Serper",
+	"Serply API Key": "Cheie API Serply",
+	"Serpstack API Key": "Cheie API Serpstack",
+	"Server connection verified": "Conexiunea la server a fost verificată",
+	"Set as default": "Setează ca implicit",
+	"Set CFG Scale": "Setează scala CFG",
+	"Set Default Model": "Setează Modelul Implicit",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Setează modelul de încapsulare (de ex. {{model}})",
+	"Set Image Size": "Setează Dimensiunea Imaginilor",
+	"Set reranking model (e.g. {{model}})": "Setează modelul de rearanjare (de ex. {{model}})",
+	"Set Sampler": "Set Samply.",
+	"Set Scheduler": "Setare Programatorului de Sarcini",
+	"Set Steps": "Setează Pași",
+	"Set Task Model": "Setează Model de Sarcină",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Setează Voce",
+	"Set whisper model": "Setează modelul whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Setări",
+	"Settings saved successfully!": "Setările au fost salvate cu succes!",
+	"Share": "Partajează",
+	"Share Chat": "Partajează Conversația",
+	"Share to OpenWebUI Community": "Partajează cu Comunitatea OpenWebUI",
+	"Show": "Afișează",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Afișează Detaliile Administratorului în Suprapunerea Contului În Așteptare",
+	"Show shortcuts": "Afișează scurtături",
+	"Show your support!": "Arată-ți susținerea!",
+	"Showcased creativity": "Creativitate expusă",
+	"Sign in": "Autentificare",
+	"Sign in to {{WEBUI_NAME}}": "Conectează-te la {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Deconectare",
+	"Sign up": "Înregistrare",
+	"Sign up to {{WEBUI_NAME}}": "Înregistrează-te la {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Autentificare în {{WEBUI_NAME}}",
+	"Source": "Sursă",
+	"Speech Playback Speed": "Viteza de redare a vorbirii",
+	"Speech recognition error: {{error}}": "Eroare de recunoaștere vocală: {{error}}",
+	"Speech-to-Text Engine": "Motor de Conversie a Vocii în Text",
+	"Stop": "Oprire",
+	"Stop Sequence": "Oprește Secvența",
+	"Stream Chat Response": "Răspuns Stream Chat",
+	"STT Model": "Model STT",
+	"STT Settings": "Setări STT",
+	"Subtitle (e.g. about the Roman Empire)": "Subtitlu (de ex. despre Imperiul Roman)",
+	"Success": "Succes",
+	"Successfully updated.": "Actualizat cu succes.",
+	"Suggested": "Sugerat",
+	"Support": "Suport",
+	"Support this plugin:": "Susține acest plugin:",
+	"Sync directory": "Sincronizează directorul",
+	"System": "Sistem",
+	"System Instructions": "Instrucțiuni pentru sistem",
+	"System Prompt": "Prompt de Sistem",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Generarea de Etichete Prompt",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Apasă pentru a întrerupe",
+	"Tavily API Key": "Cheie API Tavily",
+	"Tell us more:": "Spune-ne mai multe:",
+	"Temperature": "Temperatură",
+	"Template": "Șablon",
+	"Temporary Chat": "Chat Temporar",
+	"Text Splitter": "Divizor de Text",
+	"Text-to-Speech Engine": "Motor de Conversie a Textului în Vorbire",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Mulțumim pentru feedback!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Dezvoltatorii din spatele acestui plugin sunt voluntari pasionați din comunitate. Dacă considerați acest plugin util, vă rugăm să luați în considerare contribuția la dezvoltarea sa.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Clasamentul de evaluare se bazează pe sistemul de rating Elo și este actualizat în timp real.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Clasamentul este în prezent în versiune beta și este posibil să ajustăm calculul evaluărilor pe măsură ce rafinăm algoritmul.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Dimensiunea maximă a fișierului în MB. Dacă dimensiunea fișierului depășește această limită, fișierul nu va fi încărcat.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Numărul maxim de fișiere care pot fi utilizate simultan în chat. Dacă numărul de fișiere depășește această limită, fișierele nu vor fi încărcate.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Scorul ar trebui să fie o valoare între 0.0 (0%) și 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Temă",
+	"Thinking...": "Gândește...",
+	"This action cannot be undone. Do you wish to continue?": "Această acțiune nu poate fi anulată. Doriți să continuați?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Acest lucru asigură că conversațiile dvs. valoroase sunt salvate în siguranță în baza de date a backend-ului dvs. Mulțumim!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Aceasta este o funcție experimentală, poate să nu funcționeze așa cum vă așteptați și este supusă schimbării în orice moment.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Această opțiune va șterge toate fișierelor existente din colecție și le va înlocui cu fișierele nou încărcate.",
+	"This response was generated by \"{{model}}\"": "Acest răspuns a fost generat de \"{{model}}\"",
+	"This will delete": "Aceasta va șterge",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Acest lucru va șterge <strong>{{NAME}}</strong> și <strong>toate conținuturile sale</strong>.",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Aceasta va reseta baza de cunoștințe și va sincroniza toate fișierele. Doriți să continuați?",
+	"Thorough explanation": "Explicație detaliată",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Este necesar URL-ul serverului Tika.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Sfat: Actualizați mai multe sloturi de variabile consecutiv apăsând tasta tab în câmpul de intrare al conversației după fiecare înlocuire.",
+	"Title": "Titlu",
+	"Title (e.g. Tell me a fun fact)": "Titlu (de ex. Spune-mi un fapt amuzant)",
+	"Title Auto-Generation": "Generare Automată a Titlului",
+	"Title cannot be an empty string.": "Titlul nu poate fi un șir gol.",
+	"Title Generation Prompt": "Prompt de Generare a Titlului",
+	"TLS": "",
+	"To access the available model names for downloading,": "Pentru a accesa numele modelelor disponibile pentru descărcare,",
+	"To access the GGUF models available for downloading,": "Pentru a accesa modelele GGUF disponibile pentru descărcare,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Pentru a accesa WebUI, vă rugăm să contactați administratorul. Administratorii pot gestiona statusurile utilizatorilor din Panoul de Administrare.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Pentru a atașa baza de cunoștințe aici, adăugați-o mai întâi în spațiul de lucru \"Knowledge\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Pentru a vă proteja confidențialitatea, doar evaluările, ID-urile modelelor, etichetele și metadatele sunt partajate din feedback-ul dumneavoastră—jurnalele de chat rămân private și nu sunt incluse.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Pentru a selecta acțiuni aici, adăugați-le mai întâi în spațiul de lucru \"Funcții\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Pentru a selecta filtrele aici, adăugați-le mai întâi în spațiul de lucru \"Funcții\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Pentru a selecta kiturile de instrumente aici, adăugați-le mai întâi în spațiul de lucru \"Instrumente\".",
+	"Toast notifications for new updates": "Notificări toast pentru actualizări noi",
+	"Today": "Astăzi",
+	"Toggle settings": "Comută setările",
+	"Toggle sidebar": "Comută bara laterală",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokeni de Păstrat la Reîmprospătarea Contextului (num_keep)",
+	"Too verbose": "Prea detaliat",
+	"Tool created successfully": "Instrumentul a fost creat cu succes",
+	"Tool deleted successfully": "Instrumentul a fost șters cu succes",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Instrumentul a fost importat cu succes",
+	"Tool Name": "",
+	"Tool updated successfully": "Instrumentul a fost actualizat cu succes",
+	"Tools": "Instrumente",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Instrumentele sunt un sistem de apelare a funcțiilor cu executare arbitrară a codului",
+	"Tools have a function calling system that allows arbitrary code execution": "Instrumentele au un sistem de apelare a funcțiilor care permite executarea arbitrară a codului",
+	"Tools have a function calling system that allows arbitrary code execution.": "Instrumentele au un sistem de apelare a funcțiilor care permite executarea arbitrară a codului.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Probleme la accesarea Ollama?",
+	"TTS Model": "Model TTS",
+	"TTS Settings": "Setări TTS",
+	"TTS Voice": "Voce TTS",
+	"Type": "Tip",
+	"Type Hugging Face Resolve (Download) URL": "Introduceți URL-ul de Rezolvare (Descărcare) Hugging Face",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Uh-oh! A apărut o problemă la conectarea la {{provider}}.",
+	"UI": "Interfață Utilizator",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Anulează Fixarea",
+	"Unravel secrets": "",
+	"Untagged": "Netichetat",
+	"Update": "Actualizează",
+	"Update and Copy Link": "Actualizează și Copiază Link-ul",
+	"Update for the latest features and improvements.": "Actualizare pentru cele mai recente caracteristici și îmbunătățiri.",
+	"Update password": "Actualizează parola",
+	"Updated": "Actualizat",
+	"Updated at": "Actualizat la",
+	"Updated At": "Actualizat la",
+	"Upload": "Încărcare",
+	"Upload a GGUF model": "Încarcă un model GGUF",
+	"Upload directory": "Încărcare director",
+	"Upload files": "Încărcați fișiere",
+	"Upload Files": "Încarcă Fișiere",
+	"Upload Pipeline": "Încarcă Conducta",
+	"Upload Progress": "Progres Încărcare",
+	"URL": "",
+	"URL Mode": "Mod URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Folosește '#' în prompt pentru a încărca și include cunoștințele tale.",
+	"Use Gravatar": "Folosește Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Folosește Inițialele",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "utilizator",
+	"User": "Utilizator",
+	"User location successfully retrieved.": "Localizarea utilizatorului a fost preluată cu succes.",
+	"Username": "",
+	"Users": "Utilizatori",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Folosind modelul implicit de arenă cu toate modelele. Faceți clic pe butonul plus pentru a adăuga modele personalizate.",
+	"Utilize": "Utilizează",
+	"Valid time units:": "Unități de timp valide:",
+	"Valves": "Valve",
+	"Valves updated": "Valve actualizate",
+	"Valves updated successfully": "Valve actualizate cu succes",
+	"variable": "variabilă",
+	"variable to have them replaced with clipboard content.": "variabilă pentru a fi înlocuite cu conținutul clipboard-ului.",
+	"Version": "Versiune",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Versiunea {{selectedVersion}} din {{totalVersions}}",
+	"Visibility": "",
+	"Voice": "Voce",
+	"Voice Input": "Intrare vocală",
+	"Warning": "Avertisment",
+	"Warning:": "Avertisment:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Avertisment: Dacă actualizați sau schimbați modelul de încapsulare, va trebui să reimportați toate documentele.",
+	"Web": "Web",
+	"Web API": "API Web",
+	"Web Loader Settings": "Setări Încărcător Web",
+	"Web Search": "Căutare Web",
+	"Web Search Engine": "Motor de Căutare Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL Webhook",
+	"WebUI Settings": "Setări WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Ce e Nou în",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (Local)",
+	"Why?": "",
+	"Widescreen Mode": "Mod Ecran Larg",
+	"Won": "Câștigat",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Spațiu de Lucru",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Scrieți o sugestie de prompt (de ex. Cine ești?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Scrieți un rezumat în 50 de cuvinte care rezumă [subiect sau cuvânt cheie].",
+	"Write something...": "Scrie ceva...",
+	"Write your model template content here": "",
+	"Yesterday": "Ieri",
+	"You": "Tu",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Puteți discuta cu un număr maxim de {{maxCount}} fișier(e) simultan.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Puteți personaliza interacțiunile dvs. cu LLM-urile adăugând amintiri prin butonul 'Gestionează' de mai jos, făcându-le mai utile și adaptate la dvs.",
+	"You cannot upload an empty file.": "Nu poți încărca un fișier gol.",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Nu aveți conversații arhivate.",
+	"You have shared this chat": "Ați partajat această conversație",
+	"You're a helpful assistant.": "Ești un asistent util.",
+	"You're now logged in.": "Acum ești autentificat.",
+	"Your account status is currently pending activation.": "Statusul contului dvs. este în așteptare pentru activare.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Întreaga dvs. contribuție va merge direct la dezvoltatorul plugin-ului; Open WebUI nu ia niciun procent. Cu toate acestea, platforma de finanțare aleasă ar putea avea propriile taxe.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Setări Încărcător Youtube"
+}
diff --git a/src/lib/i18n/locales/ru-RU/translation.json b/src/lib/i18n/locales/ru-RU/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..dd08397af16d5b20ec9f91933626b0dc950cfe4b
--- /dev/null
+++ b/src/lib/i18n/locales/ru-RU/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "-1 для отсутствия ограничений или положительное целое число для определенного ограничения.",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' или '-1' чтобы был без срока годности.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(например, `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(например, `sh webui.sh --api`)",
+	"(latest)": "(последняя)",
+	"{{ models }}": "{{ модели }}",
+	"{{user}}'s Chats": "Чаты {{user}}'а",
+	"{{webUIName}} Backend Required": "Необходимо подключение к серверу {{webUIName}}",
+	"*Prompt node ID(s) are required for image generation": "ID узлов промптов обязательны для генерации изображения",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Новая версия (v{{LATEST_VERSION}}) теперь доступна.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Модель задач используется при выполнении таких задач, как генерация заголовков для чатов и поисковых запросов в Интернете",
+	"a user": "пользователь",
+	"About": "О программе",
+	"Access": "Доступ",
+	"Access Control": "Контроль доступа",
+	"Accessible to all users": "Доступно всем пользователям",
+	"Account": "Учетная запись",
+	"Account Activation Pending": "Ожидание активации учетной записи",
+	"Accurate information": "Точная информация",
+	"Actions": "Действия",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Активируйте эту команду, набрав \"/{{COMMAND}}\" для ввода в чате.",
+	"Active Users": "Активные пользователи",
+	"Add": "Добавить",
+	"Add a model ID": "Добавить ID модели",
+	"Add a short description about what this model does": "Добавьте краткое описание того, что делает эта модель",
+	"Add a tag": "Добавьте тег",
+	"Add Arena Model": "Добавить модель арены",
+	"Add Connection": "Добавить соединение",
+	"Add Content": "Добавить контент",
+	"Add content here": "Добавить контент сюда",
+	"Add custom prompt": "Добавьте пользовательский промпт",
+	"Add Files": "Добавить файлы",
+	"Add Group": "Добавить группу",
+	"Add Memory": "Добавить воспоминание",
+	"Add Model": "Добавить модель",
+	"Add Tag": "Добавить тег",
+	"Add Tags": "Добавить теги",
+	"Add text content": "",
+	"Add User": "Добавить пользователя",
+	"Add User Group": "Добавить группу пользователей",
+	"Adjusting these settings will apply changes universally to all users.": "Изменения в этих настройках будут применены для всех пользователей.",
+	"admin": "админ",
+	"Admin": "Админ",
+	"Admin Panel": "Админ панель",
+	"Admin Settings": "Настройки администратора",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Администраторы всегда имеют доступ ко всем инструментам; пользователям нужны инструменты, назначенные для каждой модели в рабочем пространстве.",
+	"Advanced Parameters": "Расширенные Параметры",
+	"Advanced Params": "Расширенные параметры",
+	"All chats": "Все чаты",
+	"All Documents": "Все документы",
+	"All models deleted successfully": "Все модели успешно удалены",
+	"Allow Chat Delete": "Разрешить удаление чата",
+	"Allow Chat Deletion": "Разрешить удаление чата",
+	"Allow Chat Edit": "Разрешить редактирование чата",
+	"Allow File Upload": "Разрешить загрузку файлов",
+	"Allow non-local voices": "Разрешить не локальные голоса",
+	"Allow Temporary Chat": "Разрешить временные чаты",
+	"Allow User Location": "Разрешить доступ к местоположению пользователя",
+	"Allow Voice Interruption in Call": "Разрешить прерывание голоса во время вызова",
+	"Already have an account?": "У вас уже есть учетная запись?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "Удивительный",
+	"an assistant": "ассистент",
+	"and": "и",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "и создайте новую общую ссылку.",
+	"API Base URL": "Базовый адрес API",
+	"API Key": "Ключ API",
+	"API Key created.": "Ключ API создан.",
+	"API keys": "Ключи API",
+	"Application DN": "DN приложения",
+	"Application DN Password": "DN-пароль приложения",
+	"applies to all users with the \"user\" role": "применяется ко всем пользователям с ролью «пользователь»",
+	"April": "Апрель",
+	"Archive": "Архив",
+	"Archive All Chats": "Архивировать все чаты",
+	"Archived Chats": "Архив чатов",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "Вы уверены, что хотите разархивировать все заархивированные чаты?",
+	"Are you sure?": "Вы уверены?",
+	"Arena Models": "Модели арены",
+	"Artifacts": "Артефакты",
+	"Ask a question": "Задать вопрос",
+	"Assistant": "Ассистент",
+	"Attach file": "Прикрепить файл",
+	"Attention to detail": "Внимание к деталям",
+	"Attribute for Username": "Атрибут для имени пользователя",
+	"Audio": "Аудио",
+	"August": "Август",
+	"Authenticate": "Аутентификация",
+	"Auto-Copy Response to Clipboard": "Автоматическое копирование ответа в буфер обмена",
+	"Auto-playback response": "Автоматическое воспроизведение ответа",
+	"Autocomplete Generation": "Генерация автозаполнения",
+	"Autocomplete Generation Input Max Length": "Максимальная длина входных данных автозаполнения",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "строка авторизации API AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "Базовый URL адрес AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Необходим базовый адрес URL AUTOMATIC1111.",
+	"Available list": "Список доступных",
+	"available!": "доступно!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI Speech",
+	"Azure Region": "Регион Azure",
+	"Back": "Назад",
+	"Bad Response": "Плохой ответ",
+	"Banners": "Баннеры",
+	"Base Model (From)": "Базовая модель (от)",
+	"Batch Size (num_batch)": "Размер партии (num_batch)",
+	"before": "до",
+	"Being lazy": "Лениво",
+	"Bing Search V7 Endpoint": "Конечная точка поиска Bing V7",
+	"Bing Search V7 Subscription Key": "Ключ API Bing Search V7",
+	"Brave Search API Key": "Ключ API поиска Brave",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Обход проверки SSL для веб-сайтов",
+	"Call": "Вызов",
+	"Call feature is not supported when using Web STT engine": "Функция вызова не поддерживается при использовании Web STT (распознавание речи) движка",
+	"Camera": "Камера",
+	"Cancel": "Отменить",
+	"Capabilities": "Возможности",
+	"Certificate Path": "",
+	"Change Password": "Изменить пароль",
+	"Character": "",
+	"Character limit for autocomplete generation input": "Ограничение количества символов для ввода при генерации автозаполнения",
+	"Chart new frontiers": "Наметьте новые границы",
+	"Chat": "Чат",
+	"Chat Background Image": "Фоновое изображение чата",
+	"Chat Bubble UI": "Bubble UI чат",
+	"Chat Controls": "Управление чатом",
+	"Chat direction": "Направление чата",
+	"Chat Overview": "Обзор чата",
+	"Chat Permissions": "Разрешения для чата",
+	"Chat Tags Auto-Generation": "Автогенерация тегов чата",
+	"Chats": "Чаты",
+	"Check Again": "Перепроверьте ещё раз",
+	"Check for updates": "Проверить обновления",
+	"Checking for updates...": "Проверка обновлений...",
+	"Choose a model before saving...": "Выберите модель перед сохранением...",
+	"Chunk Overlap": "Перекрытие фрагментов",
+	"Chunk Params": "Параметры фрагментов",
+	"Chunk Size": "Размер фрагмента",
+	"Ciphers": "Шифры",
+	"Citation": "Цитирование",
+	"Clear memory": "Очистить воспоминания",
+	"click here": "кликните сюда",
+	"Click here for filter guides.": "Нажмите здесь, чтобы просмотреть руководства по фильтрам.",
+	"Click here for help.": "Нажмите здесь для получения помощи.",
+	"Click here to": "Нажмите здесь, чтобы",
+	"Click here to download user import template file.": "Нажмите здесь, чтобы загрузить файл шаблона импорта пользователя",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Нажмите здесь, чтобы выбрать",
+	"Click here to select a csv file.": "Нажмите здесь, чтобы выбрать csv-файл.",
+	"Click here to select a py file.": "Нажмите здесь, чтобы выбрать py-файл",
+	"Click here to upload a workflow.json file.": "Нажмите здесь, чтобы загрузить файл workflow.json.",
+	"click here.": "нажмите здесь.",
+	"Click on the user role button to change a user's role.": "Нажмите кнопку роли пользователя, чтобы изменить роль пользователя.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "В разрешении на запись в буфер обмена отказано. Пожалуйста, проверьте настройки своего браузера, чтобы предоставить необходимый доступ.",
+	"Clone": "Клонировать",
+	"Close": "Закрыть",
+	"Code execution": "Выполнение кода",
+	"Code formatted successfully": "Код успешно отформатирован",
+	"Collection": "Коллекция",
+	"Color": "Цвет",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "Базовый адрес URL ComfyUI",
+	"ComfyUI Base URL is required.": "Необходим базовый адрес URL ComfyUI.",
+	"ComfyUI Workflow": "ComfyUI Workflow",
+	"ComfyUI Workflow Nodes": "Узлы ComfyUI Workflow",
+	"Command": "Команда",
+	"Completions": "Завершения",
+	"Concurrent Requests": "Одновременные запросы",
+	"Configure": "Настроить",
+	"Configure Models": "Настройка моделей",
+	"Confirm": "Подтвердить",
+	"Confirm Password": "Подтвердите пароль",
+	"Confirm your action": "Подтвердите свое действие",
+	"Connections": "Соединение",
+	"Contact Admin for WebUI Access": "Обратитесь к администратору для получения доступа к WebUI",
+	"Content": "Содержание",
+	"Content Extraction": "Извлечение контента",
+	"Context Length": "Длина контекста",
+	"Continue Response": "Продолжить ответ",
+	"Continue with {{provider}}": "Продолжить с {{provider}}",
+	"Continue with Email": "Продолжить с Email",
+	"Continue with LDAP": "Продолжить с LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Управляйте разделением текста сообщения для запросов TTS. 'Пунктуация' разделяет на предложения, 'абзацы' - разделяет на абзацы, а 'нет' сохраняет сообщение в виде одной строки.",
+	"Controls": "Управление",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Управляет балансом между согласованностью и разнообразием выходных данных. Меньшее значение приведет к более сфокусированному и связному тексту. (По умолчанию: 5.0)",
+	"Copied": "Скопировано",
+	"Copied shared chat URL to clipboard!": "Копирование в буфер обмена выполнено успешно!",
+	"Copied to clipboard": "Скопировано в буфер обмена",
+	"Copy": "Копировать",
+	"Copy last code block": "Копировать последний блок кода",
+	"Copy last response": "Копировать последний ответ",
+	"Copy Link": "Копировать ссылку",
+	"Copy to clipboard": "Скопировать в буфер обмена",
+	"Copying to clipboard was successful!": "Копирование в буфер обмена прошло успешно!",
+	"Create": "Создать",
+	"Create a knowledge base": "Создайте базу знаний",
+	"Create a model": "Создание модели",
+	"Create Account": "Создать аккаунт",
+	"Create Admin Account": "Создать Аккаунт Администратора",
+	"Create Group": "Создать Группу",
+	"Create Knowledge": "Создать Знания",
+	"Create new key": "Создать новый ключ",
+	"Create new secret key": "Создать новый секретный ключ",
+	"Created at": "Создано",
+	"Created At": "Создано",
+	"Created by": "Создано",
+	"CSV Import": "Импорт CSV",
+	"Current Model": "Текущая модель",
+	"Current Password": "Текущий пароль",
+	"Custom": "Пользовательский",
+	"Dark": "Темная",
+	"Database": "База данных",
+	"December": "Декабрь",
+	"Default": "По умолчанию",
+	"Default (Open AI)": "По умолчанию (Open AI)",
+	"Default (SentenceTransformers)": "По умолчанию (SentenceTransformers)",
+	"Default Model": "Модель по умолчанию",
+	"Default model updated": "Модель по умолчанию обновлена",
+	"Default Models": "Модели по умолчанию",
+	"Default permissions": "Разрешения по умолчанию",
+	"Default permissions updated successfully": "Разрешения по умолчанию успешно обновлены.",
+	"Default Prompt Suggestions": "Предложения промптов по умолчанию",
+	"Default to 389 or 636 if TLS is enabled": "По умолчанию 389 или 636, если TLS включен.",
+	"Default to ALL": "По умолчанию ВСЕ",
+	"Default User Role": "Роль пользователя по умолчанию",
+	"Delete": "Удалить",
+	"Delete a model": "Удалить модель",
+	"Delete All Chats": "Удалить ВСЕ Чаты",
+	"Delete All Models": "Удалить ВСЕ Модели",
+	"Delete chat": "Удалить чат",
+	"Delete Chat": "Удалить Чат",
+	"Delete chat?": "Удалить чат?",
+	"Delete folder?": "Удалить папку?",
+	"Delete function?": "Удалить функцию?",
+	"Delete prompt?": "Удалить промпт?",
+	"delete this link": "удалить эту ссылку",
+	"Delete tool?": "Удалить этот инструмент?",
+	"Delete User": "Удалить Пользователя",
+	"Deleted {{deleteModelTag}}": "Удалено {{deleteModelTag}}",
+	"Deleted {{name}}": "Удалено {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "Опишите свою базу знаний и цели",
+	"Description": "Описание",
+	"Didn't fully follow instructions": "Не полностью следует инструкциям",
+	"Disabled": "Отключено",
+	"Discover a function": "Найти функцию",
+	"Discover a model": "Найти модель",
+	"Discover a prompt": "Найти промпт",
+	"Discover a tool": "Найти инструмент",
+	"Discover wonders": "Откройте для себя чудеса",
+	"Discover, download, and explore custom functions": "Находите, загружайте и исследуйте пользовательские функции",
+	"Discover, download, and explore custom prompts": "Находите, загружайте и исследуйте пользовательские промпты",
+	"Discover, download, and explore custom tools": "Находите, загружайте и исследуйте пользовательские инструменты",
+	"Discover, download, and explore model presets": "Находите, загружайте и исследуйте пользовательские предустановки моделей",
+	"Dismissible": "Можно отклонить",
+	"Display": "Отображать",
+	"Display Emoji in Call": "Отображать эмодзи в вызовах",
+	"Display the username instead of You in the Chat": "Отображать имя пользователя вместо 'Вы' в чате",
+	"Displays citations in the response": "Отображает цитаты в ответе",
+	"Dive into knowledge": "Погрузитесь в знания",
+	"Do not install functions from sources you do not fully trust.": "Не устанавливайте функции из источников, которым вы не полностью доверяете.",
+	"Do not install tools from sources you do not fully trust.": "Не устанавливайте инструменты из источников, которым вы не полностью доверяете.",
+	"Document": "Документ",
+	"Documentation": "Документация",
+	"Documents": "Документы",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "не устанавливает никаких внешних соединений, и ваши данные надежно хранятся на вашем локальном сервере.",
+	"Don't have an account?": "У вас нет аккаунта?",
+	"don't install random functions from sources you don't trust.": "не устанавливайте случайные функции из источников, которым вы не доверяете.",
+	"don't install random tools from sources you don't trust.": "не устанавливайте случайные инструменты из источников, которым вы не доверяете.",
+	"Don't like the style": "Не нравится стиль",
+	"Done": "Готово",
+	"Download": "Загрузить",
+	"Download canceled": "Загрузка отменена",
+	"Download Database": "Загрузить базу данных",
+	"Drag and drop a file to upload or select a file to view": "Перетащите файл для загрузки или выберите файл для просмотра.",
+	"Draw": "Рисовать",
+	"Drop any files here to add to the conversation": "Перетащите сюда файлы, чтобы добавить их в разговор",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "например, '30s','10m'. Допустимые единицы времени: 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Редактировать",
+	"Edit Arena Model": "Изменить модель арены",
+	"Edit Connection": "Изменить соединение",
+	"Edit Default Permissions": "Изменить разрешения по умолчанию",
+	"Edit Memory": "Редактировать воспоминание",
+	"Edit User": "Редактировать пользователя",
+	"Edit User Group": "Редактировать Пользовательскую Группу",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Электронная почта",
+	"Embark on adventures": "Отправляйтесь в приключения",
+	"Embedding Batch Size": "Размер пакета для встраивания",
+	"Embedding Model": "Модель встраивания",
+	"Embedding Model Engine": "Движок модели встраивания",
+	"Embedding model set to \"{{embedding_model}}\"": "Модель встраивания установлена в \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Включить аутентификацию по API Ключу",
+	"Enable autocomplete generation for chat messages": "Включить генерацию автозаполнения для сообщений чата",
+	"Enable Community Sharing": "Включить совместное использование",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Включите блокировку памяти (mlock), чтобы предотвратить выгрузку данных модели из ОЗУ. Эта опция блокирует рабочий набор страниц модели в оперативной памяти, гарантируя, что они не будут выгружены на диск. Это может помочь поддерживать производительность, избегая ошибок страниц и обеспечивая быстрый доступ к данным.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Включите отображение памяти (mmap), чтобы загрузить данные модели. Эта опция позволяет системе использовать дисковое хранилище в качестве расширения оперативной памяти, обрабатывая дисковые файлы так, как если бы они находились в оперативной памяти. Это может улучшить производительность модели за счет более быстрого доступа к данным. Однако он может работать некорректно со всеми системами и занимать значительный объем дискового пространства.",
+	"Enable Message Rating": "Разрешить оценку ответов",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Разрешить новые регистрации",
+	"Enable Web Search": "Включить поиск в Интернете",
+	"Enabled": "Включено",
+	"Engine": "Движок",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Убедитесь, что ваш CSV-файл включает в себя 4 столбца в следующем порядке: Имя, Электронная почта, Пароль, Роль.",
+	"Enter {{role}} message here": "Введите сообщение {{role}} здесь",
+	"Enter a detail about yourself for your LLMs to recall": "Введите детали о себе, чтобы LLMs могли запомнить",
+	"Enter api auth string (e.g. username:password)": "Введите строку авторизации api (например, username:password)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Введите ключ API поиска Brave",
+	"Enter certificate path": "Введите путь к сертификату",
+	"Enter CFG Scale (e.g. 7.0)": "Введите CFG Scale (например, 7.0)",
+	"Enter Chunk Overlap": "Введите перекрытие фрагмента",
+	"Enter Chunk Size": "Введите размер фрагмента",
+	"Enter description": "Введите описание",
+	"Enter Github Raw URL": "Введите необработанный URL-адрес Github",
+	"Enter Google PSE API Key": "Введите ключ API Google PSE",
+	"Enter Google PSE Engine Id": "Введите Id движка Google PSE",
+	"Enter Image Size (e.g. 512x512)": "Введите размер изображения (например, 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Введите коды языков",
+	"Enter Model ID": "Введите ID модели",
+	"Enter model tag (e.g. {{modelTag}})": "Введите тег модели (например, {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Введите количество шагов (например, 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Введите сэмплер (например, Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Введите планировщик (например, Karras)",
+	"Enter Score": "Введите оценку",
+	"Enter SearchApi API Key": "Введите ключ API SearchApi",
+	"Enter SearchApi Engine": "Введите SearchApi движок",
+	"Enter Searxng Query URL": "Введите URL-адрес запроса Searxng",
+	"Enter Seed": "Введите Seed",
+	"Enter Serper API Key": "Введите ключ API Serper",
+	"Enter Serply API Key": "Введите ключ API Serply",
+	"Enter Serpstack API Key": "Введите ключ API Serpstack",
+	"Enter server host": "Введите хост сервера",
+	"Enter server label": "Введите метку сервера",
+	"Enter server port": "Введите порт сервера",
+	"Enter stop sequence": "Введите последовательность остановки",
+	"Enter system prompt": "Введите системный промпт",
+	"Enter Tavily API Key": "Введите ключ API Tavily",
+	"Enter Tika Server URL": "Введите URL-адрес сервера Tika",
+	"Enter Top K": "Введите Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Введите URL-адрес (например, http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Введите URL-адрес (например, http://localhost:11434)",
+	"Enter Your Email": "Введите вашу электронную почту",
+	"Enter Your Full Name": "Введите ваше полное имя",
+	"Enter your message": "Введите ваше сообщение",
+	"Enter Your Password": "Введите ваш пароль",
+	"Enter Your Role": "Введите вашу роль",
+	"Enter Your Username": "",
+	"Error": "Ошибка",
+	"ERROR": "",
+	"Evaluations": "Оценки",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "Исключать",
+	"Experimental": "Экспериментальное",
+	"Explore the cosmos": "Исследуйте космос",
+	"Export": "Экспорт",
+	"Export All Archived Chats": "Экспортировать ВСЕ Архивированные Чаты",
+	"Export All Chats (All Users)": "Экспортировать все чаты (всех пользователей)",
+	"Export chat (.json)": "Экспортировать чат (.json)",
+	"Export Chats": "Экспортировать чаты",
+	"Export Config to JSON File": "Экспорт конфигурации в JSON-файл",
+	"Export Functions": "Экспортировать функции",
+	"Export Models": "Экспортировать модели",
+	"Export Presets": "",
+	"Export Prompts": "Экспортировать промпты",
+	"Export to CSV": "",
+	"Export Tools": "Экспортировать инструменты",
+	"External Models": "Внешние модели",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Не удалось создать ключ API.",
+	"Failed to read clipboard contents": "Не удалось прочитать содержимое буфера обмена",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Не удалось обновить настройки",
+	"Failed to upload file.": "Не удалось загрузить файл.",
+	"February": "Февраль",
+	"Feedback History": "История отзывов",
+	"Feedbacks": "Отзывы",
+	"Feel free to add specific details": "Не стесняйтесь добавлять конкретные детали",
+	"File": "Файл",
+	"File added successfully.": "Файл успешно добавлен.",
+	"File content updated successfully.": "Содержимое файла успешно обновлено.",
+	"File Mode": "Режим файла",
+	"File not found.": "Файл не найден.",
+	"File removed successfully.": "Файл успешно удален.",
+	"File size should not exceed {{maxSize}} MB.": "Размер файла не должен превышать {{maxSize}} МБ.",
+	"Files": "Файлы",
+	"Filter is now globally disabled": "Фильтр теперь отключен глобально",
+	"Filter is now globally enabled": "Фильтр теперь включен глобально",
+	"Filters": "Фильтры",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Определение подделки отпечатка: Невозможно использовать инициалы в качестве аватара. По умолчанию используется изображение профиля по умолчанию.",
+	"Fluidly stream large external response chunks": "Плавная потоковая передача больших фрагментов внешних ответов",
+	"Focus chat input": "Фокус ввода чата",
+	"Folder deleted successfully": "Папка успешно удалена",
+	"Folder name cannot be empty": "Имя папки не может быть пустым",
+	"Folder name cannot be empty.": "Имя папки не может быть пустым.",
+	"Folder name updated successfully": "Имя папки успешно обновлено",
+	"Followed instructions perfectly": "Идеально соответствует инструкциям",
+	"Forge new paths": "Прокладывайте новые пути",
+	"Form": "Форма",
+	"Format your variables using brackets like this:": "Отформатируйте переменные, используя такие : скобки",
+	"Frequency Penalty": "Штраф за частоту",
+	"Function": "Функция",
+	"Function created successfully": "Функция успешно создана",
+	"Function deleted successfully": "Функция успешно удалена",
+	"Function Description": "Описание Функции",
+	"Function ID": "ID Функции",
+	"Function is now globally disabled": "Функция теперь глобально отключена",
+	"Function is now globally enabled": "Функция теперь глобально включена",
+	"Function Name": "Имя Функции",
+	"Function updated successfully": "Функция успешно обновлена",
+	"Functions": "Функции",
+	"Functions allow arbitrary code execution": "Функции позволяют выполнять произвольный код",
+	"Functions allow arbitrary code execution.": "Функции позволяют выполнять произвольный код.",
+	"Functions imported successfully": "Функции успешно импортированы",
+	"General": "Общее",
+	"General Settings": "Общие настройки",
+	"Generate Image": "Сгенерировать изображение",
+	"Generating search query": "Генерация поискового запроса",
+	"Generation Info": "Информация о генерации",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Глобально",
+	"Good Response": "Хороший ответ",
+	"Google PSE API Key": "Ключ API Google PSE",
+	"Google PSE Engine Id": "Id движка Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Тактильная обратная связь",
+	"has no conversations.": "не имеет разговоров.",
+	"Hello, {{name}}": "Привет, {{name}}",
+	"Help": "Помощь",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Скрыть",
+	"Host": "",
+	"How can I help you today?": "Чем я могу помочь вам сегодня?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Гибридная поисковая система",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Я подтверждаю, что прочитал и осознаю последствия своих действий. Я осознаю риски, связанные с выполнением произвольного кода, и я проверил достоверность источника.",
+	"ID": "",
+	"Ignite curiosity": "Разожгите любопытство",
+	"Image Generation (Experimental)": "Генерация изображений (Экспериментально)",
+	"Image Generation Engine": "Механизм генерации изображений",
+	"Image Settings": "Настройки изображения",
+	"Images": "Изображения",
+	"Import Chats": "Импортировать Чаты",
+	"Import Config from JSON File": "Импорт конфигурации из JSON-файла",
+	"Import Functions": "Импортировать Функции",
+	"Import Models": "Импортировать Модели",
+	"Import Presets": "Импортировать Пресеты",
+	"Import Prompts": "Импортировать Промпты",
+	"Import Tools": "Импортировать Инструменты",
+	"Include": "Включать",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Добавьте флаг '--api-auth' при запуске stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Добавьте флаг `--api` при запуске stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Информация",
+	"Input commands": "Введите команды",
+	"Install from Github URL": "Установка с URL-адреса Github",
+	"Instant Auto-Send After Voice Transcription": "Мгновенная автоматическая отправка после расшифровки голоса",
+	"Interface": "Интерфейс",
+	"Invalid file format.": "Неверный формат файла.",
+	"Invalid Tag": "Недопустимый тег",
+	"January": "Январь",
+	"Jina API Key": "",
+	"join our Discord for help.": "присоединяйтесь к нашему Discord для помощи.",
+	"JSON": "JSON",
+	"JSON Preview": "Предварительный просмотр JSON",
+	"July": "Июль",
+	"June": "Июнь",
+	"JWT Expiration": "Истечение срока JWT",
+	"JWT Token": "Токен JWT",
+	"Keep Alive": "Поддерживать активность",
+	"Key": "",
+	"Keyboard shortcuts": "Горячие клавиши",
+	"Knowledge": "Знания",
+	"Knowledge Access": "Доступ к Знаниям",
+	"Knowledge created successfully.": "Знания созданы успешно.",
+	"Knowledge deleted successfully.": "Знания успешно удалены.",
+	"Knowledge reset successfully.": "Знания успешно сброшены.",
+	"Knowledge updated successfully": "Знания успешно обновлены",
+	"Label": "",
+	"Landing Page Mode": "Режим Целевой Страницы",
+	"Language": "Язык",
+	"Last Active": "Последний Активный",
+	"Last Modified": "Последнее Изменение",
+	"LDAP": "",
+	"LDAP server updated": "LDAP сервер обновлен",
+	"Leaderboard": "Таблица Лидеров",
+	"Leave empty for unlimited": "Оставьте пустым для неограниченного",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Оставьте пустым, чтобы использовать промпт по умолчанию, или введите пользовательский промпт",
+	"Light": "Светлый",
+	"Listening...": "Слушаю...",
+	"LLMs can make mistakes. Verify important information.": "LLMs могут допускать ошибки. Проверяйте важную информацию.",
+	"Local": "",
+	"Local Models": "Локальные модели",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Сделано сообществом OpenWebUI",
+	"Make sure to enclose them with": "Убедитесь, что они заключены в",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Убедитесь, что экспортируете файл workflow.json в формате API из ComfyUI.",
+	"Manage": "Управлять",
+	"Manage Arena Models": "Управление Ареной Моделей",
+	"Manage Ollama": "Управление Ollama",
+	"Manage Ollama API Connections": "Управление соединениями API Ollama",
+	"Manage OpenAI API Connections": "Управление соединениями API OpenAI",
+	"Manage Pipelines": "Управление конвейерами",
+	"March": "Март",
+	"Max Tokens (num_predict)": "Максимальное количество токенов (num_predict)",
+	"Max Upload Count": "Максимальное количество загрузок",
+	"Max Upload Size": "Максимальный размер загрузок",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимальное количество моделей для загрузки одновременно - 3. Пожалуйста, попробуйте позже.",
+	"May": "Май",
+	"Memories accessible by LLMs will be shown here.": "Воспоминания, доступные LLMs, будут отображаться здесь.",
+	"Memory": "Воспоминания",
+	"Memory added successfully": "Воспоминание успешно добавлено",
+	"Memory cleared successfully": "Воспоминания успешно очищены",
+	"Memory deleted successfully": "Воспоминание успешно удалено",
+	"Memory updated successfully": "Воспоминание успешно обновлено",
+	"Merge Responses": "Объединить ответы",
+	"Message rating should be enabled to use this feature": "Чтобы использовать эту функцию, необходимо включить оценку сообщения.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Сообщения, отправленные вами после создания ссылки, не будут передаваться другим. Пользователи, у которых есть URL, смогут просматривать общий чат.",
+	"Min P": "Min P",
+	"Minimum Score": "Минимальный балл",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Модель",
+	"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успешно загружена.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' уже находится в очереди на загрузку.",
+	"Model {{modelId}} not found": "Модель {{modelId}} не найдена",
+	"Model {{modelName}} is not vision capable": "Модель {{modelName}} не поддерживает зрение",
+	"Model {{name}} is now {{status}}": "Модель {{name}} теперь {{status}}",
+	"Model accepts image inputs": "Модель принимает изображения как входные данные",
+	"Model created successfully!": "Модель успешно создана!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Обнаружен путь к файловой системе модели. Для обновления требуется краткое имя модели, не удается продолжить.",
+	"Model Filtering": "Фильтрация Моделей",
+	"Model ID": "ID Модели",
+	"Model IDs": "IDs Модели",
+	"Model Name": "Имя Модели",
+	"Model not selected": "Модель не выбрана",
+	"Model Params": "Параметры модели",
+	"Model Permissions": "Разрешения Модели",
+	"Model updated successfully": "Модель успешно обновлена",
+	"Modelfile Content": "Содержимое файла модели",
+	"Models": "Модели",
+	"Models Access": "Доступ к Моделям",
+	"Models configuration saved successfully": "Конфигурация модели успешно сохранена.",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Больше",
+	"Name": "Имя",
+	"Name your knowledge base": "",
+	"New Chat": "Новый чат",
+	"New folder": "",
+	"New Password": "Новый пароль",
+	"No content found": "Контент не найден",
+	"No content to speak": "Нечего говорить",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Файлы не выбраны",
+	"No files found.": "Файлы не найдены.",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScМодели не найденыript content found.": "",
+	"No knowledge found": "Знания не найдены",
+	"No model IDs": "",
+	"No models found": "Модели не найдены",
+	"No models selected": "Модели не выбраны",
+	"No results found": "Результатов не найдено",
+	"No search query generated": "Поисковый запрос не сгенерирован",
+	"No source available": "Нет доступных источников",
+	"No users were found.": "Пользователи не найдены.",
+	"No valves to update": "Нет вентилей для обновления",
+	"None": "Нет",
+	"Not factually correct": "Не соответствует действительности",
+	"Not helpful": "Бесполезно",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Обратите внимание: Если вы установите минимальный балл, поиск будет возвращать только документы с баллом больше или равным минимальному баллу.",
+	"Notes": "",
+	"Notifications": "Уведомления",
+	"November": "Ноябрь",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Октябрь",
+	"Off": "Выключено",
+	"Okay, Let's Go!": "Давайте начнём!",
+	"OLED Dark": "OLED темная",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API отключен",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Версия Ollama",
+	"On": "Включено",
+	"Only alphanumeric characters and hyphens are allowed": "Разрешены только буквенно-цифровые символы и дефисы.",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "В строке команды разрешено использовать только буквенно-цифровые символы и дефисы.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "Доступ имеют только избранные пользователи и группы, имеющие разрешение.",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Похоже, что URL-адрес недействителен. Пожалуйста, перепроверьте и попробуйте еще раз.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Упс! Есть файлы, которые все еще загружаются. Пожалуйста, дождитесь завершения загрузки.",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Вы используете неподдерживаемый метод (только фронтенд). Пожалуйста, обслуживайте веб-интерфейс из бэкенда.",
+	"Open file": "Открыть файл",
+	"Open in full screen": "Открыть на весь экран",
+	"Open new chat": "Открыть новый чат",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"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": "Open AI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Конфигурация API OpenAI",
+	"OpenAI API Key is required.": "Требуется ключ API OpenAI.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Требуется URL-адрес API OpenAI или ключ API.",
+	"or": "или",
+	"Organize your users": "Организуйте своих пользователей",
+	"Other": "Прочее",
+	"OUTPUT": "",
+	"Output format": "Формат вывода",
+	"Overview": "Обзор",
+	"page": "страница",
+	"Password": "Пароль",
+	"Paste Large Text as File": "Вставить большой текст как файл",
+	"PDF document (.pdf)": "PDF-документ (.pdf)",
+	"PDF Extract Images (OCR)": "Извлечение изображений из PDF (OCR)",
+	"pending": "ожидающий",
+	"Permission denied when accessing media devices": "Отказано в разрешении на доступ к мультимедийным устройствам",
+	"Permission denied when accessing microphone": "Отказано в разрешении на доступ к микрофону",
+	"Permission denied when accessing microphone: {{error}}": "Отказано в разрешении на доступ к микрофону: {{error}}",
+	"Permissions": "Разрешения",
+	"Personalization": "Персонализация",
+	"Pin": "Закрепить",
+	"Pinned": "Закреплено",
+	"Pioneer insights": "Новаторские идеи",
+	"Pipeline deleted successfully": "Конвейер успешно удален",
+	"Pipeline downloaded successfully": "Конвейер успешно загружен",
+	"Pipelines": "Конвейеры",
+	"Pipelines Not Detected": "Конвейеры не обнаружены",
+	"Pipelines Valves": "Вентили конвейеров",
+	"Plain text (.txt)": "Текст в формате .txt",
+	"Playground": "Песочница",
+	"Please carefully review the following warnings:": "Пожалуйста, внимательно ознакомьтесь со следующими предупреждениями:",
+	"Please enter a prompt": "Пожалуйста, введите подсказку",
+	"Please fill in all fields.": "Пожалуйста, заполните все поля.",
+	"Please select a model first.": "Пожалуйста, сначала выберите модель.",
+	"Please select a reason": "Пожалуйста, выберите причину",
+	"Port": "",
+	"Positive attitude": "Позитивный настрой",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Предыдущие 30 дней",
+	"Previous 7 days": "Предыдущие 7 дней",
+	"Profile Image": "Изображение профиля",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Промпт (например, Расскажи мне интересный факт о Римской империи)",
+	"Prompt Content": "Содержание промпта",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Предложения промптов",
+	"Prompt updated successfully": "",
+	"Prompts": "Промпты",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Загрузить \"{{searchValue}}\" с Ollama.com",
+	"Pull a model from Ollama.com": "Загрузить модель с Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Параметры запроса",
+	"RAG Template": "Шаблон RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Прочитать вслух",
+	"Record voice": "Записать голос",
+	"Redirecting you to OpenWebUI Community": "Перенаправляем вас в сообщество OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Называйте себя \"User\" (например, \"User is learning Spanish\").",
+	"References from": "",
+	"Refused when it shouldn't have": "Отказано в доступе, когда это не должно было произойти",
+	"Regenerate": "Перегенерировать",
+	"Release Notes": "Примечания к выпуску",
+	"Relevance": "",
+	"Remove": "Удалить",
+	"Remove Model": "Удалить модель",
+	"Rename": "Переименовать",
+	"Reorder Models": "",
+	"Repeat Last N": "Повторить последние N",
+	"Request Mode": "Режим запроса",
+	"Reranking Model": "Модель реранжирования",
+	"Reranking model disabled": "Модель реранжирования отключена",
+	"Reranking model set to \"{{reranking_model}}\"": "Модель реранжирования установлена на \"{{reranking_model}}\"",
+	"Reset": "Сбросить",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Сбросить каталог загрузок",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Уведомления об ответах не могут быть активированы, поскольку доступ к веб-сайту был заблокирован. Пожалуйста, перейдите к настройкам своего браузера, чтобы предоставить необходимый доступ.",
+	"Response splitting": "Разделение ответов",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Ввод форматированного текста для чата",
+	"RK": "",
+	"Role": "Роль",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Запустить",
+	"Running": "Выполняется",
+	"Save": "Сохранить",
+	"Save & Create": "Сохранить и создать",
+	"Save & Update": "Сохранить и обновить",
+	"Save As Copy": "Сохранить как копию",
+	"Save Tag": "Сохранить тег",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Прямое сохранение журналов чата в хранилище вашего браузера больше не поддерживается. Пожалуйста, потратьте минуту, чтобы скачать и удалить ваши журналы чата, нажав на кнопку ниже. Не волнуйтесь, вы легко сможете повторно импортировать свои журналы чата в бэкенд через",
+	"Scroll to bottom when switching between branches": "Прокручивать вниз при переключении веток",
+	"Search": "Поиск",
+	"Search a model": "Поиск модели",
+	"Search Base": "",
+	"Search Chats": "Поиск в чатах",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Поиск функций",
+	"Search Knowledge": "",
+	"Search Models": "Поиск моделей",
+	"Search options": "",
+	"Search Prompts": "Поиск промптов",
+	"Search Result Count": "Количество результатов поиска",
+	"Search the web": "",
+	"Search Tools": "Поиск инструментов",
+	"SearchApi API Key": "Ключ SearchApi API",
+	"SearchApi Engine": "Движок SearchApi",
+	"Searched {{count}} sites_one": "Просмотрено {count}} sites_one",
+	"Searched {{count}} sites_few": "Просмотрено {{count}} sites_few",
+	"Searched {{count}} sites_many": "Просмотрено {{count}} sites_many",
+	"Searched {{count}} sites_other": "Просмотрено {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Поиск \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Поиск знания для \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL-адрес запроса Searxng",
+	"See readme.md for instructions": "Смотрите readme.md для инструкций",
+	"See what's new": "Посмотреть, что нового",
+	"Seed": "Сид",
+	"Select a base model": "Выберите базовую модель",
+	"Select a engine": "Выберите движок",
+	"Select a function": "Выберите функцию",
+	"Select a group": "",
+	"Select a model": "Выберите модель",
+	"Select a pipeline": "Выберите конвейер",
+	"Select a pipeline url": "Выберите URL-адрес конвейера",
+	"Select a tool": "Выберите инструмент",
+	"Select Engine": "Выберите движок",
+	"Select Knowledge": "",
+	"Select model": "Выберите модель",
+	"Select only one model to call": "Выберите только одну модель для вызова",
+	"Selected model(s) do not support image inputs": "Выбранные модели не поддерживают ввод изображений",
+	"Semantic distance to query": "",
+	"Send": "Отправить",
+	"Send a Message": "Отправить сообщение",
+	"Send message": "Отправить сообщение",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Отправляет в запросе \"stream_options: { include_usage: true }\".\nПоддерживаемые провайдеры будут возвращать информацию об использовании токена в ответе, когда это будет установлено.",
+	"September": "Сентябрь",
+	"Serper API Key": "Ключ API Serper",
+	"Serply API Key": "Ключ API Serply",
+	"Serpstack API Key": "Ключ API Serpstack",
+	"Server connection verified": "Соединение с сервером проверено",
+	"Set as default": "Установить по умолчанию",
+	"Set CFG Scale": "Установить CFG Scale",
+	"Set Default Model": "Установить модель по умолчанию",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Установить модель эмбеддинга (например, {{model}})",
+	"Set Image Size": "Установить размер изображения",
+	"Set reranking model (e.g. {{model}})": "Установить модель реранжирования (например, {{model}})",
+	"Set Sampler": "Установить сэмплер",
+	"Set Scheduler": "Установить планировщик",
+	"Set Steps": "Установить шаги",
+	"Set Task Model": "Установить модель задачи",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Установить голос",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Настройки",
+	"Settings saved successfully!": "Настройки успешно сохранены!",
+	"Share": "Поделиться",
+	"Share Chat": "Поделиться чатом",
+	"Share to OpenWebUI Community": "Поделиться с сообществом OpenWebUI",
+	"Show": "Показать",
+	"Show \"What's New\" modal on login": "Показывать окно «Что нового» при входе в систему",
+	"Show Admin Details in Account Pending Overlay": "Показывать данные администратора в оверлее ожидающей учетной записи",
+	"Show shortcuts": "Показать горячие клавиши",
+	"Show your support!": "Поддержите нас!",
+	"Showcased creativity": "Продемонстрирован творческий подход",
+	"Sign in": "Войти",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Выйти",
+	"Sign up": "Зарегистрироваться",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Источник",
+	"Speech Playback Speed": "Скорость воспроизведения речи",
+	"Speech recognition error: {{error}}": "Ошибка распознавания речи: {{error}}",
+	"Speech-to-Text Engine": "Система распознавания речи",
+	"Stop": "",
+	"Stop Sequence": "Последовательность остановки",
+	"Stream Chat Response": "Потоковый вывод ответа",
+	"STT Model": "Модель распознавания речи",
+	"STT Settings": "Настройки распознавания речи",
+	"Subtitle (e.g. about the Roman Empire)": "Подзаголовок (например, о Римской империи)",
+	"Success": "Успех",
+	"Successfully updated.": "Успешно обновлено.",
+	"Suggested": "Предложено",
+	"Support": "Поддержать",
+	"Support this plugin:": "Поддержите этот плагин",
+	"Sync directory": "",
+	"System": "Система",
+	"System Instructions": "",
+	"System Prompt": "Системный промпт",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Нажмите, чтобы прервать",
+	"Tavily API Key": "Ключ API Tavily",
+	"Tell us more:": "Пожалуйста, расскажите нам больше:",
+	"Temperature": "Температура",
+	"Template": "Шаблон",
+	"Temporary Chat": "Временный чат",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Система синтеза речи",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Спасибо за вашу обратную связь!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Разработчики этого плагина - увлеченные волонтеры из сообщества. Если вы считаете этот плагин полезным, пожалуйста, подумайте о том, чтобы внести свой вклад в его разработку.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Максимальный размер файла в МБ. Если размер файла превысит это ограничение, файл не будет загружен.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Максимальное количество файлов, которые могут быть использованы одновременно в чате. Если количество файлов превысит это ограничение, файлы не будут загружены.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Оценка должна быть значением между 0,0 (0%) и 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Температура модели. Повышение температуры заставит модель отвечать более творчески. (По умолчанию: 0,8)",
+	"Theme": "Тема",
+	"Thinking...": "Думаю...",
+	"This action cannot be undone. Do you wish to continue?": "Это действие нельзя отменить. Вы хотите продолжить?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Это обеспечивает сохранение ваших ценных разговоров в безопасной базе данных на вашем сервере. Спасибо!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Это экспериментальная функция, она может работать не так, как ожидалось, и может быть изменена в любое время.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Этот параметр определяет, сколько токенов сохраняется при обновлении контекста. Например, если установлено значение 2, будут сохранены два последних токена контекста разговора. Сохранение контекста может помочь сохранить непрерывность разговора, но может снизить способность реагировать на новые темы. (По умолчанию: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Этот параметр устанавливает максимальное количество токенов, которые модель может сгенерировать в своем ответе. Увеличение этого предела позволяет модели предоставлять более длинные ответы, но также может увеличить вероятность создания бесполезного или нерелевантного контента. (По умолчанию: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Эта опция удалит все существующие файлы в коллекции и заменит их вновь загруженными файлами.",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Это приведет к удалению",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "При этом будет удален <strong>{{NAME}}</strong> и <strong>все его содержимое</strong>.",
+	"This will delete all models including custom models": "Это приведет к удалению всех моделей, включая пользовательские модели.",
+	"This will delete all models including custom models and cannot be undone.": "При этом будут удалены все модели, включая пользовательские, и это действие нельзя будет отменить.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Это сбросит базу знаний и синхронизирует все файлы. Хотите продолжить?",
+	"Thorough explanation": "Подробное объяснение",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Требуется URL-адрес сервера Tika.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Совет: Обновляйте несколько переменных подряд, нажимая клавишу Tab в поле ввода чата после каждой замены.",
+	"Title": "Заголовок",
+	"Title (e.g. Tell me a fun fact)": "Заголовок (например, Расскажи мне интересный факт)",
+	"Title Auto-Generation": "Автогенерация заголовка",
+	"Title cannot be an empty string.": "Заголовок не может быть пустой строкой.",
+	"Title Generation Prompt": "Промпт для генерации заголовка",
+	"TLS": "",
+	"To access the available model names for downloading,": "Чтобы получить доступ к доступным для загрузки именам моделей,",
+	"To access the GGUF models available for downloading,": "Чтобы получить доступ к моделям GGUF, доступным для загрузки,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Чтобы получить доступ к WebUI, пожалуйста, обратитесь к администратору. Администраторы могут управлять статусами пользователей из панели администратора.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Чтобы прикрепить сюда базу знаний, сначала добавьте её в \"Знания\" рабочего пространства.",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Чтобы выбрать действия, сначала добавьте их в \"Функции\" рабочего пространства.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Чтобы выбрать фильтры, сначала добавьте их в \"Функции\" рабочего пространства.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Чтобы выбрать инструменты, сначала добавьте их в \"Инструменты\" рабочего пространства.",
+	"Toast notifications for new updates": "Уведомления о обновлениях",
+	"Today": "Сегодня",
+	"Toggle settings": "Переключить настройки",
+	"Toggle sidebar": "Переключить боковую панель",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Количество токенов для сохранения при обновлении контекста (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Инструмент успешно создан",
+	"Tool deleted successfully": "Инструмент успешно удален",
+	"Tool Description": "Описание Инструмента",
+	"Tool ID": "ID Инструмента",
+	"Tool imported successfully": "Инструмент успешно импортирован",
+	"Tool Name": "Имя Инструмента",
+	"Tool updated successfully": "Инструмент успешно обновлен",
+	"Tools": "Инструменты",
+	"Tools Access": "Доступ к инструментам",
+	"Tools are a function calling system with arbitrary code execution": "Инструменты - это система вызова функций с выполнением произвольного кода",
+	"Tools have a function calling system that allows arbitrary code execution": "Инструменты имеют систему вызова функций, которая позволяет выполнять произвольный код",
+	"Tools have a function calling system that allows arbitrary code execution.": "Инструменты имеют систему вызова функций, которая позволяет выполнять произвольный код.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Проблемы с доступом к Ollama?",
+	"TTS Model": "Модель TTS",
+	"TTS Settings": "Настройки TTS",
+	"TTS Voice": "Голос TTS",
+	"Type": "Тип",
+	"Type Hugging Face Resolve (Download) URL": "Введите URL-адрес Hugging Face Resolve (загрузки)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Упс! Возникла проблема подключения к {{provider}}.",
+	"UI": "Пользовательский интерфейс",
+	"Unarchive All": "Разархивировать ВСЁ",
+	"Unarchive All Archived Chats": "Разархивировать ВСЕ Заархивированные Чаты",
+	"Unarchive Chat": "Разархивировать чат",
+	"Unlock mysteries": "Разблокируйте тайны",
+	"Unpin": "Открепить",
+	"Unravel secrets": "Разгадать секреты",
+	"Untagged": "Без тегов",
+	"Update": "Обновить",
+	"Update and Copy Link": "Обновить и скопировать ссылку",
+	"Update for the latest features and improvements.": "Обновитесь для получения последних функций и улучшений.",
+	"Update password": "Обновить пароль",
+	"Updated": "Обновлено",
+	"Updated at": "Обновлено",
+	"Updated At": "",
+	"Upload": "Загрузить",
+	"Upload a GGUF model": "Загрузить модель GGUF",
+	"Upload directory": "Загрузить каталог",
+	"Upload files": "Загрузить файлы",
+	"Upload Files": "Загрузить файлы",
+	"Upload Pipeline": "Загрузить конвейер",
+	"Upload Progress": "Прогресс загрузки",
+	"URL": "",
+	"URL Mode": "Режим URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "Используйте «#» в строке ввода, чтобы загрузить и включить свои знания.",
+	"Use Gravatar": "Использовать Gravatar",
+	"Use groups to group your users and assign permissions.": "Используйте группы, чтобы группировать пользователей и назначать разрешения.",
+	"Use Initials": "Использовать инициалы",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "пользователь",
+	"User": "Пользователь",
+	"User location successfully retrieved.": "Местоположение пользователя успешно получено.",
+	"Username": "Имя пользователя",
+	"Users": "Пользователи",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Использование модели арены по умолчанию со всеми моделями. Нажмите кнопку «плюс», чтобы добавить пользовательские модели.",
+	"Utilize": "Используйте",
+	"Valid time units:": "Допустимые единицы времени:",
+	"Valves": "Вентили",
+	"Valves updated": "Вентили обновлены",
+	"Valves updated successfully": "Вентили успешно обновлены",
+	"variable": "переменная",
+	"variable to have them replaced with clipboard content.": "переменную, чтобы заменить их содержимым буфера обмена.",
+	"Version": "Версия",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "Видимость",
+	"Voice": "Голос",
+	"Voice Input": "",
+	"Warning": "Предупреждение",
+	"Warning:": "Предупреждение:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Предупреждение. Включение этого параметра позволит пользователям загружать произвольный код на сервер.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Предупреждение: Если вы обновите или измените модель эмбеддинга, вам нужно будет повторно импортировать все документы.",
+	"Web": "Веб",
+	"Web API": "Веб API",
+	"Web Loader Settings": "Настройки веб-загрузчика",
+	"Web Search": "Веб-поиск",
+	"Web Search Engine": "Поисковая система",
+	"Web Search Query Generation": "Генерация запросов веб-поиска",
+	"Webhook URL": "URL-адрес веб-хука",
+	"WebUI Settings": "Настройки WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI будет отправлять запросы к \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI будет отправлять запросы к \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Чего вы пытаетесь достичь?",
+	"What are you working on?": "Над чем вы работаете?",
+	"What’s New in": "Что нового в",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Если эта функция включена, модель будет отвечать на каждое сообщение чата в режиме реального времени, генерируя ответ, как только пользователь отправит сообщение. Этот режим полезен для приложений живого чата, но может повлиять на производительность на более медленном оборудовании.",
+	"wherever you are": "где бы ты ни был",
+	"Whisper (Local)": "Whisper (Локально)",
+	"Why?": "Почему?",
+	"Widescreen Mode": "Широкоэкранный режим",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Работает совместно с top-k. Более высокое значение (например, 0,95) приведет к созданию более разнообразного текста, а более низкое значение (например, 0,5) приведет к созданию более сфокусированного и консервативного текста. (По умолчанию: 0,9)",
+	"Workspace": "Рабочее пространство",
+	"Workspace Permissions": "Разрешения для Рабочего пространства",
+	"Write a prompt suggestion (e.g. Who are you?)": "Напишите предложение промпта (например, Кто вы?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Напишите резюме в 50 словах, которое кратко описывает [тему или ключевое слово].",
+	"Write something...": "Напишите что-нибудь...",
+	"Write your model template content here": "Напишите здесь содержимое шаблона вашей модели.",
+	"Yesterday": "Вчера",
+	"You": "Вы",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Одновременно вы можете общаться только с максимальным количеством файлов {{maxCount}}.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Вы можете персонализировать свое взаимодействие с LLMs, добавив воспоминания с помощью кнопки \"Управлять\" ниже, что сделает их более полезными и адаптированными для вас.",
+	"You cannot upload an empty file.": "Вы не можете загрузить пустой файл.",
+	"You do not have permission to upload files.": "У вас нет разрешения на загрузку файлов.",
+	"You have no archived conversations.": "У вас нет архивированных бесед.",
+	"You have shared this chat": "Вы поделились этим чатом",
+	"You're a helpful assistant.": "Вы полезный ассистент.",
+	"You're now logged in.": "Вы вошли в систему.",
+	"Your account status is currently pending activation.": "В настоящее время ваша учетная запись ожидает активации.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Весь ваш взнос будет направлен непосредственно разработчику плагина; Open WebUI не взимает никаких процентов. Однако выбранная платформа финансирования может иметь свои собственные сборы.",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "Настройки загрузчика YouTube"
+}
diff --git a/src/lib/i18n/locales/sr-RS/translation.json b/src/lib/i18n/locales/sr-RS/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..f5cd6e73aa1b960eff9a6b579db48c9dabdf8992
--- /dev/null
+++ b/src/lib/i18n/locales/sr-RS/translation.json
@@ -0,0 +1,1026 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "„s“, „m“, „h“, „d“, „w“ или „-1“ за без истека.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(нпр. `sh webui.sh --api`)",
+	"(latest)": "(најновије)",
+	"{{ models }}": "{{ модели }}",
+	"{{user}}'s Chats": "Ћаскања корисника {{user}}",
+	"{{webUIName}} Backend Required": "Захтева се {{webUIName}} позадинац",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Модел задатка се користи приликом извршавања задатака као што су генерисање наслова за ћаскања и упите за Веб претрагу",
+	"a user": "корисник",
+	"About": "О нама",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Налог",
+	"Account Activation Pending": "",
+	"Accurate information": "Прецизне информације",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "Додај",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Додавање кратког описа о томе шта овај модел ради",
+	"Add a tag": "Додај ознаку",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Додај прилагођен упит",
+	"Add Files": "Додај датотеке",
+	"Add Group": "",
+	"Add Memory": "Додај меморију",
+	"Add Model": "Додај модел",
+	"Add Tag": "",
+	"Add Tags": "Додај ознаке",
+	"Add text content": "",
+	"Add User": "Додај корисника",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Прилагођавање ових подешавања ће применити промене на све кориснике.",
+	"admin": "админ",
+	"Admin": "",
+	"Admin Panel": "Админ табла",
+	"Admin Settings": "Админ подешавања",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "Напредни параметри",
+	"Advanced Params": "Напредни парамови",
+	"All chats": "",
+	"All Documents": "Сви документи",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Дозволи брисање ћаскања",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Већ имате налог?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "помоћник",
+	"and": "и",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "и направи нову дељену везу.",
+	"API Base URL": "Основна адреса API-ја",
+	"API Key": "API кључ",
+	"API Key created.": "API кључ направљен.",
+	"API keys": "API кључеви",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Април",
+	"Archive": "Архива",
+	"Archive All Chats": "Архивирај све ћаскања",
+	"Archived Chats": "Архивирана ћаскања",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Да ли сте сигурни?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Приложи датотеку",
+	"Attention to detail": "Пажња на детаље",
+	"Attribute for Username": "",
+	"Audio": "Звук",
+	"August": "Август",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Самостално копирање одговора у оставу",
+	"Auto-playback response": "Самостално пуштање одговора",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "Основна адреса за AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Потребна је основна адреса за AUTOMATIC1111.",
+	"Available list": "",
+	"available!": "доступно!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Назад",
+	"Bad Response": "Лош одговор",
+	"Banners": "Барјаке",
+	"Base Model (From)": "Основни модел (од)",
+	"Batch Size (num_batch)": "",
+	"before": "пре",
+	"Being lazy": "Бити лењ",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Апи кључ за храбру претрагу",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Заобиђи SSL потврђивање за веб странице",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "Откажи",
+	"Capabilities": "Могућности",
+	"Certificate Path": "",
+	"Change Password": "Промени лозинку",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Ћаскање",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Интерфејс балона ћаскања",
+	"Chat Controls": "",
+	"Chat direction": "Смер ћаскања",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Ћаскања",
+	"Check Again": "Провери поново",
+	"Check for updates": "Потражи ажурирања",
+	"Checking for updates...": "Траже се ажурирања...",
+	"Choose a model before saving...": "Изабери модел пре чувања...",
+	"Chunk Overlap": "Преклапање делова",
+	"Chunk Params": "Параметри делова",
+	"Chunk Size": "Величина дела",
+	"Ciphers": "",
+	"Citation": "Цитат",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Кликните овде за помоћ.",
+	"Click here to": "Кликните овде да",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Кликните овде да изаберете",
+	"Click here to select a csv file.": "Кликните овде да изаберете csv датотеку.",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "кликните овде.",
+	"Click on the user role button to change a user's role.": "Кликните на дугме за улогу корисника да промените улогу корисника.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Клон",
+	"Close": "Затвори",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Колекција",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "Основна адреса за ComfyUI",
+	"ComfyUI Base URL is required.": "Потребна је основна адреса за ComfyUI.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Наредба",
+	"Completions": "",
+	"Concurrent Requests": "Упоредни захтеви",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Потврди лозинку",
+	"Confirm your action": "",
+	"Connections": "Везе",
+	"Contact Admin for WebUI Access": "",
+	"Content": "Садржај",
+	"Content Extraction": "",
+	"Context Length": "Дужина контекста",
+	"Continue Response": "Настави одговор",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Адреса дељеног ћаскања ископирана у оставу!",
+	"Copied to clipboard": "",
+	"Copy": "Копирај",
+	"Copy last code block": "Копирај последњи блок кода",
+	"Copy last response": "Копирај последњи одговор",
+	"Copy Link": "Копирај везу",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Успешно копирање у оставу!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Креирање модела",
+	"Create Account": "Направи налог",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Направи нови кључ",
+	"Create new secret key": "Направи нови тајни кључ",
+	"Created at": "Направљено у",
+	"Created At": "Направљено у",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Тренутни модел",
+	"Current Password": "Тренутна лозинка",
+	"Custom": "Прилагођено",
+	"Dark": "Тамна",
+	"Database": "База података",
+	"December": "Децембар",
+	"Default": "Подразумевано",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Подразумевано (SentenceTransformers)",
+	"Default Model": "Подразумевани модел",
+	"Default model updated": "Подразумевани модел ажуриран",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Подразумевани предлози упита",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Подразумевана улога корисника",
+	"Delete": "Обриши",
+	"Delete a model": "Обриши модел",
+	"Delete All Chats": "Избриши сва ћаскања",
+	"Delete All Models": "",
+	"Delete chat": "Обриши ћаскање",
+	"Delete Chat": "Обриши ћаскање",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "обриши ову везу",
+	"Delete tool?": "",
+	"Delete User": "Обриши корисника",
+	"Deleted {{deleteModelTag}}": "Обрисано {{deleteModelTag}}",
+	"Deleted {{name}}": "Избрисано {{наме}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Опис",
+	"Didn't fully follow instructions": "Упутства нису праћена у потпуности",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Откријте модел",
+	"Discover a prompt": "Откриј упит",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Откријте, преузмите и истражите прилагођене упите",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Откријте, преузмите и истражите образце модела",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "Прикажи корисничко име уместо Ти у чату",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Документ",
+	"Documentation": "",
+	"Documents": "Документи",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "не отвара никакве спољне везе и ваши подаци остају сигурно на вашем локално хостованом серверу.",
+	"Don't have an account?": "Немате налог?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Не свиђа ми се стил",
+	"Done": "",
+	"Download": "Преузми",
+	"Download canceled": "Преузимање отказано",
+	"Download Database": "Преузми базу података",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Убаците било које датотеке овде да их додате у разговор",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "нпр. '30s', '10m'. Важеће временске јединице су 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Уреди",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Уреди корисника",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Е-пошта",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Модел уградње",
+	"Embedding Model Engine": "Мотор модела уградње",
+	"Embedding model set to \"{{embedding_model}}\"": "Модел уградње подешен на \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Омогући дељење заједнице",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Омогући нове пријаве",
+	"Enable Web Search": "Омогући Wеб претрагу",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Уверите се да ваша CSV датотека укључује 4 колоне у овом редоследу: Име, Е-пошта, Лозинка, Улога.",
+	"Enter {{role}} message here": "Унесите {{role}} поруку овде",
+	"Enter a detail about yourself for your LLMs to recall": "Унесите детаље за себе да ће LLMs преузимати",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Унесите БРАВЕ Сеарцх АПИ кључ",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Унесите преклапање делова",
+	"Enter Chunk Size": "Унесите величину дела",
+	"Enter description": "",
+	"Enter Github Raw URL": "Унесите Гитхуб Раw УРЛ адресу",
+	"Enter Google PSE API Key": "Унесите Гоогле ПСЕ АПИ кључ",
+	"Enter Google PSE Engine Id": "Унесите Гоогле ПСЕ ИД машине",
+	"Enter Image Size (e.g. 512x512)": "Унесите величину слике (нпр. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Унесите кодове језика",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Унесите ознаку модела (нпр. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Унесите број корака (нпр. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Унесите резултат",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Унесите УРЛ адресу Сеарxнг упита",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Унесите Серпер АПИ кључ",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "Унесите Серпстацк АПИ кључ",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Унесите секвенцу заустављања",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Унесите Топ К",
+	"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 Your Email": "Унесите вашу е-пошту",
+	"Enter Your Full Name": "Унесите ваше име и презиме",
+	"Enter your message": "",
+	"Enter Your Password": "Унесите вашу лозинку",
+	"Enter Your Role": "Унесите вашу улогу",
+	"Enter Your Username": "",
+	"Error": "Грешка",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Експериментално",
+	"Explore the cosmos": "",
+	"Export": "Извоз",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Извези сва ћаскања (сви корисници)",
+	"Export chat (.json)": "",
+	"Export Chats": "Извези ћаскања",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Извези моделе",
+	"Export Presets": "",
+	"Export Prompts": "Извези упите",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Неуспешно стварање API кључа.",
+	"Failed to read clipboard contents": "Неуспешно читање садржаја оставе",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "Фебруар",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Слободно додајте специфичне детаље",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Режим датотеке",
+	"File not found.": "Датотека није пронађена.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Откривено лажно представљање отиска прста: Немогуће је користити иницијале као аватар. Прелазак на подразумевану профилну слику.",
+	"Fluidly stream large external response chunks": "Течно стримујте велике спољне делове одговора",
+	"Focus chat input": "Усредсредите унос ћаскања",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Упутства су савршено праћена",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Фреквентна казна",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Опште",
+	"General Settings": "Општа подешавања",
+	"Generate Image": "",
+	"Generating search query": "Генерисање упита претраге",
+	"Generation Info": "Информације о стварању",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Добар одговор",
+	"Google PSE API Key": "Гоогле ПСЕ АПИ кључ",
+	"Google PSE Engine Id": "Гоогле ПСЕ ИД мотора",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "нема разговора.",
+	"Hello, {{name}}": "Здраво, {{name}}",
+	"Help": "Помоћ",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Сакриј",
+	"Host": "",
+	"How can I help you today?": "Како могу да вам помогнем данас?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Хибридна претрага",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Стварање слика (експериментално)",
+	"Image Generation Engine": "Мотор за стварање слика",
+	"Image Settings": "Подешавања слике",
+	"Images": "Слике",
+	"Import Chats": "Увези ћаскања",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Увези моделе",
+	"Import Presets": "",
+	"Import Prompts": "Увези упите",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Укључи `--api` заставицу при покретању stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Инфо",
+	"Input commands": "Унеси наредбе",
+	"Install from Github URL": "Инсталирај из Гитхуб УРЛ адресе",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "Изглед",
+	"Invalid file format.": "",
+	"Invalid Tag": "Неисправна ознака",
+	"January": "Јануар",
+	"Jina API Key": "",
+	"join our Discord for help.": "придружите се нашем Дискорду за помоћ.",
+	"JSON": "JSON",
+	"JSON Preview": "ЈСОН Преглед",
+	"July": "Јул",
+	"June": "Јун",
+	"JWT Expiration": "Истек JWT-а",
+	"JWT Token": "JWT жетон",
+	"Keep Alive": "Одржи трајање",
+	"Key": "",
+	"Keyboard shortcuts": "Пречице на тастатури",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Језик",
+	"Last Active": "Последња активност",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Светла",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "ВЈМ-ови (LLM-ови) могу правити грешке. Проверите важне податке.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "ЛНД",
+	"Made by OpenWebUI Community": "Израдила OpenWebUI заједница",
+	"Make sure to enclose them with": "Уверите се да их затворите са",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Управљање цевоводима",
+	"March": "Март",
+	"Max Tokens (num_predict)": "Маx Токенс (нум_предицт)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Највише 3 модела могу бити преузета истовремено. Покушајте поново касније.",
+	"May": "Мај",
+	"Memories accessible by LLMs will be shown here.": "Памћења које ће бити појављена од овог LLM-а ће бити приказана овде.",
+	"Memory": "Памћење",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Поруке које пошаљете након стварања ваше везе неће бити подељене. Корисници са URL-ом ће моћи да виде дељено ћаскање.",
+	"Min P": "",
+	"Minimum Score": "Најмањи резултат",
+	"Mirostat": "Миростат",
+	"Mirostat Eta": "Миростат Ета",
+	"Mirostat Tau": "Миростат Тау",
+	"MMMM DD, YYYY": "ММММ ДД, ГГГГ",
+	"MMMM DD, YYYY HH:mm": "ММММ ДД, ГГГГ ЧЧ:мм",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Модел „{{modelName}}“ је успешно преузет.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Модел „{{modelTag}}“ је већ у реду за преузимање.",
+	"Model {{modelId}} not found": "Модел {{modelId}} није пронађен",
+	"Model {{modelName}} is not vision capable": "Модел {{моделНаме}} није способан за вид",
+	"Model {{name}} is now {{status}}": "Модел {{наме}} је сада {{статус}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Откривена путања система датотека модела. За ажурирање је потребан кратак назив модела, не може се наставити.",
+	"Model Filtering": "",
+	"Model ID": "ИД модела",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Модел није изабран",
+	"Model Params": "Модел Парамс",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Садржај модел-датотеке",
+	"Models": "Модели",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Више",
+	"Name": "Име",
+	"Name your knowledge base": "",
+	"New Chat": "Ново ћаскање",
+	"New folder": "",
+	"New Password": "Нова лозинка",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Нема резултата",
+	"No search query generated": "Није генерисан упит за претрагу",
+	"No source available": "Нема доступног извора",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Нико",
+	"Not factually correct": "Није чињенично тачно",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Напомена: ако подесите најмањи резултат, претрага ће вратити само документе са резултатом већим или једнаким најмањем резултату.",
+	"Notes": "",
+	"Notifications": "Обавештења",
+	"November": "Новембар",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "нум _тхреад (Оллама)",
+	"OAuth ID": "",
+	"October": "Октобар",
+	"Off": "Искључено",
+	"Okay, Let's Go!": "У реду, хајде да кренемо!",
+	"OLED Dark": "OLED тамна",
+	"Ollama": "Ollama",
+	"Ollama API": "Оллама АПИ",
+	"Ollama API disabled": "Оллама АПИ онемогућен",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Издање Ollama-е",
+	"On": "Укључено",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Само алфанумерички знакови и цртице су дозвољени у низу наредби.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Изгледа да је адреса неважећа. Молимо вас да проверите и покушате поново.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Користите неподржани метод (само фронтенд). Молимо вас да покренете WebUI са бекенда.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Покрени ново ћаскање",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "Подешавање OpenAI API-ја",
+	"OpenAI API Key is required.": "Потребан је OpenAI API кључ.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Потребан је OpenAI URL/кључ.",
+	"or": "или",
+	"Organize your users": "",
+	"Other": "Остало",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Лозинка",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF документ (.pdf)",
+	"PDF Extract Images (OCR)": "Извлачење PDF слика (OCR)",
+	"pending": "на чекању",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "Приступ микрофону је одбијен: {{error}}",
+	"Permissions": "",
+	"Personalization": "Прилагођавање",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Цевоводи",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Вентили за цевоводе",
+	"Plain text (.txt)": "Обичан текст (.txt)",
+	"Playground": "Игралиште",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Позитиван став",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Претходних 30 дана",
+	"Previous 7 days": "Претходних 7 дана",
+	"Profile Image": "Слика профила",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Упит (нпр. „реци ми занимљивост о Римском царству“)",
+	"Prompt Content": "Садржај упита",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Предлози упита",
+	"Prompt updated successfully": "",
+	"Prompts": "Упити",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Повуците \"{{searchValue}}\" са Ollama.com",
+	"Pull a model from Ollama.com": "Повуците модел са Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Параметри упита",
+	"RAG Template": "RAG шаблон",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Прочитај наглас",
+	"Record voice": "Сними глас",
+	"Redirecting you to OpenWebUI Community": "Преусмеравање на OpenWebUI заједницу",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "Одбијено када није требало",
+	"Regenerate": "Регенериши",
+	"Release Notes": "Напомене о издању",
+	"Relevance": "",
+	"Remove": "Уклони",
+	"Remove Model": "Уклони модел",
+	"Rename": "Преименуј",
+	"Reorder Models": "",
+	"Repeat Last N": "Понови последњих N",
+	"Request Mode": "Режим захтева",
+	"Reranking Model": "Модел поновног рангирања",
+	"Reranking model disabled": "Модел поновног рангирања онемогућен",
+	"Reranking model set to \"{{reranking_model}}\"": "Модел поновног рангирања подешен на \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Улога",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "ДНЛ",
+	"Run": "",
+	"Running": "",
+	"Save": "Сачувај",
+	"Save & Create": "Сачувај и направи",
+	"Save & Update": "Сачувај и ажурирај",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Чување ћаскања директно у складиште вашег прегледача више није подржано. Одвојите тренутак да преузмете и избришете ваша ћаскања кликом на дугме испод. Не брините, можете лако поново увезти ваша ћаскања у бекенд кроз",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Претражи",
+	"Search a model": "Претражи модел",
+	"Search Base": "",
+	"Search Chats": "Претражи ћаскања",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Модели претраге",
+	"Search options": "",
+	"Search Prompts": "Претражи упите",
+	"Search Result Count": "Број резултата претраге",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Претражио {{цоунт}} ситес_оне",
+	"Searched {{count}} sites_few": "Претражио {{цоунт}} ситес_феw",
+	"Searched {{count}} sites_other": "Претражио {{цоунт}} ситес_отхер",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "УРЛ адреса Сеарxнг упита",
+	"See readme.md for instructions": "Погледај readme.md за упутства",
+	"See what's new": "Погледај шта је ново",
+	"Seed": "Семе",
+	"Select a base model": "Избор основног модела",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Изабери модел",
+	"Select a pipeline": "Избор цевовода",
+	"Select a pipeline url": "Избор урл адресе цевовода",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Изабери модел",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "Изабрани модели не подржавају уносе слика",
+	"Semantic distance to query": "",
+	"Send": "Пошаљи",
+	"Send a Message": "Пошаљи поруку",
+	"Send message": "Пошаљи поруку",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Септембар",
+	"Serper API Key": "Серпер АПИ кључ",
+	"Serply API Key": "",
+	"Serpstack API Key": "Серпстацк АПИ кључ",
+	"Server connection verified": "Веза са сервером потврђена",
+	"Set as default": "Подеси као подразумевано",
+	"Set CFG Scale": "",
+	"Set Default Model": "Подеси као подразумевани модел",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Подеси модел уградње (нпр. {{model}})",
+	"Set Image Size": "Подеси величину слике",
+	"Set reranking model (e.g. {{model}})": "Подеси модел поновног рангирања (нпр. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Подеси кораке",
+	"Set Task Model": "Постављање модела задатка",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Подеси глас",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Подешавања",
+	"Settings saved successfully!": "Подешавања успешно сачувана!",
+	"Share": "Подели",
+	"Share Chat": "Подели ћаскање",
+	"Share to OpenWebUI Community": "Подели са OpenWebUI заједницом",
+	"Show": "Прикажи",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "Прикажи пречице",
+	"Show your support!": "",
+	"Showcased creativity": "Приказана креативност",
+	"Sign in": "Пријави се",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Одјави се",
+	"Sign up": "Региструј се",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Извор",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Грешка у препознавању говора: {{error}}",
+	"Speech-to-Text Engine": "Мотор за говор у текст",
+	"Stop": "",
+	"Stop Sequence": "Секвенца заустављања",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "STT подешавања",
+	"Subtitle (e.g. about the Roman Empire)": "Поднаслов (нпр. о Римском царству)",
+	"Success": "Успех",
+	"Successfully updated.": "Успешно ажурирано.",
+	"Suggested": "Предложено",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "Систем",
+	"System Instructions": "",
+	"System Prompt": "Системски упит",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Реците нам више:",
+	"Temperature": "Температура",
+	"Template": "Шаблон",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Мотор за текст у говор",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Хвала на вашем коментару!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Резултат треба да буде вредност између 0.0 (0%) и 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Тема",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Ово осигурава да су ваши вредни разговори безбедно сачувани у вашој бекенд бази података. Хвала вам!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Детаљно објашњење",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Савет: ажурирајте више променљивих слотова узастопно притиском на тастер Таб у уносу ћаскања након сваке замене.",
+	"Title": "Наслов",
+	"Title (e.g. Tell me a fun fact)": "Наслов (нпр. „реци ми занимљивост“)",
+	"Title Auto-Generation": "Самостално стварање наслова",
+	"Title cannot be an empty string.": "Наслов не може бити празан низ.",
+	"Title Generation Prompt": "Упит за стварање наслова",
+	"TLS": "",
+	"To access the available model names for downloading,": "Да бисте приступили доступним именима модела за преузимање,",
+	"To access the GGUF models available for downloading,": "Да бисте приступили GGUF моделима доступним за преузимање,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "Данас",
+	"Toggle settings": "Пребаци подешавања",
+	"Toggle sidebar": "Пребаци бочну траку",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Топ К",
+	"Top P": "Топ П",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Проблеми са приступом Ollama-и?",
+	"TTS Model": "",
+	"TTS Settings": "TTS подешавања",
+	"TTS Voice": "",
+	"Type": "Тип",
+	"Type Hugging Face Resolve (Download) URL": "Унесите Hugging Face Resolve (Download) адресу",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Упс! Дошло је до проблема при повезивању са {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Ажурирај и копирај везу",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Ажурирај лозинку",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Отпреми GGUF модел",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Отпремање датотека",
+	"Upload Pipeline": "",
+	"Upload Progress": "Напредак отпремања",
+	"URL": "",
+	"URL Mode": "Режим адресе",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Користи Граватар",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Користи иницијале",
+	"use_mlock (Ollama)": "усе _млоцк (Оллама)",
+	"use_mmap (Ollama)": "усе _ммап (Оллама)",
+	"user": "корисник",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Корисници",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Искористи",
+	"Valid time units:": "Важеће временске јединице:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "променљива",
+	"variable to have them replaced with clipboard content.": "променљива за замену са садржајем оставе.",
+	"Version": "Издање",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Упозорење",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Упозорење: ако ажурирате или промените ваш модел уградње, мораћете поново да увезете све документе.",
+	"Web": "Веб",
+	"Web API": "",
+	"Web Loader Settings": "Подешавања веб учитавача",
+	"Web Search": "Wеб претрага",
+	"Web Search Engine": "Wеб претраживач",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Адреса веб-куке",
+	"WebUI Settings": "Подешавања веб интерфејса",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Шта је ново у",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Радни простор",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Напишите предлог упита (нпр. „ко си ти?“)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Напишите сажетак у 50 речи који резимира [тему или кључну реч].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Јуче",
+	"You": "Ти",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Немате архивиране разговоре.",
+	"You have shared this chat": "Поделили сте ово ћаскање",
+	"You're a helpful assistant.": "Ти си користан помоћник.",
+	"You're now logged in.": "Сада сте пријављени.",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Јутјуб",
+	"Youtube Loader Settings": "Подешавања Јутјуб учитавача"
+}
diff --git a/src/lib/i18n/locales/sv-SE/translation.json b/src/lib/i18n/locales/sv-SE/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..d05040f1dca35219e0e4d7b1894d0f553f02121b
--- /dev/null
+++ b/src/lib/i18n/locales/sv-SE/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' eller '-1' för inget utgångsdatum",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(t.ex. `sh webui.sh --api`)",
+	"(latest)": "(senaste)",
+	"{{ models }}": "{{ modeller }}",
+	"{{user}}'s Chats": "{{user}}s Chats",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend krävs",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "En uppgiftsmodell används när du utför uppgifter som att generera titlar för chattar och webbsökningsfrågor",
+	"a user": "en användare",
+	"About": "Om",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Konto",
+	"Account Activation Pending": "Kontoaktivering väntar",
+	"Accurate information": "Exakt information",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktiva användare",
+	"Add": "Lägg till",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Lägg till en kort beskrivning av vad den här modellen gör",
+	"Add a tag": "Lägg till en tagg",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Lägg till en anpassad instruktion",
+	"Add Files": "Lägg till filer",
+	"Add Group": "",
+	"Add Memory": "Lägg till minne",
+	"Add Model": "Lägg till modell",
+	"Add Tag": "",
+	"Add Tags": "Lägg till taggar",
+	"Add text content": "",
+	"Add User": "Lägg till användare",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Justering av dessa inställningar kommer att tillämpa ändringar universellt för alla användare.",
+	"admin": "administratör",
+	"Admin": "Admin",
+	"Admin Panel": "Administrationspanel",
+	"Admin Settings": "Administratörsinställningar",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Administratörer har tillgång till alla verktyg hela tiden, medan användare behöver verktyg som tilldelas per modell i arbetsytan.",
+	"Advanced Parameters": "Avancerade parametrar",
+	"Advanced Params": "Avancerade parametrar",
+	"All chats": "",
+	"All Documents": "Alla dokument",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Tillåt chattborttagning",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Tillåt icke-lokala röster",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "Har du redan ett konto?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "en assistent",
+	"and": "och",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "och skapa en ny delad länk.",
+	"API Base URL": "API-bas-URL",
+	"API Key": "API-nyckel",
+	"API Key created.": "API-nyckel skapad.",
+	"API keys": "API-nycklar",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "april",
+	"Archive": "Arkiv",
+	"Archive All Chats": "Arkivera alla chattar",
+	"Archived Chats": "Arkiverade chattar",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Är du säker?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Bifoga fil",
+	"Attention to detail": "Detaljerad uppmärksamhet",
+	"Attribute for Username": "",
+	"Audio": "Ljud",
+	"August": "augusti",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Svara AutoCopy till urklipp",
+	"Auto-playback response": "Automatisk uppspelning",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 bas-URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 bas-URL krävs.",
+	"Available list": "",
+	"available!": "tillgänglig!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Tillbaka",
+	"Bad Response": "Felaktig respons",
+	"Banners": "Banners",
+	"Base Model (From)": "Basmodell (Från)",
+	"Batch Size (num_batch)": "Batchstorlek (num_batch)",
+	"before": "för",
+	"Being lazy": "Lägg till",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "API-nyckel för Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Kringgå SSL-verifiering för webbplatser",
+	"Call": "Samtal",
+	"Call feature is not supported when using Web STT engine": "Samtalsfunktionen är inte kompatibel med Web Tal-till-text motor",
+	"Camera": "Kamera",
+	"Cancel": "Avbryt",
+	"Capabilities": "Kapaciteter",
+	"Certificate Path": "",
+	"Change Password": "Ändra lösenord",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Chatt",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "Chatbubblar UI",
+	"Chat Controls": "",
+	"Chat direction": "Chattriktning",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chattar",
+	"Check Again": "Kontrollera igen",
+	"Check for updates": "Sök efter uppdateringar",
+	"Checking for updates...": "Söker efter uppdateringar...",
+	"Choose a model before saving...": "Välj en modell innan du sparar...",
+	"Chunk Overlap": "Överlappning",
+	"Chunk Params": "Chunk-parametrar",
+	"Chunk Size": "Chunk-storlek",
+	"Ciphers": "",
+	"Citation": "Citat",
+	"Clear memory": "Rensa minnet",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Klicka här för hjälp.",
+	"Click here to": "Klicka här för att",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Klicka här för att välja",
+	"Click here to select a csv file.": "Klicka här för att välja en csv-fil.",
+	"Click here to select a py file.": "Klicka här för att välja en python-fil.",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "klicka här.",
+	"Click on the user role button to change a user's role.": "Klicka på knappen för användarroll för att ändra en användares roll.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "Klon",
+	"Close": "Stäng",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "Samling",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "ComfyUI Base URL krävs.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Kommando",
+	"Completions": "",
+	"Concurrent Requests": "Parallella anrop",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "Bekräfta lösenord",
+	"Confirm your action": "",
+	"Connections": "Anslutningar",
+	"Contact Admin for WebUI Access": "Kontakta administratören för att få åtkomst till WebUI",
+	"Content": "Innehåll",
+	"Content Extraction": "",
+	"Context Length": "Kontextlängd",
+	"Continue Response": "Fortsätt svar",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "Kopierad delad chatt-URL till urklipp!",
+	"Copied to clipboard": "",
+	"Copy": "Kopiera",
+	"Copy last code block": "Kopiera sista kodblock",
+	"Copy last response": "Kopiera sista svar",
+	"Copy Link": "Kopiera länk",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Kopiering till urklipp lyckades!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Skapa en modell",
+	"Create Account": "Skapa konto",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Skapa ny nyckel",
+	"Create new secret key": "Skapa ny hemlig nyckel",
+	"Created at": "Skapad",
+	"Created At": "Skapad",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "Aktuell modell",
+	"Current Password": "Nuvarande lösenord",
+	"Custom": "Anpassad",
+	"Dark": "Mörk",
+	"Database": "Databas",
+	"December": "december",
+	"Default": "Standard",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Standard (SentenceTransformers)",
+	"Default Model": "Standardmodell",
+	"Default model updated": "Standardmodell uppdaterad",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Standardinstruktionsförslag",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Standardanvändarroll",
+	"Delete": "Radera",
+	"Delete a model": "Ta bort en modell",
+	"Delete All Chats": "Ta bort alla chattar",
+	"Delete All Models": "",
+	"Delete chat": "Radera chatt",
+	"Delete Chat": "Radera chatt",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "radera denna länk",
+	"Delete tool?": "",
+	"Delete User": "Radera användare",
+	"Deleted {{deleteModelTag}}": "Raderad {{deleteModelTag}}",
+	"Deleted {{name}}": "Borttagen {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Beskrivning",
+	"Didn't fully follow instructions": "Följde inte instruktionerna",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "Upptäck en modell",
+	"Discover a prompt": "Upptäck en instruktion",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "Upptäck, ladda ner och utforska anpassade instruktioner",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "Upptäck, ladda ner och utforska modellförinställningar",
+	"Dismissible": "Kan stängas",
+	"Display": "",
+	"Display Emoji in Call": "Visa Emoji under samtal",
+	"Display the username instead of You in the Chat": "Visa användarnamnet istället för du i chatten",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "Dokument",
+	"Documentation": "Dokumentation",
+	"Documents": "Dokument",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "gör inga externa anslutningar, och dina data förblir säkra på din lokalt värdade server.",
+	"Don't have an account?": "Har du inget konto?",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "Tycker inte om utseendet",
+	"Done": "",
+	"Download": "Ladda ner",
+	"Download canceled": "Nedladdning avbruten",
+	"Download Database": "Ladda ner databas",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Släpp filer här för att lägga till i samtalet",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "t.ex. '30s', '10m'. Giltiga tidsenheter är 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Redigera",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "Redigera användare",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "E-post",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Batchstorlek för inbäddning",
+	"Embedding Model": "Inbäddningsmodell",
+	"Embedding Model Engine": "Motor för inbäddningsmodell",
+	"Embedding model set to \"{{embedding_model}}\"": "Inbäddningsmodell inställd på \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Aktivera community-delning",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Aktivera nya registreringar",
+	"Enable Web Search": "Aktivera webbsökning",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Se till att din CSV-fil innehåller fyra kolumner i denna ordning: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Skriv {{role}} meddelande här",
+	"Enter a detail about yourself for your LLMs to recall": "Skriv en detalj om dig själv för att dina LLMs ska komma ihåg",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Ange API-nyckel för Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Ange chunköverlappning",
+	"Enter Chunk Size": "Ange chunkstorlek",
+	"Enter description": "",
+	"Enter Github Raw URL": "Ange Github Raw URL",
+	"Enter Google PSE API Key": "Ange Google PSE API-nyckel",
+	"Enter Google PSE Engine Id": "Ange Google PSE Engine Id",
+	"Enter Image Size (e.g. 512x512)": "Ange bildstorlek (t.ex. 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Skriv språkkoder",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Ange modelltagg (t.ex. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Ange antal steg (t.ex. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Ange betyg",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Ange Searxng Query URL",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Ange Serper API-nyckel",
+	"Enter Serply API Key": "Ange Serply API-nyckel",
+	"Enter Serpstack API Key": "Ange Serpstack API-nyckel",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Ange stoppsekvens",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "Ange Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Ange URL (t.ex. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Ange URL (t.ex. http://localhost:11434)",
+	"Enter Your Email": "Ange din e-post",
+	"Enter Your Full Name": "Ange ditt fullständiga namn",
+	"Enter your message": "",
+	"Enter Your Password": "Ange ditt lösenord",
+	"Enter Your Role": "Ange din roll",
+	"Enter Your Username": "",
+	"Error": "Fel",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Experimentell",
+	"Explore the cosmos": "",
+	"Export": "Export",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Exportera alla chattar (alla användare)",
+	"Export chat (.json)": "Exportera chatt (.json)",
+	"Export Chats": "Exportera chattar",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "Exportera modeller",
+	"Export Presets": "",
+	"Export Prompts": "Exportera instruktioner",
+	"Export to CSV": "",
+	"Export Tools": "Exportera verktyg",
+	"External Models": "Externa modeller",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Misslyckades med att skapa API-nyckel.",
+	"Failed to read clipboard contents": "Misslyckades med att läsa urklippsinnehåll",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Misslyckades med att uppdatera inställningarna",
+	"Failed to upload file.": "",
+	"February": "februari",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Tveka inte att lägga till specifika detaljer",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Fil-läge",
+	"File not found.": "Fil hittades inte.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Fingeravtrycksmanipulering upptäckt: Kan inte använda initialer som avatar. Återställning till standardprofilbild.",
+	"Fluidly stream large external response chunks": "Strömma flytande stora externa svarschunks",
+	"Focus chat input": "Fokusera på chattfältet",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Följde instruktionerna perfekt",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Straff för frekvens",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "Allmän",
+	"General Settings": "Allmänna inställningar",
+	"Generate Image": "Generera bild",
+	"Generating search query": "Genererar sökfråga",
+	"Generation Info": "Info om generation",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "Bra svar",
+	"Google PSE API Key": "Google PSE API-nyckel",
+	"Google PSE Engine Id": "Google PSE Engine Id",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "har inga samtal.",
+	"Hello, {{name}}": "Hej, {{name}}",
+	"Help": "Hjälp",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Dölj",
+	"Host": "",
+	"How can I help you today?": "Hur kan jag hjälpa dig idag?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Hybrid sökning",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Bildgenerering (experimentell)",
+	"Image Generation Engine": "Bildgenereringsmotor",
+	"Image Settings": "Bildinställningar",
+	"Images": "Bilder",
+	"Import Chats": "Importera chattar",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "Importera modeller",
+	"Import Presets": "",
+	"Import Prompts": "Importera instruktioner",
+	"Import Tools": "Importera verktyg",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Inkludera flaggan `--api` när du kör stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Information",
+	"Input commands": "Indatakommandon",
+	"Install from Github URL": "Installera från Github-URL",
+	"Instant Auto-Send After Voice Transcription": "Skicka automatiskt efter rösttranskribering",
+	"Interface": "Gränssnitt",
+	"Invalid file format.": "",
+	"Invalid Tag": "Ogiltig tagg",
+	"January": "januari",
+	"Jina API Key": "",
+	"join our Discord for help.": "gå med i vår Discord för hjälp.",
+	"JSON": "JSON",
+	"JSON Preview": "Förhandsversion av JSON",
+	"July": "juli",
+	"June": "juni",
+	"JWT Expiration": "JWT-utgångsdatum",
+	"JWT Token": "JWT-token",
+	"Keep Alive": "Keep Alive",
+	"Key": "",
+	"Keyboard shortcuts": "Tangentbordsgenvägar",
+	"Knowledge": "Kunskap",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Språk",
+	"Last Active": "Senast aktiv",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Ljus",
+	"Listening...": "Lyssnar...",
+	"LLMs can make mistakes. Verify important information.": "LLM:er kan göra misstag. Granska viktig information.",
+	"Local": "",
+	"Local Models": "Lokala modeller",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Skapad av OpenWebUI Community",
+	"Make sure to enclose them with": "Se till att bifoga dem med",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "Hantera",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Hantera rörledningar",
+	"March": "mars",
+	"Max Tokens (num_predict)": "Maximalt antal tokens (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Högst 3 modeller kan laddas ner samtidigt. Vänligen försök igen senare.",
+	"May": "maj",
+	"Memories accessible by LLMs will be shown here.": "Minnen som LLM:er kan komma åt visas här.",
+	"Memory": "Minnen",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Meddelanden du skickar efter att ha skapat din länk kommer inte att delas. Användare med URL:en kommer att kunna se delad chatt.",
+	"Min P": "",
+	"Minimum Score": "Tröskel",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Modellen '{{modelName}}' har laddats ner framgångsrikt.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Modellen '{{modelTag}}' är redan i kö för nedladdning.",
+	"Model {{modelId}} not found": "Modell {{modelId}} hittades inte",
+	"Model {{modelName}} is not vision capable": "Modellen {{modelName}} är inte synkapabel",
+	"Model {{name}} is now {{status}}": "Modellen {{name}} är nu {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Modellens filsystemväg upptäckt. Modellens kortnamn krävs för uppdatering, kan inte fortsätta.",
+	"Model Filtering": "",
+	"Model ID": "Modell-ID",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Modell inte vald",
+	"Model Params": "Modell Params",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "Modelfilens innehåll",
+	"Models": "Modeller",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Mer",
+	"Name": "Namn",
+	"Name your knowledge base": "",
+	"New Chat": "Ny chatt",
+	"New folder": "",
+	"New Password": "Nytt lösenord",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Inga resultat hittades",
+	"No search query generated": "Ingen sökfråga genererad",
+	"No source available": "Ingen tillgänglig källa",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "Ingen",
+	"Not factually correct": "Inte faktiskt korrekt",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Obs: Om du anger en tröskel kommer sökningen endast att returnera dokument med ett betyg som är större än eller lika med tröskeln.",
+	"Notes": "",
+	"Notifications": "Notifikationer",
+	"November": "november",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "oktober",
+	"Off": "Av",
+	"Okay, Let's Go!": "Okej, nu kör vi!",
+	"OLED Dark": "Mörk (OLED)",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API inaktiverat",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama-version",
+	"On": "På",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Endast alfanumeriska tecken och bindestreck är tillåtna i kommandosträngen.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hoppsan! Det ser ut som om URL:en är ogiltig. Dubbelkolla gärna och försök igen.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hoppsan! Du använder en ej stödd metod (endast frontend). Vänligen servera WebUI från backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Öppna ny chatt",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API-konfig",
+	"OpenAI API Key is required.": "OpenAI API-nyckel krävs.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI-URL/nyckel krävs.",
+	"or": "eller",
+	"Organize your users": "",
+	"Other": "Andra",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Lösenord",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF-dokument (.pdf)",
+	"PDF Extract Images (OCR)": "PDF Extrahera bilder (OCR)",
+	"pending": "väntande",
+	"Permission denied when accessing media devices": "Nekad behörighet vid åtkomst till mediaenheter",
+	"Permission denied when accessing microphone": "Nekad behörighet vid åtkomst till mikrofon",
+	"Permission denied when accessing microphone: {{error}}": "Tillstånd nekades vid åtkomst till mikrofon: {{error}}",
+	"Permissions": "",
+	"Personalization": "Personalisering",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "Rörledningar",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "Ventiler för rörledningar",
+	"Plain text (.txt)": "Text (.txt)",
+	"Playground": "Lekplats",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Positivt inställning",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Föregående 30 dagar",
+	"Previous 7 days": "Föregående 7 dagar",
+	"Profile Image": "Profilbild",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Instruktion (t.ex. Berätta en kuriosa om Romerska Imperiet)",
+	"Prompt Content": "Instruktionens innehåll",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Instruktionsförslag",
+	"Prompt updated successfully": "",
+	"Prompts": "Instruktioner",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ladda ner \"{{searchValue}}\" från Ollama.com",
+	"Pull a model from Ollama.com": "Ladda ner en modell från Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Inställningar för sökfråga",
+	"RAG Template": "RAG-mall",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Läs igenom",
+	"Record voice": "Spela in röst",
+	"Redirecting you to OpenWebUI Community": "Omdirigerar dig till OpenWebUI Community",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Referera till dig själv som \"Användare\" (t.ex. \"Användaren lär sig spanska\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Avvisades när det inte borde ha gjort det",
+	"Regenerate": "Regenerera",
+	"Release Notes": "Versionsinformation",
+	"Relevance": "",
+	"Remove": "Ta bort",
+	"Remove Model": "Ta bort modell",
+	"Rename": "Byt namn",
+	"Reorder Models": "",
+	"Repeat Last N": "Upprepa senaste N",
+	"Request Mode": "Frågeläge",
+	"Reranking Model": "Reranking modell",
+	"Reranking model disabled": "Reranking modell inaktiverad",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking modell inställd på \"{{reranking_model}}\"",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Återställ uppladdningskatalog",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Roll",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "Kör",
+	"Save": "Spara",
+	"Save & Create": "Spara och skapa",
+	"Save & Update": "Spara och uppdatera",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Att spara chatloggar direkt till din webbläsares lagring stöds inte längre. Ta en stund och ladda ner och radera dina chattloggar genom att klicka på knappen nedan. Oroa dig inte, du kan enkelt importera dina chattloggar till backend genom",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "Sök",
+	"Search a model": "Sök efter en modell",
+	"Search Base": "",
+	"Search Chats": "Sök i chattar",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "Sök modeller",
+	"Search options": "",
+	"Search Prompts": "Sök instruktioner",
+	"Search Result Count": "Antal sökresultat",
+	"Search the web": "",
+	"Search Tools": "Sökverktyg",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "Sökte på {{count}} sites_one",
+	"Searched {{count}} sites_other": "Sökte på {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "Söker \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "Searxng Query URL",
+	"See readme.md for instructions": "Se readme.md för instruktioner",
+	"See what's new": "Se vad som är nytt",
+	"Seed": "Seed",
+	"Select a base model": "Välj en basmodell",
+	"Select a engine": "Välj en motor",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "Välj en modell",
+	"Select a pipeline": "Välj en rörledning",
+	"Select a pipeline url": "Välj en URL för rörledningen",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "Välj en modell",
+	"Select only one model to call": "Välj endast en modell att ringa",
+	"Selected model(s) do not support image inputs": "Valda modeller stöder inte bildinmatningar",
+	"Semantic distance to query": "",
+	"Send": "Skicka",
+	"Send a Message": "Skicka ett meddelande",
+	"Send message": "Skicka meddelande",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "september",
+	"Serper API Key": "Serper API-nyckel",
+	"Serply API Key": "Serply API-nyckel",
+	"Serpstack API Key": "Serpstack API-nyckel",
+	"Server connection verified": "Serveranslutning verifierad",
+	"Set as default": "Ange som standard",
+	"Set CFG Scale": "",
+	"Set Default Model": "Ange standardmodell",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Ställ in embedding modell (t.ex. {{model}})",
+	"Set Image Size": "Ange bildstorlek",
+	"Set reranking model (e.g. {{model}})": "Ställ in reranking modell (t.ex. {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Ange steg",
+	"Set Task Model": "Ange uppgiftsmodell",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Ange röst",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Inställningar",
+	"Settings saved successfully!": "Inställningar sparades framgångsrikt!",
+	"Share": "Dela",
+	"Share Chat": "Dela chatt",
+	"Share to OpenWebUI Community": "Dela till OpenWebUI Community",
+	"Show": "Visa",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Visa administratörsinformation till väntande konton",
+	"Show shortcuts": "Visa genvägar",
+	"Show your support!": "",
+	"Showcased creativity": "Visade kreativitet",
+	"Sign in": "Logga in",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Logga ut",
+	"Sign up": "Registrera dig",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Källa",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Fel vid taligenkänning: {{error}}",
+	"Speech-to-Text Engine": "Tal-till-text-motor",
+	"Stop": "",
+	"Stop Sequence": "Stoppsekvens",
+	"Stream Chat Response": "",
+	"STT Model": "Tal-till-text-modell",
+	"STT Settings": "Tal-till-text-inställningar",
+	"Subtitle (e.g. about the Roman Empire)": "Undertext (t.ex. om Romerska Imperiet)",
+	"Success": "Framgång",
+	"Successfully updated.": "Uppdaterades framgångsrikt.",
+	"Suggested": "Föreslagen",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "System",
+	"System Instructions": "",
+	"System Prompt": "Systeminstruktion",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "Berätta mer:",
+	"Temperature": "Temperatur",
+	"Template": "Mall",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Text-till-tal-motor",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Tack för din feedback!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Betyget ska vara ett värde mellan 0.0 (0%) och 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Detta säkerställer att dina värdefulla samtal sparas säkert till din backend-databas. Tack!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Detta är en experimentell funktion som kanske inte fungerar som förväntat och som kan komma att ändras när som helst.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Djupare förklaring",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Tips: Uppdatera fler variabler genom att trycka på tabb-tangenten i chattinmatningen efter varje ersättning.",
+	"Title": "Titel",
+	"Title (e.g. Tell me a fun fact)": "Titel (t.ex. Berätta en kuriosa)",
+	"Title Auto-Generation": "Automatisk generering av titel",
+	"Title cannot be an empty string.": "Titeln får inte vara en tom sträng.",
+	"Title Generation Prompt": "Instruktion för titelgenerering",
+	"TLS": "",
+	"To access the available model names for downloading,": "För att komma åt de tillgängliga modellnamnen för nedladdning,",
+	"To access the GGUF models available for downloading,": "För att komma åt de GGUF-modellerna som finns tillgängliga för nedladdning,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "För att få tillgång till WebUI, kontakta administratören. Administratörer kan hantera behörigheter från administrationspanelen.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Om du vill välja verktygslådor här måste du först lägga till dem i arbetsytan \"Verktyg\".",
+	"Toast notifications for new updates": "",
+	"Today": "Idag",
+	"Toggle settings": "Växla inställningar",
+	"Toggle sidebar": "Växla sidofält",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Tokens att behålla vid kontextuppdatering (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "Verktyg",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "Topp K",
+	"Top P": "Topp P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Problem med att komma åt Ollama?",
+	"TTS Model": "Text-till-tal-modell",
+	"TTS Settings": "Text-till-tal-inställningar",
+	"TTS Voice": "Text-till-tal-röst",
+	"Type": "Typ",
+	"Type Hugging Face Resolve (Download) URL": "Skriv Hugging Face Resolve (nedladdning) URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Oj då! Det uppstod ett problem med anslutningen till {{provider}}.",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "Uppdatera och kopiera länk",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Uppdatera lösenord",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Ladda upp en GGUF-modell",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Ladda upp filer",
+	"Upload Pipeline": "Ladda upp rörledning",
+	"Upload Progress": "Uppladdningsframsteg",
+	"URL": "",
+	"URL Mode": "URL-läge",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Använd Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Använd initialer",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "användare",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "Användare",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Använd",
+	"Valid time units:": "Giltiga tidsenheter:",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "variabel",
+	"variable to have them replaced with clipboard content.": "variabel för att få dem ersatta med urklippsinnehåll.",
+	"Version": "Version",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "Varning",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Varning: Om du uppdaterar eller ändrar din embedding modell måste du importera alla dokument igen.",
+	"Web": "Webb",
+	"Web API": "Webb-API",
+	"Web Loader Settings": "Web Loader-inställningar",
+	"Web Search": "Webbsökning",
+	"Web Search Engine": "Webbsökmotor",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook-URL",
+	"WebUI Settings": "WebUI-inställningar",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Vad är nytt i",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (lokal)",
+	"Why?": "",
+	"Widescreen Mode": "Bredbildsläge",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Arbetsyta",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Skriv ett instruktionsförslag (t.ex. Vem är du?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Skriv en sammanfattning på 50 ord som sammanfattar [ämne eller nyckelord].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Igår",
+	"You": "Dig",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Du kan anpassa dina interaktioner med stora språkmodeller genom att lägga till minnen via knappen 'Hantera' nedan, så att de blir mer användbara och skräddarsydda för dig.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Du har inga arkiverade samtal.",
+	"You have shared this chat": "Du har delat denna chatt",
+	"You're a helpful assistant.": "Du är en hjälpsam assistent.",
+	"You're now logged in.": "Du är nu inloggad.",
+	"Your account status is currently pending activation.": "Ditt konto väntar på att bli aktiverat",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Loader-inställningar"
+}
diff --git a/src/lib/i18n/locales/th-TH/translation.json b/src/lib/i18n/locales/th-TH/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..e1fa1e2856a638f0b4d4b1f725cfb8b80e760a54
--- /dev/null
+++ b/src/lib/i18n/locales/th-TH/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' หรือ '-1' สำหรับไม่มีการหมดอายุ",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(เช่น `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(เช่น `sh webui.sh --api`)",
+	"(latest)": "(ล่าสุด)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "การสนทนาของ {{user}}",
+	"{{webUIName}} Backend Required": "ต้องการ Backend ของ {{webUIName}}",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "ใช้โมเดลงานเมื่อทำงานเช่นการสร้างหัวข้อสำหรับการสนทนาและการค้นหาเว็บ",
+	"a user": "ผู้ใช้",
+	"About": "เกี่ยวกับ",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "บัญชี",
+	"Account Activation Pending": "การเปิดใช้งานบัญชีอยู่ระหว่างดำเนินการ",
+	"Accurate information": "ข้อมูลที่ถูกต้อง",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "ผู้ใช้ที่ใช้งานอยู่",
+	"Add": "เพิ่ม",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "เพิ่มคำอธิบายสั้นๆ เกี่ยวกับสิ่งที่โมเดลนี้ทำ",
+	"Add a tag": "เพิ่มแท็ก",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "เพิ่มพรอมต์ที่กำหนดเอง",
+	"Add Files": "เพิ่มไฟล์",
+	"Add Group": "",
+	"Add Memory": "เพิ่มความจำ",
+	"Add Model": "เพิ่มโมเดล",
+	"Add Tag": "",
+	"Add Tags": "เพิ่มแท็ก",
+	"Add text content": "",
+	"Add User": "เพิ่มผู้ใช้",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "การปรับการตั้งค่าเหล่านี้จะนำไปใช้กับผู้ใช้ทั้งหมด",
+	"admin": "ผู้ดูแลระบบ",
+	"Admin": "ผู้ดูแลระบบ",
+	"Admin Panel": "แผงผู้ดูแลระบบ",
+	"Admin Settings": "การตั้งค่าผู้ดูแลระบบ",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "ผู้ดูแลระบบสามารถเข้าถึงเครื่องมือทั้งหมดได้ตลอดเวลา; ผู้ใช้ต้องการเครื่องมือที่กำหนดต่อโมเดลในพื้นที่ทำงาน",
+	"Advanced Parameters": "พารามิเตอร์ขั้นสูง",
+	"Advanced Params": "พารามิเตอร์ขั้นสูง",
+	"All chats": "",
+	"All Documents": "เอกสารทั้งหมด",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "อนุญาตการลบการสนทนา",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "อนุญาตเสียงที่ไม่ใช่ท้องถิ่น",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "อนุญาตตำแหน่งผู้ใช้",
+	"Allow Voice Interruption in Call": "อนุญาตการแทรกเสียงในสาย",
+	"Already have an account?": "มีบัญชีอยู่แล้ว?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "ผู้ช่วย",
+	"and": "และ",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "และสร้างลิงก์ที่แชร์ใหม่",
+	"API Base URL": "URL ฐานของ API",
+	"API Key": "คีย์ API",
+	"API Key created.": "สร้างคีย์ API แล้ว",
+	"API keys": "คีย์ API",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "เมษายน",
+	"Archive": "เก็บถาวร",
+	"Archive All Chats": "เก็บถาวรการสนทนาทั้งหมด",
+	"Archived Chats": "การสนทนาที่เก็บถาวร",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "คุณแน่ใจหรือ?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "แนบไฟล์",
+	"Attention to detail": "ใส่ใจในรายละเอียด",
+	"Attribute for Username": "",
+	"Audio": "เสียง",
+	"August": "สิงหาคม",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "ตอบสนองการคัดลอกอัตโนมัติไปยังคลิปบอร์ด",
+	"Auto-playback response": "ตอบสนองการเล่นอัตโนมัติ",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "สตริงการตรวจสอบ API ของ AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL": "URL ฐานของ AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "ต้องการ URL ฐานของ AUTOMATIC1111",
+	"Available list": "",
+	"available!": "พร้อมใช้งาน!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "กลับ",
+	"Bad Response": "การตอบสนองที่ไม่ดี",
+	"Banners": "แบนเนอร์",
+	"Base Model (From)": "โมเดลพื้นฐาน (จาก)",
+	"Batch Size (num_batch)": "ขนาดชุด (num_batch)",
+	"before": "ก่อน",
+	"Being lazy": "ขี้เกียจ",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "คีย์ API ของ Brave Search",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "ข้ามการตรวจสอบ SSL สำหรับเว็บไซต์",
+	"Call": "โทร",
+	"Call feature is not supported when using Web STT engine": "ไม่รองรับฟีเจอร์การโทรเมื่อใช้เครื่องยนต์ Web STT",
+	"Camera": "กล้อง",
+	"Cancel": "ยกเลิก",
+	"Capabilities": "ความสามารถ",
+	"Certificate Path": "",
+	"Change Password": "เปลี่ยนรหัสผ่าน",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "แชท",
+	"Chat Background Image": "ภาพพื้นหลังแชท",
+	"Chat Bubble UI": "UI ฟองแชท",
+	"Chat Controls": "การควบคุมแชท",
+	"Chat direction": "ทิศทางการแชท",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "แชท",
+	"Check Again": "ตรวจสอบอีกครั้ง",
+	"Check for updates": "ตรวจสอบการอัปเดต",
+	"Checking for updates...": "กำลังตรวจสอบการอัปเดต...",
+	"Choose a model before saving...": "เลือกโมเดลก่อนบันทึก...",
+	"Chunk Overlap": "ทับซ้อนส่วนข้อมูล",
+	"Chunk Params": "พารามิเตอร์ส่วนข้อมูล",
+	"Chunk Size": "ขนาดส่วนข้อมูล",
+	"Ciphers": "",
+	"Citation": "การอ้างอิง",
+	"Clear memory": "ล้างความจำ",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "คลิกที่นี่เพื่อขอความช่วยเหลือ",
+	"Click here to": "คลิกที่นี่เพื่อ",
+	"Click here to download user import template file.": "คลิกที่นี่เพื่อดาวน์โหลดไฟล์แม่แบบนำเข้าผู้ใช้",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "คลิกที่นี่เพื่อเลือก",
+	"Click here to select a csv file.": "คลิกที่นี่เพื่อเลือกไฟล์ csv",
+	"Click here to select a py file.": "คลิกที่นี่เพื่อเลือกไฟล์ py",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "คลิกที่นี่",
+	"Click on the user role button to change a user's role.": "คลิกที่ปุ่มบทบาทผู้ใช้เพื่อเปลี่ยนบทบาทของผู้ใช้",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "การอนุญาตเขียนคลิปบอร์ดถูกปฏิเสธ โปรดตรวจสอบการตั้งค่าเบราว์เซอร์ของคุณเพื่อให้สิทธิ์ที่จำเป็น",
+	"Clone": "โคลน",
+	"Close": "ปิด",
+	"Code execution": "",
+	"Code formatted successfully": "จัดรูปแบบโค้ดสำเร็จแล้ว",
+	"Collection": "คอลเลคชัน",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL ฐานของ ComfyUI",
+	"ComfyUI Base URL is required.": "ต้องการ URL ฐานของ ComfyUI",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "คำสั่ง",
+	"Completions": "",
+	"Concurrent Requests": "คำขอพร้อมกัน",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "ยืนยัน",
+	"Confirm Password": "ยืนยันรหัสผ่าน",
+	"Confirm your action": "ยืนยันการดำเนินการของคุณ",
+	"Connections": "การเชื่อมต่อ",
+	"Contact Admin for WebUI Access": "ติดต่อผู้ดูแลระบบสำหรับการเข้าถึง WebUI",
+	"Content": "เนื้อหา",
+	"Content Extraction": "การสกัดเนื้อหา",
+	"Context Length": "ความยาวของบริบท",
+	"Continue Response": "ตอบสนองต่อไป",
+	"Continue with {{provider}}": "ดำเนินการต่อด้วย {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "การควบคุม",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "คัดลอก URL แชทที่แชร์ไปยังคลิปบอร์ดแล้ว!",
+	"Copied to clipboard": "",
+	"Copy": "คัดลอก",
+	"Copy last code block": "คัดลอกบล็อกโค้ดสุดท้าย",
+	"Copy last response": "คัดลอกการตอบสนองล่าสุด",
+	"Copy Link": "คัดลอกลิงก์",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "คัดลอกไปยังคลิปบอร์ดสำเร็จแล้ว!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "สร้างโมเดล",
+	"Create Account": "สร้างบัญชี",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "สร้างคีย์ใหม่",
+	"Create new secret key": "สร้างคีย์ลับใหม่",
+	"Created at": "สร้างเมื่อ",
+	"Created At": "สร้างเมื่อ",
+	"Created by": "สร้างโดย",
+	"CSV Import": "นำเข้า CSV",
+	"Current Model": "โมเดลปัจจุบัน",
+	"Current Password": "รหัสผ่านปัจจุบัน",
+	"Custom": "กำหนดเอง",
+	"Dark": "มืด",
+	"Database": "ฐานข้อมูล",
+	"December": "ธันวาคม",
+	"Default": "ค่าเริ่มต้น",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "ค่าเริ่มต้น (SentenceTransformers)",
+	"Default Model": "โมเดลค่าเริ่มต้น",
+	"Default model updated": "อัปเดตโมเดลค่าเริ่มต้นแล้ว",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "คำแนะนำพรอมต์ค่าเริ่มต้น",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "บทบาทผู้ใช้ค่าเริ่มต้น",
+	"Delete": "ลบ",
+	"Delete a model": "ลบโมเดล",
+	"Delete All Chats": "ลบการสนทนาทั้งหมด",
+	"Delete All Models": "",
+	"Delete chat": "ลบแชท",
+	"Delete Chat": "ลบแชท",
+	"Delete chat?": "ลบแชท?",
+	"Delete folder?": "",
+	"Delete function?": "ลบฟังก์ชัน?",
+	"Delete prompt?": "ลบพรอมต์?",
+	"delete this link": "ลบลิงก์นี้",
+	"Delete tool?": "ลบเครื่องมือ?",
+	"Delete User": "ลบผู้ใช้",
+	"Deleted {{deleteModelTag}}": "ลบ {{deleteModelTag}}",
+	"Deleted {{name}}": "ลบ {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "คำอธิบาย",
+	"Didn't fully follow instructions": "ไม่ได้ปฏิบัติตามคำแนะนำทั้งหมด",
+	"Disabled": "ปิดใช้งาน",
+	"Discover a function": "ค้นหาฟังก์ชัน",
+	"Discover a model": "ค้นหาโมเดล",
+	"Discover a prompt": "ค้นหาพรอมต์",
+	"Discover a tool": "ค้นหาเครื่องมือ",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "ค้นหา ดาวน์โหลด และสำรวจฟังก์ชันที่กำหนดเอง",
+	"Discover, download, and explore custom prompts": "ค้นหา ดาวน์โหลด และสำรวจพรอมต์ที่กำหนดเอง",
+	"Discover, download, and explore custom tools": "ค้นหา ดาวน์โหลด และสำรวจเครื่องมือที่กำหนดเอง",
+	"Discover, download, and explore model presets": "ค้นหา ดาวน์โหลด และสำรวจพรีเซ็ตโมเดล",
+	"Dismissible": "ยกเลิกได้",
+	"Display": "",
+	"Display Emoji in Call": "แสดงอิโมจิในการโทร",
+	"Display the username instead of You in the Chat": "แสดงชื่อผู้ใช้แทนคุณในการแชท",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "อย่าติดตั้งฟังก์ชันจากแหล่งที่คุณไม่ไว้วางใจอย่างเต็มที่",
+	"Do not install tools from sources you do not fully trust.": "อย่าติดตั้งเครื่องมือจากแหล่งที่คุณไม่ไว้วางใจอย่างเต็มที่",
+	"Document": "เอกสาร",
+	"Documentation": "เอกสารประกอบ",
+	"Documents": "เอกสาร",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "ไม่เชื่อมต่อภายนอกใดๆ และข้อมูลของคุณจะอยู่บนเซิร์ฟเวอร์ที่โฮสต์ในท้องถิ่นของคุณอย่างปลอดภัย",
+	"Don't have an account?": "ยังไม่มีบัญชี?",
+	"don't install random functions from sources you don't trust.": "อย่าติดตั้งฟังก์ชันแบบสุ่มจากแหล่งที่คุณไม่ไว้วางใจ",
+	"don't install random tools from sources you don't trust.": "อย่าติดตั้งเครื่องมือแบบสุ่มจากแหล่งที่คุณไม่ไว้วางใจ",
+	"Don't like the style": "ไม่ชอบสไตล์นี้",
+	"Done": "เสร็จสิ้น",
+	"Download": "ดาวน์โหลด",
+	"Download canceled": "ยกเลิกการดาวน์โหลด",
+	"Download Database": "ดาวน์โหลดฐานข้อมูล",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "วางไฟล์ใดๆ ที่นี่เพื่อเพิ่มในการสนทนา",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "เช่น '30s', '10m' หน่วยเวลาที่ถูกต้องคือ 's', 'm', 'h'",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "แก้ไข",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "แก้ไขความจำ",
+	"Edit User": "แก้ไขผู้ใช้",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "อีเมล",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "ขนาดชุดการฝัง",
+	"Embedding Model": "โมเดลการฝัง",
+	"Embedding Model Engine": "เครื่องยนต์โมเดลการฝัง",
+	"Embedding model set to \"{{embedding_model}}\"": "ตั้งค่าโมเดลการฝังเป็น \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "เปิดใช้งานการแชร์ในชุมชน",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "เปิดใช้งานการสมัครใหม่",
+	"Enable Web Search": "เปิดใช้งานการค้นหาเว็บ",
+	"Enabled": "เปิดใช้งาน",
+	"Engine": "เครื่องยนต์",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "ตรวจสอบว่าไฟล์ CSV ของคุณมี 4 คอลัมน์ในลำดับนี้: ชื่อ, อีเมล, รหัสผ่าน, บทบาท",
+	"Enter {{role}} message here": "ใส่ข้อความ {{role}} ที่นี่",
+	"Enter a detail about yourself for your LLMs to recall": "ใส่รายละเอียดเกี่ยวกับตัวคุณสำหรับ LLMs ของคุณให้จดจำ",
+	"Enter api auth string (e.g. username:password)": "ใส่สตริงการตรวจสอบ API (เช่น username:password)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "ใส่คีย์ API ของ Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "ใส่การทับซ้อนส่วนข้อมูล",
+	"Enter Chunk Size": "ใส่ขนาดส่วนข้อมูล",
+	"Enter description": "",
+	"Enter Github Raw URL": "ใส่ URL ดิบของ Github",
+	"Enter Google PSE API Key": "ใส่คีย์ API ของ Google PSE",
+	"Enter Google PSE Engine Id": "ใส่รหัสเครื่องยนต์ของ Google PSE",
+	"Enter Image Size (e.g. 512x512)": "ใส่ขนาดภาพ (เช่น 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "ใส่รหัสภาษา",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "ใส่แท็กโมเดล (เช่น {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "ใส่จำนวนขั้นตอน (เช่น 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "ใส่คะแนน",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "ใส URL การค้นหาของ Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "ใส่คีย์ API ของ Serper",
+	"Enter Serply API Key": "ใส่คีย์ API ของ Serply",
+	"Enter Serpstack API Key": "ใส่คีย์ API ของ Serpstack",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "ใส่ลำดับหยุด",
+	"Enter system prompt": "ใส่พรอมต์ระบบ",
+	"Enter Tavily API Key": "ใส่คีย์ API ของ Tavily",
+	"Enter Tika Server URL": "ใส่ URL เซิร์ฟเวอร์ของ Tika",
+	"Enter Top K": "ใส่ Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "ใส่ URL (เช่น http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "ใส่ URL (เช่น http://localhost:11434)",
+	"Enter Your Email": "ใส่อีเมลของคุณ",
+	"Enter Your Full Name": "ใส่ชื่อเต็มของคุณ",
+	"Enter your message": "ใส่ข้อความของคุณ",
+	"Enter Your Password": "ใส่รหัสผ่านของคุณ",
+	"Enter Your Role": "ใส่บทบาทของคุณ",
+	"Enter Your Username": "",
+	"Error": "ข้อผิดพลาด",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "การทดลอง",
+	"Explore the cosmos": "",
+	"Export": "ส่งออก",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "ส่งออกการสนทนาทั้งหมด (ผู้ใช้ทั้งหมด)",
+	"Export chat (.json)": "ส่งออกการสนทนา (.json)",
+	"Export Chats": "ส่งออกการสนทนา",
+	"Export Config to JSON File": "",
+	"Export Functions": "ส่งออกฟังก์ชัน",
+	"Export Models": "ส่งออกโมเดล",
+	"Export Presets": "",
+	"Export Prompts": "ส่งออกพรอมต์",
+	"Export to CSV": "",
+	"Export Tools": "ส่งออกเครื่องมือ",
+	"External Models": "โมเดลภายนอก",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "สร้างคีย์ API ล้มเหลว",
+	"Failed to read clipboard contents": "อ่านเนื้อหาคลิปบอร์ดล้มเหลว",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "อัปเดตการตั้งค่าล้มเหลว",
+	"Failed to upload file.": "",
+	"February": "กุมภาพันธ์",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "สามารถเพิ่มรายละเอียดเฉพาะได้",
+	"File": "ไฟล์",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "โหมดไฟล์",
+	"File not found.": "ไม่พบไฟล์",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "ไฟล์",
+	"Filter is now globally disabled": "การกรองถูกปิดใช้งานทั่วโลกแล้ว",
+	"Filter is now globally enabled": "การกรองถูกเปิดใช้งานทั่วโลกแล้ว",
+	"Filters": "ตัวกรอง",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "ตรวจพบการปลอมแปลงลายนิ้วมือ: ไม่สามารถใช้ชื่อย่อเป็นอวตารได้ ใช้รูปโปรไฟล์เริ่มต้น",
+	"Fluidly stream large external response chunks": "สตรีมชิ้นส่วนการตอบสนองขนาดใหญ่จากภายนอกอย่างลื่นไหล",
+	"Focus chat input": "โฟกัสการป้อนแชท",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "ปฏิบัติตามคำแนะนำอย่างสมบูรณ์แบบ",
+	"Forge new paths": "",
+	"Form": "ฟอร์ม",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "การลงโทษความถี่",
+	"Function": "",
+	"Function created successfully": "สร้างฟังก์ชันสำเร็จ",
+	"Function deleted successfully": "ลบฟังก์ชันสำเร็จ",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "ฟังก์ชันถูกปิดใช้งานทั่วโลกแล้ว",
+	"Function is now globally enabled": "ฟังก์ชันถูกเปิดใช้งานทั่วโลกแล้ว",
+	"Function Name": "",
+	"Function updated successfully": "อัปเดตฟังก์ชันสำเร็จ",
+	"Functions": "ฟังก์ชัน",
+	"Functions allow arbitrary code execution": "ฟังก์ชันอนุญาตการเรียกใช้โค้ดโดยพลการ",
+	"Functions allow arbitrary code execution.": "ฟังก์ชันอนุญาตการเรียกใช้โค้ดโดยพลการ",
+	"Functions imported successfully": "นำเข้าฟังก์ชันสำเร็จ",
+	"General": "ทั่วไป",
+	"General Settings": "การตั้งค่าทั่วไป",
+	"Generate Image": "สร้างภาพ",
+	"Generating search query": "สร้างคำค้นหา",
+	"Generation Info": "ข้อมูลการสร้าง",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "ทั่วโลก",
+	"Good Response": "การตอบสนองที่ดี",
+	"Google PSE API Key": "คีย์ API ของ Google PSE",
+	"Google PSE Engine Id": "รหัสเครื่องยนต์ของ Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "",
+	"has no conversations.": "ไม่มีการสนทนา",
+	"Hello, {{name}}": "สวัสดี, {{name}}",
+	"Help": "ช่วยเหลือ",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "ซ่อน",
+	"Host": "",
+	"How can I help you today?": "วันนี้ฉันจะช่วยอะไรคุณได้บ้าง?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "การค้นหาแบบไฮบริด",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "ฉันรับทราบว่าฉันได้อ่านและเข้าใจผลกระทบของการกระทำของฉัน ฉันทราบถึงความเสี่ยงที่เกี่ยวข้องกับการเรียกใช้โค้ดโดยพลการและฉันได้ตรวจสอบความน่าเชื่อถือของแหล่งที่มาแล้ว",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "การสร้างภาพ (การทดลอง)",
+	"Image Generation Engine": "เครื่องยนต์การสร้างภาพ",
+	"Image Settings": "การตั้งค่าภาพ",
+	"Images": "ภาพ",
+	"Import Chats": "นำเข้าการสนทนา",
+	"Import Config from JSON File": "",
+	"Import Functions": "นำเข้าฟังก์ชัน",
+	"Import Models": "นำเข้าโมเดล",
+	"Import Presets": "",
+	"Import Prompts": "นำเข้าพรอมต์",
+	"Import Tools": "นำเข้าเครื่องมือ",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "รวมแฟลก `--api-auth` เมื่อเรียกใช้ stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "รวมแฟลก `--api` เมื่อเรียกใช้ stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "ข้อมูล",
+	"Input commands": "คำสั่งป้อนข้อมูล",
+	"Install from Github URL": "ติดตั้งจาก URL ของ Github",
+	"Instant Auto-Send After Voice Transcription": "ส่งอัตโนมัติทันทีหลังจากการถอดเสียง",
+	"Interface": "อินเทอร์เฟซ",
+	"Invalid file format.": "",
+	"Invalid Tag": "แท็กไม่ถูกต้อง",
+	"January": "มกราคม",
+	"Jina API Key": "",
+	"join our Discord for help.": "เข้าร่วม Discord ของเราเพื่อขอความช่วยเหลือ",
+	"JSON": "JSON",
+	"JSON Preview": "ดูตัวอย่าง JSON",
+	"July": "กรกฎาคม",
+	"June": "มิถุนายน",
+	"JWT Expiration": "การหมดอายุของ JWT",
+	"JWT Token": "โทเค็น JWT",
+	"Keep Alive": "คงอยู่",
+	"Key": "",
+	"Keyboard shortcuts": "ทางลัดแป้นพิมพ์",
+	"Knowledge": "ความรู้",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "ภาษา",
+	"Last Active": "ใช้งานล่าสุด",
+	"Last Modified": "แก้ไขล่าสุด",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "แสง",
+	"Listening...": "กำลังฟัง...",
+	"LLMs can make mistakes. Verify important information.": "LLMs สามารถทำผิดพลาดได้ ตรวจสอบข้อมูลสำคัญ",
+	"Local": "",
+	"Local Models": "โมเดลท้องถิ่น",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "สร้างโดยชุมชน OpenWebUI",
+	"Make sure to enclose them with": "",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "จัดการ",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "จัดการไปป์ไลน์",
+	"March": "มีนาคม",
+	"Max Tokens (num_predict)": "โทเค็นสูงสุด (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "สามารถดาวน์โหลดโมเดลได้สูงสุด 3 โมเดลในเวลาเดียวกัน โปรดลองอีกครั้งในภายหลัง",
+	"May": "พฤษภาคม",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "ความจำ",
+	"Memory added successfully": "เพิ่มโมเดลสำเร็จ",
+	"Memory cleared successfully": "ล้าง",
+	"Memory deleted successfully": "ลบโมเดลสำเร็จ",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "ข้อความที่คุณส่งหลังจากสร้างลิงก์ของคุณแล้วจะไม่ถูกแชร์ ผู้ใช้ที่มี URL จะสามารถดูแชทที่แชร์ได้",
+	"Min P": "",
+	"Minimum Score": "คะแนนขั้นต่ำ",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "โมเดล '{{modelName}}' ถูกดาวน์โหลดเรียบร้อยแล้ว",
+	"Model '{{modelTag}}' is already in queue for downloading.": "โมเดล '{{modelTag}}' กำลังอยู่ในคิวสำหรับการดาวน์โหลด",
+	"Model {{modelId}} not found": "ไม่พบโมเดล {{modelId}}",
+	"Model {{modelName}} is not vision capable": "โมเดล {{modelName}} ไม่มีคุณสมบัติวิสชั่น",
+	"Model {{name}} is now {{status}}": "โมเดล {{name}} ขณะนี้ {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "สร้างโมเดลสำเร็จ!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "ตรวจพบเส้นทางระบบไฟล์ของโมเดล ต้องการชื่อย่อของโมเดลสำหรับการอัปเดต ไม่สามารถดำเนินการต่อได้",
+	"Model Filtering": "",
+	"Model ID": "รหัสโมเดล",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "ยังไม่ได้เลือกโมเดล",
+	"Model Params": "พารามิเตอร์ของโมเดล",
+	"Model Permissions": "",
+	"Model updated successfully": "อัปเดตโมเดลเรียบร้อยแล้ว",
+	"Modelfile Content": "เนื้อหาของไฟล์โมเดล",
+	"Models": "โมเดล",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "เพิ่มเติม",
+	"Name": "ชื่อ",
+	"Name your knowledge base": "",
+	"New Chat": "แชทใหม่",
+	"New folder": "",
+	"New Password": "รหัสผ่านใหม่",
+	"No content found": "",
+	"No content to speak": "ไม่มีเนื้อหาที่จะพูด",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "ไม่ได้เลือกไฟล์",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "ไม่มีผลลัพธ์",
+	"No search query generated": "ไม่มีการสร้างคำค้นหา",
+	"No source available": "ไม่มีแหล่งข้อมูล",
+	"No users were found.": "",
+	"No valves to update": "ไม่มีวาล์วที่จะอัปเดต",
+	"None": "ไม่มี",
+	"Not factually correct": "ไม่ถูกต้องตามข้อเท็จจริง",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "หมายเหตุ: หากคุณตั้งค่าคะแนนขั้นต่ำ การค้นหาจะคืนเอกสารที่มีคะแนนมากกว่าหรือเท่ากับคะแนนขั้นต่ำเท่านั้น",
+	"Notes": "",
+	"Notifications": "การแจ้งเตือน",
+	"November": "พฤศจิกายน",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "ตุลาคม",
+	"Off": "ปิด",
+	"Okay, Let's Go!": "ตกลง ไปกัน!",
+	"OLED Dark": "OLED โหมดมื",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "ปิด Ollama API",
+	"Ollama API settings updated": "",
+	"Ollama Version": "เวอร์ชั่น Ollama",
+	"On": "เปิด",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "อนุญาตให้ใช้เฉพาะอักขระตัวอักษรและตัวเลข รวมถึงเครื่องหมายขีดกลางในสตริงคำสั่งเท่านั้น",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "อุ๊บส์! ดูเหมือนว่า URL ไม่ถูกต้อง กรุณาตรวจสอบและลองใหม่อีกครั้ง",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "อุ๊บส์! คุณกำลังใช้วิธีที่ไม่รองรับ (เฉพาะเว็บส่วนหน้า) กรุณาให้บริการ WebUI จากเว็บส่วนแบ็กเอนด์",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "เปิดแชทใหม่",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"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 API": "OpenAI API",
+	"OpenAI API Config": "การตั้งค่า OpenAI API",
+	"OpenAI API Key is required.": "จำเป็นต้องใช้คีย์ OpenAI API",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "จำเป็นต้องใช้ URL/คีย์ OpenAI",
+	"or": "หรือ",
+	"Organize your users": "",
+	"Other": "อื่น ๆ",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "รหัสผ่าน",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "เอกสาร PDF (.pdf)",
+	"PDF Extract Images (OCR)": "การแยกรูปภาพจาก PDF (OCR)",
+	"pending": "รอดำเนินการ",
+	"Permission denied when accessing media devices": "ถูกปฏิเสธเมื่อเข้าถึงอุปกรณ์",
+	"Permission denied when accessing microphone": "ถูกปฏิเสธเมื่อเข้าถึงไมโครโฟน",
+	"Permission denied when accessing microphone: {{error}}": "การอนุญาตถูกปฏิเสธเมื่อเข้าถึงไมโครโฟน: {{error}}",
+	"Permissions": "",
+	"Personalization": "การปรับแต่ง",
+	"Pin": "ปักหมุด",
+	"Pinned": "ปักหมุดแล้ว",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "ลบไปป์ไลน์เรียบร้อยแล้ว",
+	"Pipeline downloaded successfully": "ดาวน์โหลดไปป์ไลน์เรียบร้อยแล้ว",
+	"Pipelines": "ไปป์ไลน์",
+	"Pipelines Not Detected": "ไม่พบไปป์ไลน์",
+	"Pipelines Valves": "วาล์วของไปป์ไลน์",
+	"Plain text (.txt)": "ไฟล์ข้อความ (.txt)",
+	"Playground": "สนามทดสอบ",
+	"Please carefully review the following warnings:": "โปรดตรวจสอบคำเตือนต่อไปนี้อย่างละเอียด:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "ทัศนคติด้านบวก",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 วันที่ผ่านมา",
+	"Previous 7 days": "7 วันที่ผ่านมา",
+	"Profile Image": "รูปโปรไฟล์",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "พรอมต์ (เช่น บอกข้อเท็จจริงที่น่าสนุกเกี่ยวกับจักรวรรดิโรมัน)",
+	"Prompt Content": "เนื้อหาพรอมต์",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "",
+	"Prompt updated successfully": "",
+	"Prompts": "พรอมต์",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "",
+	"Query Generation Prompt": "",
+	"Query Params": "พารามิเตอร์การค้นหา",
+	"RAG Template": "แม่แบบ RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "อ่านออกเสียง",
+	"Record voice": "บันทึกเสียง",
+	"Redirecting you to OpenWebUI Community": "กำลังเปลี่ยนเส้นทางคุณไปยังชุมชน OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "เรียกตัวเองว่า \"ผู้ใช้\" (เช่น \"ผู้ใช้กำลังเรียนภาษาสเปน\")",
+	"References from": "",
+	"Refused when it shouldn't have": "ปฏิเสธเมื่อไม่ควรทำ",
+	"Regenerate": "สร้างใหม่",
+	"Release Notes": "บันทึกรุ่น",
+	"Relevance": "",
+	"Remove": "ลบ",
+	"Remove Model": "ลบโมเดล",
+	"Rename": "เปลี่ยนชื่อ",
+	"Reorder Models": "",
+	"Repeat Last N": "ทำซ้ำครั้งล่าสุด N",
+	"Request Mode": "โหมดคำขอ",
+	"Reranking Model": "จัดอันดับใหม่โมเดล",
+	"Reranking model disabled": "ปิดการใช้งานโมเดลการจัดอันดับใหม่",
+	"Reranking model set to \"{{reranking_model}}\"": "ตั้งค่าโมเดลการจัดอันดับใหม่เป็น \"{{reranking_model}}\"",
+	"Reset": "รีเซ็ต",
+	"Reset All Models": "",
+	"Reset Upload Directory": "รีเซ็ตไดเร็กทอรีการอัปโหลด",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "ไม่สามารถเปิดการแจ้งเตือนการตอบสนองได้เนื่องจากเว็บไซต์ปฏิเสธ กรุณาเข้าการตั้งค่าเบราว์เซอร์ของคุณเพื่อให้สิทธิ์การเข้าถึงที่จำเป็น",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "บทบาท",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "",
+	"Running": "กำลังทำงาน",
+	"Save": "บันทึก",
+	"Save & Create": "บันทึกและสร้าง",
+	"Save & Update": "บันทึกและอัปเดต",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "การบันทึกบันทึกการสนทนาโดยตรงไปยังที่จัดเก็บในเบราว์เซอร์ของคุณไม่ได้รับการสนับสนุนอีกต่อไป โปรดสละเวลาสักครู่เพื่อดาวน์โหลดและลบบันทึกการสนทนาของคุณโดยคลิกที่ปุ่มด้านล่าง ไม่ต้องกังวล คุณสามารถนำเข้าบันทึกการสนทนาของคุณกลับไปยังส่วนแบ็กเอนด์ได้อย่างง่ายดายผ่าน",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "ค้นหา",
+	"Search a model": "ค้นหาโมเดล",
+	"Search Base": "",
+	"Search Chats": "ค้นหาแชท",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "ค้นหาฟังก์ชัน",
+	"Search Knowledge": "",
+	"Search Models": "ค้นหาโมเดล",
+	"Search options": "",
+	"Search Prompts": "ค้นหาพรอมต์",
+	"Search Result Count": "จำนวนผลลัพธ์การค้นหา",
+	"Search the web": "",
+	"Search Tools": "เครื่องมือค้นหา",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "ค้นหา {{count}} เว็บไซต์",
+	"Searched {{count}} sites_other": "ค้นหา {{count}} เว็บไซต์",
+	"Searching \"{{searchQuery}}\"": "กำลังค้นหา \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL คำค้นหา",
+	"See readme.md for instructions": "ดู readme.md สำหรับคำแนะนำ",
+	"See what's new": "ดูสิ่งที่ใหม่",
+	"Seed": "Seed",
+	"Select a base model": "เลือกโมเดลฐาน",
+	"Select a engine": "เลือกเอนจิน",
+	"Select a function": "เลือกฟังก์ชัน",
+	"Select a group": "",
+	"Select a model": "เลือกโมเดล",
+	"Select a pipeline": "เลือกไปป์ไลน์",
+	"Select a pipeline url": "เลือก URL ไปป์ไลน์",
+	"Select a tool": "เลือกเครื่องมือ",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "เลือกโมเดล",
+	"Select only one model to call": "เลือกเพียงโมเดลเดียวที่จะใช้",
+	"Selected model(s) do not support image inputs": "โมเดลที่เลือกไม่รองรับภาพ",
+	"Semantic distance to query": "",
+	"Send": "ส่ง",
+	"Send a Message": "ส่งข้อความ",
+	"Send message": "ส่งข้อความ",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "กันยายน",
+	"Serper API Key": "คีย์ API ของ Serper",
+	"Serply API Key": "คีย์ API ของ Serply",
+	"Serpstack API Key": "คีย์ API ของ Serpstack",
+	"Server connection verified": "ยืนยันการเชื่อมต่อเซิร์ฟเวอร์แล้ว",
+	"Set as default": "ตั้งเป็นค่าเริ่มต้น",
+	"Set CFG Scale": "",
+	"Set Default Model": "ตั้งโมเดลเริ่มต้น",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "ตั้งค่าโมเดลการฝัง (เช่น {{model}})",
+	"Set Image Size": "ตั้งค่าขนาดภาพ",
+	"Set reranking model (e.g. {{model}})": "ตั้งค่าโมเดลการจัดอันดับใหม่ (เช่น {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "ตั้งค่าขั้นตอน",
+	"Set Task Model": "ตั้งค่าโมเดลงาน",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "ตั้งค่าเสียง",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "การตั้งค่า",
+	"Settings saved successfully!": "บันทึกการตั้งค่าเรียบร้อยแล้ว!",
+	"Share": "แชร์",
+	"Share Chat": "แชร์แชท",
+	"Share to OpenWebUI Community": "แชร์ไปยังชุมชน OpenWebUI",
+	"Show": "แสดง",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "แสดงรายละเอียดผู้ดูแลระบบในหน้าจอรอการอนุมัติบัญชี",
+	"Show shortcuts": "แสดงทางลัด",
+	"Show your support!": "แสดงการสนับสนุนของคุณ!",
+	"Showcased creativity": "แสดงความคิดสร้างสรรค์",
+	"Sign in": "ลงชื่อเข้าใช้",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "ลงชื่อออก",
+	"Sign up": "สมัครสมาชิก",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "แหล่งที่มา",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "ข้อผิดพลาดในการรู้จำเสียง: {{error}}",
+	"Speech-to-Text Engine": "เครื่องมือแปลงเสียงเป็นข้อความ",
+	"Stop": "",
+	"Stop Sequence": "หยุดลำดับ",
+	"Stream Chat Response": "",
+	"STT Model": "โมเดลแปลงเสียงเป็นข้อความ",
+	"STT Settings": "การตั้งค่าแปลงเสียงเป็นข้อความ",
+	"Subtitle (e.g. about the Roman Empire)": "คำบรรยาย (เช่น เกี่ยวกับจักรวรรดิโรมัน)",
+	"Success": "สำเร็จ",
+	"Successfully updated.": "อัปเดตเรียบร้อยแล้ว",
+	"Suggested": "แนะนำ",
+	"Support": "สนับสนุน",
+	"Support this plugin:": "สนับสนุนปลั๊กอินนี้:",
+	"Sync directory": "",
+	"System": "ระบบ",
+	"System Instructions": "",
+	"System Prompt": "ระบบพรอมต์",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "แตะเพื่อขัดจังหวะ",
+	"Tavily API Key": "คีย์ API ของ Tavily",
+	"Tell us more:": "บอกเรามากขึ้น:",
+	"Temperature": "อุณหภูมิ",
+	"Template": "แม่แบบ",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "เครื่องมือแปลงข้อความเป็นเสียง",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "ขอบคุณสำหรับความคิดเห็นของคุณ!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "นักพัฒนาที่อยู่เบื้องหลังปลั๊กอินนี้เป็นอาสาสมัครที่มีชื่นชอบการแบ่งบัน หากคุณพบว่าปลั๊กอินนี้มีประโยชน์ โปรดพิจารณาสนับสนุนการพัฒนาของเขา",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "คะแนนควรอยู่ระหว่าง 0.0 (0%) ถึง 1.0 (100%)",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "ธีม",
+	"Thinking...": "กำลังคิด...",
+	"This action cannot be undone. Do you wish to continue?": "การกระทำนี้ไม่สามารถย้อนกลับได้ คุณต้องการดำเนินการต่อหรือไม่?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "สิ่งนี้ทำให้มั่นใจได้ว่าการสนทนาที่มีค่าของคุณจะถูกบันทึกอย่างปลอดภัยในฐานข้อมูลแบ็กเอนด์ของคุณ ขอบคุณ!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "นี่เป็นฟีเจอร์ทดลอง อาจไม่ทำงานตามที่คาดไว้และอาจมีการเปลี่ยนแปลงได้ตลอดเวลา",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "สิ่งนี้จะลบ",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "คำอธิบายอย่างละเอียด",
+	"Tika": "Tika",
+	"Tika Server URL required.": "จำเป็นต้องมี URL ของเซิร์ฟเวอร์ Tika",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "เคล็ดลับ: อัปเดตช่องตัวแปรหลายช่องติดต่อกันโดยการกดปุ่มแท็บในช่องใส่ข้อความแชทหลังจากแต่ละการแทนที่",
+	"Title": "ชื่อเรื่อง",
+	"Title (e.g. Tell me a fun fact)": "ชื่อเรื่อง (เช่น บอกข้อเท็จจริงที่น่าสนุก)",
+	"Title Auto-Generation": "การสร้างชื่ออัตโนมัติ",
+	"Title cannot be an empty string.": "ชื่อเรื่องไม่สามารถเป็นสตริงว่างได้",
+	"Title Generation Prompt": "พรอมต์การสร้างชื่อเรื่อง",
+	"TLS": "",
+	"To access the available model names for downloading,": "ในการเข้าถึงชื่อโมเดลที่มีให้ดาวน์โหลด",
+	"To access the GGUF models available for downloading,": "ในการเข้าถึงโมเดล GGUF ที่มีให้ดาวน์โหลด",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "ในการเข้าถึง WebUI โปรดติดต่อผู้ดูแลระบบ ผู้ดูแลระบบสามารถจัดการสถานะผู้ใช้จากแผงควบคุมผู้ดูแลระบบ",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "ในการเลือกฟิลเตอร์ที่นี่ ให้เพิ่มไปยังพื้นที่ทำงาน \"ฟังก์ชัน\" ก่อน",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "ในการเลือกชุดเครื่องมือที่นี่ ให้เพิ่มไปยังพื้นที่ทำงาน \"เครื่องมือ\" ก่อน",
+	"Toast notifications for new updates": "",
+	"Today": "วันนี้",
+	"Toggle settings": "สลับการตั้งค่า",
+	"Toggle sidebar": "สลับแถบด้านข้าง",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "โทเค็นที่เก็บไว้เมื่อรีเฟรชบริบท (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "สร้างเครื่องมือเรียบร้อยแล้ว",
+	"Tool deleted successfully": "ลบเครื่องมือเรียบร้อยแล้ว",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "นำเข้าเครื่องมือเรียบร้อยแล้ว",
+	"Tool Name": "",
+	"Tool updated successfully": "อัปเดตเครื่องมือเรียบร้อยแล้ว",
+	"Tools": "เครื่องมือ",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "เครื่องมือคือระบบการเรียกใช้ฟังก์ชันที่สามารถดำเนินการโค้ดใดๆ ได้",
+	"Tools have a function calling system that allows arbitrary code execution": "เครื่องมือมีระบบการเรียกใช้ฟังก์ชันที่สามารถดำเนินการโค้ดใดๆ ได้",
+	"Tools have a function calling system that allows arbitrary code execution.": "เครื่องมือมีระบบการเรียกใช้ฟังก์ชันที่สามารถดำเนินการโค้ดใดๆ ได้",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "มีปัญหาในการเข้าถึง Ollama?",
+	"TTS Model": "โมเดลแปลงข้อความเป็นเสียง",
+	"TTS Settings": "การตั้งค่าแปลงข้อความเป็นเสียง",
+	"TTS Voice": "เสียงแปลงข้อความเป็นเสียง",
+	"Type": "ประเภท",
+	"Type Hugging Face Resolve (Download) URL": "พิมพ์ URL ของ Hugging Face Resolve (Download)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "อุ๊ย! มีปัญหาในการเชื่อมต่อกับ {{provider}}",
+	"UI": "ส่วนติดต่อผู้ใช้",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "ยกเลิกการปักหมุด",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "อัปเดต",
+	"Update and Copy Link": "อัปเดตและคัดลอกลิงก์",
+	"Update for the latest features and improvements.": "",
+	"Update password": "อัปเดตรหัสผ่าน",
+	"Updated": "",
+	"Updated at": "อัปเดตเมื่อ",
+	"Updated At": "",
+	"Upload": "อัปโหลด",
+	"Upload a GGUF model": "อัปโหลดโมเดล GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "อัปโหลดไฟล์",
+	"Upload Pipeline": "อัปโหลดพายป์ไลน์",
+	"Upload Progress": "ความคืบหน้าการอัปโหลด",
+	"URL": "",
+	"URL Mode": "โหมด URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "ใช้ Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "ใช้ตัวย่อ",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "ผู้ใช้",
+	"User": "",
+	"User location successfully retrieved.": "ดึงตำแหน่งที่ตั้งของผู้ใช้เรียบร้อยแล้ว",
+	"Username": "",
+	"Users": "ผู้ใช้",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "ใช้",
+	"Valid time units:": "หน่วยเวลาใช้ได้:",
+	"Valves": "วาล์ว",
+	"Valves updated": "วาล์วที่อัปเดตแล้ว",
+	"Valves updated successfully": "อัปเดตวาล์วเรียบร้อยแล้ว",
+	"variable": "ตัวแปร",
+	"variable to have them replaced with clipboard content.": "ตัวแปรเพื่อให้แทนที่ด้วยเนื้อหาคลิปบอร์ด",
+	"Version": "เวอร์ชัน",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "เสียง",
+	"Voice Input": "",
+	"Warning": "คำเตือน",
+	"Warning:": "คำเตือน:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "คำเตือน: หากคุณอัปเดตหรือเปลี่ยนโมเดลการฝัง คุณจะต้องนำเข้าเอกสารทั้งหมดอีกครั้ง",
+	"Web": "เว็บ",
+	"Web API": "เว็บ API",
+	"Web Loader Settings": "การตั้งค่าเว็บโหลดเดอร์",
+	"Web Search": "การค้นหาเว็บ",
+	"Web Search Engine": "เครื่องมือค้นหาเว็บ",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL ของ Webhook",
+	"WebUI Settings": "การตั้งค่า WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "มีอะไรใหม่ใน",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (โลคอล)",
+	"Why?": "",
+	"Widescreen Mode": "โหมดหน้าจอกว้าง",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "พื้นที่ทำงาน",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "เขียนคำแนะนำพรอมต์ (เช่น คุณคือใคร?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "เขียนสรุปใน 50 คำที่สรุป [หัวข้อหรือคำสำคัญ]",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "เมื่อวาน",
+	"You": "คุณ",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "คุณสามารถปรับแต่งการโต้ตอบของคุณกับ LLMs โดยเพิ่มความทรงจำผ่านปุ่ม 'จัดการ' ด้านล่าง ทำให้มันมีประโยชน์และเหมาะกับคุณมากขึ้น",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "คุณไม่มีการสนทนาที่เก็บถาวร",
+	"You have shared this chat": "คุณได้แชร์แชทนี้แล้ว",
+	"You're a helpful assistant.": "คุณคือผู้ช่วยที่มีประโยชน์",
+	"You're now logged in.": "คุณเข้าสู่ระบบแล้ว",
+	"Your account status is currently pending activation.": "สถานะบัญชีของคุณกำลังรอการเปิดใช้งาน",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "การสนับสนุนทั้งหมดของคุณจะไปยังนักพัฒนาปลั๊กอินโดยตรง; Open WebUI ไม่รับส่วนแบ่งใด ๆ อย่างไรก็ตาม แพลตฟอร์มการระดมทุนที่เลือกอาจมีค่าธรรมเนียมของตัวเอง",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "การตั้งค่าโหลดเดอร์ Youtube"
+}
diff --git a/src/lib/i18n/locales/tk-TM/translation.json b/src/lib/i18n/locales/tk-TM/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..c065f8789d2989db3511b30a908230411a028723
--- /dev/null
+++ b/src/lib/i18n/locales/tk-TM/translation.json
@@ -0,0 +1,716 @@
+{
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' ýa-da '-1' möhlet ýok.",
+	"(Beta)": "(Beta)",
+	"(e.g. `sh webui.sh --api`)": "(meselem, `sh webui.sh --api`)",
+	"(latest)": "(iň soňky)",
+	"{{ models }}": "{{ modeller }}",
+	"{{ owner }}: You cannot delete a base model": "{{ owner }}: Esasy modeli öçürip bilmersiňiz",
+	"{{modelName}} is thinking...": "{{modelName}} pikirlenýär...",
+	"{{user}}'s Chats": "{{user}}'iň Çatlary",
+	"{{webUIName}} Backend Required": "{{webUIName}} Backend Zerur",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Çatlar we web gözleg soraglary üçin başlyk döretmek ýaly wezipeleri ýerine ýetirýän wagty ulanylýar",
+	"a user": "ulanyjy",
+	"About": "Barada",
+	"Account": "Hasap",
+	"Accurate information": "Takyk maglumat",
+	"Add": "Goş",
+	"Add a model id": "Model ID goş",
+	"Add a short description about what this model does": "Bu modeliň näme edýändigi barada gysgaça düşündiriş goşuň",
+	"Add a short title for this prompt": "Bu düşündiriş üçin gysga başlyk goşuň",
+	"Add a tag": "Bir tag goşuň",
+	"Add custom prompt": "Özboluşly düşündiriş goşuň",
+	"Add Docs": "Resminamalar goş",
+	"Add Files": "Faýllar goş",
+	"Add Memory": "Ýat goş",
+	"Add message": "Habar goş",
+	"Add Model": "Model goş",
+	"Add Tags": "Taglar goş",
+	"Add User": "Ulanyjy goş",
+	"Adjusting these settings will apply changes universally to all users.": "Bu sazlamalary düzetmek ähli ulanyjylara birmeňzeş üýtgeşmeler girizer.",
+	"admin": "admin",
+	"Admin Panel": "Admin Paneli",
+	"Admin Settings": "Admin Sazlamalary",
+	"Advanced Parameters": "Ösen Parametrler",
+	"Advanced Params": "Ösen Parametrler",
+	"all": "ähli",
+	"All Documents": "Ähli Resminamalar",
+	"All Users": "Ähli Ulanyjylar",
+	"Allow": "Rugsat ber",
+	"Allow Chat Deletion": "Çaty öçürmäge rugsat ber",
+	"alphanumeric characters and hyphens": "harply-sanjy belgiler we defisler",
+	"Already have an account?": "Hasabyňyz barmy?",
+	"an assistant": "kömekçi",
+	"An error occurred while processing files.": "",
+	"and": "we",
+	"and create a new shared link.": "we täze paýlaşylan baglanyşyk dörediň.",
+	"API Base URL": "API Esasy URL",
+	"API Key": "API Açar",
+	"API Key created.": "API Açar döredildi.",
+	"API keys": "API açarlary",
+	"April": "Aprel",
+	"Archive": "Arhiw",
+	"Archive All Chats": "Ähli Çatlary Arhiwle",
+	"Archived Chats": "Arhiwlenen Çatlar",
+	"are allowed - Activate this command by typing": "rugsat berilýär - bu buýrugy ýazyň",
+	"Are you sure?": "Kepillikmi?",
+	"Attach file": "Faýl goş",
+	"Attention to detail": "Detala üns",
+	"Audio": "Audio",
+	"August": "Awgust",
+	"Auto-playback response": "Awto-gaýtadan jogap",
+	"Auto-send input after 3 sec.": "3 sekuntdan soň awtomatiki ugrat",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Esasy URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Esasy URL zerur.",
+	"available!": "elýeterli!",
+	"Back": "Yzyna",
+	"Bad Response": "Erbet Jogap",
+	"Banners": "Bannerler",
+	"Base Model (From)": "Esasy Model (Kimden)",
+	"before": "öň",
+	"Being lazy": "Ýaltalyk",
+	"Brave Search API Key": "Brave Gözleg API Açar",
+	"Bypass SSL verification for Websites": "Web sahypalary üçin SSL barlagyny geçmek",
+	"Cancel": "Ýatyrmak",
+	"Capabilities": "Ukyplar",
+	"Change Password": "Paroly Üýtget",
+	"Chat": "Çat",
+	"Chat Bubble UI": "Çat Bubble UI",
+	"Chat direction": "Çat ugrukdyryş",
+	"Chat History": "Çat Taryhy",
+	"Chat History is off for this browser.": "Bu brauzer üçin Çat Taryhy öçürildi.",
+	"Chats": "Çatlar",
+	"Check Again": "Ýene Barla",
+	"Check for updates": "Täzelenmeleri barla",
+	"Checking for updates...": "Täzelenmeleri barlamak...",
+	"Choose a model before saving...": "Saklamazdan ozal model saýlaň...",
+	"Chunk Overlap": "Bölüm Aşyrmasy",
+	"Chunk Params": "Bölüm Parametrleri",
+	"Chunk Size": "Bölüm Ölçegi",
+	"Citation": "Sitata",
+	"Click here for help.": "Kömek üçin şu ýere basyň.",
+	"Click here to": "Şu ýere basyň",
+	"Click here to select": "Saýlamak üçin şu ýere basyň",
+	"Click here to select a csv file.": "CSV faýly saýlamak üçin şu ýere basyň.",
+	"Click here to select documents.": "Resminamalary saýlamak üçin şu ýere basyň.",
+	"click here.": "şu ýere basyň.",
+	"Click on the user role button to change a user's role.": "Ulanyjynyň roluny üýtgetmek üçin ulanyjy roly düwmesine basyň.",
+	"Clone": "Klon",
+	"Close": "Ýap",
+	"Collection": "Kolleksiýa",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Esasy URL",
+	"ComfyUI Base URL is required.": "ComfyUI Esasy URL zerur.",
+	"Command": "Buýruk",
+	"Concurrent Requests": "Meňzeş Haýyşlar",
+	"Confirm Password": "Paroly Tassyklap",
+	"Connections": "Baglanyşyklar",
+	"Content": "Mazmuny",
+	"Context Length": "Kontekst Uzynlygy",
+	"Continue Response": "Jogap Bermegi Dowam et",
+	"Conversation Mode": "Söhbet Reseimi",
+	"Copied shared chat URL to clipboard!": "Paýlaşylan çat URL buferine göçürildi!",
+	"Copy": "Göçür",
+	"Copy last code block": "Soňky kod blokyny göçür",
+	"Copy last response": "Soňky jogaby göçür",
+	"Copy Link": "Baglanyşygy Göçür",
+	"Copying to clipboard was successful!": "Buferine göçürmek üstünlikli boldy!",
+	"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Aşakdaky sorag üçin 3-5 sözden ybarat gysgaça söz düzümi dörediň, 3-5 söz çäklerine berk eýeriň we 'başlyk' sözüni ulanmaň:",
+	"Create a model": "Model döret",
+	"Create Account": "Hasap döret",
+	"Create new key": "Täze açar döret",
+	"Create new secret key": "Täze gizlin açar döret",
+	"Created at": "Döredilen wagty",
+	"Created At": "Döredilen wagty",
+	"Create a knowledge base": "",
+	"Current Model": "Häzirki Model",
+	"Current Password": "Häzirki Parol",
+	"Custom": "Özboluşly",
+	"Customize models for a specific purpose": "Anyk maksat üçin modelleri düzmek",
+	"Dark": "Garaňky",
+	"Database": "Mazada",
+	"December": "Dekabr",
+	"Default": "Nokatlaýyn",
+	"Default (Automatic1111)": "Nokatlaýyn (Automatic1111)",
+	"Default (SentenceTransformers)": "Nokatlaýyn (SentenceTransformers)",
+	"Default (Web API)": "Nokatlaýyn (Web API)",
+	"Default Model": "Nokatlaýyn Model",
+	"Default model set successfully!": "Nokatlaýyn model üstünlikli gurnaldy!",
+	"Default Temperature": "Nokatlaýyn Temperaturasy",
+	"Define what this key is used for...": "Bu açaryň näme üçin ulanylýandygyny kesgitle...",
+	"Delete": "Öçür",
+	"Delete Account": "Hasaby Öçür",
+	"Delete Account Forever": "Hasaby Möhletsyz Öçür",
+	"Delete all": "Ählisini öçür",
+	"Delete All Chats": "Ähli Çatlary Öçür",
+	"Delete this Document": "Bu Resminamany Öçür",
+	"Deleted": "Öçürilen",
+	"Deleted Forever": "Möhletsyz Öçürilen",
+	"Description": "Düşündiriş",
+	"Describe your knowledge base and objectives": "",
+	"Developers": "Öndürijiler",
+	"Directory": "Katalog",
+	"Directory Type": "Katalog Typy",
+	"Disable": "Ýatyrmak",
+	"Disabled": "Ýatyrylan",
+	"Disconnected": "Baglanşyk kesildi",
+	"Display": "Görkeziş",
+	"Display Text": "Teksti Görkeziş",
+	"Document": "Resminama",
+	"Document File": "Resminama Faýly",
+	"Document Name": "Resminama Ady",
+	"Documents": "Resminamalar",
+	"Done": "Tamam",
+	"Downloading updates...": "Täzelenmeleri ýükläp...",
+	"Drag and drop files here": "Faýllary şu ýere süýräň we goýuň",
+	"Drop your .json, .txt, .csv or .md files here": "JSON, TXT, CSV ýa-da MD faýllaryňyzy şu ýere goýuň",
+	"Duplicate": "Göçür",
+	"Each request will contain a max of n documents": "Her haýyş n sany resminama bilen çäklenýär",
+	"Edit": "Redaktirle",
+	"Edit Labels": "Bellikleri Redaktirle",
+	"Edit message": "Habary Redaktirle",
+	"Edit Parameters": "Parametrleri Redaktirle",
+	"Email": "Email",
+	"Email Sent": "Email Iberildi",
+	"Enable": "Işjeňleşdir",
+	"Enable Character AI": "Häsiýetli AI Işjeňleşdir",
+	"Enable Web Search": "Web Gözlegini Işjeňleşdir",
+	"Enabled": "Işjeň",
+	"Encrypt messages": "Habarlar kodlansyn",
+	"Enter your email": "Email giriziň",
+	"Entries": "Girişler",
+	"Environment": "Daşky Gurşaw",
+	"Error": "Ýalňyşlyk",
+	"Error (400)": "Ýalňyşlyk (400)",
+	"Error (403)": "Ýalňyşlyk (403)",
+	"Error (404)": "Ýalňyşlyk (404)",
+	"Error (500)": "Ýalňyşlyk (500)",
+	"Error occurred": "Ýalňyşlyk ýüze çykdy",
+	"Example": "Mysal",
+	"Example(s)": "Mysal(lar)",
+	"Examples": "Mysallar",
+	"Explain a concept": "Bir konsepsiýany düşündiriň",
+	"Explore": "Barla",
+	"Export": "Eksport",
+	"Export All Data": "Ähli Maglumatlary Eksportla",
+	"Export data": "Maglumatlary Eksportla",
+	"External API": "Daşarky API",
+	"External API Endpoint": "Daşarky API Nokady",
+	"External ID": "Daşarky ID",
+	"Extra Models": "Goşmaça Modeller",
+	"Failed": "Netijesiz",
+	"Fallback": "Düşmek",
+	"False": "Ýalňyş",
+	"Family name": "Maşgala ady",
+	"FAQ": "Sorag-jogaplar",
+	"February": "Fewral",
+	"Field": "Meýdan",
+	"File": "Faýl",
+	"File Name": "Faýl Ady",
+	"File Type": "Faýl Typy",
+	"Files": "Faýllar",
+	"Fill out all required fields.": "Ähli zerur meýdanlary dolduryň.",
+	"Filter": "Süzgüç",
+	"Find your API Key here": "API Açaryňyzy şu ýerden tapyň",
+	"First name": "Ady",
+	"Following parameters are available in the command and are mandatory to specify:": "Buýruga degişli aşakdaky parametrler elýeterlidir we görkezmek hökmandyr:",
+	"For": "Üçin",
+	"Forever": "Möhletsyz",
+	"Forgot Password?": "Paroly unutdyňyzmy?",
+	"Forgot your password?": "Paroly unutdyňyzmy?",
+	"Free and Paid plans available": "Mugt we Tölegli planlar elýeterli",
+	"Friday": "Anna",
+	"From": "Kimden",
+	"Full Access": "Doly Elýeterlilik",
+	"Full Name": "Doly Ady",
+	"Full name": "Doly ady",
+	"Functions": "Funksiýalar",
+	"General": "Umumy",
+	"Generate": "Döret",
+	"Generate email templates": "Email şablonlaryny döret",
+	"Generate from a template": "Şablondan döret",
+	"Generate high-quality images": "Ýokary hilli suratlar döret",
+	"Generate professional profile descriptions": "Professional profil düşündirişleri döret",
+	"Generate prompts for language models": "Dil modelleri üçin düşündirişleri döret",
+	"Generate realistic photos": "Hakyky suratlary döret",
+	"Generate transcripts of audio and video": "Audio we wideo transkriptlerini döret",
+	"Generate translations": "Terjimeler döret",
+	"Generated from API keys": "API açarlaryndan döredildi",
+	"Generating...": "Döredilýär...",
+	"Generator": "Generator",
+	"Get Started": "Başlaň",
+	"Go": "Git",
+	"Go Back": "Yzyna Git",
+	"Go to": "Git",
+	"Go to link": "Baglanyşyga Git",
+	"Group": "Topar",
+	"Guidance": "Gollama",
+	"has been changed successfully": "üstünlikli üýtgedildi",
+	"have been changed successfully": "üstünlikli üýtgedildi",
+	"Hello": "Salam",
+	"Help": "Kömek",
+	"Hide": "Gizle",
+	"Hide all chats": "Ähli çatlary gizle",
+	"Hide Model": "Modeli Gizle",
+	"Hide Sidebar": "Gapdal Paneli Gizle",
+	"Hide Toolbar": "Gurallar Panelini Gizle",
+	"High Quality": "Ýokary Hilli",
+	"Home": "Baş Sahypa",
+	"Hours": "Sagady",
+	"Human-like responses": "Adam görnüşli jogaplar",
+	"Humorous": "Gülkünç",
+	"Identify objects in an image": "Suratda zatlary tanamak",
+	"If you need to quickly translate text from one language to another, use translation prompts.": "Bir dilden beýlekisine tekst terjime etmeli bolsaňyz, terjime düşündirişlerini ulanyň.",
+	"If you want to delete the account and all associated data, select": "Hasaby we degişli ähli maglumatlary öçürmek isleseňiz, saýlaň",
+	"If you're facing issues, try again later.": "Meseleler bar bolsa, soňra täzeden synanyşyň.",
+	"Image": "Surat",
+	"Image Generation": "Surat Döretme",
+	"Import": "Import",
+	"Import Data": "Maglumatlary Importla",
+	"In Progress": "Dowam edýär",
+	"Inactivity Timeout": "Işjeňsiz Töhmet",
+	"Incorrect email or password.": "Nädogry email ýa-da parol.",
+	"Info": "Maglumat",
+	"Information": "Maglumat",
+	"Information updated successfully!": "Maglumat üstünlikli täzelendi!",
+	"Input": "Girdi",
+	"Input Parameters": "Girdi Parametrleri",
+	"Installation Guide": "Gurnama Gollanma",
+	"Integrate custom models": "Özboluşly modelleri integrirle",
+	"Integrate with an external API": "Daşarky API bilen integrirle",
+	"Integration": "Integrasiýa",
+	"Internet Required": "Internet Zerur",
+	"Invalid API key.": "Nädogry API açar.",
+	"Invalid credentials, try again.": "Nädogry maglumatlar, täzeden synanyşyň.",
+	"Invalid or expired API key.": "Nädogry ýa-da möhleti geçen API açar.",
+	"It looks like we encountered an error. Please try again.": "Ýalňyşlyk ýüze çykdy. Täzeden synanyşyň.",
+	"January": "Ýanwar",
+	"Job title": "Iş Ady",
+	"Join": "Goşul",
+	"Join Date": "Goşulma Senesi",
+	"July": "Iýul",
+	"June": "Iýun",
+	"Just now": "Just now",
+	"Key": "Açar",
+	"Key (hidden)": "Açar (gizlin)",
+	"Key Details": "Açar Maglumatlar",
+	"Key Management": "Açar Dolandyryşy",
+	"Language": "Dil",
+	"Language Model": "Dil Modeli",
+	"Last access": "Soňky elýeterlilik",
+	"Last Access": "Soňky elýeterlilik",
+	"Last edited": "Soňky redaktirlenen",
+	"Last modified": "Soňky üýtgedilen",
+	"Last Modified": "Soňky üýtgedilen",
+	"Last name": "Familiýasy",
+	"Last update": "Soňky täzelenme",
+	"Last updated": "Soňky täzelenen",
+	"Later": "Soň",
+	"Launch": "Gur",
+	"Learn": "Öwren",
+	"Learn More": "Has köp öwreniň",
+	"License": "Rugsat",
+	"Light": "Açyk",
+	"Link": "Baglanyşyk",
+	"Link expired": "Baglanyşygyň möhleti geçdi",
+	"Load": "Ýükle",
+	"Loading": "Ýüklenýär",
+	"Local Models": "Ýerli Modeller",
+	"Log out": "Çyk",
+	"Logged out": "Çykdy",
+	"Logged out successfully": "Üstünlikli çykdy",
+	"Login": "Giriş",
+	"Login Required": "Giriş Zerur",
+	"Logs": "Loglar",
+	"Low": "Pes",
+	"Low Quality": "Pes Hilli",
+	"Maintain custom codebase": "Özboluşly kod bazasyny sakla",
+	"Management": "Dolandyryş",
+	"Manual Input": "El bilen Girdi",
+	"March": "Mart",
+	"Max File Count": "",
+	"Max File Size(MB)": "",
+	"Mark as Read": "Okalan hökmünde belläň",
+	"Match": "Gab",
+	"May": "Maý",
+	"Memory": "Ýat",
+	"Memory saved": "Ýat saklanyldy",
+	"Menu": "Menýu",
+	"Message": "Habar",
+	"Message limit reached for today. Please wait until tomorrow.": "Bu günki habar çägi geçdi. Ertir garaşyň.",
+	"Messages": "Habarlar",
+	"Method": "Usul",
+	"Microphone": "Mikrofon",
+	"Minute": "Minut",
+	"Minutes": "Minutlar",
+	"Model": "Model",
+	"Model Details": "Model Maglumatlary",
+	"Model History": "Model Taryhy",
+	"Model Management": "Model Dolandyryşy",
+	"Model name": "Model ady",
+	"Model URL": "Model URL",
+	"Mode": "Reseimi",
+	"Moderate Quality": "Orta Hilli",
+	"Modified": "Üýtgedilen",
+	"Modify User": "Ulanyjyny Üýtget",
+	"Monday": "Duşenbe",
+	"Monetization": "Pul gazanmak",
+	"Month": "Aý",
+	"More": "Has köp",
+	"More Info": "Has köp Maglumat",
+	"More options": "Has köp opsiýalar",
+	"Most Recent": "Iň Täze",
+	"Multiple file import is limited to": "Köp faýl importy çäkli",
+	"Name": "Ady",
+	"Name your knowledge base": "",
+	"Name (hidden)": "Ady (gizlin)",
+	"Name is required": "Ady zerur",
+	"Navigate": "Gez",
+	"Need help?": "Kömek gerekmi?",
+	"New": "Täze",
+	"New Key": "Täze Açar",
+	"New Label": "Täze Bellik",
+	"New Password": "Täze Parol",
+	"New Secret Key": "Täze Gizlin Açar",
+	"New User": "Täze Ulanyjy",
+	"Next": "Indiki",
+	"No": "Ýok",
+	"No access": "Elýeterlilik ýok",
+	"No access.": "Elýeterlilik ýok.",
+	"No admins": "Adminler ýok",
+	"No archived chats": "Arhiwlenen çatlar ýok",
+	"No data found": "Maglumat tapylmady",
+	"No models available": "Modeller elýeterli däl",
+	"No permission to add a model.": "Model goşmak üçin rugsat ýok.",
+	"No permission to archive chat.": "Çaty arhiwlemek üçin rugsat ýok.",
+	"No permission to create chat.": "Çat döretmek üçin rugsat ýok.",
+	"No permission to delete chat.": "Çaty öçürmek üçin rugsat ýok.",
+	"No permission to edit chat.": "Çaty redaktirlemek üçin rugsat ýok.",
+	"No permission to view chat.": "Çaty görmek üçin rugsat ýok.",
+	"No shared chats": "Paýlaşylan çatlar ýok",
+	"No usage": "Ulanyş ýok",
+	"Non-admin users can only view chat details": "Admin däl ulanyjylar diňe çat maglumatlaryny görüp bilerler",
+	"None": "Hiç",
+	"Not Found": "Tapylmady",
+	"Not started": "Başlanmady",
+	"November": "Noýabr",
+	"October": "Oktýabr",
+	"Okay": "Bolýar",
+	"On": "Işjeň",
+	"Once you have added and configured your model, it will appear in the model dropdown list on the chat screen.": "Model goşup we konfigurirleýänden soň, çat ekranynda model aşak düşýän sanawda peýda bolar.",
+	"Only": "Diňe",
+	"Open": "Aç",
+	"OpenAI Base URL": "OpenAI Esasy URL",
+	"OpenAI Key": "OpenAI Açar",
+	"OpenAI Model": "OpenAI Model",
+	"OpenAI Token": "OpenAI Token",
+	"OpenAI URL": "OpenAI URL",
+	"Options": "Opsiýalar",
+	"Other": "Başga",
+	"Other Parameters": "Başga Parametrler",
+	"Owner": "Eýesi",
+	"Owner ID": "Eýesi ID",
+	"Page": "Sahypa",
+	"Parameter": "Parametr",
+	"Parameters": "Parametrler",
+	"Password": "Parol",
+	"Password must be at least 8 characters long": "Parol iň azyndan 8 harp bolmaly",
+	"Password must include at least one number and one letter": "Parol iň azyndan bir san we bir harp bolmaly",
+	"Paste copied text here...": "Göçürilen tekst şu ýere goýuň...",
+	"Paste text here...": "Tekst şu ýere goýuň...",
+	"PDF": "PDF",
+	"PDF Generation": "PDF Döretme",
+	"Pending": "Garaşylýar",
+	"Permission": "Rugsat",
+	"Personal Information": "Şahsy Maglumat",
+	"Photo": "Surat",
+	"Photos": "Suratlar",
+	"Please add a model.": "Model goşuň.",
+	"Please add more content": "Köp mazmun goşuň",
+	"Please enter your email to reset your password": "Parolyňyzy täzeden goýmak üçin email giriziň",
+	"Please try again later": "Soňra täzeden synanyşyň",
+	"Plugin": "Plagin",
+	"Plugin Settings": "Plagin Sazlamalary",
+	"Position": "Ýerleşýän ýeri",
+	"Post": "Post",
+	"Potential Risks": "Mümkin Töwekgelçilikler",
+	"Preparing your data...": "Maglumatlaryňyzy taýýarlaýar...",
+	"Preprocessing...": "Deslapky işlem...",
+	"Preview": "Öň-üşürgi",
+	"Previous": "Öňki",
+	"Print": "Çap et",
+	"Privacy Policy": "Gizlinlik Syýasaty",
+	"Processing": "Işlenýär",
+	"Profile": "Profil",
+	"Prompt": "Düşündiriş",
+	"Prompts": "Düşündirişler",
+	"Public": "Jemgyýetçilik",
+	"Quality": "Hil",
+	"Quantity": "Mukdar",
+	"Quick Start": "Çalt Başla",
+	"Read More": "Has köp oka",
+	"Realistic": "Hakyky",
+	"Recent": "Täze",
+	"Recent Access": "Täze Elýeterlilik",
+	"Recent Chats": "Täze Çatlar",
+	"Recent Documents": "Täze Resminamalar",
+	"Recent Files": "Täze Faýllar",
+	"Recipient": "Alyjy",
+	"Recognize speech and convert it to text": "Gürleýişi tanap tekste öwrüň",
+	"Records": "Ýazgylar",
+	"Reference": "Salgy",
+	"Refresh": "Täzeläň",
+	"Registration Date": "Hasaba alynma Senesi",
+	"Remove": "Aýyr",
+	"Remove Model": "Modeli Aýyr",
+	"Remove user": "Ulanyjyny aýyr",
+	"Rename": "Adyny Üýtget",
+	"Reorder": "Gaýtadan Sargyt Et",
+	"Request": "Haýyş",
+	"Required": "Zerur",
+	"Reset": "Täzeden Guruň",
+	"Reset Password": "Paroly Täzeden Guruň",
+	"Resources": "Resurslar",
+	"Response": "Jogap",
+	"Restored": "Dikeldilen",
+	"Results": "Netijeler",
+	"Review": "Syn",
+	"Review Prompt": "Düşündirişi Synla",
+	"Reviews": "Synlar",
+	"Role": "Roli",
+	"Save": "Sakla",
+	"Save Changes": "Üýtgeşmeleri Sakla",
+	"Saved": "Saklanan",
+	"Saturday": "Şenbe",
+	"Scale": "Şkalasy",
+	"Scan the code": "Kody Skanirle",
+	"Search": "Gözleg",
+	"Search All Chats": "Ähli Çatlary Gözle",
+	"Search for documents": "Resminamalary Gözle",
+	"Search for models": "Modelleri Gözle",
+	"Search for users": "Ulanyjylary Gözle",
+	"Search in chat": "Çatda gözle",
+	"Search query": "Gözleg soragy",
+	"Search options": "",
+	"Search...": "Gözleg...",
+	"Secret Key": "Gizlin Açar",
+	"See more": "Has köp gör",
+	"Select": "Saýla",
+	"Select a model": "Bir model saýla",
+	"Select a role": "Roli saýla",
+	"Select Chat": "Çat saýla",
+	"Select File": "Faýl saýla",
+	"Select Label": "Belligi saýla",
+	"Send": "Iber",
+	"Send and receive messages": "Habar iber we kabul et",
+	"Send Message": "Habar Iber",
+	"Sent": "Iberilen",
+	"Separate each entry with a new line": "Her girişi täze setir bilen aýraň",
+	"Separate multiple entries with a comma or new line": "Birnäçe girişi üzgüç ýa-da täze setir bilen aýraň",
+	"September": "Sentýabr",
+	"Server error": "Serwer ýalňyşlygy",
+	"Service Unavailable": "Hyzmat Elýeterli Däl",
+	"Session": "Sessia",
+	"Settings": "Sazlamalar",
+	"Setup": "Gurnama",
+	"Share": "Paýlaş",
+	"Share Chat": "Çaty Paýlaş",
+	"Share this chat with others": "Bu çaty beýlekiler bilen paýlaş",
+	"Shared": "Paýlaşylan",
+	"Shared Chats": "Paýlaşylan Çatlar",
+	"Show": "Görkez",
+	"Show all": "Ählisini görkez",
+	"Show all labels": "Ähli belligi görkez",
+	"Show All Prompts": "Ähli Düşündirişleri Görkez",
+	"Show API Keys": "API Açarlaryny Görkez",
+	"Show Model": "Modeli Görkez",
+	"Show Sidebar": "Gapdal Paneli Görkez",
+	"Show toolbar": "Gurallar Panelini Görkez",
+	"Sign In": "Giriş",
+	"Sign Out": "Çyk",
+	"Sign Up": "Hasaba al",
+	"Sign up": "Hasaba al",
+	"Sign up to get started": "Başlamak üçin hasaba alyň",
+	"Simple and advanced options available": "Ýönekeý we kämilleşdirilen opsiýalar elýeterli",
+	"Simply follow the guide to get started.": "Başlamak üçin görkezmä eýeriň.",
+	"Skip": "Geç",
+	"Smart Completion": "Akyldar Tamamlama",
+	"Software": "Programma üpjünçiligi",
+	"Sorry, an error occurred while processing your request.": "Bagyşlaň, haýyşyňyzy işlemekde ýalňyşlyk ýüze çykdy.",
+	"Sorry, the page you are looking for does not exist.": "Bagyşlaň, gözleýän sahypaňyz ýok.",
+	"Sorry, this link has expired.": "Bagyşlaň, bu baglanyşygyň möhleti geçdi.",
+	"Source": "Çeşme",
+	"Source Language": "Çeşme Dili",
+	"Space": "Ýer",
+	"Special Characters": "Aýratyn Harplar",
+	"Specify the model type": "Model typyny kesgitle",
+	"Standard": "Standart",
+	"Start": "Başla",
+	"Start a New Chat": "Täze Çat Başla",
+	"Start Chat": "Çat Başla",
+	"Start Date": "Başlangyç Sene",
+	"Start Time": "Başlanýan wagt",
+	"Started": "Başlandy",
+	"Status": "Ýagdaýy",
+	"Stop": "Bes et",
+	"Store your data securely": "Maglumatlaryňyzy howpsuz saklaň",
+	"Subject": "Tema",
+	"Submit": "Tabşyr",
+	"Success": "Üstünlik",
+	"Summary": "Jemleýji",
+	"Sunday": "Ýekşenbe",
+	"Support": "Goldaw",
+	"Switch to another model": "Başga modele geçiň",
+	"Switch to another model type": "Başga model typyna geçiň",
+	"System": "Sistema",
+	"System Requirements": "Sistema Talaplary",
+	"Table": "Jadwal",
+	"Tag": "Bellik",
+	"Tag List": "Bellik Sanawy",
+	"Take a tour": "Syýahat et",
+	"Talk to your data": "Maglumatlaryňyz bilen gürleşiň",
+	"Target Language": "Maksat Dili",
+	"Team": "Topar",
+	"Template": "Şablon",
+	"Templates": "Şablonlar",
+	"Temporary": "Wagtylaýyn",
+	"Test": "Synag",
+	"Text": "Tekst",
+	"Text Generation": "Tekst Döretme",
+	"Text to Image": "Tekstden Surat",
+	"Text to Speech": "Tekstden Söz",
+	"The data you need to integrate is currently unavailable.": "Integrirlemeli maglumatlaryňyz häzirki wagtda elýeterli däl.",
+	"The model has been added successfully!": "Model üstünlikli goşuldy!",
+	"The model type you are trying to add already exists.": "Goşmak isleýän model typyňyz eýýäm bar.",
+	"The page you requested could not be found.": "Soranyňyz sahypa tapylmady.",
+	"There are no prompts available at the moment.": "Häzirlikçe düşündirişler elýeterli däl.",
+	"There was an error adding the model.": "Model goşulmakda ýalňyşlyk ýüze çykdy.",
+	"There was an error deleting the model.": "Modeli öçürmekde ýalňyşlyk ýüze çykdy.",
+	"There was an error loading the models.": "Modelleri ýüklemekde ýalňyşlyk ýüze çykdy.",
+	"There was an error updating the model.": "Modeli täzeläp bolmady.",
+	"There was an error while archiving the chat.": "Çaty arhiwlemekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while creating the chat.": "Çat döretmekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while deleting the chat.": "Çaty öçürmekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while editing the chat.": "Çaty redaktirlemekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while fetching the chat.": "Çaty getirmekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while saving the data.": "Maglumatlary saklamakda ýalňyşlyk ýüze çykdy.",
+	"There was an error while sending the message.": "Habary ibermekde ýalňyşlyk ýüze çykdy.",
+	"There was an error while updating the user.": "Ulanyjyny täzeläp bolmady.",
+	"These settings are global and will apply to all users and models.": "Bu sazlamalar umumy we ähli ulanyjylara we modellere degişlidir.",
+	"This action cannot be undone.": "Bu hereket yzyna dolanylyp bilinmez.",
+	"This email is already in use.": "Bu email eýýäm ulanylýar.",
+	"This email is not registered.": "Bu email hasaba alynmady.",
+	"This is the end of the chat": "Bu çatyň soňy",
+	"This link is expired or invalid.": "Bu baglanyşygyň möhleti geçdi ýa-da nädogry.",
+	"This model is already integrated.": "Bu model eýýäm integrirlenen.",
+	"This page does not exist.": "Bu sahypa ýok.",
+	"This will remove the user from the system.": "Bu ulanyjyny sistemadan aýyrar.",
+	"Thursday": "Penşenbe",
+	"Time": "Wagt",
+	"Time Limit Exceeded": "Wagt Limiti Geçdi",
+	"Timezone": "Wagt zolak",
+	"Title": "Ady",
+	"Today": "Şu gün",
+	"Token": "Token",
+	"Token limit exceeded.": "Token çägi geçdi.",
+	"Token or URL is incorrect.": "Token ýa-da URL nädogry.",
+	"Too many requests. Please try again later.": "Örän köp haýyşlar. Soňra täzeden synanyşyň.",
+	"Total Chats": "Jemi Çatlar",
+	"Total Memory": "Jemi Ýat",
+	"Total Storage": "Jemi Sakla",
+	"Total Users": "Jemi Ulanyjylar",
+	"Training": "Okuw",
+	"Tuesday": "Sişenbe",
+	"Type": "Typ",
+	"Unable to archive the chat.": "Çaty arhiwläp bolmady.",
+	"Unable to change the model.": "Modeli üýtgedip bolmady.",
+	"Unable to complete the request.": "Haýyşy tamamlap bolmady.",
+	"Unable to create chat.": "Çat döretmek mümkin däl.",
+	"Unable to delete the model.": "Modeli öçürmek mümkin däl.",
+	"Unable to delete the user.": "Ulanyjyny öçürmek mümkin däl.",
+	"Unable to find the requested resource.": "Soralan resurs tapylmady.",
+	"Unable to import data.": "Maglumatlary import edip bolmady.",
+	"Unable to load the chat.": "Çaty ýüklemek mümkin däl.",
+	"Unable to load the settings.": "Sazlamalary ýüklemek mümkin däl.",
+	"Unable to login.": "Giriş mümkin däl.",
+	"Unable to process the request.": "Haýyşy işläp bolmady.",
+	"Unable to reset password.": "Paroly täzeden gurmak mümkin däl.",
+	"Unable to retrieve data.": "Maglumatlary almak mümkin däl.",
+	"Unable to save": "Saklap bolmady",
+	"Unable to save the data.": "Maglumatlary saklap bolmady.",
+	"Unable to update": "Täzeläp bolmady",
+	"Unable to update the model.": "Modeli täzeläp bolmady.",
+	"Unable to update the user.": "Ulanyjyny täzeläp bolmady.",
+	"Unauthorized": "Rugsatsyz",
+	"Undo": "Yza al",
+	"Unlink": "Baglanyşygy aýyr",
+	"Unread Messages": "Okalmadyk Habarlar",
+	"Unshare": "Paýlaşma",
+	"Unverified": "Tassyklanmadyk",
+	"Update": "Täzeläň",
+	"Update successful": "Üstünlikli täzelenme",
+	"Updated": "Täzelenen",
+	"Updated at": "Täzelendi",
+	"Upload": "Ýükle",
+	"Upload Data": "Maglumat Ýükle",
+	"Upload file": "Faýl ýükle",
+	"Uploading": "Ýüklenýär",
+	"Usage": "Ulanyş",
+	"User": "Ulanyjy",
+	"User added": "Ulanyjy goşuldy",
+	"User deleted": "Ulanyjy öçürildi",
+	"User does not exist": "Ulanyjy ýok",
+	"User Guide": "Ulanyjy Gollanmasy",
+	"User ID": "Ulanyjy ID",
+	"User Management": "Ulanyjy Dolandyryşy",
+	"User Role": "Ulanyjy Roli",
+	"Username": "Ulanyjy Ady",
+	"Username is required": "Ulanyjy ady zerur",
+	"Users": "Ulanyjylar",
+	"Value": "Gymmaty",
+	"Verify": "Tassykla",
+	"Version": "Wersiýasy",
+	"View": "Gör",
+	"View All": "Ählisini gör",
+	"View archived": "Arhiwlenenleri gör",
+	"View Details": "Maglumatlary Gör",
+	"Voice Input": "Ses Girdi",
+	"Voice Recording": "Ses Ýazgysy",
+	"Volume": "Göwrümi",
+	"Warning": "Duýduryş",
+	"Wednesday": "Çarşenbe",
+	"Welcome": "Hoş geldiňiz",
+	"Welcome to ChatGPT! How can I help you today?": "ChatGPT-e hoş geldiňiz! Size nähili kömek edip bilerin?",
+	"Welcome to our service!": "Hyzmatymyza hoş geldiňiz!",
+	"Welcome!": "Hoş geldiňiz!",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"Width": "Ini",
+	"Work": "Iş",
+	"Write": "Ýaz",
+	"Write a review": "Syn ýaz",
+	"Write code": "Kod ýazyň",
+	"Write content": "Mazmun ýazyň",
+	"Year": "Ýyl",
+	"Yes": "Hawa",
+	"Yesterday": "Düýn",
+	"You are not authorized to view this content.": "Bu mazmuny görmek üçin rugsadyňyz ýok.",
+	"You can only add a maximum of": "Diňe iň köpüniň",
+	"You can request access from your administrator": "Administratoryňyzdan elýeterlilik haýyş edip bilersiňiz",
+	"You have reached your usage limit for today.": "Bu günki ulanyş çägiňize ýetdiňiz.",
+	"You have to choose a model first": "Ilki bilen model saýlamaly",
+	"You need a valid email": "Dogrudan email gerek",
+	"You will need to log in again to view the updated content": "Täzelenen mazmuny görmek üçin täzeden girmeli bolarsyňyz",
+	"Your data is safe with us": "Maglumatlaryňyz bizde howpsuz",
+	"Your email": "Emailiňiz",
+	"Your email address": "Email adresiňiz",
+	"Your message": "Habaryňyz",
+	"Your name": "Adyňyz",
+	"Your new password": "Täze parolyňyz",
+	"Your password": "Parolyňyz",
+	"Your payment is successful.": "Tölegiňiz üstünlikli boldy.",
+	"Your session has expired. Please log in again.": "Sessiaňyz tamamlandy. Täzeden giriň.",
+	"Your username": "Ulanyjy adyňyz",
+	"You're offline.": "Offline.",
+	"You've reached your token limit for the day.": "Günüňize token çägiňize ýetdiňiz.",
+	"ZIP Code": "Poçta Kody"
+}
diff --git a/src/lib/i18n/locales/tk-TW/translation.json b/src/lib/i18n/locales/tk-TW/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..99575141a355d7de670082371a1667b3e022599f
--- /dev/null
+++ b/src/lib/i18n/locales/tk-TW/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "",
+	"(latest)": "",
+	"{{ models }}": "",
+	"{{user}}'s Chats": "",
+	"{{webUIName}} Backend Required": "",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "",
+	"a user": "",
+	"About": "",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "",
+	"Account Activation Pending": "",
+	"Accurate information": "",
+	"Actions": "",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "",
+	"Add": "",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "",
+	"Add a tag": "",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "",
+	"Add Files": "",
+	"Add Group": "",
+	"Add Memory": "",
+	"Add Model": "",
+	"Add Tag": "",
+	"Add Tags": "",
+	"Add text content": "",
+	"Add User": "",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "",
+	"admin": "",
+	"Admin": "",
+	"Admin Panel": "",
+	"Admin Settings": "",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
+	"Advanced Parameters": "",
+	"Advanced Params": "",
+	"All chats": "",
+	"All Documents": "",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "",
+	"Allow Temporary Chat": "",
+	"Allow User Location": "",
+	"Allow Voice Interruption in Call": "",
+	"Already have an account?": "",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "",
+	"and": "",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "",
+	"API Base URL": "",
+	"API Key": "",
+	"API Key created.": "",
+	"API keys": "",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "",
+	"Archive": "",
+	"Archive All Chats": "",
+	"Archived Chats": "",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "",
+	"Attention to detail": "",
+	"Attribute for Username": "",
+	"Audio": "",
+	"August": "",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "",
+	"Auto-playback response": "",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "",
+	"AUTOMATIC1111 Base URL is required.": "",
+	"Available list": "",
+	"available!": "",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "",
+	"Bad Response": "",
+	"Banners": "",
+	"Base Model (From)": "",
+	"Batch Size (num_batch)": "",
+	"before": "",
+	"Being lazy": "",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "",
+	"Call": "",
+	"Call feature is not supported when using Web STT engine": "",
+	"Camera": "",
+	"Cancel": "",
+	"Capabilities": "",
+	"Certificate Path": "",
+	"Change Password": "",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "",
+	"Chat Background Image": "",
+	"Chat Bubble UI": "",
+	"Chat Controls": "",
+	"Chat direction": "",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "",
+	"Check Again": "",
+	"Check for updates": "",
+	"Checking for updates...": "",
+	"Choose a model before saving...": "",
+	"Chunk Overlap": "",
+	"Chunk Params": "",
+	"Chunk Size": "",
+	"Ciphers": "",
+	"Citation": "",
+	"Clear memory": "",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "",
+	"Click here to": "",
+	"Click here to download user import template file.": "",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "",
+	"Click here to select a csv file.": "",
+	"Click here to select a py file.": "",
+	"Click here to upload a workflow.json file.": "",
+	"click here.": "",
+	"Click on the user role button to change a user's role.": "",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "",
+	"Clone": "",
+	"Close": "",
+	"Code execution": "",
+	"Code formatted successfully": "",
+	"Collection": "",
+	"Color": "",
+	"ComfyUI": "",
+	"ComfyUI Base URL": "",
+	"ComfyUI Base URL is required.": "",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "",
+	"Completions": "",
+	"Concurrent Requests": "",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "",
+	"Confirm Password": "",
+	"Confirm your action": "",
+	"Connections": "",
+	"Contact Admin for WebUI Access": "",
+	"Content": "",
+	"Content Extraction": "",
+	"Context Length": "",
+	"Continue Response": "",
+	"Continue with {{provider}}": "",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "",
+	"Copied shared chat URL to clipboard!": "",
+	"Copied to clipboard": "",
+	"Copy": "",
+	"Copy last code block": "",
+	"Copy last response": "",
+	"Copy Link": "",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "",
+	"Create Account": "",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "",
+	"Create new secret key": "",
+	"Created at": "",
+	"Created At": "",
+	"Created by": "",
+	"CSV Import": "",
+	"Current Model": "",
+	"Current Password": "",
+	"Custom": "",
+	"Dark": "",
+	"Database": "",
+	"December": "",
+	"Default": "",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "",
+	"Default Model": "",
+	"Default model updated": "",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "",
+	"Delete": "",
+	"Delete a model": "",
+	"Delete All Chats": "",
+	"Delete All Models": "",
+	"Delete chat": "",
+	"Delete Chat": "",
+	"Delete chat?": "",
+	"Delete folder?": "",
+	"Delete function?": "",
+	"Delete prompt?": "",
+	"delete this link": "",
+	"Delete tool?": "",
+	"Delete User": "",
+	"Deleted {{deleteModelTag}}": "",
+	"Deleted {{name}}": "",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "",
+	"Didn't fully follow instructions": "",
+	"Disabled": "",
+	"Discover a function": "",
+	"Discover a model": "",
+	"Discover a prompt": "",
+	"Discover a tool": "",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "",
+	"Discover, download, and explore custom prompts": "",
+	"Discover, download, and explore custom tools": "",
+	"Discover, download, and explore model presets": "",
+	"Dismissible": "",
+	"Display": "",
+	"Display Emoji in Call": "",
+	"Display the username instead of You in the Chat": "",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "",
+	"Do not install tools from sources you do not fully trust.": "",
+	"Document": "",
+	"Documentation": "",
+	"Documents": "",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "",
+	"Don't have an account?": "",
+	"don't install random functions from sources you don't trust.": "",
+	"don't install random tools from sources you don't trust.": "",
+	"Don't like the style": "",
+	"Done": "",
+	"Download": "",
+	"Download canceled": "",
+	"Download Database": "",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "",
+	"Edit User": "",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "",
+	"Embedding Model Engine": "",
+	"Embedding model set to \"{{embedding_model}}\"": "",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "",
+	"Enable Web Search": "",
+	"Enabled": "",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "",
+	"Enter {{role}} message here": "",
+	"Enter a detail about yourself for your LLMs to recall": "",
+	"Enter api auth string (e.g. username:password)": "",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "",
+	"Enter Chunk Size": "",
+	"Enter description": "",
+	"Enter Github Raw URL": "",
+	"Enter Google PSE API Key": "",
+	"Enter Google PSE Engine Id": "",
+	"Enter Image Size (e.g. 512x512)": "",
+	"Enter Jina API Key": "",
+	"Enter language codes": "",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "",
+	"Enter Seed": "",
+	"Enter Serper API Key": "",
+	"Enter Serply API Key": "",
+	"Enter Serpstack API Key": "",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "",
+	"Enter system prompt": "",
+	"Enter Tavily API Key": "",
+	"Enter Tika Server URL": "",
+	"Enter Top K": "",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "",
+	"Enter URL (e.g. http://localhost:11434)": "",
+	"Enter Your Email": "",
+	"Enter Your Full Name": "",
+	"Enter your message": "",
+	"Enter Your Password": "",
+	"Enter Your Role": "",
+	"Enter Your Username": "",
+	"Error": "",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "",
+	"Explore the cosmos": "",
+	"Export": "",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "",
+	"Export chat (.json)": "",
+	"Export Chats": "",
+	"Export Config to JSON File": "",
+	"Export Functions": "",
+	"Export Models": "",
+	"Export Presets": "",
+	"Export Prompts": "",
+	"Export to CSV": "",
+	"Export Tools": "",
+	"External Models": "",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "",
+	"Failed to read clipboard contents": "",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "",
+	"Failed to upload file.": "",
+	"February": "",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "",
+	"File": "",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "",
+	"File not found.": "",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "",
+	"Filter is now globally disabled": "",
+	"Filter is now globally enabled": "",
+	"Filters": "",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "",
+	"Fluidly stream large external response chunks": "",
+	"Focus chat input": "",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "",
+	"Function": "",
+	"Function created successfully": "",
+	"Function deleted successfully": "",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "",
+	"Function is now globally enabled": "",
+	"Function Name": "",
+	"Function updated successfully": "",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "",
+	"Functions allow arbitrary code execution.": "",
+	"Functions imported successfully": "",
+	"General": "",
+	"General Settings": "",
+	"Generate Image": "",
+	"Generating search query": "",
+	"Generation Info": "",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "",
+	"Good Response": "",
+	"Google PSE API Key": "",
+	"Google PSE Engine Id": "",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "",
+	"Haptic Feedback": "",
+	"has no conversations.": "",
+	"Hello, {{name}}": "",
+	"Help": "",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "",
+	"Host": "",
+	"How can I help you today?": "",
+	"How would you rate this response?": "",
+	"Hybrid Search": "",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "",
+	"Image Generation Engine": "",
+	"Image Settings": "",
+	"Images": "",
+	"Import Chats": "",
+	"Import Config from JSON File": "",
+	"Import Functions": "",
+	"Import Models": "",
+	"Import Presets": "",
+	"Import Prompts": "",
+	"Import Tools": "",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "",
+	"Input commands": "",
+	"Install from Github URL": "",
+	"Instant Auto-Send After Voice Transcription": "",
+	"Interface": "",
+	"Invalid file format.": "",
+	"Invalid Tag": "",
+	"January": "",
+	"Jina API Key": "",
+	"join our Discord for help.": "",
+	"JSON": "",
+	"JSON Preview": "",
+	"July": "",
+	"June": "",
+	"JWT Expiration": "",
+	"JWT Token": "",
+	"Keep Alive": "",
+	"Key": "",
+	"Keyboard shortcuts": "",
+	"Knowledge": "",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "",
+	"Last Active": "",
+	"Last Modified": "",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "",
+	"Listening...": "",
+	"LLMs can make mistakes. Verify important information.": "",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "",
+	"Made by OpenWebUI Community": "",
+	"Make sure to enclose them with": "",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "",
+	"Manage": "",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "",
+	"March": "",
+	"Max Tokens (num_predict)": "",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "",
+	"May": "",
+	"Memories accessible by LLMs will be shown here.": "",
+	"Memory": "",
+	"Memory added successfully": "",
+	"Memory cleared successfully": "",
+	"Memory deleted successfully": "",
+	"Memory updated successfully": "",
+	"Merge Responses": "",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "",
+	"Min P": "",
+	"Minimum Score": "",
+	"Mirostat": "",
+	"Mirostat Eta": "",
+	"Mirostat Tau": "",
+	"MMMM DD, YYYY": "",
+	"MMMM DD, YYYY HH:mm": "",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "",
+	"Model '{{modelTag}}' is already in queue for downloading.": "",
+	"Model {{modelId}} not found": "",
+	"Model {{modelName}} is not vision capable": "",
+	"Model {{name}} is now {{status}}": "",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "",
+	"Model Filtering": "",
+	"Model ID": "",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "",
+	"Model Params": "",
+	"Model Permissions": "",
+	"Model updated successfully": "",
+	"Modelfile Content": "",
+	"Models": "",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "",
+	"Name": "",
+	"Name your knowledge base": "",
+	"New Chat": "",
+	"New folder": "",
+	"New Password": "",
+	"No content found": "",
+	"No content to speak": "",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "",
+	"No search query generated": "",
+	"No source available": "",
+	"No users were found.": "",
+	"No valves to update": "",
+	"None": "",
+	"Not factually correct": "",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "",
+	"Notes": "",
+	"Notifications": "",
+	"November": "",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "",
+	"OAuth ID": "",
+	"October": "",
+	"Off": "",
+	"Okay, Let's Go!": "",
+	"OLED Dark": "",
+	"Ollama": "",
+	"Ollama API": "",
+	"Ollama API disabled": "",
+	"Ollama API settings updated": "",
+	"Ollama Version": "",
+	"On": "",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"OpenAI": "",
+	"OpenAI API": "",
+	"OpenAI API Config": "",
+	"OpenAI API Key is required.": "",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "",
+	"or": "",
+	"Organize your users": "",
+	"Other": "",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "",
+	"PDF Extract Images (OCR)": "",
+	"pending": "",
+	"Permission denied when accessing media devices": "",
+	"Permission denied when accessing microphone": "",
+	"Permission denied when accessing microphone: {{error}}": "",
+	"Permissions": "",
+	"Personalization": "",
+	"Pin": "",
+	"Pinned": "",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "",
+	"Pipeline downloaded successfully": "",
+	"Pipelines": "",
+	"Pipelines Not Detected": "",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "",
+	"Playground": "",
+	"Please carefully review the following warnings:": "",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "",
+	"Previous 7 days": "",
+	"Profile Image": "",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "",
+	"Prompt Content": "",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "",
+	"Prompt updated successfully": "",
+	"Prompts": "",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "",
+	"Pull a model from Ollama.com": "",
+	"Query Generation Prompt": "",
+	"Query Params": "",
+	"RAG Template": "",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "",
+	"Record voice": "",
+	"Redirecting you to OpenWebUI Community": "",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "",
+	"References from": "",
+	"Refused when it shouldn't have": "",
+	"Regenerate": "",
+	"Release Notes": "",
+	"Relevance": "",
+	"Remove": "",
+	"Remove Model": "",
+	"Rename": "",
+	"Reorder Models": "",
+	"Repeat Last N": "",
+	"Request Mode": "",
+	"Reranking Model": "",
+	"Reranking model disabled": "",
+	"Reranking model set to \"{{reranking_model}}\"": "",
+	"Reset": "",
+	"Reset All Models": "",
+	"Reset Upload Directory": "",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "",
+	"Rosé Pine": "",
+	"Rosé Pine Dawn": "",
+	"RTL": "",
+	"Run": "",
+	"Running": "",
+	"Save": "",
+	"Save & Create": "",
+	"Save & Update": "",
+	"Save As Copy": "",
+	"Save Tag": "",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "",
+	"Scroll to bottom when switching between branches": "",
+	"Search": "",
+	"Search a model": "",
+	"Search Base": "",
+	"Search Chats": "",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "",
+	"Search Knowledge": "",
+	"Search Models": "",
+	"Search options": "",
+	"Search Prompts": "",
+	"Search Result Count": "",
+	"Search the web": "",
+	"Search Tools": "",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_one": "",
+	"Searched {{count}} sites_other": "",
+	"Searching \"{{searchQuery}}\"": "",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "",
+	"See readme.md for instructions": "",
+	"See what's new": "",
+	"Seed": "",
+	"Select a base model": "",
+	"Select a engine": "",
+	"Select a function": "",
+	"Select a group": "",
+	"Select a model": "",
+	"Select a pipeline": "",
+	"Select a pipeline url": "",
+	"Select a tool": "",
+	"Select Engine": "",
+	"Select Knowledge": "",
+	"Select model": "",
+	"Select only one model to call": "",
+	"Selected model(s) do not support image inputs": "",
+	"Semantic distance to query": "",
+	"Send": "",
+	"Send a Message": "",
+	"Send message": "",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "",
+	"Serper API Key": "",
+	"Serply API Key": "",
+	"Serpstack API Key": "",
+	"Server connection verified": "",
+	"Set as default": "",
+	"Set CFG Scale": "",
+	"Set Default Model": "",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "",
+	"Set Image Size": "",
+	"Set reranking model (e.g. {{model}})": "",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "",
+	"Set Task Model": "",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "",
+	"Settings saved successfully!": "",
+	"Share": "",
+	"Share Chat": "",
+	"Share to OpenWebUI Community": "",
+	"Show": "",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "",
+	"Show shortcuts": "",
+	"Show your support!": "",
+	"Showcased creativity": "",
+	"Sign in": "",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "",
+	"Sign up": "",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "",
+	"Speech-to-Text Engine": "",
+	"Stop": "",
+	"Stop Sequence": "",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "",
+	"Subtitle (e.g. about the Roman Empire)": "",
+	"Success": "",
+	"Successfully updated.": "",
+	"Suggested": "",
+	"Support": "",
+	"Support this plugin:": "",
+	"Sync directory": "",
+	"System": "",
+	"System Instructions": "",
+	"System Prompt": "",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "",
+	"Tavily API Key": "",
+	"Tell us more:": "",
+	"Temperature": "",
+	"Template": "",
+	"Temporary Chat": "",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "",
+	"Tfs Z": "",
+	"Thanks for your feedback!": "",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "",
+	"Thinking...": "",
+	"This action cannot be undone. Do you wish to continue?": "",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "",
+	"Tika": "",
+	"Tika Server URL required.": "",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "",
+	"Title": "",
+	"Title (e.g. Tell me a fun fact)": "",
+	"Title Auto-Generation": "",
+	"Title cannot be an empty string.": "",
+	"Title Generation Prompt": "",
+	"TLS": "",
+	"To access the available model names for downloading,": "",
+	"To access the GGUF models available for downloading,": "",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "",
+	"To select filters here, add them to the \"Functions\" workspace first.": "",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "",
+	"Toast notifications for new updates": "",
+	"Today": "",
+	"Toggle settings": "",
+	"Toggle sidebar": "",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "",
+	"Tool deleted successfully": "",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "",
+	"Tool Name": "",
+	"Tool updated successfully": "",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution": "",
+	"Tools have a function calling system that allows arbitrary code execution.": "",
+	"Top K": "",
+	"Top P": "",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "",
+	"TTS Model": "",
+	"TTS Settings": "",
+	"TTS Voice": "",
+	"Type": "",
+	"Type Hugging Face Resolve (Download) URL": "",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "",
+	"UI": "",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "",
+	"Update and Copy Link": "",
+	"Update for the latest features and improvements.": "",
+	"Update password": "",
+	"Updated": "",
+	"Updated at": "",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "",
+	"Upload Pipeline": "",
+	"Upload Progress": "",
+	"URL": "",
+	"URL Mode": "",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "",
+	"use_mlock (Ollama)": "",
+	"use_mmap (Ollama)": "",
+	"user": "",
+	"User": "",
+	"User location successfully retrieved.": "",
+	"Username": "",
+	"Users": "",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "",
+	"Valid time units:": "",
+	"Valves": "",
+	"Valves updated": "",
+	"Valves updated successfully": "",
+	"variable": "",
+	"variable to have them replaced with clipboard content.": "",
+	"Version": "",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "",
+	"Voice Input": "",
+	"Warning": "",
+	"Warning:": "",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "",
+	"Web": "",
+	"Web API": "",
+	"Web Loader Settings": "",
+	"Web Search": "",
+	"Web Search Engine": "",
+	"Web Search Query Generation": "",
+	"Webhook URL": "",
+	"WebUI Settings": "",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "",
+	"You": "",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "",
+	"You have shared this chat": "",
+	"You're a helpful assistant.": "",
+	"You're now logged in.": "",
+	"Your account status is currently pending activation.": "",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
+	"Youtube": "",
+	"Youtube Loader Settings": ""
+}
diff --git a/src/lib/i18n/locales/tr-TR/translation.json b/src/lib/i18n/locales/tr-TR/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..6bf981999cc0423e0346e58c9ddff0e8ed201561
--- /dev/null
+++ b/src/lib/i18n/locales/tr-TR/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' veya süresiz için '-1'.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(örn. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(örn. `sh webui.sh --api`)",
+	"(latest)": "(en son)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}}'ın Sohbetleri",
+	"{{webUIName}} Backend Required": "{{webUIName}} Arka-uç Gerekli",
+	"*Prompt node ID(s) are required for image generation": "*Görüntü oluşturma için düğüm kimlikleri gereklidir",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Bir görev modeli, sohbetler ve web arama sorguları için başlık oluşturma gibi görevleri yerine getirirken kullanılır",
+	"a user": "bir kullanıcı",
+	"About": "Hakkında",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Hesap",
+	"Account Activation Pending": "Hesap Aktivasyonu Bekleniyor",
+	"Accurate information": "Doğru bilgi",
+	"Actions": "Aksiyonlar",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Aktif Kullanıcılar",
+	"Add": "Ekle",
+	"Add a model ID": "Bir model kimliği ekleyin",
+	"Add a short description about what this model does": "Bu modelin ne yaptığı hakkında kısa bir açıklama ekleyin",
+	"Add a tag": "Bir etiket ekleyin",
+	"Add Arena Model": "",
+	"Add Connection": "Bağlantı Ekle",
+	"Add Content": "İçerik Ekle",
+	"Add content here": "Buraya içerik ekleyin",
+	"Add custom prompt": "Özel prompt ekleyin",
+	"Add Files": "Dosyalar Ekle",
+	"Add Group": "",
+	"Add Memory": "Bellek Ekle",
+	"Add Model": "Model Ekle",
+	"Add Tag": "Etiket Ekle",
+	"Add Tags": "Etiketler Ekle",
+	"Add text content": "Metin içeriği ekleme",
+	"Add User": "Kullanıcı Ekle",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Bu ayarların yapılması, değişiklikleri tüm kullanıcılara evrensel olarak uygulayacaktır.",
+	"admin": "yönetici",
+	"Admin": "Yönetici",
+	"Admin Panel": "Yönetici Paneli",
+	"Admin Settings": "Yönetici Ayarları",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Yöneticiler her zaman tüm araçlara erişebilir; kullanıcıların çalışma alanındaki model başına atanmış araçlara ihtiyacı vardır.",
+	"Advanced Parameters": "Gelişmiş Parametreler",
+	"Advanced Params": "Gelişmiş Parametreler",
+	"All chats": "Tüm sohbetler",
+	"All Documents": "Tüm Belgeler",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Sohbet Silmeye İzin Ver",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Yerel olmayan seslere izin verin",
+	"Allow Temporary Chat": "Geçici Sohbetlere İzin Ver",
+	"Allow User Location": "Kullanıcı Konumuna İzin Ver",
+	"Allow Voice Interruption in Call": "Aramada Ses Kesintisine İzin Ver",
+	"Already have an account?": "Zaten bir hesabınız mı var?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "bir asistan",
+	"and": "ve",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "ve yeni bir paylaşılan bağlantı oluşturun.",
+	"API Base URL": "API Temel URL",
+	"API Key": "API Anahtarı",
+	"API Key created.": "API Anahtarı oluşturuldu.",
+	"API keys": "API anahtarları",
+	"Application DN": "Uygulama DN",
+	"Application DN Password": "Uygulama DN Parola",
+	"applies to all users with the \"user\" role": "",
+	"April": "Nisan",
+	"Archive": "Arşiv",
+	"Archive All Chats": "Tüm Sohbetleri Arşivle",
+	"Archived Chats": "Arşivlenmiş Sohbetler",
+	"archived-chat-export": "arşivlenmiş-sohbet-aktarımı",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Emin misiniz?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "Bir soru sorun",
+	"Assistant": "Asistan",
+	"Attach file": "Dosya ekle",
+	"Attention to detail": "Ayrıntılara dikkat",
+	"Attribute for Username": "Kullanıcı Adı için Özellik",
+	"Audio": "Ses",
+	"August": "Ağustos",
+	"Authenticate": "Kimlik Doğrulama",
+	"Auto-Copy Response to Clipboard": "Yanıtı Panoya Otomatik Kopyala",
+	"Auto-playback response": "Yanıtı otomatik oynatma",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 API Kimlik Doğrulama Dizesi",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 Temel URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 Temel URL gereklidir.",
+	"Available list": "Mevcut liste",
+	"available!": "mevcut!",
+	"Awful": "",
+	"Azure AI Speech": "Azure AI Konuşma",
+	"Azure Region": "Azure Bölgesi",
+	"Back": "Geri",
+	"Bad Response": "Kötü Yanıt",
+	"Banners": "Afişler",
+	"Base Model (From)": "Temel Model ('den)",
+	"Batch Size (num_batch)": "Yığın Boyutu (num_batch)",
+	"before": "önce",
+	"Being lazy": "Tembelleşiyor",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Brave Search API Anahtarı",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Web Siteleri için SSL doğrulamasını atlayın",
+	"Call": "Arama",
+	"Call feature is not supported when using Web STT engine": "Web STT motoru kullanılırken arama özelliği desteklenmiyor",
+	"Camera": "Kamera",
+	"Cancel": "İptal",
+	"Capabilities": "Yetenekler",
+	"Certificate Path": "",
+	"Change Password": "Parola Değiştir",
+	"Character": "Karakter",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Sohbet",
+	"Chat Background Image": "Sohbet Arka Plan Resmi",
+	"Chat Bubble UI": "Sohbet Balonu Arayüzü",
+	"Chat Controls": "Sohbet Kontrolleri",
+	"Chat direction": "Sohbet Yönü",
+	"Chat Overview": "Sohbet Genel Bakış",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Sohbetler",
+	"Check Again": "Tekrar Kontrol Et",
+	"Check for updates": "Güncellemeleri kontrol et",
+	"Checking for updates...": "Güncellemeler kontrol ediliyor...",
+	"Choose a model before saving...": "Kaydetmeden önce bir model seçin...",
+	"Chunk Overlap": "Chunk Çakışması",
+	"Chunk Params": "Chunk Parametreleri",
+	"Chunk Size": "Chunk Boyutu",
+	"Ciphers": "",
+	"Citation": "Alıntı",
+	"Clear memory": "Belleği temizle",
+	"click here": "buraya tıklayın",
+	"Click here for filter guides.": "Filtre kılavuzları için buraya tıklayın.",
+	"Click here for help.": "Yardım için buraya tıklayın.",
+	"Click here to": "Şunu yapmak için buraya tıklayın:",
+	"Click here to download user import template file.": "Kullanıcı içe aktarma şablon dosyasını indirmek için buraya tıklayın.",
+	"Click here to learn more about faster-whisper and see the available models.": "faster-whisper hakkında daha fazla bilgi edinmek ve mevcut modelleri görmek için buraya tıklayın.",
+	"Click here to select": "Seçmek için buraya tıklayın",
+	"Click here to select a csv file.": "Bir CSV dosyası seçmek için buraya tıklayın.",
+	"Click here to select a py file.": "Bir py dosyası seçmek için buraya tıklayın.",
+	"Click here to upload a workflow.json file.": "Bir workflow.json dosyası yüklemek için buraya tıklayın.",
+	"click here.": "buraya tıklayın.",
+	"Click on the user role button to change a user's role.": "Bir kullanıcının rolünü değiştirmek için kullanıcı rolü düğmesine tıklayın.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Panoya yazma izni reddedildi. Tarayıcı ayarlarını kontrol ederek gerekli izinleri sağlayabilirsiniz.",
+	"Clone": "Klon",
+	"Close": "Kapat",
+	"Code execution": "",
+	"Code formatted successfully": "Kod başarıyla biçimlendirildi",
+	"Collection": "Koleksiyon",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Temel URL",
+	"ComfyUI Base URL is required.": "ComfyUI Temel URL gerekli.",
+	"ComfyUI Workflow": "ComfyUI İş Akışı",
+	"ComfyUI Workflow Nodes": "ComfyUI İş Akışı Düğümleri",
+	"Command": "Komut",
+	"Completions": "",
+	"Concurrent Requests": "Eşzamanlı İstekler",
+	"Configure": "Yapılandırma",
+	"Configure Models": "",
+	"Confirm": "Onayla",
+	"Confirm Password": "Parolayı Onayla",
+	"Confirm your action": "İşleminizi onaylayın",
+	"Connections": "Bağlantılar",
+	"Contact Admin for WebUI Access": "WebUI Erişimi için Yöneticiyle İletişime Geçin",
+	"Content": "İçerik",
+	"Content Extraction": "İçerik Çıkarma",
+	"Context Length": "Bağlam Uzunluğu",
+	"Continue Response": "Yanıta Devam Et",
+	"Continue with {{provider}}": "{{provider}} ile devam et",
+	"Continue with Email": "E-posta ile devam edin",
+	"Continue with LDAP": "LDAP ile devam edin",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Mesaj metninin TTS istekleri için nasıl bölüneceğini kontrol edin. 'Noktalama' cümlelere, 'paragraflar' paragraflara böler ve 'hiçbiri' mesajı tek bir dize olarak tutar.",
+	"Controls": "Kontroller",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Çıktının tutarlılığı ve çeşitliliği arasındaki dengeyi kontrol eder. Daha düşük bir değer, daha odaklanmış ve tutarlı bir metinle sonuçlanacaktır. (Varsayılan: 5.0)",
+	"Copied": "Kopyalandı",
+	"Copied shared chat URL to clipboard!": "Paylaşılan sohbet URL'si panoya kopyalandı!",
+	"Copied to clipboard": "Panoya kopyalandı",
+	"Copy": "Kopyala",
+	"Copy last code block": "Son kod bloğunu kopyala",
+	"Copy last response": "Son yanıtı kopyala",
+	"Copy Link": "Bağlantıyı Kopyala",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Panoya kopyalama başarılı!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Bir model oluştur",
+	"Create Account": "Hesap Oluştur",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Yeni anahtar oluştur",
+	"Create new secret key": "Yeni gizli anahtar oluştur",
+	"Created at": "Oluşturulma tarihi",
+	"Created At": "Şu Tarihte Oluşturuldu:",
+	"Created by": "Şunun tarafından oluşturuldu:",
+	"CSV Import": "CSV İçe Aktarma",
+	"Current Model": "Mevcut Model",
+	"Current Password": "Mevcut Parola",
+	"Custom": "Özel",
+	"Dark": "Koyu",
+	"Database": "Veritabanı",
+	"December": "Aralık",
+	"Default": "Varsayılan",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Varsayılan (SentenceTransformers)",
+	"Default Model": "Varsayılan Model",
+	"Default model updated": "Varsayılan model güncellendi",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Varsayılan Prompt Önerileri",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Varsayılan Kullanıcı Rolü",
+	"Delete": "Sil",
+	"Delete a model": "Bir modeli sil",
+	"Delete All Chats": "Tüm Sohbetleri Sil",
+	"Delete All Models": "",
+	"Delete chat": "Sohbeti sil",
+	"Delete Chat": "Sohbeti Sil",
+	"Delete chat?": "Sohbeti sil?",
+	"Delete folder?": "",
+	"Delete function?": "Fonksiyonu sil?",
+	"Delete prompt?": "Promptu sil?",
+	"delete this link": "bu bağlantıyı sil",
+	"Delete tool?": "Aracı sil?",
+	"Delete User": "Kullanıcıyı Sil",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} silindi",
+	"Deleted {{name}}": "{{name}} silindi",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Açıklama",
+	"Didn't fully follow instructions": "Talimatları tam olarak takip etmedi",
+	"Disabled": "Devre Dışı",
+	"Discover a function": "Bir fonksiyon keşfedin",
+	"Discover a model": "Bir model keşfedin",
+	"Discover a prompt": "Bir prompt keşfedin",
+	"Discover a tool": "Bir araç keşfedin",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Özel fonksiyonları keşfedin, indirin ve inceleyin",
+	"Discover, download, and explore custom prompts": "Özel promptları keşfedin, indirin ve inceleyin",
+	"Discover, download, and explore custom tools": "Özel araçları keşfedin, indirin ve inceleyin",
+	"Discover, download, and explore model presets": "Model ön ayarlarını keşfedin, indirin ve inceleyin",
+	"Dismissible": "Reddedilebilir",
+	"Display": "",
+	"Display Emoji in Call": "Aramada Emoji Göster",
+	"Display the username instead of You in the Chat": "Sohbet'te Siz yerine kullanıcı adını göster",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Tamamen güvenmediğiniz kaynaklardan fonksiyonlar yüklemeyin.",
+	"Do not install tools from sources you do not fully trust.": "Tamamen güvenmediğiniz kaynaklardan araçlar yüklemeyin.",
+	"Document": "Belge",
+	"Documentation": "Dökümantasyon",
+	"Documents": "Belgeler",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "herhangi bir harici bağlantı yapmaz ve verileriniz güvenli bir şekilde yerel olarak barındırılan sunucunuzda kalır.",
+	"Don't have an account?": "Hesabınız yok mu?",
+	"don't install random functions from sources you don't trust.": "Tanımadığınız kaynaklardan rastgele fonksiyonlar yüklemeyin.",
+	"don't install random tools from sources you don't trust.": "Tanımadığınız kaynaklardan rastgele araçlar yüklemeyin.",
+	"Don't like the style": "Tarzını beğenmedim",
+	"Done": "Tamamlandı",
+	"Download": "İndir",
+	"Download canceled": "İndirme iptal edildi",
+	"Download Database": "Veritabanını İndir",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Sohbete eklemek istediğiniz dosyaları buraya bırakın",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "örn. '30s', '10m'. Geçerli zaman birimleri 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Düzenle",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Belleği Düzenle",
+	"Edit User": "Kullanıcıyı Düzenle",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "E-posta",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "Gömme Yığın Boyutu",
+	"Embedding Model": "Gömme Modeli",
+	"Embedding Model Engine": "Gömme Modeli Motoru",
+	"Embedding model set to \"{{embedding_model}}\"": "Gömme modeli \"{{embedding_model}}\" olarak ayarlandı",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Topluluk Paylaşımını Etkinleştir",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Mesaj Değerlendirmeyi Etkinleştir",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Yeni Kayıtları Etkinleştir",
+	"Enable Web Search": "Web Aramasını Etkinleştir",
+	"Enabled": "Etkin",
+	"Engine": "Motor",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "CSV dosyanızın şu sırayla 4 sütun içerdiğinden emin olun: İsim, E-posta, Şifre, Rol.",
+	"Enter {{role}} message here": "Buraya {{role}} mesajını girin",
+	"Enter a detail about yourself for your LLMs to recall": "LLM'lerinizin hatırlaması için kendiniz hakkında bir bilgi girin",
+	"Enter api auth string (e.g. username:password)": "Api auth dizesini girin (örn. kullanıcı adı:parola)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Brave Search API Anahtarını Girin",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "CFG Ölçeğini Girin (örn. 7.0)",
+	"Enter Chunk Overlap": "Chunk Örtüşmesini Girin",
+	"Enter Chunk Size": "Chunk Boyutunu Girin",
+	"Enter description": "",
+	"Enter Github Raw URL": "Github Raw URL'sini girin",
+	"Enter Google PSE API Key": "Google PSE API Anahtarını Girin",
+	"Enter Google PSE Engine Id": "Google PSE Engine Id'sini Girin",
+	"Enter Image Size (e.g. 512x512)": "Görüntü Boyutunu Girin (örn. 512x512)",
+	"Enter Jina API Key": "Jina API Anahtarını Girin",
+	"Enter language codes": "Dil kodlarını girin",
+	"Enter Model ID": "Model ID'sini Girin",
+	"Enter model tag (e.g. {{modelTag}})": "Model etiketini girin (örn. {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Adım Sayısını Girin (örn. 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Örnekleyiciyi Girin (örn. Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Zamanlayıcıyı Girin (örn. Karras)",
+	"Enter Score": "Skoru Girin",
+	"Enter SearchApi API Key": "Arama-API Anahtarını Girin",
+	"Enter SearchApi Engine": "Arama-API Motorunu Girin",
+	"Enter Searxng Query URL": "Searxng Sorgu URL'sini girin",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Serper API Anahtarını Girin",
+	"Enter Serply API Key": "Serply API Anahtarını Girin",
+	"Enter Serpstack API Key": "Serpstack API Anahtarını Girin",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Durdurma dizisini girin",
+	"Enter system prompt": "Sistem promptunu girin",
+	"Enter Tavily API Key": "Tavily API Anahtarını Girin",
+	"Enter Tika Server URL": "Tika Sunucu URL'sini Girin",
+	"Enter Top K": "Top K'yı girin",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "URL'yi Girin (örn. http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "URL'yi Girin (e.g. http://localhost:11434)",
+	"Enter Your Email": "E-postanızı Girin",
+	"Enter Your Full Name": "Tam Adınızı Girin",
+	"Enter your message": "Mesajınızı girin",
+	"Enter Your Password": "Parolanızı Girin",
+	"Enter Your Role": "Rolünüzü Girin",
+	"Enter Your Username": "",
+	"Error": "Hata",
+	"ERROR": "",
+	"Evaluations": "Değerlendirmeler",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "Hariç tut",
+	"Experimental": "Deneysel",
+	"Explore the cosmos": "Evreni keşfet",
+	"Export": "Dışa Aktar",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Tüm Sohbetleri Dışa Aktar (Tüm Kullanıcılar)",
+	"Export chat (.json)": "Sohbeti dışa aktar (.json)",
+	"Export Chats": "Sohbetleri Dışa Aktar",
+	"Export Config to JSON File": "",
+	"Export Functions": "Fonksiyonları Dışa Aktar",
+	"Export Models": "Modelleri Dışa Aktar",
+	"Export Presets": "",
+	"Export Prompts": "Promptları Dışa Aktar",
+	"Export to CSV": "CSV'ye Aktar",
+	"Export Tools": "Araçları Dışa Aktar",
+	"External Models": "Modelleri Dışa Aktar",
+	"Failed to add file.": "Dosya eklenemedi.",
+	"Failed to create API Key.": "API Anahtarı oluşturulamadı.",
+	"Failed to read clipboard contents": "Pano içeriği okunamadı",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Ayarlar güncellenemedi",
+	"Failed to upload file.": "",
+	"February": "Şubat",
+	"Feedback History": "Geri Bildirim Geçmişi",
+	"Feedbacks": "Geri Bildirimler",
+	"Feel free to add specific details": "Spesifik ayrıntılar eklemekten çekinmeyin",
+	"File": "Dosya",
+	"File added successfully.": "Dosya başarıyla eklendi.",
+	"File content updated successfully.": "Dosya içeriği başarıyla güncellendi.",
+	"File Mode": "Dosya Modu",
+	"File not found.": "Dosya bulunamadı.",
+	"File removed successfully.": "Dosya başarıyla kaldırıldı.",
+	"File size should not exceed {{maxSize}} MB.": "Dosya boyutu {{maxSize}} MB'yi aşmamalıdır.",
+	"Files": "Dosyalar",
+	"Filter is now globally disabled": "Filtre artık global olarak devre dışı",
+	"Filter is now globally enabled": "Filtre artık global olarak devrede",
+	"Filters": "Filtreler",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Parmak izi sahteciliği tespit edildi: Avatar olarak baş harfler kullanılamıyor. Varsayılan profil resmine dönülüyor.",
+	"Fluidly stream large external response chunks": "Büyük harici yanıt chunklarını akıcı bir şekilde yayınlayın",
+	"Focus chat input": "Sohbet girişine odaklan",
+	"Folder deleted successfully": "Klasör başarıyla silindi",
+	"Folder name cannot be empty": "Klasör adı boş olamaz",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "Klasör adı başarıyla güncellendi",
+	"Followed instructions perfectly": "Talimatları mükemmel şekilde takip etti",
+	"Forge new paths": "Yeni yollar açın",
+	"Form": "Form",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Frekans Cezası",
+	"Function": "Fonksiyon",
+	"Function created successfully": "Fonksiyon başarıyla oluşturuldu",
+	"Function deleted successfully": "Fonksiyon başarıyla silindi",
+	"Function Description": "Fonksiyon Açıklaması",
+	"Function ID": "",
+	"Function is now globally disabled": "Fonksiyon artık global olarak devre dışı",
+	"Function is now globally enabled": "Fonksiyon artık global olarak aktif",
+	"Function Name": "Fonksiyon Adı",
+	"Function updated successfully": "Fonksiyon başarıyla güncellendi",
+	"Functions": "Fonksiyonlar",
+	"Functions allow arbitrary code execution": "Fonksiyonlar keyfi kod yürütülmesine izin verir",
+	"Functions allow arbitrary code execution.": "Fonksiyonlar keyfi kod yürütülmesine izin verir.",
+	"Functions imported successfully": "Fonksiyonlar başarıyla içe aktarıldı",
+	"General": "Genel",
+	"General Settings": "Genel Ayarlar",
+	"Generate Image": "Görsel Üret",
+	"Generating search query": "Arama sorgusu oluşturma",
+	"Generation Info": "Üretim Bilgisi",
+	"Get started": "Başlayın",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Evrensel",
+	"Good Response": "İyi Yanıt",
+	"Google PSE API Key": "Google PSE API Anahtarı",
+	"Google PSE Engine Id": "Google PSE Engine Id",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "Gruplar",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Dokunsal Geri Bildirim",
+	"has no conversations.": "hiç konuşması yok.",
+	"Hello, {{name}}": "Merhaba, {{name}}",
+	"Help": "Yardım",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Gizle",
+	"Host": "Ana bilgisayar",
+	"How can I help you today?": "Bugün size nasıl yardımcı olabilirim?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Karma Arama",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Eylemimin sonuçlarını okuduğumu ve anladığımı kabul ediyorum. Rastgele kod çalıştırmayla ilgili risklerin farkındayım ve kaynağın güvenilirliğini doğruladım.",
+	"ID": "",
+	"Ignite curiosity": "Merak uyandırın",
+	"Image Generation (Experimental)": "Görüntü Oluşturma (Deneysel)",
+	"Image Generation Engine": "Görüntü Oluşturma Motoru",
+	"Image Settings": "Görüntü Ayarları",
+	"Images": "Görüntüler",
+	"Import Chats": "Sohbetleri İçe Aktar",
+	"Import Config from JSON File": "",
+	"Import Functions": "Fonksiyonları İçe Aktar",
+	"Import Models": "Modelleri İçe Aktar",
+	"Import Presets": "",
+	"Import Prompts": "Promptları İçe Aktar",
+	"Import Tools": "Araçları İçe Aktar",
+	"Include": "Dahil etmek",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "stable-diffusion-webui çalıştırılırken `--api-auth` bayrağını dahil edin",
+	"Include `--api` flag when running stable-diffusion-webui": "stable-diffusion-webui çalıştırılırken `--api` bayrağını dahil edin",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Bilgi",
+	"Input commands": "Giriş komutları",
+	"Install from Github URL": "Github URL'sinden yükleyin",
+	"Instant Auto-Send After Voice Transcription": "Ses Transkripsiyonundan Sonra Anında Otomatik Gönder",
+	"Interface": "Arayüz",
+	"Invalid file format.": "",
+	"Invalid Tag": "Geçersiz etiket",
+	"January": "Ocak",
+	"Jina API Key": "",
+	"join our Discord for help.": "yardım için Discord'umuza katılın.",
+	"JSON": "JSON",
+	"JSON Preview": "JSON Önizlemesi",
+	"July": "Temmuz",
+	"June": "Haziran",
+	"JWT Expiration": "JWT Bitişi",
+	"JWT Token": "JWT Token",
+	"Keep Alive": "Canlı Tut",
+	"Key": "",
+	"Keyboard shortcuts": "Klavye kısayolları",
+	"Knowledge": "Bilgi",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Dil",
+	"Last Active": "Son Aktivite",
+	"Last Modified": "Son Düzenleme",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP sunucusu güncellendi",
+	"Leaderboard": "Liderlik Tablosu",
+	"Leave empty for unlimited": "Sınırsız için boş bırakınız",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Varsayılan promptu kullanmak için boş bırakın veya özel bir prompt girin",
+	"Light": "Açık",
+	"Listening...": "Dinleniyor...",
+	"LLMs can make mistakes. Verify important information.": "LLM'ler hata yapabilir. Önemli bilgileri doğrulayın.",
+	"Local": "Yerel",
+	"Local Models": "Yerel Modeller",
+	"Lost": "Kayıp",
+	"LTR": "Soldan Sağa",
+	"Made by OpenWebUI Community": "OpenWebUI Topluluğu tarafından yapılmıştır",
+	"Make sure to enclose them with": "Değişkenlerinizi şu şekilde biçimlendirin:",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "ComfyUI'dan API formatında bir workflow.json dosyası olarak dışa aktardığınızdan emin olun.",
+	"Manage": "Yönet",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Pipelineları Yönet",
+	"March": "Mart",
+	"Max Tokens (num_predict)": "Maksimum Token (num_predict)",
+	"Max Upload Count": "Maksimum Yükleme Sayısı",
+	"Max Upload Size": "Maksimum Yükleme Boyutu",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Aynı anda en fazla 3 model indirilebilir. Lütfen daha sonra tekrar deneyin.",
+	"May": "Mayıs",
+	"Memories accessible by LLMs will be shown here.": "LLM'ler tarafından erişilebilen bellekler burada gösterilecektir.",
+	"Memory": "Bellek",
+	"Memory added successfully": "Bellek başarıyla eklendi",
+	"Memory cleared successfully": "Bellek başarıyle temizlendi",
+	"Memory deleted successfully": "Bellek başarıyla silindi",
+	"Memory updated successfully": "Bellek başarıyla güncellendi",
+	"Merge Responses": "Yanıtları Birleştir",
+	"Message rating should be enabled to use this feature": "Bu özelliği kullanmak için mesaj derecelendirmesi etkinleştirilmelidir",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Bağlantınızı oluşturduktan sonra gönderdiğiniz mesajlar paylaşılmayacaktır. URL'ye sahip kullanıcılar paylaşılan sohbeti görüntüleyebilecektir.",
+	"Min P": "Min P",
+	"Minimum Score": "Minimum Skor",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "DD MMMM YYYY",
+	"MMMM DD, YYYY HH:mm": "DD MMMM YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "DD MMMM YYYY hh:mm:ss A",
+	"Model": "Model",
+	"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' başarıyla indirildi.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' zaten indirme sırasında.",
+	"Model {{modelId}} not found": "{{modelId}} bulunamadı",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} görüntü yeteneğine sahip değil",
+	"Model {{name}} is now {{status}}": "{{name}} modeli artık {{status}}",
+	"Model accepts image inputs": "Model görüntü girdilerini kabul eder",
+	"Model created successfully!": "Model başarıyla oluşturuldu!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model dosya sistemi yolu algılandı. Güncelleme için model kısa adı gerekli, devam edilemiyor.",
+	"Model Filtering": "",
+	"Model ID": "Model ID",
+	"Model IDs": "Model Kimlikleri",
+	"Model Name": "Model Adı",
+	"Model not selected": "Model seçilmedi",
+	"Model Params": "Model Parametreleri",
+	"Model Permissions": "",
+	"Model updated successfully": "Model başarıyla güncellendi",
+	"Modelfile Content": "Model Dosyası İçeriği",
+	"Models": "Modeller",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Daha Fazla",
+	"Name": "Ad",
+	"Name your knowledge base": "",
+	"New Chat": "Yeni Sohbet",
+	"New folder": "Yeni Dosya",
+	"New Password": "Yeni Parola",
+	"No content found": "İçerik bulunamadı",
+	"No content to speak": "Konuşacak içerik yok",
+	"No distance available": "Mesafe mevcut değil",
+	"No feedbacks found": "Geri bildirim bulunamadı",
+	"No file selected": "Hiçbir dosya seçilmedi",
+	"No files found.": "Dosya bulunamadı.",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "HTML, CSS veya JavaScript içeriği bulunamadı.",
+	"No knowledge found": "Bilgi bulunamadı",
+	"No model IDs": "",
+	"No models found": "Model bulunamadı",
+	"No models selected": "",
+	"No results found": "Sonuç bulunamadı",
+	"No search query generated": "Hiç arama sorgusu oluşturulmadı",
+	"No source available": "Kaynak mevcut değil",
+	"No users were found.": "",
+	"No valves to update": "Güncellenecek valvler yok",
+	"None": "Yok",
+	"Not factually correct": "Gerçeklere göre doğru değil",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Not: Minimum bir skor belirlerseniz, arama yalnızca minimum skora eşit veya daha yüksek bir skora sahip belgeleri getirecektir.",
+	"Notes": "",
+	"Notifications": "Bildirimler",
+	"November": "Kasım",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Ekim",
+	"Off": "Kapalı",
+	"Okay, Let's Go!": "Tamam, Hadi Başlayalım!",
+	"OLED Dark": "OLED Koyu",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API'si devre dışı",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Ollama Sürümü",
+	"On": "Açık",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Komut dizisinde yalnızca alfasayısal karakterler ve tireler kabul edilir.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Hop! URL geçersiz gibi görünüyor. Lütfen tekrar kontrol edin ve yeniden deneyin.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Hop! Desteklenmeyen bir yöntem kullanıyorsunuz (yalnızca önyüz). Lütfen WebUI'yi arkayüzden sunun.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Yeni sohbet aç",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Open-WebUI sürümü (v{{OPEN_WEBUI_VERSION}}) gerekli sürümden (v{{REQUIRED_VERSION}}) düşük",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API Konfigürasyonu",
+	"OpenAI API Key is required.": "OpenAI API Anahtarı gereklidir.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "OpenAI URL/Anahtar gereklidir.",
+	"or": "veya",
+	"Organize your users": "",
+	"Other": "Diğer",
+	"OUTPUT": "",
+	"Output format": "Çıktı formatı",
+	"Overview": "Genel Bakış",
+	"page": "",
+	"Password": "Parola",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "PDF belgesi (.pdf)",
+	"PDF Extract Images (OCR)": "PDF Görüntülerini Çıkart (OCR)",
+	"pending": "beklemede",
+	"Permission denied when accessing media devices": "Medya cihazlarına erişim izni reddedildi",
+	"Permission denied when accessing microphone": "Mikrofona erişim izni reddedildi",
+	"Permission denied when accessing microphone: {{error}}": "Mikrofona erişim izni reddedildi: {{error}}",
+	"Permissions": "",
+	"Personalization": "Kişiselleştirme",
+	"Pin": "Sabitle",
+	"Pinned": "Sabitlenmiş",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline başarıyla silindi",
+	"Pipeline downloaded successfully": "Pipeline başarıyla güncellendi",
+	"Pipelines": "Pipelinelar",
+	"Pipelines Not Detected": "Pipeline Tespit Edilmedi",
+	"Pipelines Valves": "Pipeline Valvleri",
+	"Plain text (.txt)": "Düz metin (.txt)",
+	"Playground": "Oyun Alanı",
+	"Please carefully review the following warnings:": "Lütfen aşağıdaki uyarıları dikkatlice inceleyin:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Olumlu yaklaşım",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "Önceki 30 gün",
+	"Previous 7 days": "Önceki 7 gün",
+	"Profile Image": "Profil Fotoğrafı",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (örn. Roma İmparatorluğu hakkında ilginç bir bilgi verin)",
+	"Prompt Content": "Prompt İçeriği",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Prompt önerileri",
+	"Prompt updated successfully": "",
+	"Prompts": "Promptlar",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com'dan \"{{searchValue}}\" çekin",
+	"Pull a model from Ollama.com": "Ollama.com'dan bir model çekin",
+	"Query Generation Prompt": "",
+	"Query Params": "Sorgu Parametreleri",
+	"RAG Template": "RAG Şablonu",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Sesli Oku",
+	"Record voice": "Ses kaydı yap",
+	"Redirecting you to OpenWebUI Community": "OpenWebUI Topluluğuna yönlendiriliyorsunuz",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Kendinizden \"User\" olarak bahsedin (örneğin, \"User İspanyolca öğreniyor\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Reddedilmemesi gerekirken reddedildi",
+	"Regenerate": "Tekrar Oluştur",
+	"Release Notes": "Sürüm Notları",
+	"Relevance": "",
+	"Remove": "Kaldır",
+	"Remove Model": "Modeli Kaldır",
+	"Rename": "Yeniden Adlandır",
+	"Reorder Models": "",
+	"Repeat Last N": "Son N'yi Tekrar Et",
+	"Request Mode": "İstek Modu",
+	"Reranking Model": "Yeniden Sıralama Modeli",
+	"Reranking model disabled": "Yeniden sıralama modeli devre dışı bırakıldı",
+	"Reranking model set to \"{{reranking_model}}\"": "Yeniden sıralama modeli \"{{reranking_model}}\" olarak ayarlandı",
+	"Reset": "Sıfırla",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Yükleme Dizinini Sıfırla",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Web sitesi izinleri reddedildiğinden yanıt bildirimleri etkinleştirilemiyor. Gerekli erişimi sağlamak için lütfen tarayıcı ayarlarınızı ziyaret edin.",
+	"Response splitting": "Yanıt bölme",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Rol",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "Sağdan Sola",
+	"Run": "Çalıştır",
+	"Running": "Çalışıyor",
+	"Save": "Kaydet",
+	"Save & Create": "Kaydet ve Oluştur",
+	"Save & Update": "Kaydet ve Güncelle",
+	"Save As Copy": "Kopya Olarak Kaydet",
+	"Save Tag": "Etiketi Kaydet",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Sohbet kayıtlarının doğrudan tarayıcınızın depolama alanına kaydedilmesi artık desteklenmemektedir. Lütfen aşağıdaki butona tıklayarak sohbet kayıtlarınızı indirmek ve silmek için bir dakikanızı ayırın. Endişelenmeyin, sohbet günlüklerinizi arkayüze kolayca yeniden aktarabilirsiniz:",
+	"Scroll to bottom when switching between branches": "Dallar arasında geçiş yaparken en alta kaydır",
+	"Search": "Ara",
+	"Search a model": "Bir model ara",
+	"Search Base": "",
+	"Search Chats": "Sohbetleri Ara",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Fonksiyonları Ara",
+	"Search Knowledge": "",
+	"Search Models": "Modelleri Ara",
+	"Search options": "",
+	"Search Prompts": "Prompt Ara",
+	"Search Result Count": "Arama Sonucu Sayısı",
+	"Search the web": "",
+	"Search Tools": "Arama Araçları",
+	"SearchApi API Key": "Arama-API API Anahtarı",
+	"SearchApi Engine": "Arama-API Motoru",
+	"Searched {{count}} sites_one": "Arandı {{count}} sites_one",
+	"Searched {{count}} sites_other": "Arandı {{count}} sites_other",
+	"Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" aranıyor",
+	"Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" için Bilgi aranıyor",
+	"Searxng Query URL": "Searxng Sorgu URL'si",
+	"See readme.md for instructions": "Yönergeler için readme.md dosyasına bakın",
+	"See what's new": "Yeniliklere göz atın",
+	"Seed": "Seed",
+	"Select a base model": "Bir temel model seç",
+	"Select a engine": "Bir motor seç",
+	"Select a function": "Bir fonksiyon seç",
+	"Select a group": "",
+	"Select a model": "Bir model seç",
+	"Select a pipeline": "Bir pipeline seç",
+	"Select a pipeline url": "Bir pipeline URL'si seç",
+	"Select a tool": "Bir araç seç",
+	"Select Engine": "Motor Seç",
+	"Select Knowledge": "",
+	"Select model": "Model seç",
+	"Select only one model to call": "Arama için sadece bir model seç",
+	"Selected model(s) do not support image inputs": "Seçilen model(ler) görüntü girişlerini desteklemiyor",
+	"Semantic distance to query": "",
+	"Send": "Gönder",
+	"Send a Message": "Bir Mesaj Gönder",
+	"Send message": "Mesaj gönder",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "İsteğe `stream_options: { include_usage: true }` gönderir.\nDesteklenen sağlayıcılar, ayarlandığında yanıtta token kullanım bilgilerini döndürecektir.",
+	"September": "Eylül",
+	"Serper API Key": "Serper API Anahtarı",
+	"Serply API Key": "Serply API Anahtarı",
+	"Serpstack API Key": "Serpstack API Anahtarı",
+	"Server connection verified": "Sunucu bağlantısı doğrulandı",
+	"Set as default": "Varsayılan olarak ayarla",
+	"Set CFG Scale": "CFG Ölçeğini Ayarla",
+	"Set Default Model": "Varsayılan Modeli Ayarla",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Gömme modelini ayarlayın (örn. {{model}})",
+	"Set Image Size": "Görüntü Boyutunu Ayarla",
+	"Set reranking model (e.g. {{model}})": "Yeniden sıralama modelini ayarlayın (örn. {{model}})",
+	"Set Sampler": "Örnekleyiciyi Ayarla",
+	"Set Scheduler": "Zamanlayıcıyı Ayarla",
+	"Set Steps": "Adımları Ayarla",
+	"Set Task Model": "Görev Modeli Ayarla",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Ses Ayarla",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Ayarlar",
+	"Settings saved successfully!": "Ayarlar başarıyla kaydedildi!",
+	"Share": "Paylaş",
+	"Share Chat": "Sohbeti Paylaş",
+	"Share to OpenWebUI Community": "OpenWebUI Topluluğu ile Paylaş",
+	"Show": "Göster",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Yönetici Ayrıntılarını Hesap Bekliyor Ekranında Göster",
+	"Show shortcuts": "Kısayolları göster",
+	"Show your support!": "Desteğinizi gösterin!",
+	"Showcased creativity": "Sergilenen yaratıcılık",
+	"Sign in": "Oturum aç",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Çıkış Yap",
+	"Sign up": "Kaydol",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Kaynak",
+	"Speech Playback Speed": "Konuşma Oynatma Hızı",
+	"Speech recognition error: {{error}}": "Konuşma tanıma hatası: {{error}}",
+	"Speech-to-Text Engine": "Konuşmadan Metne Motoru",
+	"Stop": "",
+	"Stop Sequence": "Diziyi Durdur",
+	"Stream Chat Response": "",
+	"STT Model": "STT Modeli",
+	"STT Settings": "STT Ayarları",
+	"Subtitle (e.g. about the Roman Empire)": "Alt başlık (örn. Roma İmparatorluğu hakkında)",
+	"Success": "Başarılı",
+	"Successfully updated.": "Başarıyla güncellendi.",
+	"Suggested": "Önerilen",
+	"Support": "Destek",
+	"Support this plugin:": "Bu eklentiyi destekleyin:",
+	"Sync directory": "",
+	"System": "Sistem",
+	"System Instructions": "",
+	"System Prompt": "Sistem Promptu",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Durdurmak için dokunun",
+	"Tavily API Key": "Tavily API Anahtarı",
+	"Tell us more:": "Bize daha fazlasını anlat:",
+	"Temperature": "Temperature",
+	"Template": "Şablon",
+	"Temporary Chat": "Geçici Sohbet",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Metinden Sese Motoru",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Geri bildiriminiz için teşekkürler!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Bu eklentinin arkasındaki geliştiriciler topluluktan tutkulu gönüllülerdir. Bu eklentinin yararlı olduğunu düşünüyorsanız, gelişimine katkıda bulunmayı düşünün.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "MB cinsinden maksimum dosya boyutu. Dosya boyutu bu sınırı aşarsa, dosya yüklenmeyecektir.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Sohbette aynı anda kullanılabilecek maksimum dosya sayısı. Dosya sayısı bu sınırı aşarsa, dosyalar yüklenmeyecektir.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Puan 0.0 (%0) ile 1.0 (%100) arasında bir değer olmalıdır.",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Tema",
+	"Thinking...": "Düşünüyor...",
+	"This action cannot be undone. Do you wish to continue?": "Bu eylem geri alınamaz. Devam etmek istiyor musunuz?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Bu, önemli konuşmalarınızın güvenli bir şekilde arkayüz veritabanınıza kaydedildiğini garantiler. Teşekkür ederiz!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Bu deneysel bir özelliktir, beklendiği gibi çalışmayabilir ve her an değişiklik yapılabilir.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Bu silinecek",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Kapsamlı açıklama",
+	"Tika": "",
+	"Tika Server URL required.": "Tika Sunucu URL'si gereklidir.",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "İpucu: Her değiştirmeden sonra sohbet girişinde tab tuşuna basarak birden fazla değişken yuvasını art arda güncelleyin.",
+	"Title": "Başlık",
+	"Title (e.g. Tell me a fun fact)": "Başlık (e.g. Bana ilginç bir bilgi ver)",
+	"Title Auto-Generation": "Otomatik Başlık Oluşturma",
+	"Title cannot be an empty string.": "Başlık boş bir dize olamaz.",
+	"Title Generation Prompt": "Başlık Oluşturma Promptu",
+	"TLS": "",
+	"To access the available model names for downloading,": "İndirilebilir mevcut model adlarına erişmek için,",
+	"To access the GGUF models available for downloading,": "İndirilebilir mevcut GGUF modellerine erişmek için,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "WebUI'ye erişmek için lütfen yöneticiyle iletişime geçin. Yöneticiler kullanıcı durumlarını Yönetici Panelinden yönetebilir.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Burada eylemleri seçmek için öncelikle bunları \"İşlevler\" çalışma alanına ekleyin.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Filtreleri burada seçmek için öncelikle bunları \"İşlevler\" çalışma alanına ekleyin.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Araçları burada seçmek için öncelikle bunları \"Araçlar\" çalışma alanına ekleyin.",
+	"Toast notifications for new updates": "",
+	"Today": "Bugün",
+	"Toggle settings": "Ayarları Aç/Kapat",
+	"Toggle sidebar": "Kenar Çubuğunu Aç/Kapat",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "Bağlam Yenilemesinde Korunacak Tokenler (num_keep)",
+	"Too verbose": "",
+	"Tool created successfully": "Araç başarıyla oluşturuldu",
+	"Tool deleted successfully": "Araç başarıyla silindi",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Araç başarıyla içe aktarıldı",
+	"Tool Name": "",
+	"Tool updated successfully": "Araç başarıyla güncellendi",
+	"Tools": "Araçlar",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Araçlar, keyfi kod yürütme ile bir fonksiyon çağırma sistemine sahiptir",
+	"Tools have a function calling system that allows arbitrary code execution": "Araçlar, keyfi kod yürütme izni veren bir fonksiyon çağırma sistemine sahiptir",
+	"Tools have a function calling system that allows arbitrary code execution.": "Araçlar, keyfi kod yürütme izni veren bir fonksiyon çağırma sistemine sahiptir.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama'ya erişmede sorun mu yaşıyorsunuz?",
+	"TTS Model": "TTS Modeli",
+	"TTS Settings": "TTS Ayarları",
+	"TTS Voice": "TTS Sesi",
+	"Type": "Tür",
+	"Type Hugging Face Resolve (Download) URL": "HuggingFace Çözümleme (İndirme) URL'sini Yazın",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ah! {{provider}}'a bağlanırken bir sorun oluştu.",
+	"UI": "Arayüz",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Sabitlemeyi Kaldır",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Güncelle",
+	"Update and Copy Link": "Güncelle ve Bağlantıyı Kopyala",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Parolayı Güncelle",
+	"Updated": "",
+	"Updated at": "Şu tarihte güncellendi:",
+	"Updated At": "",
+	"Upload": "Yükle",
+	"Upload a GGUF model": "Bir GGUF modeli yükle",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Dosyaları Yükle",
+	"Upload Pipeline": "Pipeline Yükle",
+	"Upload Progress": "Yükleme İlerlemesi",
+	"URL": "",
+	"URL Mode": "URL Modu",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Gravatar Kullan",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Baş Harfleri Kullan",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "kullanıcı",
+	"User": "",
+	"User location successfully retrieved.": "Kullanıcı konumu başarıyla alındı.",
+	"Username": "",
+	"Users": "Kullanıcılar",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Kullan",
+	"Valid time units:": "Geçerli zaman birimleri:",
+	"Valves": "Valvler",
+	"Valves updated": "Valvler güncellendi",
+	"Valves updated successfully": "Valvler başarıyla güncellendi",
+	"variable": "değişken",
+	"variable to have them replaced with clipboard content.": "panodaki içerikle değiştirilmesi için değişken.",
+	"Version": "Sürüm",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Ses",
+	"Voice Input": "",
+	"Warning": "Uyarı",
+	"Warning:": "Uyarı:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Uyarı: Gömme modelinizi günceller veya değiştirirseniz, tüm belgeleri yeniden içe aktarmanız gerekecektir.",
+	"Web": "Web",
+	"Web API": "Web API",
+	"Web Loader Settings": "Web Yükleyici Ayarları",
+	"Web Search": "Web Araması",
+	"Web Search Engine": "Web Arama Motoru",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI Ayarları",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Yenilikler:",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "Whisper (Yerel)",
+	"Why?": "",
+	"Widescreen Mode": "Geniş Ekran Modu",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Çalışma Alanı",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Bir prompt önerisi yazın (örn. Sen kimsin?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "[Konuyu veya anahtar kelimeyi] özetleyen 50 kelimelik bir özet yazın.",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Dün",
+	"You": "Sen",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Aynı anda en fazla {{maxCount}} dosya ile sohbet edebilirsiniz.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Aşağıdaki 'Yönet' düğmesi aracılığıyla bellekler ekleyerek LLM'lerle etkileşimlerinizi kişiselleştirebilir, onları daha yararlı ve size özel hale getirebilirsiniz.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Arşivlenmiş sohbetleriniz yok.",
+	"You have shared this chat": "Bu sohbeti paylaştınız",
+	"You're a helpful assistant.": "Sen yardımsever bir asistansın.",
+	"You're now logged in.": "Şimdi giriş yaptınız.",
+	"Your account status is currently pending activation.": "Hesap durumunuz şu anda etkinleştirilmeyi bekliyor.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Tüm katkınız doğrudan eklenti geliştiricisine gidecektir; Open WebUI herhangi bir yüzde almaz. Ancak seçilen finansman platformunun kendi ücretleri olabilir.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Youtube Yükleyici Ayarları"
+}
diff --git a/src/lib/i18n/locales/uk-UA/translation.json b/src/lib/i18n/locales/uk-UA/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..36cd4f4ad12f0f37dd61ac28956b726510a1cb3c
--- /dev/null
+++ b/src/lib/i18n/locales/uk-UA/translation.json
@@ -0,0 +1,1027 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' для відсутності терміну дії.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(e.g. `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(e.g. `sh webui.sh --api`)",
+	"(latest)": "(остання)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "Чати {{user}}а",
+	"{{webUIName}} Backend Required": "Необхідно підключення бекенду {{webUIName}}",
+	"*Prompt node ID(s) are required for image generation": "*Для генерації зображення потрібно вказати ідентифікатор(и) вузла(ів)",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "Нова версія (v{{LATEST_VERSION}}) зараз доступна.",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Модель задач використовується при виконанні таких завдань, як генерація заголовків для чатів та пошукових запитів в Інтернеті",
+	"a user": "користувача",
+	"About": "Про програму",
+	"Access": "Доступ",
+	"Access Control": "Контроль доступу",
+	"Accessible to all users": "Доступно всім користувачам",
+	"Account": "Обліковий запис",
+	"Account Activation Pending": "Очікування активації облікового запису",
+	"Accurate information": "Точна інформація",
+	"Actions": "Дії",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "Активуйте цю команду, ввівши \"/{{COMMAND}}\" у введення чату.",
+	"Active Users": "Активні користувачі",
+	"Add": "Додати",
+	"Add a model ID": "Додайти ID моделі",
+	"Add a short description about what this model does": "Додайте короткий опис того, що робить ця модель",
+	"Add a tag": "Додайти тег",
+	"Add Arena Model": "Додати модель Arena",
+	"Add Connection": "Додати з'єднання",
+	"Add Content": "Додати вміст",
+	"Add content here": "Додайте вміст сюди",
+	"Add custom prompt": "Додати користувацьку підказку",
+	"Add Files": "Додати файли",
+	"Add Group": "Додати групу",
+	"Add Memory": "Додати пам'ять",
+	"Add Model": "Додати модель",
+	"Add Tag": "Додати тег",
+	"Add Tags": "Додати теги",
+	"Add text content": "Додати текстовий вміст",
+	"Add User": "Додати користувача",
+	"Add User Group": "Додати групу користувачів",
+	"Adjusting these settings will apply changes universally to all users.": "Зміни в цих налаштуваннях будуть застосовані для всіх користувачів.",
+	"admin": "адмін",
+	"Admin": "Адмін",
+	"Admin Panel": "Адмін-панель",
+	"Admin Settings": "Адмін-панель",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Адміністратори мають доступ до всіх інструментів у будь-який час; користувачам потрібні інструменти, призначені для кожної моделі в робочій області.",
+	"Advanced Parameters": "Розширені параметри",
+	"Advanced Params": "Розширені параметри",
+	"All chats": "Усі чати",
+	"All Documents": "Усі документи",
+	"All models deleted successfully": "Всі моделі видалені успішно",
+	"Allow Chat Delete": "Дозволити видалення чату",
+	"Allow Chat Deletion": "Дозволити видалення чату",
+	"Allow Chat Edit": "Дозволити редагування чату",
+	"Allow File Upload": "Дозволити завантаження файлів",
+	"Allow non-local voices": "Дозволити не локальні голоси",
+	"Allow Temporary Chat": "Дозволити тимчасовий чат",
+	"Allow User Location": "Доступ до місцезнаходження",
+	"Allow Voice Interruption in Call": "Дозволити переривання голосу під час виклику",
+	"Already have an account?": "Вже є обліковий запис?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "Альтернатива параметру top_p, яка має на меті забезпечити баланс якості та різноманітності. Параметр p представляє мінімальну ймовірність для того, щоб токен був врахований, відносно ймовірності найбільш ймовірного токена. Наприклад, при p=0.05 і найбільш імовірному токені з ймовірністю 0.9, логіти зі значенням менше 0.045 будуть відфільтровані. (За замовчуванням: 0.0)",
+	"Amazing": "Чудово",
+	"an assistant": "асистента",
+	"and": "та",
+	"and {{COUNT}} more": "та ще {{COUNT}}",
+	"and create a new shared link.": "і створіть нове спільне посилання.",
+	"API Base URL": "URL-адреса API",
+	"API Key": "Ключ API",
+	"API Key created.": "Ключ API створено.",
+	"API keys": "Ключі API",
+	"Application DN": "DN застосунку",
+	"Application DN Password": "Пароль DN застосунку",
+	"applies to all users with the \"user\" role": "стосується всіх користувачів з роллю \"користувач\"",
+	"April": "Квітень",
+	"Archive": "Архів",
+	"Archive All Chats": "Архівувати всі чати",
+	"Archived Chats": "Архівовані чати",
+	"archived-chat-export": "експорт-архівованих-чатів",
+	"Are you sure you want to unarchive all archived chats?": "Ви впевнені, що хочете розархівувати всі архівовані чати?",
+	"Are you sure?": "Ви впевнені?",
+	"Arena Models": "Моделі Arena",
+	"Artifacts": "Артефакти",
+	"Ask a question": "Задати питання",
+	"Assistant": "Асистент",
+	"Attach file": "Прикріпити файл",
+	"Attention to detail": "Увага до деталей",
+	"Attribute for Username": "Атрибут для імені користувача",
+	"Audio": "Аудіо",
+	"August": "Серпень",
+	"Authenticate": "Автентифікувати",
+	"Auto-Copy Response to Clipboard": "Автокопіювання відповіді в буфер обміну",
+	"Auto-playback response": "Автоматичне відтворення відповіді",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Рядок авторизації API",
+	"AUTOMATIC1111 Base URL": "URL-адреса AUTOMATIC1111",
+	"AUTOMATIC1111 Base URL is required.": "Необхідна URL-адреса AUTOMATIC1111.",
+	"Available list": "Список доступності",
+	"available!": "доступно!",
+	"Awful": "Жахливо",
+	"Azure AI Speech": "Мовлення Azure AI",
+	"Azure Region": "Регіон Azure",
+	"Back": "Назад",
+	"Bad Response": "Неправильна відповідь",
+	"Banners": "Прапори",
+	"Base Model (From)": "Базова модель (від)",
+	"Batch Size (num_batch)": "Розмір партії (num_batch)",
+	"before": "до того, як",
+	"Being lazy": "Не поспішати",
+	"Bing Search V7 Endpoint": "Точка доступу Bing Search V7",
+	"Bing Search V7 Subscription Key": "Ключ підписки Bing Search V7",
+	"Brave Search API Key": "Ключ API пошуку Brave",
+	"By {{name}}": "Від {{name}}",
+	"Bypass SSL verification for Websites": "Обхід SSL-перевірки для веб-сайтів",
+	"Call": "Виклик",
+	"Call feature is not supported when using Web STT engine": "Функція виклику не підтримується при використанні Web STT (розпізнавання мовлення) рушія",
+	"Camera": "Камера",
+	"Cancel": "Скасувати",
+	"Capabilities": "Можливості",
+	"Certificate Path": "Шлях до сертифіката",
+	"Change Password": "Змінити пароль",
+	"Character": "Персонаж",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "Відкривати нові горизонти",
+	"Chat": "Чат",
+	"Chat Background Image": "Фонове зображення чату",
+	"Chat Bubble UI": "Чат у вигляді бульбашок",
+	"Chat Controls": "Керування чатом",
+	"Chat direction": "Напрям чату",
+	"Chat Overview": "Огляд чату",
+	"Chat Permissions": "Дозволи чату",
+	"Chat Tags Auto-Generation": "Автоматична генерація тегів чату",
+	"Chats": "Чати",
+	"Check Again": "Перевірити ще раз",
+	"Check for updates": "Перевірити оновлення",
+	"Checking for updates...": "Перевірка оновлень...",
+	"Choose a model before saving...": "Оберіть модель перед збереженням...",
+	"Chunk Overlap": "Перекриття фрагментів",
+	"Chunk Params": "Параметри фрагментів",
+	"Chunk Size": "Розмір фрагменту",
+	"Ciphers": "Шифри",
+	"Citation": "Цитування",
+	"Clear memory": "Очистити пам'ять",
+	"click here": "натисніть тут",
+	"Click here for filter guides.": "Натисніть тут для інструкцій із фільтрації",
+	"Click here for help.": "Натисніть тут, щоб отримати допомогу.",
+	"Click here to": "Натисніть тут, щоб",
+	"Click here to download user import template file.": "Натисніть тут, щоб завантажити файл шаблону імпорту користувача.",
+	"Click here to learn more about faster-whisper and see the available models.": "Натисніть тут, щоб дізнатися більше про faster-whisper та переглянути доступні моделі.",
+	"Click here to select": "Натисніть тут, щоб обрати",
+	"Click here to select a csv file.": "Натисніть тут, щоб обрати csv-файл.",
+	"Click here to select a py file.": "Натисніть тут, щоб обрати py-файл.",
+	"Click here to upload a workflow.json file.": "Натисніть тут, щоб завантажити файл workflow.json.",
+	"click here.": "натисніть тут.",
+	"Click on the user role button to change a user's role.": "Натисніть кнопку ролі користувача, щоб змінити роль користувача.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Відмовлено в дозволі на запис до буфера обміну. Будь ласка, перевірте налаштування вашого браузера, щоб надати необхідний доступ.",
+	"Clone": "Клонувати",
+	"Close": "Закрити",
+	"Code execution": "Виконання коду",
+	"Code formatted successfully": "Код успішно відформатовано",
+	"Collection": "Колекція",
+	"Color": "Колір",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "URL-адреса ComfyUI",
+	"ComfyUI Base URL is required.": "Необхідно вказати URL-адресу ComfyUI.",
+	"ComfyUI Workflow": "ComfyUI Workflow",
+	"ComfyUI Workflow Nodes": "Вузли Workflow в ComfyUI",
+	"Command": "Команда",
+	"Completions": "Завершення",
+	"Concurrent Requests": "Одночасні запити",
+	"Configure": "Налаштувати",
+	"Configure Models": "",
+	"Confirm": "Підтвердити",
+	"Confirm Password": "Підтвердіть пароль",
+	"Confirm your action": "Підтвердіть свою дію",
+	"Connections": "З'єднання",
+	"Contact Admin for WebUI Access": "Зверніться до адміна для отримання доступу до WebUI",
+	"Content": "Зміст",
+	"Content Extraction": "Вилучення вмісту",
+	"Context Length": "Довжина контексту",
+	"Continue Response": "Продовжити відповідь",
+	"Continue with {{provider}}": "Продовжити з {{provider}}",
+	"Continue with Email": "Продовжити з електронною поштою",
+	"Continue with LDAP": "Продовжити з LDAP",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "Керування розбиттям тексту повідомлення для TTS-запитів. 'Punctuation' розбиває на речення, 'paragraphs' розбиває на абзаци, а 'none' залишає повідомлення як один рядок.",
+	"Controls": "Керування",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "Контролює баланс між зв'язністю та різноманітністю виходу. Нижче значення призведе до більш зосередженого та зв'язного тексту. (За замовчуванням: 5.0)",
+	"Copied": "Скопійовано",
+	"Copied shared chat URL to clipboard!": "Скопійовано URL-адресу спільного чату в буфер обміну!",
+	"Copied to clipboard": "Скопійовано в буфер обміну",
+	"Copy": "Копіювати",
+	"Copy last code block": "Копіювати останній блок коду",
+	"Copy last response": "Копіювати останню відповідь",
+	"Copy Link": "Копіювати посилання",
+	"Copy to clipboard": "Копіювати в буфер обміну",
+	"Copying to clipboard was successful!": "Копіювання в буфер обміну виконано успішно!",
+	"Create": "Створити",
+	"Create a knowledge base": "Створити базу знань",
+	"Create a model": "Створити модель",
+	"Create Account": "Створити обліковий запис",
+	"Create Admin Account": "Створити обліковий запис адміністратора",
+	"Create Group": "Створити групу",
+	"Create Knowledge": "Створити знання",
+	"Create new key": "Створити новий ключ",
+	"Create new secret key": "Створити новий секретний ключ",
+	"Created at": "Створено у",
+	"Created At": "Створено у",
+	"Created by": "Створено",
+	"CSV Import": "Імпорт CSV",
+	"Current Model": "Поточна модель",
+	"Current Password": "Поточний пароль",
+	"Custom": "Налаштувати",
+	"Dark": "Темна",
+	"Database": "База даних",
+	"December": "Грудень",
+	"Default": "За замовчуванням",
+	"Default (Open AI)": "За замовчуванням (Open AI)",
+	"Default (SentenceTransformers)": "За замовчуванням (SentenceTransformers)",
+	"Default Model": "Модель за замовчуванням",
+	"Default model updated": "Модель за замовчуванням оновлено",
+	"Default Models": "",
+	"Default permissions": "Дозволи за замовчуванням",
+	"Default permissions updated successfully": "Дозволи за замовчуванням успішно оновлено",
+	"Default Prompt Suggestions": "Пропозиції промтів замовчуванням",
+	"Default to 389 or 636 if TLS is enabled": "За замовчуванням використовується 389 або 636, якщо TLS увімкнено.",
+	"Default to ALL": "За замовчуванням — ВСІ.",
+	"Default User Role": "Роль користувача за замовчуванням",
+	"Delete": "Видалити",
+	"Delete a model": "Видалити модель",
+	"Delete All Chats": "Видалити усі чати",
+	"Delete All Models": "Видалити всі моделі",
+	"Delete chat": "Видалити чат",
+	"Delete Chat": "Видалити чат",
+	"Delete chat?": "Видалити чат?",
+	"Delete folder?": "Видалити папку?",
+	"Delete function?": "Видалити функцію?",
+	"Delete prompt?": "Видалити промт?",
+	"delete this link": "видалити це посилання",
+	"Delete tool?": "Видалити інструмент?",
+	"Delete User": "Видалити користувача",
+	"Deleted {{deleteModelTag}}": "Видалено {{deleteModelTag}}",
+	"Deleted {{name}}": "Видалено {{name}}",
+	"Deleted User": "Видалений користувач",
+	"Describe your knowledge base and objectives": "Опишіть вашу базу знань та цілі",
+	"Description": "Опис",
+	"Didn't fully follow instructions": "Не повністю дотримувалися інструкцій",
+	"Disabled": "Вимкнено",
+	"Discover a function": "Знайдіть функцію",
+	"Discover a model": "Знайдіть модель",
+	"Discover a prompt": "Знайдіть промт",
+	"Discover a tool": "Знайдіть інструмент",
+	"Discover wonders": "Відкривайте чудеса",
+	"Discover, download, and explore custom functions": "Знайдіть, завантажте та досліджуйте налаштовані функції",
+	"Discover, download, and explore custom prompts": "Знайдіть, завантажте та досліджуйте налаштовані промти",
+	"Discover, download, and explore custom tools": "Знайдіть, завантажте та досліджуйте налаштовані інструменти",
+	"Discover, download, and explore model presets": "Знайдіть, завантажте та досліджуйте налаштування моделей",
+	"Dismissible": "Неприйнятно",
+	"Display": "Відображення",
+	"Display Emoji in Call": "Відображати емодзі у викликах",
+	"Display the username instead of You in the Chat": "Показувати ім'я користувача замість 'Ви' в чаті",
+	"Displays citations in the response": "Показує посилання у відповіді",
+	"Dive into knowledge": "Зануртесь у знання",
+	"Do not install functions from sources you do not fully trust.": "Не встановлюйте функції з джерел, яким ви не повністю довіряєте.",
+	"Do not install tools from sources you do not fully trust.": "Не встановлюйте інструменти з джерел, яким ви не повністю довіряєте.",
+	"Document": "Документ",
+	"Documentation": "Документація",
+	"Documents": "Документи",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "не встановлює жодних зовнішніх з'єднань, і ваші дані залишаються в безпеці на вашому локальному сервері.",
+	"Don't have an account?": "Немає облікового запису?",
+	"don't install random functions from sources you don't trust.": "не встановлюйте випадкові функції з джерел, яким ви не довіряєте.",
+	"don't install random tools from sources you don't trust.": "не встановлюйте випадкові інструменти з джерел, яким ви не довіряєте.",
+	"Don't like the style": "Не подобається стиль",
+	"Done": "Готово",
+	"Download": "Завантажити",
+	"Download canceled": "Завантаження скасовано",
+	"Download Database": "Завантажити базу даних",
+	"Drag and drop a file to upload or select a file to view": "Перетягніть файл для завантаження або виберіть файл для перегляду",
+	"Draw": "Малювати",
+	"Drop any files here to add to the conversation": "Перетягніть сюди файли, щоб додати до розмови",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "напр., '30s','10m'. Дійсні одиниці часу: 'с', 'хв', 'г'.",
+	"e.g. A filter to remove profanity from text": "напр., фільтр для видалення нецензурної лексики з тексту",
+	"e.g. My Filter": "напр., Мій фільтр",
+	"e.g. My Tools": "напр., Мої інструменти",
+	"e.g. my_filter": "напр., my_filter",
+	"e.g. my_tools": "напр., my_tools",
+	"e.g. Tools for performing various operations": "напр., Інструменти для виконання різних операцій",
+	"Edit": "Редагувати",
+	"Edit Arena Model": "Редагувати модель Arena",
+	"Edit Connection": "Редагувати з'єднання",
+	"Edit Default Permissions": "Редагувати дозволи за замовчуванням",
+	"Edit Memory": "Редагувати пам'ять",
+	"Edit User": "Редагувати користувача",
+	"Edit User Group": "Редагувати групу користувачів",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Ел. пошта",
+	"Embark on adventures": "Вирушайте в пригоди",
+	"Embedding Batch Size": "Розмір пакету під час вбудовування",
+	"Embedding Model": "Модель вбудовування",
+	"Embedding Model Engine": "Рушій моделі вбудовування ",
+	"Embedding model set to \"{{embedding_model}}\"": "Встановлена модель вбудовування \"{{embedding_model}}\"",
+	"Enable API Key Auth": "Увімкнути автентифікацію за допомогою API ключа",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Увімкнути спільний доступ",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "Увімкнути блокування пам'яті (mlock), щоб запобігти виведенню даних моделі з оперативної пам'яті. Цей параметр блокує робочий набір сторінок моделі в оперативній пам'яті, гарантуючи, що вони не будуть виведені на диск. Це може допомогти підтримувати продуктивність, уникати помилок сторінок та забезпечувати швидкий доступ до даних.",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "Увімкнути відображення пам'яті (mmap) для завантаження даних моделі. Цей параметр дозволяє системі використовувати дискове сховище як розширення оперативної пам'яті, трактуючи файли на диску, як ніби вони знаходяться в RAM. Це може покращити продуктивність моделі, дозволяючи швидший доступ до даних. Однак, він може не працювати коректно на всіх системах і може споживати значну кількість дискового простору.",
+	"Enable Message Rating": "Увімкнути оцінку повідомлень",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "Увімкнути вибірку Mirostat для контролю над непередбачуваністю. (За замовчуванням: 0, 0 = Вимкнено, 1 = Mirostat, 2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "Дозволити нові реєстрації",
+	"Enable Web Search": "Увімкнути веб-пошук",
+	"Enabled": "Увімкнено",
+	"Engine": "Рушій",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Переконайтеся, що ваш CSV-файл містить 4 колонки в такому порядку: Ім'я, Email, Пароль, Роль.",
+	"Enter {{role}} message here": "Введіть повідомлення {{role}} тут",
+	"Enter a detail about yourself for your LLMs to recall": "Введіть відомості про себе для запам'ятовування вашими LLM.",
+	"Enter api auth string (e.g. username:password)": "Введіть рядок авторизації api (напр, ім'я користувача:пароль)",
+	"Enter Application DN": "Введіть DN застосунку",
+	"Enter Application DN Password": "Введіть пароль DN застосунку",
+	"Enter Bing Search V7 Endpoint": "Введіть точку доступу Bing Search V7",
+	"Enter Bing Search V7 Subscription Key": "Введіть ключ підписки Bing Search V7",
+	"Enter Brave Search API Key": "Введіть ключ API для пошуку Brave",
+	"Enter certificate path": "Введіть шлях до сертифіката",
+	"Enter CFG Scale (e.g. 7.0)": "Введіть масштаб CFG (напр., 7.0)",
+	"Enter Chunk Overlap": "Введіть перекриття фрагменту",
+	"Enter Chunk Size": "Введіть розмір фрагменту",
+	"Enter description": "Введіть опис",
+	"Enter Github Raw URL": "Введіть Raw URL-адресу Github",
+	"Enter Google PSE API Key": "Введіть ключ API Google PSE",
+	"Enter Google PSE Engine Id": "Введіть Google PSE Engine Id",
+	"Enter Image Size (e.g. 512x512)": "Введіть розмір зображення (напр., 512x512)",
+	"Enter Jina API Key": "Введіть ключ API для Jina",
+	"Enter language codes": "Введіть мовні коди",
+	"Enter Model ID": "Введіть ID моделі",
+	"Enter model tag (e.g. {{modelTag}})": "Введіть тег моделі (напр., {{modelTag}})",
+	"Enter Mojeek Search API Key": "Введіть API ключ для пошуку Mojeek",
+	"Enter Number of Steps (e.g. 50)": "Введіть кількість кроків (напр., 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "Введіть семплер (напр., Euler a)",
+	"Enter Scheduler (e.g. Karras)": "Введіть планувальник (напр., Karras)",
+	"Enter Score": "Введіть бал",
+	"Enter SearchApi API Key": "Введіть ключ API для SearchApi",
+	"Enter SearchApi Engine": "Введіть SearchApi рушія",
+	"Enter Searxng Query URL": "Введіть URL-адресу запиту Searxng",
+	"Enter Seed": "Введіть насіння",
+	"Enter Serper API Key": "Введіть ключ API Serper",
+	"Enter Serply API Key": "Введіть ключ API Serply",
+	"Enter Serpstack API Key": "Введіть ключ API Serpstack",
+	"Enter server host": "Введіть хост сервера",
+	"Enter server label": "Введіть мітку сервера",
+	"Enter server port": "Введіть порт сервера",
+	"Enter stop sequence": "Введіть символ зупинки",
+	"Enter system prompt": "Введіть системний промт",
+	"Enter Tavily API Key": "Введіть ключ API Tavily",
+	"Enter Tika Server URL": "Введіть URL-адресу сервера Tika ",
+	"Enter Top K": "Введіть Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Введіть URL-адресу (напр., http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Введіть URL-адресу (напр., http://localhost:11434)",
+	"Enter Your Email": "Введіть вашу ел. пошту",
+	"Enter Your Full Name": "Введіть ваше ім'я",
+	"Enter your message": "Введіть повідомлення ",
+	"Enter Your Password": "Введіть ваш пароль",
+	"Enter Your Role": "Введіть вашу роль",
+	"Enter Your Username": "Введіть своє ім'я користувача",
+	"Error": "Помилка",
+	"ERROR": "ПОМИЛКА",
+	"Evaluations": "Оцінювання",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Приклад: (&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "Приклад: ВСІ",
+	"Example: ou=users,dc=foo,dc=example": "Приклад: ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "Приклад: sAMAccountName або uid або userPrincipalName",
+	"Exclude": "Виключити",
+	"Experimental": "Експериментальне",
+	"Explore the cosmos": "Досліджуйте космос",
+	"Export": "Експорт",
+	"Export All Archived Chats": "Експорт всіх архівованих чатів",
+	"Export All Chats (All Users)": "Експорт всіх чатів (всіх користувачів)",
+	"Export chat (.json)": "Експорт чату (.json)",
+	"Export Chats": "Експорт чатів",
+	"Export Config to JSON File": "Експорт конфігурації у файл JSON",
+	"Export Functions": "Експорт функцій ",
+	"Export Models": "Експорт моделей",
+	"Export Presets": "Експорт пресетів",
+	"Export Prompts": "Експорт промтів",
+	"Export to CSV": "Експорт в CSV",
+	"Export Tools": "Експорт інструментів",
+	"External Models": "Зовнішні моделі",
+	"Failed to add file.": "Не вдалося додати файл.",
+	"Failed to create API Key.": "Не вдалося створити API ключ.",
+	"Failed to read clipboard contents": "Не вдалося прочитати вміст буфера обміну",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Не вдалося оновити налаштування",
+	"Failed to upload file.": "Не вдалося завантажити файл.",
+	"February": "Лютий",
+	"Feedback History": "Історія відгуків",
+	"Feedbacks": "Відгуки",
+	"Feel free to add specific details": "Не соромтеся додавати конкретні деталі",
+	"File": "Файл",
+	"File added successfully.": "Файл успішно додано.",
+	"File content updated successfully.": "Вміст файлу успішно оновлено.",
+	"File Mode": "Файловий режим",
+	"File not found.": "Файл не знайдено.",
+	"File removed successfully.": "Файл успішно видалено.",
+	"File size should not exceed {{maxSize}} MB.": "Розмір файлу не повинен перевищувати {{maxSize}} МБ.",
+	"Files": "Файли",
+	"Filter is now globally disabled": "Фільтр глобально вимкнено",
+	"Filter is now globally enabled": "Фільтр увімкнено глобально",
+	"Filters": "Фільтри",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Виявлено підробку відбитків: Неможливо використовувати ініціали як аватар. Повернення до зображення профілю за замовчуванням.",
+	"Fluidly stream large external response chunks": "Плавно передавати великі фрагменти зовнішніх відповідей",
+	"Focus chat input": "Фокус вводу чату",
+	"Folder deleted successfully": "Папку успішно видалено",
+	"Folder name cannot be empty": "Назва папки не може бути порожньою",
+	"Folder name cannot be empty.": "Назва папки не може бути порожньою.",
+	"Folder name updated successfully": "Назву папки успішно оновлено",
+	"Followed instructions perfectly": "Бездоганно дотримувався інструкцій",
+	"Forge new paths": "Прокладайте нові шляхи",
+	"Form": "Форма",
+	"Format your variables using brackets like this:": "Форматуйте свої змінні, використовуючи фігурні дужки таким чином:",
+	"Frequency Penalty": "Штраф за частоту",
+	"Function": "Функція",
+	"Function created successfully": "Функцію успішно створено",
+	"Function deleted successfully": "Функцію успішно видалено",
+	"Function Description": "Опис функції",
+	"Function ID": "ID функції",
+	"Function is now globally disabled": "Функція зараз глобально вимкнена",
+	"Function is now globally enabled": "Функція зараз глобально увімкнена ",
+	"Function Name": "Назва функції",
+	"Function updated successfully": "Функцію успішно оновлено",
+	"Functions": "Функції",
+	"Functions allow arbitrary code execution": "Функції дозволяють виконання довільного коду",
+	"Functions allow arbitrary code execution.": "Функції дозволяють виконання довільного коду.",
+	"Functions imported successfully": "Функції успішно імпортовано",
+	"General": "Загальні",
+	"General Settings": "Загальні налаштування",
+	"Generate Image": "Створити зображення",
+	"Generating search query": "Сформувати пошуковий запит",
+	"Generation Info": "Інформація про генерацію",
+	"Get started": "Почати",
+	"Get started with {{WEBUI_NAME}}": "Почати з {{WEBUI_NAME}}",
+	"Global": "Глоб.",
+	"Good Response": "Гарна відповідь",
+	"Google PSE API Key": "Ключ API Google PSE",
+	"Google PSE Engine Id": "Id рушія Google PSE",
+	"Group created successfully": "Групу успішно створено",
+	"Group deleted successfully": "Групу успішно видалено",
+	"Group Description": "Опис групи",
+	"Group Name": "Назва групи",
+	"Group updated successfully": "Групу успішно оновлено",
+	"Groups": "Групи",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Тактильний зворотній зв'язок",
+	"has no conversations.": "не має розмов.",
+	"Hello, {{name}}": "Привіт, {{name}}",
+	"Help": "Допоможіть",
+	"Help us create the best community leaderboard by sharing your feedback history!": "Допоможіть нам створити найкращу таблицю лідерів спільноти, поділившись історією своїх відгуків!",
+	"Hex Color": "Шістнадцятковий колір",
+	"Hex Color - Leave empty for default color": "Шістнадцятковий колір — залиште порожнім для кольору за замовчуванням",
+	"Hide": "Приховати",
+	"Host": "Хост",
+	"How can I help you today?": "Чим я можу допомогти вам сьогодні?",
+	"How would you rate this response?": "Як би ви оцінили цю відповідь?",
+	"Hybrid Search": "Гібридний пошук",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Я підтверджую, що прочитав і розумію наслідки своїх дій. Я усвідомлюю ризики, пов'язані з виконанням довільного коду, і перевірив надійність джерела.",
+	"ID": "ID",
+	"Ignite curiosity": "Запаліть цікавість",
+	"Image Generation (Experimental)": "Генерування зображень (експериментально)",
+	"Image Generation Engine": "Механізм генерації зображень",
+	"Image Settings": "Налаштування зображення",
+	"Images": "Зображення",
+	"Import Chats": "Імпорт чатів",
+	"Import Config from JSON File": "Імпорт конфігурації з файлу JSON",
+	"Import Functions": "Імпорт функцій ",
+	"Import Models": "Імпорт моделей",
+	"Import Presets": "Імпорт пресетів",
+	"Import Prompts": "Імпорт промтів",
+	"Import Tools": "Імпорт інструментів",
+	"Include": "Включити",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "Включіть прапорець `--api-auth` під час запуску stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "Включіть прапор `--api` при запуску stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "Впливає на швидкість, з якою алгоритм реагує на зворотній зв'язок від згенерованого тексту. Нижча швидкість навчання призведе до повільнішої корекції, тоді як вища швидкість навчання зробить алгоритм більш реакційним. (За замовчуванням: 0.1)",
+	"Info": "Інфо",
+	"Input commands": "Команди вводу",
+	"Install from Github URL": "Встановіть з URL-адреси Github",
+	"Instant Auto-Send After Voice Transcription": "Миттєва автоматична відправка після транскрипції голосу",
+	"Interface": "Інтерфейс",
+	"Invalid file format.": "Неправильний формат файлу.",
+	"Invalid Tag": "Недійсний тег",
+	"January": "Січень",
+	"Jina API Key": "Ключ API для Jina",
+	"join our Discord for help.": "приєднуйтеся до нашого Discord для допомоги.",
+	"JSON": "JSON",
+	"JSON Preview": "Перегляд JSON",
+	"July": "Липень",
+	"June": "Червень",
+	"JWT Expiration": "Термін дії JWT",
+	"JWT Token": "Токен JWT",
+	"Keep Alive": "Зберегти активність",
+	"Key": "Ключ",
+	"Keyboard shortcuts": "Клавіатурні скорочення",
+	"Knowledge": "Знання",
+	"Knowledge Access": "Доступ до знань",
+	"Knowledge created successfully.": "Знання успішно створено.",
+	"Knowledge deleted successfully.": "Знання успішно видалено.",
+	"Knowledge reset successfully.": "Знання успішно скинуто.",
+	"Knowledge updated successfully": "Знання успішно оновлено",
+	"Label": "Мітка",
+	"Landing Page Mode": "Режим головної сторінки",
+	"Language": "Мова",
+	"Last Active": "Остання активність",
+	"Last Modified": "Востаннє змінено",
+	"LDAP": "LDAP",
+	"LDAP server updated": "Сервер LDAP оновлено",
+	"Leaderboard": "Таблиця лідерів",
+	"Leave empty for unlimited": "Залиште порожнім для необмеженого розміру",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "Залиште порожнім, щоб включити всі моделі з кінцевої точки \"{{URL}}/api/tags\"",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "Залиште порожнім, щоб включити всі моделі з кінцевої точки \"{{URL}}/models\"",
+	"Leave empty to include all models or select specific models": "Залиште порожнім, щоб включити всі моделі, або виберіть конкретні моделі.",
+	"Leave empty to use the default prompt, or enter a custom prompt": "Залиште порожнім для використання стандартного запиту, або введіть власний запит",
+	"Light": "Світла",
+	"Listening...": "Слухаю...",
+	"LLMs can make mistakes. Verify important information.": "LLMs можуть помилятися. Перевірте важливу інформацію.",
+	"Local": "Локальний",
+	"Local Models": "Локальні моделі",
+	"Lost": "Втрачене",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Зроблено спільнотою OpenWebUI",
+	"Make sure to enclose them with": "Переконайтеся, що вони закриті",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Обов'язково експортуйте файл workflow.json у форматі API з ComfyUI.",
+	"Manage": "Керувати",
+	"Manage Arena Models": "Керувати моделями Arena",
+	"Manage Ollama": "Керувати Ollama",
+	"Manage Ollama API Connections": "Керувати з'єднаннями Ollama API",
+	"Manage OpenAI API Connections": "Керувати з'єднаннями OpenAI API",
+	"Manage Pipelines": "Керування конвеєрами",
+	"March": "Березень",
+	"Max Tokens (num_predict)": "Макс токенів (num_predict)",
+	"Max Upload Count": "Макс. кількість завантажень",
+	"Max Upload Size": "Макс. розмір завантаження",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Максимум 3 моделі можна завантажити одночасно. Будь ласка, спробуйте пізніше.",
+	"May": "Травень",
+	"Memories accessible by LLMs will be shown here.": "Пам'ять, яка доступна LLM, буде показана тут.",
+	"Memory": "Пам'ять",
+	"Memory added successfully": "Пам'ять додано успішно",
+	"Memory cleared successfully": "Пам'ять успішно очищено",
+	"Memory deleted successfully": "Пам'ять успішно видалено",
+	"Memory updated successfully": "Пам'ять успішно оновлено",
+	"Merge Responses": "Об'єднати відповіді",
+	"Message rating should be enabled to use this feature": "Оцінювання повідомлень має бути увімкнено для використання цієї функції.",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Повідомлення, які ви надішлете після створення посилання, не будуть доступні для інших. Користувачі, які мають URL, зможуть переглядати спільний чат.",
+	"Min P": "Min P",
+	"Minimum Score": "Мінімальний бал",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "MMMM DD, YYYY hh:mm:ss A",
+	"Model": "Модель",
+	"Model '{{modelName}}' has been successfully downloaded.": "Модель '{{modelName}}' успішно завантажено.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Модель '{{modelTag}}' вже знаходиться в черзі на завантаження.",
+	"Model {{modelId}} not found": "Модель {{modelId}} не знайдено",
+	"Model {{modelName}} is not vision capable": "Модель {{modelName}} не здатна бачити",
+	"Model {{name}} is now {{status}}": "Модель {{name}} тепер має {{status}}",
+	"Model accepts image inputs": "Модель приймає зображеня",
+	"Model created successfully!": "Модель створено успішно!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Виявлено шлях до файлової системи моделі. Для оновлення потрібно вказати коротке ім'я моделі, не вдасться продовжити.",
+	"Model Filtering": "Фільтрація моделей",
+	"Model ID": "ID моделі",
+	"Model IDs": "ID моделей",
+	"Model Name": "Назва моделі",
+	"Model not selected": "Модель не вибрана",
+	"Model Params": "Параметри моделі",
+	"Model Permissions": "Дозволи моделей",
+	"Model updated successfully": "Модель успішно оновлено",
+	"Modelfile Content": "Вміст файлу моделі",
+	"Models": "Моделі",
+	"Models Access": "Доступ до моделей",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "API ключ для пошуку Mojeek",
+	"more": "більше",
+	"More": "Більше",
+	"Name": "Ім'я",
+	"Name your knowledge base": "Назвіть вашу базу знань",
+	"New Chat": "Новий чат",
+	"New folder": "Нова папка",
+	"New Password": "Новий пароль",
+	"No content found": "Контент не знайдено.",
+	"No content to speak": "Нема чого говорити",
+	"No distance available": "Відстань недоступна",
+	"No feedbacks found": "Відгуків не знайдено",
+	"No file selected": "Файл не обрано",
+	"No files found.": "Файли не знайдено.",
+	"No groups with access, add a group to grant access": "Немає груп з доступом, додайте групу для надання доступу",
+	"No HTML, CSS, or JavaScript content found.": "HTML, CSS або JavaScript контент не знайдено.",
+	"No knowledge found": "Знання не знайдено.",
+	"No model IDs": "Немає ID моделей",
+	"No models found": "Моделей не знайдено",
+	"No models selected": "",
+	"No results found": "Не знайдено жодного результату",
+	"No search query generated": "Пошуковий запит не сформовано",
+	"No source available": "Джерело не доступне",
+	"No users were found.": "Користувачів не знайдено.",
+	"No valves to update": "Немає клапанів для оновлення",
+	"None": "Нема",
+	"Not factually correct": "Не відповідає дійсності",
+	"Not helpful": "Не корисно",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Примітка: Якщо ви встановите мінімальну кількість балів, пошук поверне лише документи з кількістю балів, більшою або рівною мінімальній кількості балів.",
+	"Notes": "Примітки",
+	"Notifications": "Сповіщення",
+	"November": "Листопад",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "Жовтень",
+	"Off": "Вимк",
+	"Okay, Let's Go!": "Гаразд, давайте почнемо!",
+	"OLED Dark": "Темний OLED",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API вимкнено",
+	"Ollama API settings updated": "Налаштування Ollama API оновлено",
+	"Ollama Version": "Версія Ollama",
+	"On": "Увімк",
+	"Only alphanumeric characters and hyphens are allowed": "Дозволені тільки алфавітно-цифрові символи та дефіси",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "У рядку команди дозволено використовувати лише алфавітно-цифрові символи та дефіси.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "Редагувати можна лише колекції, створіть нову базу знань, щоб редагувати або додавати документи.",
+	"Only select users and groups with permission can access": "Тільки вибрані користувачі та групи з дозволом можуть отримати доступ",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Упс! Схоже, що URL-адреса невірна. Будь ласка, перевірте ще раз та спробуйте ще раз.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "Упс! Деякі файли все ще завантажуються. Будь ласка, зачекайте, поки завантаження завершиться.",
+	"Oops! There was an error in the previous response.": "Упс! Сталася помилка в попередній відповіді.",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Упс! Ви використовуєте непідтримуваний метод (тільки для фронтенду). Будь ласка, обслуговуйте WebUI з бекенду.",
+	"Open file": "Відкрити файл",
+	"Open in full screen": "Відкрити на весь екран",
+	"Open new chat": "Відкрити новий чат",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI використовує faster-whisper внутрішньо.",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI використовує вбудовування голосів SpeechT5 та CMU Arctic.",
+	"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 API": "OpenAI API",
+	"OpenAI API Config": "Конфігурація OpenAI API",
+	"OpenAI API Key is required.": "Потрібен ключ OpenAI API.",
+	"OpenAI API settings updated": "Налаштування OpenAI API оновлено",
+	"OpenAI URL/Key required.": "Потрібен OpenAI URL/ключ.",
+	"or": "або",
+	"Organize your users": "Організуйте своїх користувачів",
+	"Other": "Інше",
+	"OUTPUT": "ВИХІД",
+	"Output format": "Формат відповіді",
+	"Overview": "Огляд",
+	"page": "сторінка",
+	"Password": "Пароль",
+	"Paste Large Text as File": "Вставити великий текст як файл",
+	"PDF document (.pdf)": "PDF документ (.pdf)",
+	"PDF Extract Images (OCR)": "Розпізнавання зображень з PDF (OCR)",
+	"pending": "на розгляді",
+	"Permission denied when accessing media devices": "Відмовлено в доступі до медіапристроїв",
+	"Permission denied when accessing microphone": "Відмовлено у доступі до мікрофона",
+	"Permission denied when accessing microphone: {{error}}": "Доступ до мікрофона заборонено: {{error}}",
+	"Permissions": "Дозволи",
+	"Personalization": "Персоналізація",
+	"Pin": "Зачепити",
+	"Pinned": "Зачеплено",
+	"Pioneer insights": "Прокладайте нові шляхи до знань",
+	"Pipeline deleted successfully": "Конвеєр успішно видалено",
+	"Pipeline downloaded successfully": "Конвеєр успішно завантажено",
+	"Pipelines": "Конвеєри",
+	"Pipelines Not Detected": "Конвеєрів не знайдено",
+	"Pipelines Valves": "Клапани конвеєрів",
+	"Plain text (.txt)": "Простий текст (.txt)",
+	"Playground": "Майданчик",
+	"Please carefully review the following warnings:": "Будь ласка, уважно ознайомтеся з наступними попередженнями:",
+	"Please enter a prompt": "Будь ласка, введіть підказку",
+	"Please fill in all fields.": "Будь ласка, заповніть всі поля.",
+	"Please select a model first.": "",
+	"Please select a reason": "Будь ласка, виберіть причину",
+	"Port": "Порт",
+	"Positive attitude": "Позитивне ставлення",
+	"Prefix ID": "ID префікса",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "ID префікса використовується для уникнення конфліктів з іншими підключеннями шляхом додавання префікса до ID моделей — залиште порожнім, щоб вимкнути",
+	"Previous 30 days": "Попередні 30 днів",
+	"Previous 7 days": "Попередні 7 днів",
+	"Profile Image": "Зображення профілю",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Підказка (напр., розкажіть мені цікавий факт про Римську імперію)",
+	"Prompt Content": "Зміст промту",
+	"Prompt created successfully": "Підказку успішно створено",
+	"Prompt suggestions": "Швидкі промти",
+	"Prompt updated successfully": "Підказку успішно оновлено",
+	"Prompts": "Промти",
+	"Prompts Access": "Доступ до підказок",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Завантажити \"{{searchValue}}\" з Ollama.com",
+	"Pull a model from Ollama.com": "Завантажити модель з Ollama.com",
+	"Query Generation Prompt": "Підказка для генерації запиту",
+	"Query Params": "Параметри запиту",
+	"RAG Template": "Шаблон RAG",
+	"Rating": "Оцінка",
+	"Re-rank models by topic similarity": "Перестановка моделей за схожістю тем",
+	"Read Aloud": "Читати вголос",
+	"Record voice": "Записати голос",
+	"Redirecting you to OpenWebUI Community": "Перенаправляємо вас до спільноти OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "Знижує ймовірність генерації безглуздих відповідей. Вищі значення (напр., 100) призведуть до більш різноманітних відповідей, тоді як нижчі значення (напр., 10) будуть більш обережними. (За замовчуванням: 40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Називайте себе \"Користувач\" (напр., \"Користувач вивчає іспанську мову\")",
+	"References from": "Посилання з",
+	"Refused when it shouldn't have": "Відмовив, коли не мав би",
+	"Regenerate": "Регенерувати",
+	"Release Notes": "Нотатки до випуску",
+	"Relevance": "Актуальність",
+	"Remove": "Видалити",
+	"Remove Model": "Видалити модель",
+	"Rename": "Перейменувати",
+	"Reorder Models": "",
+	"Repeat Last N": "Повторити останні N",
+	"Request Mode": "Режим запиту",
+	"Reranking Model": "Модель переранжування",
+	"Reranking model disabled": "Модель переранжування вимкнена",
+	"Reranking model set to \"{{reranking_model}}\"": "Модель переранжування встановлено на \"{{reranking_model}}\"",
+	"Reset": "Скидання",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Скинути каталог завантажень",
+	"Reset Vector Storage/Knowledge": "Скинути векторне сховище/Знання",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Сповіщення про відповіді не можуть бути активовані, оскільки вам було відмовлено в доступі до веб-сайту. Будь ласка, відвідайте налаштування вашого браузера, щоб надати необхідний доступ.",
+	"Response splitting": "Розбиття відповіді",
+	"Result": "Результат",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "Ввід тексту з форматуванням для чату",
+	"RK": "RK",
+	"Role": "Роль",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Запустити",
+	"Running": "Виконується",
+	"Save": "Зберегти",
+	"Save & Create": "Зберегти та створити",
+	"Save & Update": "Зберегти та оновити",
+	"Save As Copy": "Зберегти як копію",
+	"Save Tag": "Зберегти тег",
+	"Saved": "Збережено",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Збереження журналів чату безпосередньо в сховище вашого браузера більше не підтримується. Будь ласка, завантажте та видаліть журнали чату, натиснувши кнопку нижче. Не хвилюйтеся, ви можете легко повторно імпортувати журнали чату до бекенду через",
+	"Scroll to bottom when switching between branches": "Перемотувати до кінця при перемиканні між гілками",
+	"Search": "Пошук",
+	"Search a model": "Шукати модель",
+	"Search Base": "База пошуку",
+	"Search Chats": "Пошук в чатах",
+	"Search Collection": "Шукати колекцію",
+	"Search Filters": "Фільтри пошуку",
+	"search for tags": "шукати теги",
+	"Search Functions": "Пошук функцій",
+	"Search Knowledge": "Шукати знання",
+	"Search Models": "Пошук моделей",
+	"Search options": "Опції пошуку",
+	"Search Prompts": "Пошук промтів",
+	"Search Result Count": "Кількість результатів пошуку",
+	"Search the web": "Шукати в Інтернеті",
+	"Search Tools": "Пошуку інструментів",
+	"SearchApi API Key": "Ключ API для SearchApi",
+	"SearchApi Engine": "Рушій SearchApi",
+	"Searched {{count}} sites_one": "Переглянуто {{count}} сайт",
+	"Searched {{count}} sites_few": "Переглянуто {{count}} сайти",
+	"Searched {{count}} sites_many": "Переглянуто {{count}} сайтів",
+	"Searched {{count}} sites_other": "Переглянуто {{count}} сайтів",
+	"Searching \"{{searchQuery}}\"": "Шукаю \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "Пошук знань для \"{{searchQuery}}\"",
+	"Searxng Query URL": "URL-адреса запиту Searxng",
+	"See readme.md for instructions": "Див. readme.md для інструкцій",
+	"See what's new": "Подивіться, що нового",
+	"Seed": "Сід",
+	"Select a base model": "Обрати базову модель",
+	"Select a engine": "Оберіть рушій",
+	"Select a function": "Оберіть функцію",
+	"Select a group": "Вибрати групу",
+	"Select a model": "Оберіть модель",
+	"Select a pipeline": "Оберіть конвеєр",
+	"Select a pipeline url": "Оберіть адресу конвеєра",
+	"Select a tool": "Оберіть інструмент",
+	"Select Engine": "Виберіть двигун",
+	"Select Knowledge": "Вибрати знання",
+	"Select model": "Обрати модель",
+	"Select only one model to call": "Оберіть лише одну модель для виклику",
+	"Selected model(s) do not support image inputs": "Вибрані модель(і) не підтримують вхідні зображення",
+	"Semantic distance to query": "Семантична відстань до запиту",
+	"Send": "Надіслати",
+	"Send a Message": "Надіслати повідомлення",
+	"Send message": "Надіслати повідомлення",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "Відправляє `stream_options: { include_usage: true }` у запиті.\nПідтримувані постачальники повернуть інформацію про використання токену у відповіді, якщо вона встановлена.",
+	"September": "Вересень",
+	"Serper API Key": "Ключ API Serper",
+	"Serply API Key": "Ключ API Serply",
+	"Serpstack API Key": "Ключ API Serpstack",
+	"Server connection verified": "З'єднання з сервером підтверджено",
+	"Set as default": "Встановити за замовчуванням",
+	"Set CFG Scale": "Встановити масштаб CFG",
+	"Set Default Model": "Встановити модель за замовчуванням",
+	"Set embedding model": "Встановити модель вбудовування",
+	"Set embedding model (e.g. {{model}})": "Встановити модель вбудовування (напр, {{model}})",
+	"Set Image Size": "Встановити розмір зображення",
+	"Set reranking model (e.g. {{model}})": "Встановити модель переранжування (напр., {{model}})",
+	"Set Sampler": "Встановити семплер",
+	"Set Scheduler": "Встановити планувальник",
+	"Set Steps": "Встановити кроки",
+	"Set Task Model": "Встановити модель задач",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "Встановити кількість пристроїв GPU, що використовується для обробки інформації. Ця опція керує кількістю пристроїв GPU (якщо доступні), які використовуються для обробки надходження запитів. Збільшення цього значення може суттєво підвищити продуктивність моделей, оптимізованих за допомогою апаратного прискорення GPU, але також може споживати більше енергії та ресурсів GPU.",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "Встановити кількість робочих потоків, що використовуються для обробки інформації. Ця опція керує кількістю потоків, що використовуються для обробки надходження запитів одночасно. Збільшення цього значення може підвищити продуктивність при великій одночасності робіт, але також може споживати більше ресурсів CPU.",
+	"Set Voice": "Встановити голос",
+	"Set whisper model": "Встановити модель whisper",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "Встановлює, на скільки кроків назад модель повинна звертатися, щоб запобігти повторенням. (За замовчуванням: 64, 0 = вимкнено, -1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "Встановлює ступінь покарання за повторення. Чим вище значення (напр., 1.5), тим суворіше буде покарання за повтори; низьке значення (напр., 0.9) передбачає більш пом'якшувальним. (За замовчуванням: 1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "Встановлює насіння випадкового числа для генерації. Вказавши конкретне число, модель буде генерувати той самий текст для одного й того ж запиту. (За замовчуванням: випадкове)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "Встановлює розмір вікна контексту, яке використовується для генерації наступного токена. (За замовчуванням: 2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "Встановлює послідовності зупинки, які будуть використовуватися. Коли зустрічається така послідовність, LLM припиняє генерацію тексту і повертає результат. Можна встановити кілька послідовностей зупинки, вказавши кілька окремих параметрів зупинки у файлі моделі.",
+	"Settings": "Налаштування",
+	"Settings saved successfully!": "Налаштування успішно збережено!",
+	"Share": "Поділитися",
+	"Share Chat": "Поділитися чатом",
+	"Share to OpenWebUI Community": "Поділитися зі спільнотою OpenWebUI",
+	"Show": "Показати",
+	"Show \"What's New\" modal on login": "Показати модальне вікно \"Що нового\" під час входу.",
+	"Show Admin Details in Account Pending Overlay": "Відобразити дані адміна у вікні очікування облікового запису",
+	"Show shortcuts": "Показати клавіатурні скорочення",
+	"Show your support!": "Підтримайте нас!",
+	"Showcased creativity": "Продемонстрований креатив",
+	"Sign in": "Увійти",
+	"Sign in to {{WEBUI_NAME}}": "Увійти в {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "Увійти до {{WEBUI_NAME}} за допомогою LDAP",
+	"Sign Out": "Вийти",
+	"Sign up": "Зареєструватися",
+	"Sign up to {{WEBUI_NAME}}": "Зареєструватися в {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "Увійти в {{WEBUI_NAME}}",
+	"Source": "Джерело",
+	"Speech Playback Speed": "Швидкість відтворення мовлення",
+	"Speech recognition error: {{error}}": "Помилка розпізнавання мови: {{error}}",
+	"Speech-to-Text Engine": "Система розпізнавання мови",
+	"Stop": "Зупинити",
+	"Stop Sequence": "Символ зупинки",
+	"Stream Chat Response": "Відповідь стрім-чату",
+	"STT Model": "Модель STT ",
+	"STT Settings": "Налаштування STT",
+	"Subtitle (e.g. about the Roman Empire)": "Підзаголовок (напр., про Римську імперію)",
+	"Success": "Успіх",
+	"Successfully updated.": "Успішно оновлено.",
+	"Suggested": "Запропоновано",
+	"Support": "Підтримати",
+	"Support this plugin:": "Підтримайте цей плагін:",
+	"Sync directory": "Синхронізувати каталог",
+	"System": "Система",
+	"System Instructions": "Системні інструкції",
+	"System Prompt": "Системний промт",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "Підказка для генерації тегів",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Вибірка з відрізанням хвоста використовується для зменшення впливу малоймовірних токенів на результат. Вищі значення (напр., 2.0) зменшують цей вплив більше, в той час як значення 1.0 вимикає цю настройку. (За замовчуванням: 1)",
+	"Tap to interrupt": "Натисніть, щоб перервати",
+	"Tavily API Key": "Ключ API Tavily",
+	"Tell us more:": "Розкажи нам більше:",
+	"Temperature": "Температура",
+	"Template": "Шаблон",
+	"Temporary Chat": "Тимчасовий чат",
+	"Text Splitter": "Роздільник тексту",
+	"Text-to-Speech Engine": "Система синтезу мови",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Дякуємо за ваш відгук!",
+	"The Application Account DN you bind with for search": "DN облікового запису застосунку, з яким ви здійснюєте прив'язку для пошуку",
+	"The base to search for users": "База для пошуку користувачів",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Розмір пакету визначає, скільки текстових запитів обробляється одночасно. Більший розмір пакету може підвищити продуктивність та швидкість моделі, але також вимагає більше пам'яті. (За замовчуванням: 512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Розробники цього плагіна - пристрасні волонтери зі спільноти. Якщо ви вважаєте цей плагін корисним, будь ласка, зробіть свій внесок у його розвиток.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "Таблиця лідерів оцінки базується на системі рейтингу Ело і оновлюється в реальному часі.",
+	"The LDAP attribute that maps to the username that users use to sign in.": "LDAP-атрибут, який відповідає за ім'я користувача, яке використовують користувачі для входу.",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "Таблиця лідерів наразі в бета-версії, і ми можемо коригувати розрахунки рейтингів у міру вдосконалення алгоритму.",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "Максимальний розмір файлу в МБ. Якщо розмір файлу перевищує цей ліміт, файл не буде завантажено.",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "Максимальна кількість файлів, які можна використати одночасно в чаті. Якщо кількість файлів перевищує цей ліміт, файли не будуть завантажені.",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Оцінка повинна бути в діапазоні від 0.0 (0%) до 1.0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "Температура моделі. Збільшення температури зробить відповіді моделі більш креативними. (За замовчуванням: 0.8)",
+	"Theme": "Тема",
+	"Thinking...": "Думаю...",
+	"This action cannot be undone. Do you wish to continue?": "Цю дію не можна скасувати. Ви бажаєте продовжити?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Це забезпечує збереження ваших цінних розмов у безпечному бекенд-сховищі. Дякуємо!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Це експериментальна функція, вона може працювати не так, як очікувалося, і може бути змінена в будь-який час.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "Ця опція керує кількістю токенів, яка зберігається під час оновлення контексту. Наприклад, якщо встановити як 2, останні 2 токени контексту розмови будуть збережені. Збереження контексту може допомогти підтримувати безперервність розмови, але воно також може зменшити здатність відповідати на нові теми. (За замовчуванням: 24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "Ця опція встановлює максимальну кількість токенів, які модель може генерувати в своєму відповіді. Збільшення цього обмеження дозволяє моделі надавати довші відповіді, але також може збільшити вірогідність генерації недопоможного чи невідповідного вмісту. (За замовчуванням: 128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Цей варіант видалить усі існуючі файли в колекції та замінить їх новими завантаженими файлами.",
+	"This response was generated by \"{{model}}\"": "Цю відповідь згенеровано за допомогою \"{{model}}\"",
+	"This will delete": "Це призведе до видалення",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Це видалить <strong>{{NAME}}</strong> та <strong>всі його вмісти</strong>.",
+	"This will delete all models including custom models": "Це видалить усі моделі, включаючи користувацькі моделі",
+	"This will delete all models including custom models and cannot be undone.": "Це видалить усі моделі, включаючи користувацькі моделі, і не може бути скасовано.",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "Це скине базу знань і синхронізує всі файли. Ви бажаєте продовжити?",
+	"Thorough explanation": "Детальне пояснення",
+	"Tika": "Tika",
+	"Tika Server URL required.": "Потрібна URL-адреса сервера Tika.",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Порада: Оновіть кілька слотів змінних послідовно, натискаючи клавішу табуляції у вікні чату після кожної заміни.",
+	"Title": "Заголовок",
+	"Title (e.g. Tell me a fun fact)": "Заголовок (напр., Розкажіть мені цікавий факт)",
+	"Title Auto-Generation": "Автогенерація заголовків",
+	"Title cannot be an empty string.": "Заголовок не може бути порожнім рядком.",
+	"Title Generation Prompt": "Промт для генерування заголовків",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "Щоб отримати доступ до назв доступних для завантаження моделей,",
+	"To access the GGUF models available for downloading,": "Щоб отримати доступ до моделей GGUF, які можна завантажити,,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Щоб отримати доступ до веб-інтерфейсу, зверніться до адміністратора. Адміністратори можуть керувати статусами користувачів з Панелі адміністратора.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "Щоб прикріпити базу знань тут, спочатку додайте їх до робочого простору \"Знання\".",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "Для захисту вашої конфіденційності з вашими відгуками діляться лише оцінками, ID моделей, тегами та метаданими — ваші журнали чату залишаються приватними і не включаються.",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Щоб вибрати дії тут, спочатку додайте їх до робочої області \"Функції\".",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Щоб обрати фільтри тут, спочатку додайте їх до робочої області \"Функції\".",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Щоб обрати тут набори інструментів, спочатку додайте їх до робочої області \"Інструменти\".",
+	"Toast notifications for new updates": "Сповіщення Toast про нові оновлення",
+	"Today": "Сьогодні",
+	"Toggle settings": "Переключити налаштування",
+	"Toggle sidebar": "Переключити бокову панель",
+	"Token": "Токен",
+	"Tokens To Keep On Context Refresh (num_keep)": "Токени для збереження при оновленні контексту (num_keep)",
+	"Too verbose": "Занадто докладно",
+	"Tool created successfully": "Інструмент успішно створено",
+	"Tool deleted successfully": "Інструмент успішно видалено",
+	"Tool Description": "Опис інструменту",
+	"Tool ID": "ID інструменту",
+	"Tool imported successfully": "Інструмент успішно імпортовано",
+	"Tool Name": "Назва інструменту",
+	"Tool updated successfully": "Інструмент успішно оновлено",
+	"Tools": "Інструменти",
+	"Tools Access": "Доступ до інструментів",
+	"Tools are a function calling system with arbitrary code execution": "Інструменти - це система виклику функцій з довільним виконанням коду",
+	"Tools have a function calling system that allows arbitrary code execution": "Інструменти мають систему виклику функцій, яка дозволяє виконання довільного коду",
+	"Tools have a function calling system that allows arbitrary code execution.": "Інструменти мають систему виклику функцій, яка дозволяє виконання довільного коду.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Трансформери",
+	"Trouble accessing Ollama?": "Проблеми з доступом до Ollama?",
+	"TTS Model": "Модель TTS",
+	"TTS Settings": "Налаштування TTS",
+	"TTS Voice": "Голос TTS",
+	"Type": "Тип",
+	"Type Hugging Face Resolve (Download) URL": "Введіть URL ресурсу Hugging Face Resolve (завантаження)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ой! Виникла проблема при підключенні до {{provider}}.",
+	"UI": "Користувацький інтерфейс",
+	"Unarchive All": "Розархівувати все",
+	"Unarchive All Archived Chats": "Розархівувати всі архівовані чати",
+	"Unarchive Chat": "Розархівувати чат",
+	"Unlock mysteries": "Розкрийте таємниці",
+	"Unpin": "Відчепити",
+	"Unravel secrets": "Розплутуйте секрети",
+	"Untagged": "Без тегів",
+	"Update": "Оновлення",
+	"Update and Copy Link": "Оновлення та копіювання посилання",
+	"Update for the latest features and improvements.": "Оновіть програми для нових функцій та покращень.",
+	"Update password": "Оновити пароль",
+	"Updated": "Оновлено",
+	"Updated at": "Оновлено на",
+	"Updated At": "Оновлено на",
+	"Upload": "Завантажити",
+	"Upload a GGUF model": "Завантажити GGUF модель",
+	"Upload directory": "Завантажити каталог",
+	"Upload files": "Завантажити файли",
+	"Upload Files": "Завантажити файли",
+	"Upload Pipeline": "Завантажити конвеєр",
+	"Upload Progress": "Прогрес завантаження",
+	"URL": "URL",
+	"URL Mode": "Режим URL-адреси",
+	"Use '#' in the prompt input to load and include your knowledge.": "Використовуйте '#' у полі введення підказки, щоб завантажити та включити свої знання.",
+	"Use Gravatar": "Змінити аватар",
+	"Use groups to group your users and assign permissions.": "Використовуйте групи, щоб об’єднувати користувачів і призначати дозволи.",
+	"Use Initials": "Використовувати ініціали",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "користувач",
+	"User": "Користувач",
+	"User location successfully retrieved.": "Місцезнаходження користувача успішно знайдено.",
+	"Username": "Ім'я користувача",
+	"Users": "Користувачі",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "Використовуючи модель арени за замовчуванням з усіма моделями. Натисніть кнопку плюс, щоб додати користувацькі моделі.",
+	"Utilize": "Використовувати",
+	"Valid time units:": "Дійсні одиниці часу:",
+	"Valves": "Клапани",
+	"Valves updated": "Клапани оновлено",
+	"Valves updated successfully": "Клапани успішно оновлено",
+	"variable": "змінна",
+	"variable to have them replaced with clipboard content.": "змінна, щоб замінити їх вмістом буфера обміну.",
+	"Version": "Версія",
+	"Version {{selectedVersion}} of {{totalVersions}}": "Версія {{selectedVersion}} з {{totalVersions}}",
+	"Visibility": "Видимість",
+	"Voice": "Голос",
+	"Voice Input": "Голосове введення",
+	"Warning": "Увага!",
+	"Warning:": "Увага:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Попередження: Увімкнення цього дозволить користувачам завантажувати довільний код на сервер.",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Попередження: Якщо ви оновлюєте або змінюєте модель вбудовування, вам потрібно буде повторно імпортувати всі документи.",
+	"Web": "Веб",
+	"Web API": "Веб-API",
+	"Web Loader Settings": "Налаштування веб-завантажувача",
+	"Web Search": "Веб-пошук",
+	"Web Search Engine": "Веб-пошукова система",
+	"Web Search Query Generation": "",
+	"Webhook URL": "URL веб-запиту",
+	"WebUI Settings": "Налаштування WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI надсилатиме запити до \"{{url}}/api/chat\"",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI надсилатиме запити до \"{{url}}/chat/completions\"",
+	"What are you trying to achieve?": "Чого ви прагнете досягти?",
+	"What are you working on?": "Над чим ти працюєш?",
+	"What’s New in": "Що нового в",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Коли активовано, модель буде відповідати на кожне повідомлення чату в режимі реального часу, генеруючи відповідь, як тільки користувач надішле повідомлення. Цей режим корисний для застосувань життєвих вітань чатів, але може позначитися на продуктивності на повільнішому апаратному забезпеченні.",
+	"wherever you are": "де б ви не були",
+	"Whisper (Local)": "Whisper (Локально)",
+	"Why?": "Чому?",
+	"Widescreen Mode": "Широкоекранний режим",
+	"Won": "Переможець",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "Працює разом з top-k. Більше значення (напр., 0.95) приведе до більш різноманітного тексту, тоді як менше значення (напр., 0.5) згенерує більш зосереджений і консервативний текст. (За замовчуванням: 0.9)",
+	"Workspace": "Робочий простір",
+	"Workspace Permissions": "Дозволи робочого простору.",
+	"Write a prompt suggestion (e.g. Who are you?)": "Напишіть промт (напр., Хто ти?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Напишіть стислий зміст у 50 слів, який узагальнює [тема або ключове слово].",
+	"Write something...": "Напишіть щось...",
+	"Write your model template content here": "Напишіть вміст шаблону моделі тут",
+	"Yesterday": "Вчора",
+	"You": "Ви",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "Ви можете спілкуватися лише з максимальною кількістю {{maxCount}} файлів одночасно.",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Ви можете налаштувати ваші взаємодії з мовними моделями, додавши спогади через кнопку 'Керувати' внизу, що зробить їх більш корисними та персоналізованими для вас.",
+	"You cannot upload an empty file.": "Ви не можете завантажити порожній файл.",
+	"You do not have permission to upload files.": "У вас немає дозволу завантажувати файли.",
+	"You have no archived conversations.": "У вас немає архівованих розмов.",
+	"You have shared this chat": "Ви поділилися цим чатом",
+	"You're a helpful assistant.": "Ви корисний асистент.",
+	"You're now logged in.": "Ви увійшли в систему.",
+	"Your account status is currently pending activation.": "Статус вашого облікового запису наразі очікує на активацію.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Весь ваш внесок піде безпосередньо розробнику плагіна; Open WebUI не бере жодних відсотків. Однак, обрана платформа фінансування може мати свої власні збори.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Налаштування завантажувача Youtube"
+}
diff --git a/src/lib/i18n/locales/ur-PK/translation.json b/src/lib/i18n/locales/ur-PK/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..af78b7c60d62ee9d22a434be144a442327b8c60e
--- /dev/null
+++ b/src/lib/i18n/locales/ur-PK/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' یا '1-' مستقل کے لیے",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(مثال کے طور پر: `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(مثال کے طور پر: `sh webui.sh --api`)",
+	"(latest)": "(تازہ ترین)",
+	"{{ models }}": "{{ ماڈلز }}",
+	"{{user}}'s Chats": "{{ صارف }} کی بات چیت",
+	"{{webUIName}} Backend Required": "{{webUIName}} بیک اینڈ درکار ہے",
+	"*Prompt node ID(s) are required for image generation": "تصویر کی تخلیق کے لیے *پرومپٹ نوڈ آئی ڈی(ز) کی ضرورت ہے",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "نیا ورژن (v{{LATEST_VERSION}}) اب دستیاب ہے",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "ٹاسک ماڈل اس وقت استعمال ہوتا ہے جب چیٹس کے عنوانات اور ویب سرچ سوالات تیار کیے جا رہے ہوں",
+	"a user": "ایک صارف",
+	"About": "بارے میں",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "اکاؤنٹ",
+	"Account Activation Pending": "اکاؤنٹ فعال ہونے کا انتظار ہے",
+	"Accurate information": "درست معلومات",
+	"Actions": "اعمال",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "فعال صارفین",
+	"Add": "شامل",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "اس ماڈل کے کام کے بارے میں ایک مختصر وضاحت شامل کریں",
+	"Add a tag": "ٹیگ شامل کریں",
+	"Add Arena Model": "ارینا ماڈل شامل کریں",
+	"Add Connection": "",
+	"Add Content": "مواد شامل کریں",
+	"Add content here": "یہاں مواد شامل کریں",
+	"Add custom prompt": "حسب ضرورت پرامپٹ شامل کریں",
+	"Add Files": "فائلیں شامل کریں",
+	"Add Group": "",
+	"Add Memory": "میموری شامل کریں",
+	"Add Model": "ماڈل شامل کریں",
+	"Add Tag": "ٹیگ شامل کریں",
+	"Add Tags": "ٹیگز شامل کریں",
+	"Add text content": "متن کا مواد شامل کریں",
+	"Add User": "صارف شامل کریں",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "ان سیٹنگز کو ایڈجسٹ کرنے سے تمام صارفین کے لئے تبدیلیاں یکساں طور پر نافذ ہوں گی",
+	"admin": "ایڈمن",
+	"Admin": "ایڈمن",
+	"Admin Panel": "ایڈمن پینل",
+	"Admin Settings": "ایڈمن ترتیبات",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "ایڈمنز کو ہر وقت تمام ٹولز تک رسائی حاصل ہوتی ہے؛ صارفین کو ورک سپیس میں ماڈل کے حساب سے ٹولز تفویض کرنے کی ضرورت ہوتی ہے",
+	"Advanced Parameters": "پیشرفتہ پیرا میٹرز",
+	"Advanced Params": "ترقی یافتہ پیرامیٹرز",
+	"All chats": "تمام چیٹس",
+	"All Documents": "تمام دستاویزات",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "چیٹ کو حذف کرنے کی اجازت دیں",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "غیر مقامی آوازوں کی اجازت دیں",
+	"Allow Temporary Chat": "عارضی چیٹ کی اجازت دیں",
+	"Allow User Location": "صارف کی مقام کی اجازت دیں",
+	"Allow Voice Interruption in Call": "کال میں آواز کی مداخلت کی اجازت دیں",
+	"Already have an account?": "کیا پہلے سے اکاؤنٹ موجود ہے؟",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "معاون",
+	"and": "اور",
+	"and {{COUNT}} more": "اور {{COUNT}} مزید",
+	"and create a new shared link.": "اور ایک نیا مشترکہ لنک بنائیں",
+	"API Base URL": "API بنیادی URL",
+	"API Key": "اے پی آئی کلید",
+	"API Key created.": "اے پی آئی کلید بنائی گئی",
+	"API keys": "API کیز",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "اپریل",
+	"Archive": "آرکائیو",
+	"Archive All Chats": "تمام چیٹس محفوظ کریں",
+	"Archived Chats": "محفوظ شدہ بات چیت",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "کیا آپ کو یقین ہے؟",
+	"Arena Models": "ارینا ماڈلز",
+	"Artifacts": "نوادرات",
+	"Ask a question": "سوال پوچھیں",
+	"Assistant": "اسسٹنٹ",
+	"Attach file": "فائل منسلک کریں",
+	"Attention to detail": "تفصیل پر توجہ",
+	"Attribute for Username": "",
+	"Audio": "آڈیو",
+	"August": "اگست",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "جواب خودکار طور پر کلپ بورڈ پر کاپی ہو گیا",
+	"Auto-playback response": "آٹو پلے بیک جواب",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "آٹو میٹک1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 ایپلی کیشن کا تصدیقی سلسلہ",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 بنیادی URL",
+	"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 بنیادی URL درکار ہے",
+	"Available list": "دستیاب فہرست",
+	"available!": "دستیاب!",
+	"Awful": "",
+	"Azure AI Speech": "ایژور اے آئی اسپیچ",
+	"Azure Region": "ایزور ریجن",
+	"Back": "واپس",
+	"Bad Response": "غلط جواب",
+	"Banners": "بینرز",
+	"Base Model (From)": "بیس ماڈل (سے)",
+	"Batch Size (num_batch)": "بیچ سائز (num_batch)",
+	"before": "پہلے",
+	"Being lazy": "سستی کر رہا ہے",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "بریو سرچ API کلید",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "ویب سائٹس کے لیے SSL تصدیق کو نظر انداز کریں",
+	"Call": "کال کریں",
+	"Call feature is not supported when using Web STT engine": "کال کی خصوصیت ویب STT انجن استعمال کرتے وقت معاونت یافتہ نہیں ہے",
+	"Camera": "کیمرہ",
+	"Cancel": "منسوخ کریں",
+	"Capabilities": "صلاحیتیں",
+	"Certificate Path": "",
+	"Change Password": "پاس ورڈ تبدیل کریں",
+	"Character": "کردار",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "چیٹ",
+	"Chat Background Image": "چیٹ پس منظر کی تصویر",
+	"Chat Bubble UI": "چیٹ بلبل انٹرفیس",
+	"Chat Controls": "چیٹ کنٹرولز",
+	"Chat direction": "چیٹ کی سمت",
+	"Chat Overview": "چیٹ کا جائزہ",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "چیٹ ٹیگز خودکار تخلیق",
+	"Chats": "بات چیت",
+	"Check Again": "دوبارہ چیک کریں",
+	"Check for updates": "تازہ ترین معلومات کے لیے چیک کریں",
+	"Checking for updates...": "تازہ ترین معلومات کی جانچ کر رہا ہے...",
+	"Choose a model before saving...": "محفوظ کرنے سے پہلے ایک ماڈل منتخب کریں...",
+	"Chunk Overlap": "حصے کی اوورلیپ",
+	"Chunk Params": "چنک پیرا میٹرز",
+	"Chunk Size": "چنک سائز",
+	"Ciphers": "",
+	"Citation": "حوالہ",
+	"Clear memory": "میموری صاف کریں",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "مدد کے لیے یہاں کلک کریں",
+	"Click here to": "یہاں کلک کریں تاکہ",
+	"Click here to download user import template file.": "صارف امپورٹ ٹیمپلیٹ فائل ڈاؤن لوڈ کرنے کے لیے یہاں کلک کریں",
+	"Click here to learn more about faster-whisper and see the available models.": "تیز ویسپر کے بارے میں مزید جاننے اور دستیاب ماڈلز دیکھنے کے لیے یہاں کلک کریں",
+	"Click here to select": "منتخب کرنے کے لیے یہاں کلک کریں",
+	"Click here to select a csv file.": "یہاں کلک کریں تاکہ CSV فائل منتخب کریں",
+	"Click here to select a py file.": "یہاں کلک کریں تاکہ پی وائی فائل منتخب کریں",
+	"Click here to upload a workflow.json file.": "یہاں کلک کریں تاکہ ورک فلو.json فائل اپ لوڈ کریں",
+	"click here.": "یہاں کلک کریں",
+	"Click on the user role button to change a user's role.": "صارف کا کردار تبدیل کرنے کے لیے صارف کردار بٹن پر کلک کریں",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "کلپ بورڈ لکھنے کی اجازت نہیں دی گئی براہ کرم ضروری رسائی کی اجازت دینے کے لیے اپنے براؤزر کی سیٹنگز چیک کریں",
+	"Clone": "نقل کریں",
+	"Close": "بند کریں",
+	"Code execution": "کوڈ کا نفاذ",
+	"Code formatted successfully": "کوڈ کامیابی سے فارمیٹ ہو گیا",
+	"Collection": "کلیکشن",
+	"Color": "",
+	"ComfyUI": "کومفی یو آئی",
+	"ComfyUI Base URL": "کمفی یو آئی بیس یو آر ایل",
+	"ComfyUI Base URL is required.": "ComfyUI بیس یو آر ایل ضروری ہے",
+	"ComfyUI Workflow": "کومفی یو آئی ورک فلو",
+	"ComfyUI Workflow Nodes": "کومفی یو آئی ورک فلو نوڈز",
+	"Command": "کمانڈ",
+	"Completions": "تکمیل",
+	"Concurrent Requests": "ہم وقت درخواستیں",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "تصدیق کریں",
+	"Confirm Password": "پاس ورڈ کی توثیق کریں",
+	"Confirm your action": "اپنی کارروائی کی تصدیق کریں",
+	"Connections": "کنکشنز",
+	"Contact Admin for WebUI Access": "ویب یو آئی رسائی کے لیے ایڈمن سے رابطہ کریں",
+	"Content": "مواد",
+	"Content Extraction": "مواد نکالنا",
+	"Context Length": "سیاق کی لمبائی",
+	"Continue Response": "ردعمل جاری رکھیں",
+	"Continue with {{provider}}": "{{provider}} کے ساتھ جاری رکھیں",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "TTS درخواستوں کے لیے پیغام کے متن کی تقسیم کو کنٹرول کریں 'Punctuation' جملوں میں تقسیم کرتا ہے، 'paragraphs' پیراگراف میں تقسیم کرتا ہے، اور 'none' پیغام کو ایک ہی سٹرنگ کے طور پر رکھتا ہے",
+	"Controls": "کنٹرولز",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "کاپی کیا گیا",
+	"Copied shared chat URL to clipboard!": "مشترکہ چیٹ یو آر ایل کلپ بورڈ میں نقل کر دیا گیا!",
+	"Copied to clipboard": "کلپ بورڈ پر نقل کر دیا گیا",
+	"Copy": "نقل کریں",
+	"Copy last code block": "آخری کوڈ بلاک نقل کریں",
+	"Copy last response": "آخری جواب کاپی کریں",
+	"Copy Link": "لنک کاپی کریں",
+	"Copy to clipboard": "کلپ بورڈ پر کاپی کریں",
+	"Copying to clipboard was successful!": "کلپ بورڈ میں کاپی کامیاب ہوئی!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "ماڈل بنائیں",
+	"Create Account": "اکاؤنٹ بنائیں",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "علم بنائیں",
+	"Create new key": "نیا کلید بنائیں",
+	"Create new secret key": "نیا خفیہ کلید بنائیں",
+	"Created at": "پر بنایا گیا",
+	"Created At": "بنایا گیا:",
+	"Created by": "تخلیق کردہ",
+	"CSV Import": "CSV درآمد کریں",
+	"Current Model": "موجودہ ماڈل",
+	"Current Password": "موجودہ پاس ورڈ",
+	"Custom": "حسب ضرورت",
+	"Dark": "ڈارک",
+	"Database": "ڈیٹا بیس",
+	"December": "دسمبر",
+	"Default": "پہلے سے طے شدہ",
+	"Default (Open AI)": "ڈیفالٹ (اوپن اے آئی)",
+	"Default (SentenceTransformers)": "ڈیفالٹ (سینٹینس ٹرانسفارمرز)",
+	"Default Model": "ڈیفالٹ ماڈل",
+	"Default model updated": "ڈیفالٹ ماڈل اپ ڈیٹ ہو گیا",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "ڈیفالٹ پرامپٹ تجاویز",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "ڈیفالٹ صارف کا کردار",
+	"Delete": "حذف کریں",
+	"Delete a model": "ایک ماڈل حذف کریں",
+	"Delete All Chats": "تمام چیٹس حذف کریں",
+	"Delete All Models": "",
+	"Delete chat": "چیٹ حذف کریں",
+	"Delete Chat": "چیٹ حذف کریں",
+	"Delete chat?": "چیٹ حذف کریں؟",
+	"Delete folder?": "کیا فولڈر حذف کریں؟",
+	"Delete function?": "حذف کریں؟",
+	"Delete prompt?": "پرومپٹ کو حذف کریں؟",
+	"delete this link": "اس لنک کو حذف کریں",
+	"Delete tool?": "کیا آپ حذف کرنا چاہتے ہیں؟",
+	"Delete User": "صارف کو حذف کریں",
+	"Deleted {{deleteModelTag}}": "{{deleteModelTag}} حذف کر دیا گیا",
+	"Deleted {{name}}": "حذف کر دیا گیا {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "تفصیل",
+	"Didn't fully follow instructions": "ہدایات کو مکمل طور پر نہیں سمجھا",
+	"Disabled": "غیر فعال",
+	"Discover a function": "ایک فنکشن دریافت کریں",
+	"Discover a model": "ایک ماڈل دریافت کریں",
+	"Discover a prompt": "ایک اشارہ دریافت کریں",
+	"Discover a tool": "ایک ٹول دریافت کریں",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "دریافت کریں، ڈاؤن لوڈ کریں، اور کسٹم فنکشنز کو دریافت کریں",
+	"Discover, download, and explore custom prompts": "دریافت کریں، ڈاؤن لوڈ کریں، اور حسب ضرورت پرامپٹس کو دریافت کریں",
+	"Discover, download, and explore custom tools": "دریافت کریں، ڈاؤن لوڈ کریں، اور حسب ضرورت ٹولز کو دریافت کریں",
+	"Discover, download, and explore model presets": "دریافت کریں، ڈاؤن لوڈ کریں، اور ماڈل پریسیٹس کو دریافت کریں",
+	"Dismissible": "منسوخ کرنے کے قابل",
+	"Display": "",
+	"Display Emoji in Call": "کال میں ایموجی دکھائیں",
+	"Display the username instead of You in the Chat": "چیٹ میں \"آپ\" کے بجائے صارف نام دکھائیں",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "ایسی جگہوں سے فنکشنز انسٹال نہ کریں جن پر آپ مکمل بھروسہ نہیں کرتے",
+	"Do not install tools from sources you do not fully trust.": "جن ذرائع پر آپ مکمل بھروسہ نہیں کرتے، ان سے ٹولز انسٹال نہ کریں",
+	"Document": "دستاویز",
+	"Documentation": "دستاویزات",
+	"Documents": "دستاویزات",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "آپ کا ڈیٹا مقامی طور پر میزبانی شدہ سرور پر محفوظ رہتا ہے اور کوئی بیرونی رابطے نہیں بناتا",
+	"Don't have an account?": "کیا آپ کے پاس اکاؤنٹ نہیں ہے؟",
+	"don't install random functions from sources you don't trust.": "غیر معتبر ذرائع سے بے ترتیب فنکشنز انسٹال نہ کریں",
+	"don't install random tools from sources you don't trust.": "جو ذرائع آپ پر بھروسہ نہیں کرتے ان سے بے ترتیب ٹولز انسٹال نہ کریں",
+	"Don't like the style": "انداز پسند نہیں آیا",
+	"Done": "ہو گیا",
+	"Download": "ڈاؤن لوڈ کریں",
+	"Download canceled": "ڈاؤن لوڈ منسوخ کر دیا گیا",
+	"Download Database": "ڈیٹا بیس ڈاؤن لوڈ کریں",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "ڈرائنگ کریں",
+	"Drop any files here to add to the conversation": "گفتگو میں شامل کرنے کے لیے کوئی بھی فائل یہاں چھوڑیں",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "مثلاً '30s'، '10m' درست وقت کی اکائیاں ہیں 's'، 'm'، 'h'",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "ترمیم کریں",
+	"Edit Arena Model": "ایرینا ماڈل میں ترمیم کریں",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "یادداشت میں ترمیم کریں",
+	"Edit User": "صارف میں ترمیم کریں",
+	"Edit User Group": "",
+	"ElevenLabs": "الیون لیبز",
+	"Email": "ای میل",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "بیچ سائز شامل کرنا",
+	"Embedding Model": "ایمبیڈنگ ماڈل",
+	"Embedding Model Engine": "ایمبیڈنگ ماڈل انجن",
+	"Embedding model set to \"{{embedding_model}}\"": "ایمبیڈنگ ماڈل \"{{embedding_model}}\" پر سیٹ کیا گیا ہے",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "کمیونٹی شیئرنگ فعال کریں",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "پیغام کی درجہ بندی فعال کریں",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "نئے سائن اپس کو فعال کریں",
+	"Enable Web Search": "ویب تلاش فعال کریں",
+	"Enabled": "فعال کردیا گیا ہے",
+	"Engine": "انجن",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "یقینی بنائیں کہ آپ کی CSV فائل میں 4 کالم اس ترتیب میں شامل ہوں: نام، ای میل، پاس ورڈ، کردار",
+	"Enter {{role}} message here": "یہاں {{کردار}} پیغام درج کریں",
+	"Enter a detail about yourself for your LLMs to recall": "اپنی ذات کے بارے میں کوئی تفصیل درج کریں تاکہ آپ کے LLMs اسے یاد رکھ سکیں",
+	"Enter api auth string (e.g. username:password)": "اے پی آئی اتھ سٹرنگ درج کریں (مثال کے طور پر: صارف نام:پاس ورڈ)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "بریو سرچ API کلید درج کریں",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "CFG اسکیل درج کریں (مثلاً 7.0)",
+	"Enter Chunk Overlap": "چنک اوورلیپ درج کریں",
+	"Enter Chunk Size": "چنک سائز درج کریں",
+	"Enter description": "تفصیل درج کریں",
+	"Enter Github Raw URL": "گیٹ ہب را یو آر ایل درج کریں",
+	"Enter Google PSE API Key": "گوگل PSE API کلید درج کریں",
+	"Enter Google PSE Engine Id": "گوگل پی ایس ای انجن آئی ڈی درج کریں",
+	"Enter Image Size (e.g. 512x512)": "تصویر کا سائز درج کریں (مثال: 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "زبان کے کوڈ درج کریں",
+	"Enter Model ID": "ماڈل آئی ڈی درج کریں",
+	"Enter model tag (e.g. {{modelTag}})": "ماڈل ٹیگ داخل کریں (مثال کے طور پر {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "درج کریں مراحل کی تعداد (جیسے 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "نمونہ درج کریں (مثال: آئلر a)",
+	"Enter Scheduler (e.g. Karras)": "شیڈیولر درج کریں (مثلاً Karras)",
+	"Enter Score": "درجہ درج کریں",
+	"Enter SearchApi API Key": "تلاش API کلید داخل کریں",
+	"Enter SearchApi Engine": "تلاش انجن درج کریں",
+	"Enter Searxng Query URL": "سیرنگ استفسار یو آر ایل درج کریں",
+	"Enter Seed": "",
+	"Enter Serper API Key": "سرپر API کلید داخل کریں",
+	"Enter Serply API Key": "سیرپلی API کلید درج کریں",
+	"Enter Serpstack API Key": "سرپ اسٹیک API کلید درج کریں",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "اسٹاپ ترتیب درج کریں",
+	"Enter system prompt": "سسٹم پرامپٹ درج کریں",
+	"Enter Tavily API Key": "Tavily API کلید درج کریں",
+	"Enter Tika Server URL": "ٹیکا سرور یو آر ایل درج کریں",
+	"Enter Top K": "اوپر کے K درج کریں",
+	"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 Your Email": "اپنا ای میل درج کریں",
+	"Enter Your Full Name": "اپنا مکمل نام درج کریں",
+	"Enter your message": "اپنا پیغام درج کریں",
+	"Enter Your Password": "اپنا پاس ورڈ درج کریں",
+	"Enter Your Role": "اپنا کردار درج کریں",
+	"Enter Your Username": "",
+	"Error": "غلطی",
+	"ERROR": "غلطی",
+	"Evaluations": "تشخیصات",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "خارج کریں",
+	"Experimental": "تجرباتی",
+	"Explore the cosmos": "",
+	"Export": "برآمد کریں",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "تمام چیٹس برآمد کریں (تمام صارفین)",
+	"Export chat (.json)": "چیٹ برآمد کریں (.json)",
+	"Export Chats": "چیٹس برآمد کریں",
+	"Export Config to JSON File": "کنفیگ کو JSON فائل میں ایکسپورٹ کریں",
+	"Export Functions": "ایکسپورٹ فنکشنز",
+	"Export Models": "ماڈلز برآمد کریں",
+	"Export Presets": "",
+	"Export Prompts": "پرامپٹس برآمد کریں",
+	"Export to CSV": "",
+	"Export Tools": "ایکسپورٹ ٹولز",
+	"External Models": "بیرونی ماڈلز",
+	"Failed to add file.": "فائل شامل کرنے میں ناکام",
+	"Failed to create API Key.": "API کلید بنانے میں ناکام",
+	"Failed to read clipboard contents": "کلپ بورڈ مواد کو پڑھنے میں ناکام",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "ترتیبات کی تازہ کاری ناکام رہی",
+	"Failed to upload file.": "فائل اپلوڈ کرنے میں ناکامی ہوئی",
+	"February": "فروری",
+	"Feedback History": "تاریخ رائے",
+	"Feedbacks": "",
+	"Feel free to add specific details": "تفصیلات شامل کرنے کے لیے آزاد محسوس کریں",
+	"File": "فائل",
+	"File added successfully.": "فائل کامیابی سے شامل ہو گئی",
+	"File content updated successfully.": "فائل مواد کامیابی سے اپ ڈیٹ ہو گیا",
+	"File Mode": "فائل موڈ",
+	"File not found.": "فائل نہیں ملی",
+	"File removed successfully.": "فائل کامیابی سے ہٹا دی گئی",
+	"File size should not exceed {{maxSize}} MB.": "فائل کا سائز {{maxSize}} ایم بی سے زیادہ نہیں ہونا چاہیے",
+	"Files": "فائلز",
+	"Filter is now globally disabled": "فلٹر اب عالمی طور پر غیر فعال ہے",
+	"Filter is now globally enabled": "فلٹر اب عالمی طور پر فعال ہے",
+	"Filters": "فلٹرز",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "فنگر پرنٹ اسپورفنگ کا پتہ چلا: اوتار کے طور پر ابتدائی حروف استعمال کرنے سے قاصر ڈیفالٹ پروفائل تصویر منتخب کی جا رہی ہے",
+	"Fluidly stream large external response chunks": "بڑے بیرونی جوابات کے حصوں کو بہاؤ میں منتقل کریں",
+	"Focus chat input": "چیٹ ان پٹ پر توجہ مرکوز کریں",
+	"Folder deleted successfully": "پوشہ کامیابی سے حذف ہو گیا",
+	"Folder name cannot be empty": "پوشے کا نام خالی نہیں ہو سکتا",
+	"Folder name cannot be empty.": "پوشے کا نام خالی نہیں ہو سکتا",
+	"Folder name updated successfully": "فولڈر کا نام کامیابی سے اپ ڈیٹ ہوگیا",
+	"Followed instructions perfectly": "ہدایتوں کی مکمل پیروی کی گئی",
+	"Forge new paths": "",
+	"Form": "فارم",
+	"Format your variables using brackets like this:": "اپنے متغیرات کو اس طرح بریکٹس میں فارمیٹ کریں:",
+	"Frequency Penalty": "کثرت کی پابندی",
+	"Function": "فنکشن",
+	"Function created successfully": "فنکشن کامیابی سے تخلیق ہو گیا",
+	"Function deleted successfully": "فنکشن کامیابی کے ساتھ حذف ہو گیا",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "فنکشن اب عالمی طور پر غیر فعال ہے",
+	"Function is now globally enabled": "فنکشن اب عالمی طور پر فعال ہے",
+	"Function Name": "",
+	"Function updated successfully": "فنکشن کو کامیابی سے اپ ڈیٹ کر دیا گیا",
+	"Functions": "افعال",
+	"Functions allow arbitrary code execution": "فنکشنز کوڈ کے بلاواسطہ نفاذ کی اجازت دیتے ہیں",
+	"Functions allow arbitrary code execution.": "افعال صوابدیدی کوڈ کے اجرا کی اجازت دیتے ہیں",
+	"Functions imported successfully": "فنکشنز کامیابی سے درآمد ہو گئے ہیں",
+	"General": "عمومی",
+	"General Settings": "عمومی ترتیبات",
+	"Generate Image": "تصویر بنائیں",
+	"Generating search query": "تلاش کے لیے سوالیہ عبارت تیار کی جا رہی ہے",
+	"Generation Info": "جنریشن کی معلومات",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "عالمی",
+	"Good Response": "اچھا جواب",
+	"Google PSE API Key": "گوگل پی ایس ای API کی کلید",
+	"Google PSE Engine Id": "گوگل پی ایس ای انجن آئی ڈی",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "پ:مم a",
+	"Haptic Feedback": "ہاپٹک فیڈ بیک",
+	"has no conversations.": "کوئی گفتگو نہیں ہے",
+	"Hello, {{name}}": "ہیلو، {{name}}",
+	"Help": "مدد",
+	"Help us create the best community leaderboard by sharing your feedback history!": "ہمیں بہترین کمیونٹی لیڈر بورڈ بنانے میں مدد کریں، اپنی رائے کی تاریخ شیئر کر کے!",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "چھپائیں",
+	"Host": "",
+	"How can I help you today?": "میں آج آپ کی کس طرح مدد کر سکتا ہوں؟",
+	"How would you rate this response?": "",
+	"Hybrid Search": "مشترکہ تلاش",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "میں اقرار کرتا ہوں کہ میں نے پڑھ لیا ہے اور میں اپنی کارروائی کے مضمرات سمجھتا ہوں میں اس بات سے واقف ہوں کہ بلاوجہ کوڈ چلانے کے ساتھ منسلک خطرات ہوتے ہیں اور میں نے ماخذ کی اعتمادیت کی تصدیق کی ہے",
+	"ID": "شناخت",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "تصویر کی تخلیق (تجرباتی)",
+	"Image Generation Engine": "امیج جنریشن انجن",
+	"Image Settings": "تصویری ترتیبات",
+	"Images": "تصاویر",
+	"Import Chats": "چیٹس درآمد کریں",
+	"Import Config from JSON File": "JSON فائل سے تشکیلات درآمد کریں",
+	"Import Functions": "درآمد فنکشنز",
+	"Import Models": "ماڈلز درآمد کریں",
+	"Import Presets": "",
+	"Import Prompts": "پرامپٹس درآمد کریں",
+	"Import Tools": "امپورٹ ٹولز",
+	"Include": "شامل کریں",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "`--api-auth` پرچم کو چلانے کے وقت شامل کریں stable-diffusion-webui",
+	"Include `--api` flag when running stable-diffusion-webui": "اسٹیبل-ڈیفیوژن-ویب یو آئی چلانے کے دوران `--api` فلیگ شامل کریں",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "معلومات",
+	"Input commands": "کمانڈز داخل کریں",
+	"Install from Github URL": "گِٹ حب یو آر ایل سے انسٹال کریں",
+	"Instant Auto-Send After Voice Transcription": "آواز کی نقل کے بعد فوری خودکار بھیجنا",
+	"Interface": "انٹرفیس",
+	"Invalid file format.": "غلط فائل فارمیٹ",
+	"Invalid Tag": "غلط ٹیگ",
+	"January": "جنوری",
+	"Jina API Key": "",
+	"join our Discord for help.": "مدد کے لئے ہمارے ڈسکارڈ میں شامل ہوں",
+	"JSON": "JSON",
+	"JSON Preview": "JSON پیش منظر",
+	"July": "جولائی",
+	"June": "جون",
+	"JWT Expiration": "JWT کی میعاد ختم ہونا",
+	"JWT Token": "JWT ٹوکن",
+	"Keep Alive": "زندہ رکھیں",
+	"Key": "",
+	"Keyboard shortcuts": "کی بورڈ شارٹ کٹس",
+	"Knowledge": "علم",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "علم کامیابی سے تخلیق کیا گیا",
+	"Knowledge deleted successfully.": "معلومات کامیابی سے حذف ہو گئیں",
+	"Knowledge reset successfully.": "علم کو کامیابی کے ساتھ دوبارہ ترتیب دیا گیا",
+	"Knowledge updated successfully": "علم کامیابی سے تازہ کر دیا گیا ہے",
+	"Label": "",
+	"Landing Page Mode": "لینڈر صفحہ موڈ",
+	"Language": "زبان",
+	"Last Active": "آخری سرگرمی",
+	"Last Modified": "آخری ترمیم",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "لیڈر بورڈ",
+	"Leave empty for unlimited": "لامحدود کے لیے خالی چھوڑیں",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "تمام ماڈلز کو شامل کرنے کے لئے خالی چھوڑ دیں یا مخصوص ماڈلز منتخب کریں",
+	"Leave empty to use the default prompt, or enter a custom prompt": "خالی چھوڑیں تاکہ ڈیفالٹ پرامپٹ استعمال ہو، یا ایک حسب ضرورت پرامپٹ درج کریں",
+	"Light": "روشنی",
+	"Listening...": "سن رہے ہیں...",
+	"LLMs can make mistakes. Verify important information.": "ایل ایل ایم غلطیاں کر سکتے ہیں اہم معلومات کی تصدیق کریں",
+	"Local": "",
+	"Local Models": "مقامی ماڈلز",
+	"Lost": "گم شدہ",
+	"LTR": "بائیں سے دائیں",
+	"Made by OpenWebUI Community": "اوپن ویب یو آئی کمیونٹی کی جانب سے تیار کردہ",
+	"Make sure to enclose them with": "انہیں کے ساتھ شامل کریں",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "یقینی بنائیں کہ ComfyUI سے workflow.json فائل کو API فارمیٹ میں ایکسپورٹ کریں",
+	"Manage": "مینیج کریں",
+	"Manage Arena Models": "ایرینا ماڈلز کا نظم کریں",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "پائپ لائنز کا نظم کریں",
+	"March": "مارچ",
+	"Max Tokens (num_predict)": "زیادہ سے زیادہ ٹوکنز (num_predict)",
+	"Max Upload Count": "زیادہ سے زیادہ اپلوڈ تعداد",
+	"Max Upload Size": "زیادہ سے زیادہ اپلوڈ سائز",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "بیک وقت زیادہ سے زیادہ 3 ماڈل ڈاؤن لوڈ کیے جا سکتے ہیں براہ کرم بعد میں دوبارہ کوشش کریں",
+	"May": "مئی",
+	"Memories accessible by LLMs will be shown here.": "یہاں LLMs کے ذریعہ قابل رسائی یادیں دکھائی جائیں گی",
+	"Memory": "میموری",
+	"Memory added successfully": "میموری کامیابی سے شامل کر دی گئی",
+	"Memory cleared successfully": "یادداشت کامیابی سے صاف ہوگئی",
+	"Memory deleted successfully": "میموری کامیابی سے حذف ہوگئی",
+	"Memory updated successfully": "حافظہ کامیابی سے اپ ڈیٹ کر دیا گیا",
+	"Merge Responses": "جوابات کو یکجا کریں",
+	"Message rating should be enabled to use this feature": "اس فیچر کو استعمال کرنے کے لئے پیغام کی درجہ بندی فعال کی جانی چاہئے",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "آپ کے لنک بنانے کے بعد بھیجے گئے پیغامات شیئر نہیں کیے جائیں گے یو آر ایل والے صارفین شیئر کیا گیا چیٹ دیکھ سکیں گے",
+	"Min P": "کم سے کم P",
+	"Minimum Score": "کم از کم اسکور",
+	"Mirostat": "میروسٹیٹ",
+	"Mirostat Eta": "میروسٹیٹ ایٹا",
+	"Mirostat Tau": "میروسٹیٹ ٹاؤ",
+	"MMMM DD, YYYY": "MMMM DD، YYYY",
+	"MMMM DD, YYYY HH:mm": "ایم ایم ایم ایم ڈی ڈی، وائی وائی وائی وائی ایچ ایچ:ایم ایم",
+	"MMMM DD, YYYY hh:mm:ss A": "ایم ایم ایم ایم ڈی ڈی، وائی وائی وائی وائی ایچ ایچ:ایم ایم:ایس ایس اے ایم/پی ایم",
+	"Model": "ماڈل",
+	"Model '{{modelName}}' has been successfully downloaded.": "ماڈل '{{modelName}}' کامیابی سے ڈاؤن لوڈ ہو گیا ہے",
+	"Model '{{modelTag}}' is already in queue for downloading.": "ماڈل '{{modelTag}}' پہلے ہی ڈاؤن لوڈ کے لیے قطار میں ہے",
+	"Model {{modelId}} not found": "ماڈل {{modelId}} نہیں ملا",
+	"Model {{modelName}} is not vision capable": "ماڈل {{modelName}} بصری صلاحیت نہیں رکھتا",
+	"Model {{name}} is now {{status}}": "ماڈل {{name}} اب {{status}} ہے",
+	"Model accepts image inputs": "ماڈل تصویری ان پٹس قبول کرتا ہے",
+	"Model created successfully!": "ماڈل کامیابی سے تیار کر دیا گیا!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "ماڈل فائل سسٹم کا راستہ مل گیا ماڈل کا مختصر نام اپڈیٹ کے لیے ضروری ہے، جاری نہیں رہ سکتا",
+	"Model Filtering": "",
+	"Model ID": "ماڈل آئی ڈی",
+	"Model IDs": "",
+	"Model Name": "ماڈل نام",
+	"Model not selected": "ماڈل منتخب نہیں ہوا",
+	"Model Params": "ماڈل پیرامیٹرز",
+	"Model Permissions": "",
+	"Model updated successfully": "ماڈل کامیابی کے ساتھ اپ ڈیٹ ہو گیا",
+	"Modelfile Content": "ماڈل فائل مواد",
+	"Models": "ماڈلز",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "مزید",
+	"More": "مزید",
+	"Name": "نام",
+	"Name your knowledge base": "",
+	"New Chat": "نئی بات چیت",
+	"New folder": "نیا فولڈر",
+	"New Password": "نیا پاس ورڈ",
+	"No content found": "کوئی مواد نہیں ملا",
+	"No content to speak": "بولنے کے لیے کوئی مواد نہیں",
+	"No distance available": "فاصلہ دستیاب نہیں ہے",
+	"No feedbacks found": "کوئی تبصرے نہیں ملے",
+	"No file selected": "کوئی فائل منتخب نہیں کی گئی",
+	"No files found.": "کوئی فائلیں نہیں ملیں",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "کوئی HTML، CSS، یا جاوا اسکرپٹ مواد نہیں ملا",
+	"No knowledge found": "کوئی معلومات نہیں ملی",
+	"No model IDs": "",
+	"No models found": "کوئی ماڈل نہیں ملا",
+	"No models selected": "",
+	"No results found": "کوئی نتائج نہیں ملے",
+	"No search query generated": "کوئی تلاش کی درخواست نہیں بنائی گئی",
+	"No source available": "ماخذ دستیاب نہیں ہے",
+	"No users were found.": "",
+	"No valves to update": "تازہ کاری کے لئے کوئی والو نہیں",
+	"None": "کوئی نہیں",
+	"Not factually correct": "حقیقت کے مطابق نہیں ہے",
+	"Not helpful": "مددگار نہیں ہے",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "نوٹ: اگر آپ کم از کم سکور سیٹ کرتے ہیں، تو تلاش صرف ان دستاویزات کو واپس کرے گی جن کا سکور کم از کم سکور کے برابر یا اس سے زیادہ ہوگا",
+	"Notes": "نوٹس",
+	"Notifications": "اطلاعات",
+	"November": "نومبر",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (اوللاما)",
+	"OAuth ID": "OAuth آئی ڈی",
+	"October": "اکتوبر",
+	"Off": "بند",
+	"Okay, Let's Go!": "ٹھیک ہے، چلیں!",
+	"OLED Dark": "او ایل ای ڈی ڈارک",
+	"Ollama": "اولامہ",
+	"Ollama API": "اولامہ API",
+	"Ollama API disabled": "اولامہ ایپلیکیشن پروگرام انٹرفیس غیر فعال کر دیا گیا ہے",
+	"Ollama API settings updated": "",
+	"Ollama Version": "اولاما ورژن",
+	"On": "چالو",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "کمانڈ سٹرنگ میں صرف حروفی، عددی کردار اور ہائفن کی اجازت ہے",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "صرف مجموعے ترمیم کیے جا سکتے ہیں، دستاویزات کو ترمیم یا شامل کرنے کے لیے نیا علمی بنیاد بنائیں",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "اوہ! لگتا ہے کہ یو آر ایل غلط ہے براۂ کرم دوبارہ چیک کریں اور دوبارہ کوشش کریں",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "اوہ! کچھ فائلیں ابھی بھی اپ لوڈ ہو رہی ہیں براہ کرم اپ لوڈ مکمل ہونے کا انتظار کریں",
+	"Oops! There was an error in the previous response.": "اوہ! پچھلے جواب میں ایک غلطی تھی",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "اوہ! آپ ایک غیر معاون طریقہ استعمال کر رہے ہیں (صرف فرنٹ اینڈ) براہ کرم ویب یو آئی کو بیک اینڈ سے پیش کریں",
+	"Open file": "فائل کھولیں",
+	"Open in full screen": "پوری اسکرین میں کھولیں",
+	"Open new chat": "نیا چیٹ کھولیں",
+	"Open WebUI uses faster-whisper internally.": "اوپن ویب یو آئی اندرونی طور پر فاسٹر وِسپر استعمال کرتا ہے",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "اوپن WebUI ورژن (v{{OPEN_WEBUI_VERSION}}) مطلوبہ ورژن (v{{REQUIRED_VERSION}}) سے کم ہے",
+	"OpenAI": "اوپن اے آئی",
+	"OpenAI API": "اوپن اے آئی اے پی آئی\n",
+	"OpenAI API Config": "اوپن اے آئی API ترتیب",
+	"OpenAI API Key is required.": "اوپن اے آئی API کلید درکار ہے",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "اوپن اے آئی یو آر ایل/کلید درکار ہے",
+	"or": "یا",
+	"Organize your users": "",
+	"Other": "دیگر",
+	"OUTPUT": "آؤٹ پٹ",
+	"Output format": "آؤٹ پٹ فارمیٹ",
+	"Overview": "جائزہ",
+	"page": "صفحہ",
+	"Password": "پاس ورڈ",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "پی ڈی ایف دستاویز (.pdf)",
+	"PDF Extract Images (OCR)": "پی ڈی ایف سے تصاویر نکالیں (او سی آر)",
+	"pending": "زیر التواء",
+	"Permission denied when accessing media devices": "میڈیا آلات تک رسائی کے وقت اجازت مسترد کر دی گئی",
+	"Permission denied when accessing microphone": "مائیکروفون تک رسائی کی اجازت نہیں دی گئی",
+	"Permission denied when accessing microphone: {{error}}": "مائیکروفون تک رسائی کے دوران اجازت مسترد: {{error}}",
+	"Permissions": "",
+	"Personalization": "شخصی ترتیبات",
+	"Pin": "پن",
+	"Pinned": "پن کیا گیا",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "پائپ لائن کامیابی سے حذف کر دی گئی",
+	"Pipeline downloaded successfully": "پائپ لائن کامیابی سے ڈاؤن لوڈ ہو گئی",
+	"Pipelines": "پائپ لائنز",
+	"Pipelines Not Detected": "پائپ لائنز کی نشاندہی نہیں ہوئی",
+	"Pipelines Valves": "پائپ لائنز والوز",
+	"Plain text (.txt)": "سادہ متن (.txt)",
+	"Playground": "کھیل کا میدان",
+	"Please carefully review the following warnings:": "براہ کرم درج ذیل انتباہات کو احتیاط سے پڑھیں:",
+	"Please enter a prompt": "براہ کرم ایک پرامپٹ درج کریں",
+	"Please fill in all fields.": "براہ کرم تمام فیلڈز مکمل کریں",
+	"Please select a model first.": "",
+	"Please select a reason": "براہ کرم ایک وجہ منتخب کریں",
+	"Port": "",
+	"Positive attitude": "مثبت رویہ",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "پچھلے 30 دن",
+	"Previous 7 days": "پچھلے 7 دن",
+	"Profile Image": "پروفائل تصویر",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "سوال کریں (مثلاً: مجھے رومن سلطنت کے بارے میں کوئی دلچسپ حقیقت بتائیں)",
+	"Prompt Content": "مواد کا آغاز کریں",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "تجاویز کی تجویزات",
+	"Prompt updated successfully": "",
+	"Prompts": "پرومپٹس",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Ollama.com سے \"{{searchValue}}\" کو کھینچیں",
+	"Pull a model from Ollama.com": "Ollama.com سے ماڈل حاصل کریں",
+	"Query Generation Prompt": "",
+	"Query Params": "کوئری پیرامیٹرز",
+	"RAG Template": "آر اے جی سانچہ",
+	"Rating": "درجہ بندی",
+	"Re-rank models by topic similarity": "موضوع کی مماثلت کے لحاظ سے ماڈلز کی دوبارہ ترتیب دیں",
+	"Read Aloud": "بُلند آواز میں پڑھیں",
+	"Record voice": "صوت ریکارڈ کریں",
+	"Redirecting you to OpenWebUI Community": "آپ کو اوپن ویب یو آئی کمیونٹی کی طرف ری ڈائریکٹ کیا جا رہا ہے",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "خود کو \"صارف\" کے طور پر حوالہ دیں (جیسے، \"صارف ہسپانوی سیکھ رہا ہے\")",
+	"References from": "سے حوالہ جات",
+	"Refused when it shouldn't have": "جب انکار نہیں ہونا چاہیے تھا، انکار کر دیا",
+	"Regenerate": "دوبارہ تخلیق کریں",
+	"Release Notes": "ریلیز نوٹس",
+	"Relevance": "موزونیت",
+	"Remove": "ہٹا دیں",
+	"Remove Model": "ماڈل ہٹائیں",
+	"Rename": "تبدیل نام کریں",
+	"Reorder Models": "",
+	"Repeat Last N": "آخری این (N)",
+	"Request Mode": "درخواست کا موڈ",
+	"Reranking Model": "دوبارہ درجہ بندی کا ماڈل",
+	"Reranking model disabled": "دوبارہ درجہ بندی کا ماڈل غیر فعال کر دیا گیا",
+	"Reranking model set to \"{{reranking_model}}\"": "دوبارہ درجہ بندی کا ماڈل \"{{reranking_model}}\" پر مقرر کر دیا گیا ہے",
+	"Reset": "ری سیٹ",
+	"Reset All Models": "",
+	"Reset Upload Directory": "اپلوڈ ڈائریکٹری کو ری سیٹ کریں",
+	"Reset Vector Storage/Knowledge": "ویكٹر اسٹوریج/علم کو ری سیٹ کریں",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "جواب کی اطلاعات کو فعال نہیں کیا جا سکتا کیونکہ ویب سائٹ کی اجازتیں مسترد کر دی گئی ہیں براہ کرم اپنے براؤزر کی سیٹنگز پر جائیں تاکہ ضروری رسائی کی اجازت دے سکیں",
+	"Response splitting": "جواب کو تقسیم کرنا",
+	"Result": "نتیجہ",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "چیٹ کے لیے رچ ٹیکسٹ ان پٹ",
+	"RK": "آر کے",
+	"Role": "کردار",
+	"Rosé Pine": "روزے پائن",
+	"Rosé Pine Dawn": "روزے پائن ڈان",
+	"RTL": "آر ٹی ایل",
+	"Run": "چلائیں",
+	"Running": "چل رہا ہے",
+	"Save": "محفوظ کریں",
+	"Save & Create": "محفوظ کریں اور تخلیق کریں",
+	"Save & Update": "محفوظ کریں اور اپ ڈیٹ کریں",
+	"Save As Copy": "کاپی کے طور پر محفوظ کریں",
+	"Save Tag": "ٹیگ محفوظ کریں",
+	"Saved": "محفوظ شدہ",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "براہ کرم اپنے براؤزر کے اسٹوریج میں چیٹ لاگز کو محفوظ کرنا اب تعاون یافتہ نہیں ہے براہ کرم نیچے دیئے گئے بٹن پر کلک کرکے اپنے چیٹ لاگز کو ڈاؤن لوڈ اور حذف کریں فکر نہ کریں، آپ اپنے چیٹ لاگز کو بیک اینڈ میں دوبارہ آسانی سے درآمد کر سکتے ہیں",
+	"Scroll to bottom when switching between branches": "شاخیں تبدیل کرتے وقت نیچے تک سکرول کریں",
+	"Search": "تلاش کریں",
+	"Search a model": "ماڈل تلاش کریں",
+	"Search Base": "",
+	"Search Chats": "چیٹس تلاش کریں",
+	"Search Collection": "مجموعہ تلاش کریں",
+	"Search Filters": "",
+	"search for tags": "ٹیگز کے لیے تلاش کریں",
+	"Search Functions": "تلاش کے افعال",
+	"Search Knowledge": "علم تلاش کریں",
+	"Search Models": "ماڈلز تلاش کریں",
+	"Search options": "",
+	"Search Prompts": "تلاش کے اشارے",
+	"Search Result Count": "تلاش کا نتیجہ شمار ",
+	"Search the web": "",
+	"Search Tools": "تلاش کے اوزار",
+	"SearchApi API Key": "سرچ اے پی آئی کی API کلید",
+	"SearchApi Engine": "تلاش انجن API",
+	"Searched {{count}} sites_one": "{{count}} سائٹیں تلاش کی گئی ہیں_ایک",
+	"Searched {{count}} sites_other": "تلاش کردہ {{count}} سائٹس_دیگر",
+	"Searching \"{{searchQuery}}\"": "\"{{searchQuery}}\" تلاش کر رہے ہیں",
+	"Searching Knowledge for \"{{searchQuery}}\"": "\"{{searchQuery}}\" کے لیے علم کی تلاش",
+	"Searxng Query URL": "تلاش کا سوال URL",
+	"See readme.md for instructions": "ہدایات کے لیے readme.md دیکھیں",
+	"See what's new": "نیا کیا ہے دیکھیں",
+	"Seed": "بیج",
+	"Select a base model": "ایک بنیادی ماڈل منتخب کریں",
+	"Select a engine": "ایک انجن منتخب کریں",
+	"Select a function": "ایک فنکشن منتخب کریں",
+	"Select a group": "",
+	"Select a model": "ایک ماڈل منتخب کریں",
+	"Select a pipeline": "ایک پائپ لائن منتخب کریں",
+	"Select a pipeline url": "پائپ لائن یو آر ایل منتخب کریں",
+	"Select a tool": "ایک ٹول منتخب کریں",
+	"Select Engine": "انجن منتخب کریں",
+	"Select Knowledge": "علم منتخب کریں",
+	"Select model": "ماڈل منتخب کریں",
+	"Select only one model to call": "صرف ایک ماڈل کو کال کرنے کے لئے منتخب کریں",
+	"Selected model(s) do not support image inputs": "منتخب کردہ ماڈل(ز) تصویری ان پٹ کی حمایت نہیں کرتے",
+	"Semantic distance to query": "سوال کے لیے معنوی فاصلہ",
+	"Send": "بھیجیں",
+	"Send a Message": "پیغام بھیجیں",
+	"Send message": "پیغام بھیجیں",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "درخواست میں `stream_options: { include_usage: true }` بھیجتا ہے\nمعاون فراہم کنندگان، جب سیٹ کیا جاتا ہے تو، جواب میں ٹوکن کے استعمال کی معلومات واپس کر دیں گے",
+	"September": "ستمبر",
+	"Serper API Key": "سرپر API کلید",
+	"Serply API Key": "سرپلی API کی کلید",
+	"Serpstack API Key": "سرپ اسٹیک اے پی آئی کلید",
+	"Server connection verified": "سرور کنکشن تصدیق شدہ ہے",
+	"Set as default": "بطور ڈیفالٹ سیٹ کریں",
+	"Set CFG Scale": "سی ایف جی اسکیل مقرر کریں",
+	"Set Default Model": "ڈیفالٹ ماڈل سیٹ کریں",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "نقش ماڈل مرتب کریں (مثال کے طور پر {{model}})",
+	"Set Image Size": "تصویر کا سائز مقرر کریں",
+	"Set reranking model (e.g. {{model}})": "ری رینکنگ ماڈل سیٹ کریں (مثال کے طور پر {{model}})",
+	"Set Sampler": "نمونہ سیٹ کریں",
+	"Set Scheduler": "شیڈیولر مقرر کریں",
+	"Set Steps": "قدم طے کریں",
+	"Set Task Model": "ٹاسک ماڈل مرتب کریں",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "آواز کے لئے سیٹ کریں",
+	"Set whisper model": "وِسپر ماڈل مرتب کریں",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "ترتیبات",
+	"Settings saved successfully!": "ترتیبات کامیابی کے ساتھ محفوظ ہو گئیں!",
+	"Share": "اشتراک کریں",
+	"Share Chat": "چیٹ شیئر کریں",
+	"Share to OpenWebUI Community": "اوپن ویب یوآئی کمیونٹی کے ساتھ شیئر کریں\n",
+	"Show": "دکھائیں",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "اکاؤنٹ پینڈنگ اوورلے میں ایڈمن کی تفصیلات دکھائیں",
+	"Show shortcuts": "شارٹ کٹ دکھائیں",
+	"Show your support!": "اپنی حمایت دکھائیں!",
+	"Showcased creativity": "نمائش شدہ تخلیقی صلاحیتیں",
+	"Sign in": "سائن ان کریں",
+	"Sign in to {{WEBUI_NAME}}": "{{WEBUI_NAME}} میں سائن ان کریں",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "سائن آؤٹ کریں",
+	"Sign up": "سائن اپ کریں",
+	"Sign up to {{WEBUI_NAME}}": "{{WEBUI_NAME}} میں سائن اپ کریں",
+	"Signing in to {{WEBUI_NAME}}": "{{WEBUI_NAME}} میں سائن اِن کر رہے ہیں",
+	"Source": "ماخذ",
+	"Speech Playback Speed": "تقریر پلے بیک کی رفتار",
+	"Speech recognition error: {{error}}": "تقریر کی پہچان کی خرابی: {{error}}",
+	"Speech-to-Text Engine": "تقریر-سے-متن انجن",
+	"Stop": "روکیں",
+	"Stop Sequence": "ترتیب روکیں",
+	"Stream Chat Response": "اسٹریم چیٹ جواب",
+	"STT Model": "ایس ٹی ٹی ماڈل",
+	"STT Settings": "ایس ٹی ٹی ترتیبات",
+	"Subtitle (e.g. about the Roman Empire)": "ذیلی عنوان (جیسے رومن سلطنت کے بارے میں)",
+	"Success": "کامیابی",
+	"Successfully updated.": "کامیابی سے تازہ کاری ہو گئی",
+	"Suggested": "تجویز کردہ",
+	"Support": "مدد کریں",
+	"Support this plugin:": "اس پلگ ان کی حمایت کریں:",
+	"Sync directory": "ڈائریکٹری مطابقت پذیری کریں",
+	"System": "سسٹم",
+	"System Instructions": "نظام کی ہدایات",
+	"System Prompt": "سسٹم پرومپٹ",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "پرمپٹ کے لیے ٹیگز بنائیں",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "رکنے کے لئے ٹچ کریں",
+	"Tavily API Key": "ٹاویلی API کلید",
+	"Tell us more:": "ہمیں مزید بتائیں:",
+	"Temperature": "درجہ حرارت",
+	"Template": "سانچہ",
+	"Temporary Chat": "عارضی چیٹ",
+	"Text Splitter": "متن تقسیم کنندہ",
+	"Text-to-Speech Engine": "ٹیکسٹ ٹو اسپیچ انجن",
+	"Tfs Z": "ٹی ایف ایس زیڈ",
+	"Thanks for your feedback!": "آپ کی رائے کا شکریہ!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "اس پلگ ان کے پیچھے موجود ڈویلپرز کمیونٹی کے پرجوش رضاکار ہیں اگر آپ کو یہ پلگ ان مددگار لگتا ہے تو برائے مہربانی اس کی ترقی میں اپنا حصہ ڈالنے پر غور کریں",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "تشخیصی لیڈربورڈ ایلو ریٹنگ سسٹم پر مبنی ہے اور یہ حقیقی وقت میں اپ ڈیٹ ہوتا ہے",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "لیڈر بورڈ اس وقت بیٹا مرحلے میں ہے، اور جیسے جیسے ہم الگورتھم کو بہتر بنائیں گے ہم ریٹنگ کیلکولیشن کو ایڈجسٹ کرسکتے ہیں",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "زیادہ سے زیادہ فائل سائز ایم بی میں اگر فائل سائز اس حد سے تجاوز کر جاتا ہے، تو فائل اپ لوڈ نہیں ہوگی",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "چیٹ میں ایک وقت میں استعمال ہونے والی فائلوں کی زیادہ سے زیادہ تعداد اگر فائلوں کی تعداد اس حد سے تجاوز کر جائے تو فائلیں اپلوڈ نہیں کی جائیں گی",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "سکور کی قیمت کو 0.0 (0%) اور 1.0 (100%) کے درمیان ہونا چاہیے",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "تھیم",
+	"Thinking...": "سوچ رہا ہے...",
+	"This action cannot be undone. Do you wish to continue?": "یہ عمل واپس نہیں کیا جا سکتا کیا آپ جاری رکھنا چاہتے ہیں؟",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "یہ یقینی بناتا ہے کہ آپ کی قیمتی گفتگو محفوظ طریقے سے آپ کے بیک اینڈ ڈیٹا بیس میں محفوظ کی گئی ہیں شکریہ!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "یہ ایک تجرباتی خصوصیت ہے، یہ متوقع طور پر کام نہ کر سکتی ہو اور کسی بھی وقت تبدیل کی جا سکتی ہے",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "اس اختیار سے مجموعہ میں موجود تمام فائلز حذف ہو جائیں گی اور ان کی جگہ نئی اپ لوڈ کردہ فائلز لی جائیں گی",
+	"This response was generated by \"{{model}}\"": "یہ جواب \"{{model}}\" کے ذریعہ تیار کیا گیا",
+	"This will delete": "یہ حذف کر دے گا",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "یہ <strong>{{NAME}}</strong> اور <strong>اس کے تمام مواد</strong> کو حذف کر دے گا",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "یہ علمی بنیاد کو دوبارہ ترتیب دے گا اور تمام فائلز کو متوازن کرے گا کیا آپ جاری رکھنا چاہتے ہیں؟",
+	"Thorough explanation": "مکمل وضاحت",
+	"Tika": "ٹیکہ",
+	"Tika Server URL required.": "ٹکا سرور یو آر ایل درکار ہے",
+	"Tiktoken": "ٹک ٹوکن",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "ترکیب: ہر تبدیلی کے بعد چیٹ ان پٹ میں ٹیب کی کلید دباکر متعدد متغیر سلاٹوں کو مسلسل اپ ڈیٹ کریں",
+	"Title": "عنوان",
+	"Title (e.g. Tell me a fun fact)": "عنوان (مثال کے طور پر، مجھے ایک دلچسپ حقیقت بتائیں)",
+	"Title Auto-Generation": "خودکار عنوان تخلیق",
+	"Title cannot be an empty string.": "عنوان خالی اسٹرنگ نہیں ہو سکتا",
+	"Title Generation Prompt": "سرخی بنانے کی ہدایت",
+	"TLS": "",
+	"To access the available model names for downloading,": "ڈاؤن لوڈ کرنے کے لئے دستیاب ماڈل کے ناموں تک رسائی حاصل کرنے کے لئے،",
+	"To access the GGUF models available for downloading,": "GGUF ماڈلز تک رسائی حاصل کرنے کے لئے جو ڈاؤنلوڈنگ کے لئے دستیاب ہیں،",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "ویب یو آئی تک رسائی حاصل کرنے کے لیے براہ کرم منتظم سے رابطہ کریں ایڈمنز صارف کی حالت کو ایڈمن پینل سے منظم کر سکتے ہیں",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "یہاں معلوماتی بنیاد منسلک کرنے کے لیے، پہلے انہیں \"معلومات\" ورک اسپیس میں شامل کریں",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "آپ کی رازداری کے تحفظ کے لئے، صرف درجہ بندی، ماڈل IDs، ٹیگز، اور میٹا ڈیٹا آپ کے فیڈ بیک سے شیئر کیے جاتے ہیں - آپ کی چیٹ کی تفصیلات نجی رہتی ہیں اور شامل نہیں کی جاتیں ",
+	"To select actions here, add them to the \"Functions\" workspace first.": "عمل کا انتخاب کرنے کے لیے، پہلے انہیں \"افعال\" ورک اسپیس میں شامل کریں",
+	"To select filters here, add them to the \"Functions\" workspace first.": "یہاں فلٹرز منتخب کرنے کے لئے، پہلے انہیں \"فیچرز\" ورک اسپیس میں شامل کریں",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "یہاں ٹول کٹس منتخب کرنے کے لیے، پہلے انہیں \"ٹولز\" ورک اسپیس میں شامل کریں",
+	"Toast notifications for new updates": "نئے اپڈیٹس کے لئے ٹوسٹ نوٹیفیکیشنز",
+	"Today": "آج",
+	"Toggle settings": "ترتیبات کو تبدیل کریں",
+	"Toggle sidebar": "سائڈبار کو ٹوگل کریں",
+	"Token": "ٹوکَن",
+	"Tokens To Keep On Context Refresh (num_keep)": "کانٹیکسٹ ریفریش پر رکھنے کے لئے ٹوکنز (num_keep)",
+	"Too verbose": "بہت زیادہ طویل",
+	"Tool created successfully": "اوزار کامیابی کے ساتھ تخلیق کیا گیا",
+	"Tool deleted successfully": "آلہ کامیابی سے حذف ہو گیا",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "اوزار کامیابی کے ساتھ درآمد ہوا",
+	"Tool Name": "",
+	"Tool updated successfully": "ٹول کامیابی سے اپ ڈیٹ ہو گیا ہے",
+	"Tools": "اوزار",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "ٹولز ایک فنکشن کالنگ سسٹم ہیں جس میں مرضی کے مطابق کوڈ چلایا جاتا ہے",
+	"Tools have a function calling system that allows arbitrary code execution": "ٹولز کے پاس ایک فنکشن کالنگ سسٹم ہے جو اختیاری کوڈ کے نفاذ کی اجازت دیتا ہے",
+	"Tools have a function calling system that allows arbitrary code execution.": "ٹولز کے پاس ایک فنکشن کالنگ سسٹم ہے جو اختیاری کوڈ کی عمل درآمد کی اجازت دیتا ہے",
+	"Top K": "اوپر کے K",
+	"Top P": "ٹاپ پی",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Ollama تک رسائی میں مشکل؟",
+	"TTS Model": "ٹی ٹی ایس ماڈل",
+	"TTS Settings": "ٹی ٹی ایس ترتیبات",
+	"TTS Voice": "ٹی ٹی ایس آواز",
+	"Type": "ٹائپ کریں",
+	"Type Hugging Face Resolve (Download) URL": "قسم ہگنگ فیس ری زولو (ڈاؤن لوڈ) یو آر ایل",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "اوہ-اوہ! {{provider}} سے جڑنے میں مسئلہ پیش آیا",
+	"UI": "صارف انٹرفیس",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "ان پن کریں",
+	"Unravel secrets": "",
+	"Untagged": "غیر مہر شدہ",
+	"Update": "اپ ڈیٹ کریں",
+	"Update and Copy Link": "اپڈیٹ اور لنک کاپی کریں",
+	"Update for the latest features and improvements.": "تازہ ترین خصوصیات اور بہتریوں کے لیے اپ ڈیٹ کریں",
+	"Update password": "پاس ورڈ اپ ڈیٹ کریں",
+	"Updated": "اپ ڈیٹ کیا گیا",
+	"Updated at": "پر تازہ کاری کی گئی ",
+	"Updated At": "تازہ کاری کی گئی تاریخ پر",
+	"Upload": "اپ لوڈ کریں",
+	"Upload a GGUF model": "جی جی یو ایف ماڈل اپلوڈ کریں",
+	"Upload directory": "ڈائریکٹری اپلوڈ کریں",
+	"Upload files": "فائلیں اپلوڈ کریں",
+	"Upload Files": "فائلیں اپ لوڈ کریں",
+	"Upload Pipeline": "اپ لوڈ پائپ لائن",
+	"Upload Progress": "اپ لوڈ کی پیش رفت",
+	"URL": "",
+	"URL Mode": "یو آر ایل موڈ",
+	"Use '#' in the prompt input to load and include your knowledge.": "پرامپٹ ان پٹ میں '#' استعمال کریں تاکہ اپنی معلومات کو لوڈ اور شامل کریں",
+	"Use Gravatar": "گراویٹر استعمال کریں",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "ابتدائیات استعمال کریں",
+	"use_mlock (Ollama)": "استعمال کریں_mlock (Ollama)",
+	"use_mmap (Ollama)": "استعمال_mmap (Ollama)",
+	"user": "صارف",
+	"User": "صارف",
+	"User location successfully retrieved.": "صارف کا مقام کامیابی سے حاصل کیا گیا",
+	"Username": "",
+	"Users": "صارفین",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "تمام ماڈلز کے ساتھ ڈیفالٹ ارینا ماڈل استعمال کریں حسب ضرورت ماڈلز شامل کرنے کے لیے پلس بٹن پر کلک کریں",
+	"Utilize": "استعمال کریں",
+	"Valid time units:": "درست وقت کی اکائیاں:",
+	"Valves": "والو",
+	"Valves updated": "والوز کو اپ ڈیٹ کر دیا گیا",
+	"Valves updated successfully": "والو کامیابی کے ساتھ اپ ڈیٹ ہو گئے",
+	"variable": "متغیر",
+	"variable to have them replaced with clipboard content.": "انہیں کلپ بورڈ کے مواد سے تبدیل کرنے کے لیے متغیر",
+	"Version": "ورژن",
+	"Version {{selectedVersion}} of {{totalVersions}}": "ورژن {{selectedVersion}} کا {{totalVersions}} میں سے",
+	"Visibility": "",
+	"Voice": "آواز",
+	"Voice Input": "آواز داخل کریں",
+	"Warning": "انتباہ",
+	"Warning:": "انتباہ:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "انتباہ: اگر آپ اپنا ایمبیڈنگ ماڈل اپ ڈیٹ یا تبدیل کرتے ہیں تو آپ کو تمام دستاویزات کو دوبارہ درآمد کرنے کی ضرورت ہوگی",
+	"Web": "ویب",
+	"Web API": "ویب اے پی آئی",
+	"Web Loader Settings": "ویب لوڈر کی ترتیبات",
+	"Web Search": "ویب تلاش کریں",
+	"Web Search Engine": "ویب تلاش انجن",
+	"Web Search Query Generation": "",
+	"Webhook URL": "ویب ہُک یو آر ایل",
+	"WebUI Settings": "ویب UI ترتیبات",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "میں نیا کیا ہے",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "سرگوشی (مقامی)",
+	"Why?": "",
+	"Widescreen Mode": "وائڈ اسکرین موڈ",
+	"Won": "جیتا",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "ورک اسپیس",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "(مثال کے طور پر: آپ کون ہیں؟) ایک تجویز لکھیے",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "موضوع یا کلیدی لفظ کا خلاصہ 50 الفاظ میں لکھیں",
+	"Write something...": "کچھ لکھیں...",
+	"Write your model template content here": "",
+	"Yesterday": "کل",
+	"You": "آپ",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "آپ ایک وقت میں زیادہ سے زیادہ {{maxCount}} فائل(وں) کے ساتھ صرف چیٹ کر سکتے ہیں",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "آپ نیچے موجود 'Manage' بٹن کے ذریعے LLMs کے ساتھ اپنی بات چیت کو یادداشتیں شامل کرکے ذاتی بنا سکتے ہیں، جو انہیں آپ کے لیے زیادہ مددگار اور آپ کے متعلق بنائے گی",
+	"You cannot upload an empty file.": "آپ خالی فائل اپلوڈ نہیں کر سکتے",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "آپ کے پاس کوئی محفوظ شدہ مکالمات نہیں ہیں",
+	"You have shared this chat": "آپ نے یہ چیٹ شیئر کی ہے",
+	"You're a helpful assistant.": "آپ ایک معاون معاون ہیں",
+	"You're now logged in.": "آپ اب لاگ ان ہو چکے ہیں",
+	"Your account status is currently pending activation.": "آپ کے اکاؤنٹ کی حالت فی الحال فعال ہونے کے منتظر ہے",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "آپ کی پوری شراکت براہ راست پلگ ان ڈیولپر کو جائے گی؛ اوپن ویب یو آئی کوئی فیصد نہیں لیتی تاہم، منتخب کردہ فنڈنگ پلیٹ فارم کی اپنی فیس ہو سکتی ہیں",
+	"Youtube": "یوٹیوب",
+	"Youtube Loader Settings": "یوٹیوب لوڈر کی ترتیبات"
+}
diff --git a/src/lib/i18n/locales/vi-VN/translation.json b/src/lib/i18n/locales/vi-VN/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..adc070a5c35722ab1d98c08fd10999da7596046a
--- /dev/null
+++ b/src/lib/i18n/locales/vi-VN/translation.json
@@ -0,0 +1,1024 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' hoặc '-1' không hết hạn.",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "",
+	"(e.g. `sh webui.sh --api`)": "(vd: `sh webui.sh --api`)",
+	"(latest)": "(mới nhất)",
+	"{{ models }}": "{{ mô hình }}",
+	"{{user}}'s Chats": "{{user}}'s Chats",
+	"{{webUIName}} Backend Required": "{{webUIName}} Yêu cầu Backend",
+	"*Prompt node ID(s) are required for image generation": "",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "Mô hình tác vụ được sử dụng khi thực hiện các tác vụ như tạo tiêu đề cho cuộc trò chuyện và truy vấn tìm kiếm trên web",
+	"a user": "người sử dụng",
+	"About": "Giới thiệu",
+	"Access": "",
+	"Access Control": "",
+	"Accessible to all users": "",
+	"Account": "Tài khoản",
+	"Account Activation Pending": "Tài khoản đang chờ kích hoạt",
+	"Accurate information": "Thông tin chính xác",
+	"Actions": "Tác vụ",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
+	"Active Users": "Người dùng đang hoạt động",
+	"Add": "Thêm",
+	"Add a model ID": "",
+	"Add a short description about what this model does": "Thêm mô tả ngắn về những khả năng của model",
+	"Add a tag": "Thêm thẻ (tag)",
+	"Add Arena Model": "",
+	"Add Connection": "",
+	"Add Content": "",
+	"Add content here": "",
+	"Add custom prompt": "Thêm prompt tùy chỉnh",
+	"Add Files": "Thêm tệp",
+	"Add Group": "",
+	"Add Memory": "Thêm bộ nhớ",
+	"Add Model": "Thêm model",
+	"Add Tag": "Thêm thẻ",
+	"Add Tags": "thêm thẻ",
+	"Add text content": "",
+	"Add User": "Thêm người dùng",
+	"Add User Group": "",
+	"Adjusting these settings will apply changes universally to all users.": "Các thay đổi cài đặt này sẽ áp dụng cho tất cả người sử dụng.",
+	"admin": "quản trị viên",
+	"Admin": "Quản trị",
+	"Admin Panel": "Trang Quản trị",
+	"Admin Settings": "Cài đặt hệ thống",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "Quản trị viên luôn có quyền truy cập vào tất cả các tool; người dùng cần các tools được chỉ định cho mỗi mô hình trong workspace.",
+	"Advanced Parameters": "Các tham số Nâng cao",
+	"Advanced Params": "Các tham số Nâng cao",
+	"All chats": "",
+	"All Documents": "Tất cả tài liệu",
+	"All models deleted successfully": "",
+	"Allow Chat Delete": "",
+	"Allow Chat Deletion": "Cho phép Xóa nội dung chat",
+	"Allow Chat Edit": "",
+	"Allow File Upload": "",
+	"Allow non-local voices": "Cho phép giọng nói không bản xứ",
+	"Allow Temporary Chat": "Cho phép Chat nháp",
+	"Allow User Location": "Cho phép sử dụng vị trí người dùng",
+	"Allow Voice Interruption in Call": "Cho phép gián đoạn giọng nói trong cuộc gọi",
+	"Already have an account?": "Bạn đã có tài khoản?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "",
+	"Amazing": "",
+	"an assistant": "trợ lý",
+	"and": "và",
+	"and {{COUNT}} more": "",
+	"and create a new shared link.": "và tạo một link chia sẻ mới",
+	"API Base URL": "Đường dẫn tới API (API Base URL)",
+	"API Key": "API Key",
+	"API Key created.": "Khóa API đã tạo",
+	"API keys": "API Keys",
+	"Application DN": "",
+	"Application DN Password": "",
+	"applies to all users with the \"user\" role": "",
+	"April": "Tháng 4",
+	"Archive": "Lưu trữ",
+	"Archive All Chats": "Lưu tất cả các cuộc Chat",
+	"Archived Chats": "Lưu các cuộc Chat",
+	"archived-chat-export": "",
+	"Are you sure you want to unarchive all archived chats?": "",
+	"Are you sure?": "Bạn có chắc chắn không?",
+	"Arena Models": "",
+	"Artifacts": "",
+	"Ask a question": "",
+	"Assistant": "",
+	"Attach file": "Đính kèm file",
+	"Attention to detail": "Có sự chú ý đến chi tiết của vấn đề",
+	"Attribute for Username": "",
+	"Audio": "Âm thanh",
+	"August": "Tháng 8",
+	"Authenticate": "",
+	"Auto-Copy Response to Clipboard": "Tự động Sao chép Phản hồi vào clipboard",
+	"Auto-playback response": "Tự động phát lại phản hồi (Auto-playback)",
+	"Autocomplete Generation": "",
+	"Autocomplete Generation Input Max Length": "",
+	"Automatic1111": "",
+	"AUTOMATIC1111 Api Auth String": "",
+	"AUTOMATIC1111 Base URL": "Đường dẫn kết nối tới AUTOMATIC1111 (Base URL)",
+	"AUTOMATIC1111 Base URL is required.": "Base URL của AUTOMATIC1111 là bắt buộc.",
+	"Available list": "",
+	"available!": "có sẵn!",
+	"Awful": "",
+	"Azure AI Speech": "",
+	"Azure Region": "",
+	"Back": "Quay lại",
+	"Bad Response": "Trả lời KHÔNG tốt",
+	"Banners": "Biểu ngữ",
+	"Base Model (From)": "Mô hình cơ sở (từ)",
+	"Batch Size (num_batch)": "",
+	"before": "trước",
+	"Being lazy": "Lười biếng",
+	"Bing Search V7 Endpoint": "",
+	"Bing Search V7 Subscription Key": "",
+	"Brave Search API Key": "Khóa API tìm kiếm dũng cảm",
+	"By {{name}}": "",
+	"Bypass SSL verification for Websites": "Bỏ qua xác thực SSL cho các trang web",
+	"Call": "Gọi",
+	"Call feature is not supported when using Web STT engine": "Tính năng gọi điện không được hỗ trợ khi sử dụng công cụ Web STT",
+	"Camera": "",
+	"Cancel": "Hủy bỏ",
+	"Capabilities": "Năng lực",
+	"Certificate Path": "",
+	"Change Password": "Đổi Mật khẩu",
+	"Character": "",
+	"Character limit for autocomplete generation input": "",
+	"Chart new frontiers": "",
+	"Chat": "Trò chuyện",
+	"Chat Background Image": "Hình nền trò chuyện",
+	"Chat Bubble UI": "Bảng chat",
+	"Chat Controls": "Điều khiển Chats",
+	"Chat direction": "Hướng chat",
+	"Chat Overview": "",
+	"Chat Permissions": "",
+	"Chat Tags Auto-Generation": "",
+	"Chats": "Chat",
+	"Check Again": "Kiểm tra Lại",
+	"Check for updates": "Kiểm tra cập nhật",
+	"Checking for updates...": "Đang kiểm tra cập nhật...",
+	"Choose a model before saving...": "Chọn mô hình trước khi lưu...",
+	"Chunk Overlap": "Chồng lấn (overlap)",
+	"Chunk Params": "Tham số khối (chunk)",
+	"Chunk Size": "Kích thước khối (size)",
+	"Ciphers": "",
+	"Citation": "Trích dẫn",
+	"Clear memory": "Xóa bộ nhớ",
+	"click here": "",
+	"Click here for filter guides.": "",
+	"Click here for help.": "Bấm vào đây để được trợ giúp.",
+	"Click here to": "Nhấn vào đây để",
+	"Click here to download user import template file.": "Bấm vào đây để tải xuống tệp template của người dùng.",
+	"Click here to learn more about faster-whisper and see the available models.": "",
+	"Click here to select": "Bấm vào đây để chọn",
+	"Click here to select a csv file.": "Nhấn vào đây để chọn tệp csv",
+	"Click here to select a py file.": "Nhấn vào đây để chọn tệp py",
+	"Click here to upload a workflow.json file.": "Bấm vào đây để upload file worklow.json",
+	"click here.": "bấm vào đây.",
+	"Click on the user role button to change a user's role.": "Bấm vào nút trong cột VAI TRÒ để thay đổi quyền của người sử dụng.",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "Quyền ghi vào clipboard bị từ chối. Vui lòng kiểm tra cài đặt trên trình duyệt của bạn để được cấp quyền truy cập cần thiết.",
+	"Clone": "Nhân bản",
+	"Close": "Đóng",
+	"Code execution": "",
+	"Code formatted successfully": "Mã được định dạng thành công",
+	"Collection": "Tổng hợp mọi tài liệu",
+	"Color": "",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI Base URL",
+	"ComfyUI Base URL is required.": "Base URL của ComfyUI là bắt buộc.",
+	"ComfyUI Workflow": "",
+	"ComfyUI Workflow Nodes": "",
+	"Command": "Lệnh",
+	"Completions": "",
+	"Concurrent Requests": "Các truy vấn đồng thời",
+	"Configure": "",
+	"Configure Models": "",
+	"Confirm": "Xác nhận",
+	"Confirm Password": "Xác nhận Mật khẩu",
+	"Confirm your action": "Xác nhận hành động của bạn",
+	"Connections": "Kết nối",
+	"Contact Admin for WebUI Access": "Liên hệ với Quản trị viên để được cấp quyền truy cập",
+	"Content": "Nội dung",
+	"Content Extraction": "Trích xuất nội dung",
+	"Context Length": "Độ dài ngữ cảnh (Context Length)",
+	"Continue Response": "Tiếp tục trả lời",
+	"Continue with {{provider}}": "Tiếp tục với {{provider}}",
+	"Continue with Email": "",
+	"Continue with LDAP": "",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "",
+	"Controls": "",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "",
+	"Copied": "Đã sao chép",
+	"Copied shared chat URL to clipboard!": "Đã sao chép link chia sẻ trò chuyện vào clipboard!",
+	"Copied to clipboard": "",
+	"Copy": "Sao chép",
+	"Copy last code block": "Sao chép khối mã cuối cùng",
+	"Copy last response": "Sao chép phản hồi cuối cùng",
+	"Copy Link": "Sao chép link",
+	"Copy to clipboard": "",
+	"Copying to clipboard was successful!": "Sao chép vào clipboard thành công!",
+	"Create": "",
+	"Create a knowledge base": "",
+	"Create a model": "Tạo model",
+	"Create Account": "Tạo Tài khoản",
+	"Create Admin Account": "",
+	"Create Group": "",
+	"Create Knowledge": "",
+	"Create new key": "Tạo key mới",
+	"Create new secret key": "Tạo key bí mật mới",
+	"Created at": "Được tạo vào lúc",
+	"Created At": "Tạo lúc",
+	"Created by": "Tạo bởi",
+	"CSV Import": "Nạp CSV",
+	"Current Model": "Mô hình hiện tại",
+	"Current Password": "Mật khẩu hiện tại",
+	"Custom": "Tùy chỉnh",
+	"Dark": "Tối",
+	"Database": "Cơ sở dữ liệu",
+	"December": "Tháng 12",
+	"Default": "Mặc định",
+	"Default (Open AI)": "",
+	"Default (SentenceTransformers)": "Mặc định (SentenceTransformers)",
+	"Default Model": "Model mặc định",
+	"Default model updated": "Mô hình mặc định đã được cập nhật",
+	"Default Models": "",
+	"Default permissions": "",
+	"Default permissions updated successfully": "",
+	"Default Prompt Suggestions": "Đề xuất prompt mặc định",
+	"Default to 389 or 636 if TLS is enabled": "",
+	"Default to ALL": "",
+	"Default User Role": "Vai trò mặc định",
+	"Delete": "Xóa",
+	"Delete a model": "Xóa mô hình",
+	"Delete All Chats": "Xóa mọi cuộc Chat",
+	"Delete All Models": "",
+	"Delete chat": "Xóa nội dung chat",
+	"Delete Chat": "Xóa chat",
+	"Delete chat?": "Xóa chat?",
+	"Delete folder?": "",
+	"Delete function?": "Xóa function?",
+	"Delete prompt?": "Xóa prompt?",
+	"delete this link": "Xóa link này",
+	"Delete tool?": "Xóa tool?",
+	"Delete User": "Xóa người dùng",
+	"Deleted {{deleteModelTag}}": "Đã xóa {{deleteModelTag}}",
+	"Deleted {{name}}": "Đã xóa {{name}}",
+	"Deleted User": "",
+	"Describe your knowledge base and objectives": "",
+	"Description": "Mô tả",
+	"Didn't fully follow instructions": "Không tuân theo chỉ dẫn một cách đầy đủ",
+	"Disabled": "Đã tắt",
+	"Discover a function": "Khám phá function",
+	"Discover a model": "Khám phá model",
+	"Discover a prompt": "Khám phá thêm prompt mới",
+	"Discover a tool": "Khám phá tool",
+	"Discover wonders": "",
+	"Discover, download, and explore custom functions": "Tìm kiếm, tải về và khám phá thêm các function tùy chỉnh",
+	"Discover, download, and explore custom prompts": "Tìm kiếm, tải về và khám phá thêm các prompt tùy chỉnh",
+	"Discover, download, and explore custom tools": "Tìm kiếm, tải về và khám phá thêm các tool tùy chỉnh",
+	"Discover, download, and explore model presets": "Tìm kiếm, tải về và khám phá thêm các model presets",
+	"Dismissible": "Có thể loại bỏ",
+	"Display": "",
+	"Display Emoji in Call": "Hiển thị Emoji trong cuộc gọi",
+	"Display the username instead of You in the Chat": "Hiển thị tên người sử dụng thay vì 'Bạn' trong nội dung chat",
+	"Displays citations in the response": "",
+	"Dive into knowledge": "",
+	"Do not install functions from sources you do not fully trust.": "Không cài đặt các functions từ các nguồn mà bạn không hoàn toàn tin tưởng.",
+	"Do not install tools from sources you do not fully trust.": "Không cài đặt các tools từ những nguồn mà bạn không hoàn toàn tin tưởng.",
+	"Document": "Tài liệu",
+	"Documentation": "Tài liệu",
+	"Documents": "Tài liệu",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "không thực hiện bất kỳ kết nối ngoài nào, và dữ liệu của bạn vẫn được lưu trữ an toàn trên máy chủ lưu trữ cục bộ của bạn.",
+	"Don't have an account?": "Không có tài khoản?",
+	"don't install random functions from sources you don't trust.": "không cài đặt các function từ các nguồn mà bạn không tin tưởng.",
+	"don't install random tools from sources you don't trust.": "không cài đặt các tools từ các nguồn mà bạn không tin tưởng.",
+	"Don't like the style": "Không thích phong cách trả lời",
+	"Done": "Hoàn thành",
+	"Download": "Tải về",
+	"Download canceled": "Đã hủy download",
+	"Download Database": "Tải xuống Cơ sở dữ liệu",
+	"Drag and drop a file to upload or select a file to view": "",
+	"Draw": "",
+	"Drop any files here to add to the conversation": "Thả bất kỳ tệp nào ở đây để thêm vào nội dung chat",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "vd: '30s','10m'. Đơn vị thời gian hợp lệ là 's', 'm', 'h'.",
+	"e.g. A filter to remove profanity from text": "",
+	"e.g. My Filter": "",
+	"e.g. My Tools": "",
+	"e.g. my_filter": "",
+	"e.g. my_tools": "",
+	"e.g. Tools for performing various operations": "",
+	"Edit": "Chỉnh sửa",
+	"Edit Arena Model": "",
+	"Edit Connection": "",
+	"Edit Default Permissions": "",
+	"Edit Memory": "Sửa Memory",
+	"Edit User": "Thay đổi thông tin người sử dụng",
+	"Edit User Group": "",
+	"ElevenLabs": "",
+	"Email": "Email",
+	"Embark on adventures": "",
+	"Embedding Batch Size": "",
+	"Embedding Model": "Mô hình embedding",
+	"Embedding Model Engine": "Trình xử lý embedding",
+	"Embedding model set to \"{{embedding_model}}\"": "Mô hình embedding đã được thiết lập thành \"{{embedding_model}}\"",
+	"Enable API Key Auth": "",
+	"Enable autocomplete generation for chat messages": "",
+	"Enable Community Sharing": "Cho phép Chia sẻ Cộng đồng",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "",
+	"Enable Message Rating": "Cho phép phản hồi, đánh giá",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "",
+	"Enable New Sign Ups": "Cho phép đăng ký mới",
+	"Enable Web Search": "Cho phép tìm kiếm Web",
+	"Enabled": "Đã bật",
+	"Engine": "",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Đảm bảo tệp CSV của bạn bao gồm 4 cột theo thứ tự sau: Name, Email, Password, Role.",
+	"Enter {{role}} message here": "Nhập yêu cầu của {{role}} ở đây",
+	"Enter a detail about yourself for your LLMs to recall": "Nhập chi tiết về bản thân của bạn để LLMs có thể nhớ",
+	"Enter api auth string (e.g. username:password)": "Nhập chuỗi xác thực api (ví dụ: username: mật khẩu)",
+	"Enter Application DN": "",
+	"Enter Application DN Password": "",
+	"Enter Bing Search V7 Endpoint": "",
+	"Enter Bing Search V7 Subscription Key": "",
+	"Enter Brave Search API Key": "Nhập API key cho Brave Search",
+	"Enter certificate path": "",
+	"Enter CFG Scale (e.g. 7.0)": "",
+	"Enter Chunk Overlap": "Nhập Chunk chồng lấn (overlap)",
+	"Enter Chunk Size": "Nhập Kích thước Chunk",
+	"Enter description": "",
+	"Enter Github Raw URL": "Nhập URL cho Github Raw",
+	"Enter Google PSE API Key": "Nhập Google PSE API Key",
+	"Enter Google PSE Engine Id": "Nhập Google PSE Engine Id",
+	"Enter Image Size (e.g. 512x512)": "Nhập Kích thước ảnh (vd: 512x512)",
+	"Enter Jina API Key": "",
+	"Enter language codes": "Nhập mã ngôn ngữ",
+	"Enter Model ID": "",
+	"Enter model tag (e.g. {{modelTag}})": "Nhập thẻ mô hình (vd: {{modelTag}})",
+	"Enter Mojeek Search API Key": "",
+	"Enter Number of Steps (e.g. 50)": "Nhập số Steps (vd: 50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "",
+	"Enter Sampler (e.g. Euler a)": "",
+	"Enter Scheduler (e.g. Karras)": "",
+	"Enter Score": "Nhập Score",
+	"Enter SearchApi API Key": "",
+	"Enter SearchApi Engine": "",
+	"Enter Searxng Query URL": "Nhập Query URL cho Searxng",
+	"Enter Seed": "",
+	"Enter Serper API Key": "Nhập Serper API Key",
+	"Enter Serply API Key": "Nhập Serply API Key",
+	"Enter Serpstack API Key": "Nhập Serpstack API Key",
+	"Enter server host": "",
+	"Enter server label": "",
+	"Enter server port": "",
+	"Enter stop sequence": "Nhập stop sequence",
+	"Enter system prompt": "Nhập system prompt",
+	"Enter Tavily API Key": "Nhập Tavily API Key",
+	"Enter Tika Server URL": "Nhập URL cho  Tika Server",
+	"Enter Top K": "Nhập Top K",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "Nhập URL (vd: http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "Nhập URL (vd: http://localhost:11434)",
+	"Enter Your Email": "Nhập Email của bạn",
+	"Enter Your Full Name": "Nhập Họ và Tên của bạn",
+	"Enter your message": "Nhập tin nhắn của bạn",
+	"Enter Your Password": "Nhập Mật khẩu của bạn",
+	"Enter Your Role": "Nhập vai trò của bạn",
+	"Enter Your Username": "",
+	"Error": "Lỗi",
+	"ERROR": "",
+	"Evaluations": "",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "",
+	"Example: ALL": "",
+	"Example: ou=users,dc=foo,dc=example": "",
+	"Example: sAMAccountName or uid or userPrincipalName": "",
+	"Exclude": "",
+	"Experimental": "Thử nghiệm",
+	"Explore the cosmos": "",
+	"Export": "Xuất khẩu",
+	"Export All Archived Chats": "",
+	"Export All Chats (All Users)": "Tải về tất cả nội dung chat (tất cả mọi người)",
+	"Export chat (.json)": "Tải chat (.json)",
+	"Export Chats": "Tải nội dung chat về máy",
+	"Export Config to JSON File": "",
+	"Export Functions": "Tải Functions về máy",
+	"Export Models": "Tải Models về máy",
+	"Export Presets": "",
+	"Export Prompts": "Tải các prompt về máy",
+	"Export to CSV": "",
+	"Export Tools": "Tải Tools về máy",
+	"External Models": "Các model ngoài",
+	"Failed to add file.": "",
+	"Failed to create API Key.": "Lỗi khởi tạo API Key",
+	"Failed to read clipboard contents": "Không thể đọc nội dung clipboard",
+	"Failed to save models configuration": "",
+	"Failed to update settings": "Lỗi khi cập nhật các cài đặt",
+	"Failed to upload file.": "",
+	"February": "Tháng 2",
+	"Feedback History": "",
+	"Feedbacks": "",
+	"Feel free to add specific details": "Mô tả chi tiết về chất lượng của câu hỏi và phương án trả lời",
+	"File": "Tệp",
+	"File added successfully.": "",
+	"File content updated successfully.": "",
+	"File Mode": "Chế độ Tệp văn bản",
+	"File not found.": "Không tìm thấy tệp.",
+	"File removed successfully.": "",
+	"File size should not exceed {{maxSize}} MB.": "",
+	"Files": "Tệp",
+	"Filter is now globally disabled": "Bộ lọc hiện đã bị vô hiệu hóa trên toàn hệ thống",
+	"Filter is now globally enabled": "Bộ lọc hiện được kích hoạt trên toàn hệ thống",
+	"Filters": "Lọc",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Phát hiện giả mạo vân tay: Không thể sử dụng tên viết tắt làm hình đại diện. Mặc định là hình ảnh hồ sơ mặc định.",
+	"Fluidly stream large external response chunks": "Truyền tải các khối phản hồi bên ngoài lớn một cách trôi chảy",
+	"Focus chat input": "Tập trung vào nội dung chat",
+	"Folder deleted successfully": "",
+	"Folder name cannot be empty": "",
+	"Folder name cannot be empty.": "",
+	"Folder name updated successfully": "",
+	"Followed instructions perfectly": "Tuân theo chỉ dẫn một cách hoàn hảo",
+	"Forge new paths": "",
+	"Form": "",
+	"Format your variables using brackets like this:": "",
+	"Frequency Penalty": "Hình phạt tần số",
+	"Function": "",
+	"Function created successfully": "Function được tạo thành công",
+	"Function deleted successfully": "Function đã bị xóa",
+	"Function Description": "",
+	"Function ID": "",
+	"Function is now globally disabled": "Function hiện đã bị vô hiệu hóa trên toàn hệ thống",
+	"Function is now globally enabled": "Function đã được kích hoạt trên toàn hệ thống",
+	"Function Name": "",
+	"Function updated successfully": "Function được cập nhật thành công",
+	"Functions": "",
+	"Functions allow arbitrary code execution": "Các Function cho phép thực thi mã tùy ý",
+	"Functions allow arbitrary code execution.": "Các Function cho phép thực thi mã tùy ý.",
+	"Functions imported successfully": "Các function đã được nạp thành công",
+	"General": "Cài đặt chung",
+	"General Settings": "Cấu hình chung",
+	"Generate Image": "Sinh ảnh",
+	"Generating search query": "Tạo truy vấn tìm kiếm",
+	"Generation Info": "Thông tin chung",
+	"Get started": "",
+	"Get started with {{WEBUI_NAME}}": "",
+	"Global": "Toàn hệ thống",
+	"Good Response": "Trả lời tốt",
+	"Google PSE API Key": "Khóa API Google PSE",
+	"Google PSE Engine Id": "ID công cụ Google PSE",
+	"Group created successfully": "",
+	"Group deleted successfully": "",
+	"Group Description": "",
+	"Group Name": "",
+	"Group updated successfully": "",
+	"Groups": "",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "Phản hồi xúc giác",
+	"has no conversations.": "không có hội thoại",
+	"Hello, {{name}}": "Xin chào {{name}}",
+	"Help": "Trợ giúp",
+	"Help us create the best community leaderboard by sharing your feedback history!": "",
+	"Hex Color": "",
+	"Hex Color - Leave empty for default color": "",
+	"Hide": "Ẩn",
+	"Host": "",
+	"How can I help you today?": "Tôi có thể giúp gì cho bạn hôm nay?",
+	"How would you rate this response?": "",
+	"Hybrid Search": "Tìm kiếm Hybrid",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "Tôi thừa nhận rằng tôi đã đọc và tôi hiểu ý nghĩa của hành động của mình. Tôi nhận thức được những rủi ro liên quan đến việc thực thi mã tùy ý và tôi đã xác minh độ tin cậy của nguồn.",
+	"ID": "",
+	"Ignite curiosity": "",
+	"Image Generation (Experimental)": "Tạo ảnh (thử nghiệm)",
+	"Image Generation Engine": "Công cụ tạo ảnh",
+	"Image Settings": "Cài đặt ảnh",
+	"Images": "Hình ảnh",
+	"Import Chats": "Nạp lại nội dung chat",
+	"Import Config from JSON File": "",
+	"Import Functions": "Nạp Functions",
+	"Import Models": "Nạp model",
+	"Import Presets": "",
+	"Import Prompts": "Nạp các prompt lên hệ thống",
+	"Import Tools": "Nạp Tools",
+	"Include": "",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "",
+	"Include `--api` flag when running stable-diffusion-webui": "Bao gồm flag `--api` khi chạy stable-diffusion-webui",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "",
+	"Info": "Thông tin",
+	"Input commands": "Nhập các câu lệnh",
+	"Install from Github URL": "Cài đặt từ Github URL",
+	"Instant Auto-Send After Voice Transcription": "Tự động gửi ngay lập tức sau khi phiên dịch giọng nói",
+	"Interface": "Giao diện",
+	"Invalid file format.": "",
+	"Invalid Tag": "Tag không hợp lệ",
+	"January": "Tháng 1",
+	"Jina API Key": "",
+	"join our Discord for help.": "tham gia Discord của chúng tôi để được trợ giúp.",
+	"JSON": "JSON",
+	"JSON Preview": "Xem trước JSON",
+	"July": "Tháng 7",
+	"June": "Tháng 6",
+	"JWT Expiration": "JWT Hết hạn",
+	"JWT Token": "Token JWT",
+	"Keep Alive": "Giữ kết nối",
+	"Key": "",
+	"Keyboard shortcuts": "Phím tắt",
+	"Knowledge": "Kiến thức",
+	"Knowledge Access": "",
+	"Knowledge created successfully.": "",
+	"Knowledge deleted successfully.": "",
+	"Knowledge reset successfully.": "",
+	"Knowledge updated successfully": "",
+	"Label": "",
+	"Landing Page Mode": "",
+	"Language": "Ngôn ngữ",
+	"Last Active": "Truy cập gần nhất",
+	"Last Modified": "Lần sửa gần nhất",
+	"LDAP": "",
+	"LDAP server updated": "",
+	"Leaderboard": "",
+	"Leave empty for unlimited": "",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "",
+	"Leave empty to include all models or select specific models": "",
+	"Leave empty to use the default prompt, or enter a custom prompt": "",
+	"Light": "Sáng",
+	"Listening...": "Đang nghe...",
+	"LLMs can make mistakes. Verify important information.": "Hệ thống có thể tạo ra nội dung không chính xác hoặc sai. Hãy kiểm chứng kỹ lưỡng thông tin trước khi tiếp nhận và sử dụng.",
+	"Local": "",
+	"Local Models": "",
+	"Lost": "",
+	"LTR": "LTR",
+	"Made by OpenWebUI Community": "Được tạo bởi Cộng đồng OpenWebUI",
+	"Make sure to enclose them with": "Hãy chắc chắn bao quanh chúng bằng",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "Đảm bảo xuất tệp Workflow.json đúng format API của ComfyUI.",
+	"Manage": "Quản lý",
+	"Manage Arena Models": "",
+	"Manage Ollama": "",
+	"Manage Ollama API Connections": "",
+	"Manage OpenAI API Connections": "",
+	"Manage Pipelines": "Quản lý Pipelines",
+	"March": "Tháng 3",
+	"Max Tokens (num_predict)": "Tokens tối đa (num_predict)",
+	"Max Upload Count": "",
+	"Max Upload Size": "",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Tối đa 3 mô hình có thể được tải xuống cùng lúc. Vui lòng thử lại sau.",
+	"May": "Tháng 5",
+	"Memories accessible by LLMs will be shown here.": "Memory có thể truy cập bởi LLMs sẽ hiển thị ở đây.",
+	"Memory": "Memory",
+	"Memory added successfully": "Memory đã được thêm thành công",
+	"Memory cleared successfully": "Memory đã bị xóa",
+	"Memory deleted successfully": "Memory đã bị loại bỏ",
+	"Memory updated successfully": "Memory đã cập nhật thành công",
+	"Merge Responses": "Hợp nhất các phản hồi",
+	"Message rating should be enabled to use this feature": "",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "Tin nhắn bạn gửi sau khi tạo liên kết sẽ không được chia sẻ. Người dùng có URL sẽ có thể xem cuộc trò chuyện được chia sẻ.",
+	"Min P": "",
+	"Minimum Score": "Score tối thiểu",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "MMMM DD, YYYY",
+	"MMMM DD, YYYY HH:mm": "MMMM DD, YYYY HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "",
+	"Model": "",
+	"Model '{{modelName}}' has been successfully downloaded.": "Mô hình '{{modelName}}' đã được tải xuống thành công.",
+	"Model '{{modelTag}}' is already in queue for downloading.": "Mô hình '{{modelTag}}' đã có trong hàng đợi để tải xuống.",
+	"Model {{modelId}} not found": "Không tìm thấy Mô hình {{modelId}}",
+	"Model {{modelName}} is not vision capable": "Model {{modelName}} không có khả năng nhìn",
+	"Model {{name}} is now {{status}}": "Model {{name}} bây giờ là {{status}}",
+	"Model accepts image inputs": "",
+	"Model created successfully!": "Model đã được tạo thành công",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Đường dẫn hệ thống tệp mô hình được phát hiện. Tên viết tắt mô hình là bắt buộc để cập nhật, không thể tiếp tục.",
+	"Model Filtering": "",
+	"Model ID": "ID mẫu",
+	"Model IDs": "",
+	"Model Name": "",
+	"Model not selected": "Chưa chọn Mô hình",
+	"Model Params": "Mô hình Params",
+	"Model Permissions": "",
+	"Model updated successfully": "Model đã được cập nhật thành công",
+	"Modelfile Content": "Nội dung Tệp Mô hình",
+	"Models": "Mô hình",
+	"Models Access": "",
+	"Models configuration saved successfully": "",
+	"Mojeek Search API Key": "",
+	"more": "",
+	"More": "Thêm",
+	"Name": "Tên",
+	"Name your knowledge base": "",
+	"New Chat": "Tạo chat mới",
+	"New folder": "",
+	"New Password": "Mật khẩu mới",
+	"No content found": "",
+	"No content to speak": "Không có nội dung để nói",
+	"No distance available": "",
+	"No feedbacks found": "",
+	"No file selected": "Chưa có tệp nào được chọn",
+	"No files found.": "",
+	"No groups with access, add a group to grant access": "",
+	"No HTML, CSS, or JavaScript content found.": "",
+	"No knowledge found": "",
+	"No model IDs": "",
+	"No models found": "",
+	"No models selected": "",
+	"No results found": "Không tìm thấy kết quả",
+	"No search query generated": "Không có truy vấn tìm kiếm nào được tạo ra",
+	"No source available": "Không có nguồn",
+	"No users were found.": "",
+	"No valves to update": "Chưa có valves nào được cập nhật",
+	"None": "Không ai",
+	"Not factually correct": "Không chính xác so với thực tế",
+	"Not helpful": "",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Lưu ý: Nếu bạn đặt điểm (Score) tối thiểu thì tìm kiếm sẽ chỉ trả về những tài liệu có điểm lớn hơn hoặc bằng điểm tối thiểu.",
+	"Notes": "",
+	"Notifications": "Thông báo trên máy tính (Notification)",
+	"November": "Tháng 11",
+	"num_gpu (Ollama)": "",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "",
+	"October": "Tháng 10",
+	"Off": "Tắt",
+	"Okay, Let's Go!": "Được rồi, Bắt đầu thôi!",
+	"OLED Dark": "OLED Dark",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "API Ollama bị vô hiệu hóa",
+	"Ollama API settings updated": "",
+	"Ollama Version": "Phiên bản Ollama",
+	"On": "Bật",
+	"Only alphanumeric characters and hyphens are allowed": "",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "Chỉ ký tự số và gạch nối được phép trong chuỗi lệnh.",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "",
+	"Only select users and groups with permission can access": "",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "Rất tiếc! URL dường như không hợp lệ. Vui lòng kiểm tra lại và thử lại.",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "",
+	"Oops! There was an error in the previous response.": "",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Rất tiếc! Bạn đang sử dụng một phương thức không được hỗ trợ (chỉ dành cho frontend). Vui lòng cung cấp phương thức cho WebUI từ phía backend.",
+	"Open file": "",
+	"Open in full screen": "",
+	"Open new chat": "Mở nội dung chat mới",
+	"Open WebUI uses faster-whisper internally.": "",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "Phiên bản Open WebUI (v{{OPEN_WEBUI_VERSION}}) hiện thấp hơn phiên bản bắt buộc (v{{REQUIRED_VERSION}})",
+	"OpenAI": "OpenAI",
+	"OpenAI API": "API OpenAI",
+	"OpenAI API Config": "Cấu hình API OpenAI",
+	"OpenAI API Key is required.": "Bắt buộc nhập API OpenAI Key.",
+	"OpenAI API settings updated": "",
+	"OpenAI URL/Key required.": "Yêu cầu URL/Key API OpenAI.",
+	"or": "hoặc",
+	"Organize your users": "",
+	"Other": "Khác",
+	"OUTPUT": "",
+	"Output format": "",
+	"Overview": "",
+	"page": "",
+	"Password": "Mật khẩu",
+	"Paste Large Text as File": "",
+	"PDF document (.pdf)": "Tập tin PDF (.pdf)",
+	"PDF Extract Images (OCR)": "Trích xuất ảnh từ PDF (OCR)",
+	"pending": "đang chờ phê duyệt",
+	"Permission denied when accessing media devices": "Quyền truy cập các thiết bị đa phương tiện bị từ chối",
+	"Permission denied when accessing microphone": "Quyền truy cập micrô bị từ chối",
+	"Permission denied when accessing microphone: {{error}}": "Quyền truy cập micrô bị từ chối: {{error}}",
+	"Permissions": "",
+	"Personalization": "Cá nhân hóa",
+	"Pin": "Ghim lại",
+	"Pinned": "Đã ghim",
+	"Pioneer insights": "",
+	"Pipeline deleted successfully": "Pipeline đã bị xóa",
+	"Pipeline downloaded successfully": "Pipeline đã được tải về thành công",
+	"Pipelines": "",
+	"Pipelines Not Detected": "Chưa tìm thấy Pipelines",
+	"Pipelines Valves": "",
+	"Plain text (.txt)": "Văn bản thô (.txt)",
+	"Playground": "Thử nghiệm (Playground)",
+	"Please carefully review the following warnings:": "Vui lòng xem xét cẩn thận các cảnh báo sau:",
+	"Please enter a prompt": "",
+	"Please fill in all fields.": "",
+	"Please select a model first.": "",
+	"Please select a reason": "",
+	"Port": "",
+	"Positive attitude": "Thái độ tích cực",
+	"Prefix ID": "",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "",
+	"Previous 30 days": "30 ngày trước",
+	"Previous 7 days": "7 ngày trước",
+	"Profile Image": "Ảnh đại diện",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "Prompt (ví dụ: Hãy kể cho tôi một sự thật thú vị về Đế chế La Mã)",
+	"Prompt Content": "Nội dung prompt",
+	"Prompt created successfully": "",
+	"Prompt suggestions": "Gợi ý prompt",
+	"Prompt updated successfully": "",
+	"Prompts": "Prompt",
+	"Prompts Access": "",
+	"Proxy URL": "",
+	"Pull \"{{searchValue}}\" from Ollama.com": "Tải \"{{searchValue}}\" từ Ollama.com",
+	"Pull a model from Ollama.com": "Tải mô hình từ Ollama.com",
+	"Query Generation Prompt": "",
+	"Query Params": "Tham số Truy vấn",
+	"RAG Template": "Mẫu prompt cho RAG",
+	"Rating": "",
+	"Re-rank models by topic similarity": "",
+	"Read Aloud": "Đọc ra loa",
+	"Record voice": "Ghi âm",
+	"Redirecting you to OpenWebUI Community": "Đang chuyển hướng bạn đến Cộng đồng OpenWebUI",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "Hãy coi bản thân mình như \"Người dùng\" (ví dụ: \"Người dùng đang học Tiếng Tây Ban Nha\")",
+	"References from": "",
+	"Refused when it shouldn't have": "Từ chối trả lời mà nhẽ không nên làm vậy",
+	"Regenerate": "Tạo sinh lại câu trả lời",
+	"Release Notes": "Mô tả những cập nhật mới",
+	"Relevance": "",
+	"Remove": "Xóa",
+	"Remove Model": "Xóa model",
+	"Rename": "Đổi tên",
+	"Reorder Models": "",
+	"Repeat Last N": "Repeat Last N",
+	"Request Mode": "Request Mode",
+	"Reranking Model": "Reranking Model",
+	"Reranking model disabled": "Reranking model disabled",
+	"Reranking model set to \"{{reranking_model}}\"": "Reranking model được đặt thành \"{{reranking_model}}\"",
+	"Reset": "Xóa toàn bộ",
+	"Reset All Models": "",
+	"Reset Upload Directory": "Xóa toàn bộ thư mục Upload",
+	"Reset Vector Storage/Knowledge": "",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "Không thể kích hoạt thông báo vì trang web không cấp quyền. Vui lòng truy cập cài đặt trình duyệt của bạn để cấp quyền cần thiết.",
+	"Response splitting": "",
+	"Result": "",
+	"Retrieval Query Generation": "",
+	"Rich Text Input for Chat": "",
+	"RK": "",
+	"Role": "Vai trò",
+	"Rosé Pine": "Rosé Pine",
+	"Rosé Pine Dawn": "Rosé Pine Dawn",
+	"RTL": "RTL",
+	"Run": "Thực hiện",
+	"Running": "Đang chạy",
+	"Save": "Lưu",
+	"Save & Create": "Lưu & Tạo",
+	"Save & Update": "Lưu & Cập nhật",
+	"Save As Copy": "",
+	"Save Tag": "Lưu Thẻ",
+	"Saved": "",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Không còn hỗ trợ lưu trữ lịch sử chat trực tiếp vào bộ nhớ trình duyệt của bạn. Vui lòng dành thời gian để tải xuống và xóa lịch sử chat của bạn bằng cách nhấp vào nút bên dưới. Đừng lo lắng, bạn có thể dễ dàng nhập lại lịch sử chat của mình vào backend thông qua",
+	"Scroll to bottom when switching between branches": "Cuộn xuống dưới cùng khi chuyển đổi giữa các nhánh",
+	"Search": "Tìm kiếm",
+	"Search a model": "Tìm model",
+	"Search Base": "",
+	"Search Chats": "Tìm kiếm các cuộc Chat",
+	"Search Collection": "",
+	"Search Filters": "",
+	"search for tags": "",
+	"Search Functions": "Tìm kiếm Functions",
+	"Search Knowledge": "",
+	"Search Models": "Tìm model",
+	"Search options": "",
+	"Search Prompts": "Tìm prompt",
+	"Search Result Count": "Số kết quả tìm kiếm",
+	"Search the web": "",
+	"Search Tools": "Tìm kiếm Tools",
+	"SearchApi API Key": "",
+	"SearchApi Engine": "",
+	"Searched {{count}} sites_other": "Đã tìm thấy {{count}} trang web",
+	"Searching \"{{searchQuery}}\"": "Đang tìm \"{{searchQuery}}\"",
+	"Searching Knowledge for \"{{searchQuery}}\"": "",
+	"Searxng Query URL": "URL truy vấn Searxng",
+	"See readme.md for instructions": "Xem readme.md để biết hướng dẫn",
+	"See what's new": "Xem những cập nhật mới",
+	"Seed": "Seed",
+	"Select a base model": "Chọn một base model",
+	"Select a engine": "Chọn dịch vụ",
+	"Select a function": "Chọn function",
+	"Select a group": "",
+	"Select a model": "Chọn mô hình",
+	"Select a pipeline": "Chọn một quy trình",
+	"Select a pipeline url": "Chọn url quy trình",
+	"Select a tool": "Chọn tool",
+	"Select Engine": "Chọn Engine",
+	"Select Knowledge": "",
+	"Select model": "Chọn model",
+	"Select only one model to call": "Chọn model để gọi",
+	"Selected model(s) do not support image inputs": "Model được lựa chọn không hỗ trợ đầu vào là hình ảnh",
+	"Semantic distance to query": "",
+	"Send": "Gửi",
+	"Send a Message": "Gửi yêu cầu",
+	"Send message": "Gửi yêu cầu",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "",
+	"September": "Tháng 9",
+	"Serper API Key": "Khóa API Serper",
+	"Serply API Key": "",
+	"Serpstack API Key": "Khóa API Serpstack",
+	"Server connection verified": "Kết nối máy chủ đã được xác minh",
+	"Set as default": "Đặt làm mặc định",
+	"Set CFG Scale": "",
+	"Set Default Model": "Đặt Mô hình Mặc định",
+	"Set embedding model": "",
+	"Set embedding model (e.g. {{model}})": "Thiết lập mô hình embedding (ví dụ: {{model}})",
+	"Set Image Size": "Đặt Kích thước ảnh",
+	"Set reranking model (e.g. {{model}})": "Thiết lập mô hình reranking (ví dụ: {{model}})",
+	"Set Sampler": "",
+	"Set Scheduler": "",
+	"Set Steps": "Đặt Số Bước",
+	"Set Task Model": "Đặt Mô hình Tác vụ",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "",
+	"Set Voice": "Đặt Giọng nói",
+	"Set whisper model": "",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "",
+	"Settings": "Cài đặt",
+	"Settings saved successfully!": "Cài đặt đã được lưu thành công!",
+	"Share": "Chia sẻ",
+	"Share Chat": "Chia sẻ Chat",
+	"Share to OpenWebUI Community": "Chia sẻ đến Cộng đồng OpenWebUI",
+	"Show": "Hiển thị",
+	"Show \"What's New\" modal on login": "",
+	"Show Admin Details in Account Pending Overlay": "Hiển thị thông tin của Quản trị viên trên màn hình hiển thị Tài khoản đang chờ xử lý",
+	"Show shortcuts": "Hiển thị phím tắt",
+	"Show your support!": "Thể hiện sự ủng hộ của bạn!",
+	"Showcased creativity": "Thể hiện sự sáng tạo",
+	"Sign in": "Đăng nhập",
+	"Sign in to {{WEBUI_NAME}}": "",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "",
+	"Sign Out": "Đăng xuất",
+	"Sign up": "Đăng ký",
+	"Sign up to {{WEBUI_NAME}}": "",
+	"Signing in to {{WEBUI_NAME}}": "",
+	"Source": "Nguồn",
+	"Speech Playback Speed": "",
+	"Speech recognition error: {{error}}": "Lỗi nhận dạng giọng nói: {{error}}",
+	"Speech-to-Text Engine": "Công cụ Nhận dạng Giọng nói",
+	"Stop": "",
+	"Stop Sequence": "Trình tự Dừng",
+	"Stream Chat Response": "",
+	"STT Model": "",
+	"STT Settings": "Cài đặt Nhận dạng Giọng nói",
+	"Subtitle (e.g. about the Roman Empire)": "Phụ đề (ví dụ: về Đế chế La Mã)",
+	"Success": "Thành công",
+	"Successfully updated.": "Đã cập nhật thành công.",
+	"Suggested": "Gợi ý một số mẫu prompt",
+	"Support": "Hỗ trợ",
+	"Support this plugin:": "Hỗ trợ plugin này:",
+	"Sync directory": "",
+	"System": "Hệ thống",
+	"System Instructions": "",
+	"System Prompt": "Prompt Hệ thống (System Prompt)",
+	"Tags Generation": "",
+	"Tags Generation Prompt": "",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "",
+	"Tap to interrupt": "Chạm để ngừng",
+	"Tavily API Key": "",
+	"Tell us more:": "Hãy cho chúng tôi hiểu thêm về chất lượng của câu trả lời:",
+	"Temperature": "Mức độ sáng tạo",
+	"Template": "Mẫu",
+	"Temporary Chat": "Chat nháp",
+	"Text Splitter": "",
+	"Text-to-Speech Engine": "Công cụ Chuyển Văn bản thành Giọng nói",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "Cám ơn bạn đã gửi phản hồi!",
+	"The Application Account DN you bind with for search": "",
+	"The base to search for users": "",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "Các nhà phát triển đằng sau plugin này là những tình nguyện viên nhiệt huyết của cộng đồng. Nếu bạn thấy plugin này hữu ích, vui lòng cân nhắc đóng góp cho sự phát triển của nó.",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "",
+	"The LDAP attribute that maps to the username that users use to sign in.": "",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Điểm (score) phải có giá trị từ 0,0 (0%) đến 1,0 (100%).",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "",
+	"Theme": "Chủ đề",
+	"Thinking...": "Đang suy luận...",
+	"This action cannot be undone. Do you wish to continue?": "Hành động này không thể được hoàn tác. Bạn có muốn tiếp tục không?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Điều này đảm bảo rằng các nội dung chat có giá trị của bạn được lưu an toàn vào cơ sở dữ liệu backend của bạn. Cảm ơn bạn!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "Đây là tính năng thử nghiệm, có thể không hoạt động như mong đợi và có thể thay đổi bất kỳ lúc nào.",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
+	"This response was generated by \"{{model}}\"": "",
+	"This will delete": "Chat này sẽ bị xóa",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
+	"This will delete all models including custom models": "",
+	"This will delete all models including custom models and cannot be undone.": "",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "",
+	"Thorough explanation": "Giải thích kỹ lưỡng",
+	"Tika": "",
+	"Tika Server URL required.": "Bắt buộc phải nhập URL cho Tika Server ",
+	"Tiktoken": "",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Mẹo: Cập nhật nhiều khe biến liên tiếp bằng cách nhấn phím tab trong đầu vào trò chuyện sau mỗi việc thay thế.",
+	"Title": "Tiêu đề",
+	"Title (e.g. Tell me a fun fact)": "Tiêu đề (ví dụ: Hãy kể cho tôi một sự thật thú vị về...)",
+	"Title Auto-Generation": "Tự động Tạo Tiêu đề",
+	"Title cannot be an empty string.": "Tiêu đề không được phép bỏ trống",
+	"Title Generation Prompt": "Prompt tạo tiêu đề",
+	"TLS": "",
+	"To access the available model names for downloading,": "Để truy cập các tên mô hình có sẵn để tải xuống,",
+	"To access the GGUF models available for downloading,": "Để truy cập các mô hình GGUF có sẵn để tải xuống,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "Để truy cập vui lòng liên hệ với quản trị viên.",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "",
+	"To select actions here, add them to the \"Functions\" workspace first.": "Để chọn các tác vụ, bạn phải thêm chúng vào workspace \"Functions\" trước.",
+	"To select filters here, add them to the \"Functions\" workspace first.": "Để chọn các filters, bạn phải thêm chúng vào workspace \"Functions\" trước.",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "Để chọn các tookits, bạn phải thêm chúng vào workspace \"Tools\" trước.",
+	"Toast notifications for new updates": "",
+	"Today": "Hôm nay",
+	"Toggle settings": "Bật/tắt cài đặt",
+	"Toggle sidebar": "Bật/tắt thanh bên",
+	"Token": "",
+	"Tokens To Keep On Context Refresh (num_keep)": "",
+	"Too verbose": "",
+	"Tool created successfully": "Tool đã được tạo thành công",
+	"Tool deleted successfully": "Tool đã bị xóa",
+	"Tool Description": "",
+	"Tool ID": "",
+	"Tool imported successfully": "Tool đã được nạp thành công",
+	"Tool Name": "",
+	"Tool updated successfully": "Tool đã được cập nhật thành công",
+	"Tools": "",
+	"Tools Access": "",
+	"Tools are a function calling system with arbitrary code execution": "Tools là một hệ thống gọi function với việc thực thi mã tùy ý",
+	"Tools have a function calling system that allows arbitrary code execution": "Các Tools có hệ thống gọi function cho phép thực thi mã tùy ý",
+	"Tools have a function calling system that allows arbitrary code execution.": "Các Tools có hệ thống gọi function cho phép thực thi mã tùy ý.",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "",
+	"Trouble accessing Ollama?": "Gặp vấn đề khi truy cập Ollama?",
+	"TTS Model": "",
+	"TTS Settings": "Cài đặt Chuyển văn bản thành Giọng nói",
+	"TTS Voice": "",
+	"Type": "Kiểu",
+	"Type Hugging Face Resolve (Download) URL": "Nhập URL Hugging Face Resolve (Tải xuống)",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "Ồ! Đã xảy ra sự cố khi kết nối với {{provider}}.",
+	"UI": "Giao diện",
+	"Unarchive All": "",
+	"Unarchive All Archived Chats": "",
+	"Unarchive Chat": "",
+	"Unlock mysteries": "",
+	"Unpin": "Bỏ ghim",
+	"Unravel secrets": "",
+	"Untagged": "",
+	"Update": "Cập nhật",
+	"Update and Copy Link": "Cập nhật và sao chép link",
+	"Update for the latest features and improvements.": "",
+	"Update password": "Cập nhật mật khẩu",
+	"Updated": "",
+	"Updated at": "Cập nhật lúc",
+	"Updated At": "",
+	"Upload": "",
+	"Upload a GGUF model": "Tải lên mô hình GGUF",
+	"Upload directory": "",
+	"Upload files": "",
+	"Upload Files": "Tải tệp lên máy chủ",
+	"Upload Pipeline": "",
+	"Upload Progress": "Tiến trình tải tệp lên hệ thống",
+	"URL": "",
+	"URL Mode": "Chế độ URL",
+	"Use '#' in the prompt input to load and include your knowledge.": "",
+	"Use Gravatar": "Sử dụng Gravatar",
+	"Use groups to group your users and assign permissions.": "",
+	"Use Initials": "Sử dụng tên viết tắt",
+	"use_mlock (Ollama)": "use_mlock (Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "Người sử dụng",
+	"User": "",
+	"User location successfully retrieved.": "Đã truy xuất thành công vị trí của người dùng.",
+	"Username": "",
+	"Users": "Người sử dụng",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "",
+	"Utilize": "Sử dụng",
+	"Valid time units:": "Đơn vị thời gian hợp lệ:",
+	"Valves": "",
+	"Valves updated": "Cập nhật Valves",
+	"Valves updated successfully": "Đã cập nhật Valves thành công",
+	"variable": "biến",
+	"variable to have them replaced with clipboard content.": "biến để có chúng được thay thế bằng nội dung clipboard.",
+	"Version": "Version",
+	"Version {{selectedVersion}} of {{totalVersions}}": "",
+	"Visibility": "",
+	"Voice": "Giọng nói",
+	"Voice Input": "",
+	"Warning": "Cảnh báo",
+	"Warning:": "Cảnh báo:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "Cảnh báo: Nếu cập nhật hoặc thay đổi embedding model, bạn sẽ cần cập nhật lại tất cả tài liệu.",
+	"Web": "Web",
+	"Web API": "",
+	"Web Loader Settings": "Cài đặt Web Loader",
+	"Web Search": "Tìm kiếm Web",
+	"Web Search Engine": "Chức năng Tìm kiếm Web",
+	"Web Search Query Generation": "",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "Cài đặt WebUI",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "",
+	"What are you trying to achieve?": "",
+	"What are you working on?": "",
+	"What’s New in": "Thông tin mới về",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "",
+	"wherever you are": "",
+	"Whisper (Local)": "",
+	"Why?": "",
+	"Widescreen Mode": "Chế độ màn hình rộng",
+	"Won": "",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "",
+	"Workspace": "Workspace",
+	"Workspace Permissions": "",
+	"Write a prompt suggestion (e.g. Who are you?)": "Hãy viết một prompt (vd: Bạn là ai?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "Viết một tóm tắt trong vòng 50 từ cho [chủ đề hoặc từ khóa].",
+	"Write something...": "",
+	"Write your model template content here": "",
+	"Yesterday": "Hôm qua",
+	"You": "Bạn",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "Bạn có thể cá nhân hóa các tương tác của mình với LLM bằng cách thêm bộ nhớ thông qua nút 'Quản lý' bên dưới, làm cho chúng hữu ích hơn và phù hợp với bạn hơn.",
+	"You cannot upload an empty file.": "",
+	"You do not have permission to upload files.": "",
+	"You have no archived conversations.": "Bạn chưa lưu trữ một nội dung chat nào",
+	"You have shared this chat": "Bạn vừa chia sẻ chat này",
+	"You're a helpful assistant.": "Bạn là một trợ lý hữu ích.",
+	"You're now logged in.": "Bạn đã đăng nhập.",
+	"Your account status is currently pending activation.": "Tài khoản của bạn hiện đang ở trạng thái chờ kích hoạt.",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Toàn bộ đóng góp của bạn sẽ được chuyển trực tiếp đến nhà phát triển plugin; Open WebUI không lấy bất kỳ tỷ lệ phần trăm nào. Tuy nhiên, nền tảng được chọn tài trợ có thể có phí riêng.",
+	"Youtube": "Youtube",
+	"Youtube Loader Settings": "Cài đặt Youtube Loader"
+}
diff --git a/src/lib/i18n/locales/zh-CN/translation.json b/src/lib/i18n/locales/zh-CN/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..1e80d5d79e6990b8a11f24e7dfbf858033a4b640
--- /dev/null
+++ b/src/lib/i18n/locales/zh-CN/translation.json
@@ -0,0 +1,1024 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "-1 表示无限制,正整数表示具体限制”",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' 或 '-1' 表示无过期时间。",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(例如 `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
+	"(latest)": "(最新版)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}} 的对话记录",
+	"{{webUIName}} Backend Required": "{{webUIName}} 需要后端服务",
+	"*Prompt node ID(s) are required for image generation": "*图片生成需要 Prompt node ID",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "新版本(v{{LATEST_VERSION}})现已发布。",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "任务模型用于执行生成对话标题和联网搜索查询等任务",
+	"a user": "用户",
+	"About": "关于",
+	"Access": "访问",
+	"Access Control": "访问控制",
+	"Accessible to all users": "对所有用户开放",
+	"Account": "账号",
+	"Account Activation Pending": "账号待激活",
+	"Accurate information": "提供的信息很准确",
+	"Actions": "自动化",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "通过输入 \"/{{COMMAND}}\" 激活此命令",
+	"Active Users": "当前在线用户",
+	"Add": "添加",
+	"Add a model ID": "添加一个模型ID",
+	"Add a short description about what this model does": "添加有关该模型能力的简短描述",
+	"Add a tag": "添加标签",
+	"Add Arena Model": "添加竞技场模型",
+	"Add Connection": "添加一个连接",
+	"Add Content": "添加内容",
+	"Add content here": "在此添加内容",
+	"Add custom prompt": "添加自定义提示词",
+	"Add Files": "添加文件",
+	"Add Group": "添加权限组",
+	"Add Memory": "添加记忆",
+	"Add Model": "添加模型",
+	"Add Tag": "添加标签",
+	"Add Tags": "添加标签",
+	"Add text content": "添加文本内容",
+	"Add User": "添加用户",
+	"Add User Group": "添加权限组",
+	"Adjusting these settings will apply changes universally to all users.": "调整这些设置将会对所有用户应用更改。",
+	"admin": "管理员",
+	"Admin": "管理员联系方式",
+	"Admin Panel": "管理员面板",
+	"Admin Settings": "管理员设置",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "管理员拥有所有工具的访问权限;用户则需在工作空间中为每个模型单独分配工具。",
+	"Advanced Parameters": "高级参数",
+	"Advanced Params": "高级参数",
+	"All chats": "所有对话",
+	"All Documents": "所有文档",
+	"All models deleted successfully": "所有模型删除成功",
+	"Allow Chat Delete": "允许删除对话记录",
+	"Allow Chat Deletion": "允许删除对话记录",
+	"Allow Chat Edit": "允许编辑对话记录",
+	"Allow File Upload": "允许上传文件",
+	"Allow non-local voices": "允许调用非本地音色",
+	"Allow Temporary Chat": "允许临时对话",
+	"Allow User Location": "允许获取您的位置",
+	"Allow Voice Interruption in Call": "允许通话中的打断语音",
+	"Already have an account?": "已经拥有账号了?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "top_p的替代方法,目标是在质量和多样性之间取得平衡。参数p表示一个token相对于最有可能的token所需的最低概率。比如,当p=0.05且最有可能的token概率为0.9时,概率低于0.045的logits会被排除。(默认值:0.0)",
+	"Amazing": "很棒",
+	"an assistant": "AI模型",
+	"and": "和",
+	"and {{COUNT}} more": "还有 {{COUNT}} 个",
+	"and create a new shared link.": "并创建一个新的分享链接。",
+	"API Base URL": "API 基础地址",
+	"API Key": "API 密钥",
+	"API Key created.": "API 密钥已创建。",
+	"API keys": "API 密钥",
+	"Application DN": "Application DN",
+	"Application DN Password": "Application DN 密码",
+	"applies to all users with the \"user\" role": "用于所有具有“用户”角色的用户",
+	"April": "四月",
+	"Archive": "归档",
+	"Archive All Chats": "归档所有对话记录",
+	"Archived Chats": "已归档对话",
+	"archived-chat-export": "导出已归档对话",
+	"Are you sure you want to unarchive all archived chats?": "是否确认取消所有已归档的对话?",
+	"Are you sure?": "是否确定?",
+	"Arena Models": "启用竞技场匿名评价模型",
+	"Artifacts": "Artifacts",
+	"Ask a question": "提问",
+	"Assistant": "AI模型",
+	"Attach file": "添加文件",
+	"Attention to detail": "注重细节",
+	"Attribute for Username": "用户名属性",
+	"Audio": "语音",
+	"August": "八月",
+	"Authenticate": "认证",
+	"Auto-Copy Response to Clipboard": "自动复制回复到剪贴板",
+	"Auto-playback response": "自动念出回复内容",
+	"Autocomplete Generation": "自动完成生成",
+	"Autocomplete Generation Input Max Length": "自动完成生成输入最大长度",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 Api 鉴权字符串",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 基础地址",
+	"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基础地址。",
+	"Available list": "可用列表",
+	"available!": "版本可用!",
+	"Awful": "糟糕",
+	"Azure AI Speech": "Azure AI 语音",
+	"Azure Region": "Azure 区域",
+	"Back": "返回",
+	"Bad Response": "点踩此回答",
+	"Banners": "公告横幅",
+	"Base Model (From)": "基础模型 (来自)",
+	"Batch Size (num_batch)": "批大小 (num_batch)",
+	"before": "对话",
+	"Being lazy": "懒惰",
+	"Bing Search V7 Endpoint": "Bing 搜索 V7 Endpoint",
+	"Bing Search V7 Subscription Key": "Bing 搜索 V7 订阅密钥",
+	"Brave Search API Key": "Brave Search API 密钥",
+	"By {{name}}": "由 {{name}} 提供",
+	"Bypass SSL verification for Websites": "绕过网站的 SSL 验证",
+	"Call": "呼叫",
+	"Call feature is not supported when using Web STT engine": "使用 Web 语音转文字引擎时不支持呼叫功能。",
+	"Camera": "摄像头",
+	"Cancel": "取消",
+	"Capabilities": "能力",
+	"Certificate Path": "证书路径",
+	"Change Password": "更改密码",
+	"Character": "字符",
+	"Character limit for autocomplete generation input": "自动完成生成输入的字符限制",
+	"Chart new frontiers": "开拓新领域",
+	"Chat": "对话",
+	"Chat Background Image": "对话背景图片",
+	"Chat Bubble UI": "气泡样式对话",
+	"Chat Controls": "对话高级设置",
+	"Chat direction": "对话样式方向",
+	"Chat Overview": "对话概述",
+	"Chat Permissions": "对话权限",
+	"Chat Tags Auto-Generation": "自动生成对话标签",
+	"Chats": "对话",
+	"Check Again": "刷新重试",
+	"Check for updates": "检查更新",
+	"Checking for updates...": "正在检查更新...",
+	"Choose a model before saving...": "保存前选择一个模型...",
+	"Chunk Overlap": "块重叠 (Chunk Overlap)",
+	"Chunk Params": "块参数 (Chunk Params)",
+	"Chunk Size": "块大小 (Chunk Size)",
+	"Ciphers": "加密算法 (Ciphers)",
+	"Citation": "引文",
+	"Clear memory": "清除记忆",
+	"click here": "点击此处",
+	"Click here for filter guides.": "点击此处查看 filter 指南。",
+	"Click here for help.": "点击这里获取帮助。",
+	"Click here to": "点击",
+	"Click here to download user import template file.": "点击此处下载用户导入所需的模板文件。",
+	"Click here to learn more about faster-whisper and see the available models.": "点击此处了解更多关于faster-whisper的信息,并查看可用的模型。",
+	"Click here to select": "点击这里选择",
+	"Click here to select a csv file.": "点击此处选择 csv 文件。",
+	"Click here to select a py file.": "点击此处选择 py 文件。",
+	"Click here to upload a workflow.json file.": "点击此处上传 workflow.json 文件。",
+	"click here.": "点击这里。",
+	"Click on the user role button to change a user's role.": "点击角色前方的组别按钮以更改用户所属权限组。",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "写入剪贴板时被拒绝。请检查浏览器设置,授予必要权限。",
+	"Clone": "复制",
+	"Close": "关闭",
+	"Code execution": "代码执行",
+	"Code formatted successfully": "代码格式化成功",
+	"Collection": "文件集",
+	"Color": "颜色",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI 基础地址",
+	"ComfyUI Base URL is required.": "ComfyUI 基础地址为必需填写。",
+	"ComfyUI Workflow": "ComfyUI Workflow",
+	"ComfyUI Workflow Nodes": "ComfyUI Workflow 节点",
+	"Command": "命令",
+	"Completions": "续写",
+	"Concurrent Requests": "并发请求",
+	"Configure": "配置",
+	"Configure Models": "配置模型",
+	"Confirm": "确认",
+	"Confirm Password": "确认密码",
+	"Confirm your action": "确定吗?",
+	"Connections": "外部连接",
+	"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
+	"Content": "内容",
+	"Content Extraction": "内容提取",
+	"Context Length": "上下文长度",
+	"Continue Response": "继续生成",
+	"Continue with {{provider}}": "使用 {{provider}} 继续",
+	"Continue with Email": "使用邮箱登录",
+	"Continue with LDAP": "使用 LDAP 登录",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "控制消息文本如何拆分以用于 TTS 请求。“Punctuation”拆分为句子,“paragraphs”拆分为段落,“none”将消息保留为单个字符串。",
+	"Controls": "对话高级设置",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "控制输出的连贯性和多样性之间的平衡。较低的值将导致更集中和连贯的文本。(默认值:5.0)",
+	"Copied": "已复制",
+	"Copied shared chat URL to clipboard!": "已复制此对话分享链接至剪贴板!",
+	"Copied to clipboard": "已复制到剪贴板",
+	"Copy": "复制",
+	"Copy last code block": "复制最后一个代码块中的代码",
+	"Copy last response": "复制最后一次回复内容",
+	"Copy Link": "复制链接",
+	"Copy to clipboard": "复制到剪贴板",
+	"Copying to clipboard was successful!": "成功复制到剪贴板!",
+	"Create": "创建",
+	"Create a knowledge base": "创建知识库",
+	"Create a model": "创建一个模型",
+	"Create Account": "创建账号",
+	"Create Admin Account": "创建管理员账号",
+	"Create Group": "创建权限组",
+	"Create Knowledge": "创建知识",
+	"Create new key": "创建新密钥",
+	"Create new secret key": "创建新安全密钥",
+	"Created at": "创建于",
+	"Created At": "创建于",
+	"Created by": "作者",
+	"CSV Import": "通过 CSV 文件导入",
+	"Current Model": "当前模型",
+	"Current Password": "当前密码",
+	"Custom": "自定义",
+	"Dark": "暗色",
+	"Database": "数据库",
+	"December": "十二月",
+	"Default": "默认",
+	"Default (Open AI)": "默认 (OpenAI)",
+	"Default (SentenceTransformers)": "默认(SentenceTransformers)",
+	"Default Model": "默认模型",
+	"Default model updated": "默认模型已更新",
+	"Default Models": "默认模型",
+	"Default permissions": "默认权限",
+	"Default permissions updated successfully": "默认权限更新成功",
+	"Default Prompt Suggestions": "默认提示词建议",
+	"Default to 389 or 636 if TLS is enabled": "如果启用 TLS,则默认为 389 或 636",
+	"Default to ALL": "默认为 ALL",
+	"Default User Role": "默认用户角色",
+	"Delete": "删除",
+	"Delete a model": "删除一个模型",
+	"Delete All Chats": "删除所有对话记录",
+	"Delete All Models": "删除所有模型",
+	"Delete chat": "删除对话记录",
+	"Delete Chat": "删除对话记录",
+	"Delete chat?": "删除对话记录?",
+	"Delete folder?": "删除分组?",
+	"Delete function?": "删除函数?",
+	"Delete prompt?": "删除提示词?",
+	"delete this link": "此处删除这个链接",
+	"Delete tool?": "删除工具?",
+	"Delete User": "删除用户",
+	"Deleted {{deleteModelTag}}": "已删除 {{deleteModelTag}}",
+	"Deleted {{name}}": "已删除 {{name}}",
+	"Deleted User": "已删除用户",
+	"Describe your knowledge base and objectives": "描述您的知识库和目标",
+	"Description": "描述",
+	"Didn't fully follow instructions": "没有完全遵照指示",
+	"Disabled": "禁用",
+	"Discover a function": "发现更多函数",
+	"Discover a model": "发现更多模型",
+	"Discover a prompt": "发现更多提示词",
+	"Discover a tool": "发现更多工具",
+	"Discover wonders": "发现奇迹",
+	"Discover, download, and explore custom functions": "发现、下载并探索更多函数",
+	"Discover, download, and explore custom prompts": "发现、下载并探索更多自定义提示词",
+	"Discover, download, and explore custom tools": "发现、下载并探索更多工具",
+	"Discover, download, and explore model presets": "发现、下载并探索更多模型预设",
+	"Dismissible": "是否可关闭",
+	"Display": "显示",
+	"Display Emoji in Call": "在通话中显示 Emoji 表情符号",
+	"Display the username instead of You in the Chat": "在对话中显示用户名而不是“你”",
+	"Displays citations in the response": "在回复中显示引用",
+	"Dive into knowledge": "深入知识的海洋",
+	"Do not install functions from sources you do not fully trust.": "切勿安装来源不完全可信的函数。",
+	"Do not install tools from sources you do not fully trust.": "切勿安装来源不完全可信的工具。",
+	"Document": "文档",
+	"Documentation": "帮助文档",
+	"Documents": "文档",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "不会与外部建立任何连接,您的数据会安全地存储在本地托管的服务器上。",
+	"Don't have an account?": "没有账号?",
+	"don't install random functions from sources you don't trust.": "切勿随意从不完全可信的来源安装函数。",
+	"don't install random tools from sources you don't trust.": "切勿随意从不完全可信的来源安装工具。",
+	"Don't like the style": "不喜欢这个文风",
+	"Done": "完成",
+	"Download": "下载",
+	"Download canceled": "下载已取消",
+	"Download Database": "下载数据库",
+	"Drag and drop a file to upload or select a file to view": "拖动文件上传或选择文件查看",
+	"Draw": "平局",
+	"Drop any files here to add to the conversation": "拖动文件到此处以添加到对话中",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s','10m'。有效的时间单位是秒:'s',分:'m',时:'h'。",
+	"e.g. A filter to remove profanity from text": "例如:一个用于过滤文本中不当内容的过滤器",
+	"e.g. My Filter": "例如:我的过滤器",
+	"e.g. My Tools": "例如:我的工具",
+	"e.g. my_filter": "例如:my_filter",
+	"e.g. my_tools": "例如:my_tools",
+	"e.g. Tools for performing various operations": "例如:用于执行各种操作的工具",
+	"Edit": "编辑",
+	"Edit Arena Model": "编辑竞技场模型",
+	"Edit Connection": "编辑连接",
+	"Edit Default Permissions": "编辑默认权限",
+	"Edit Memory": "编辑记忆",
+	"Edit User": "编辑用户",
+	"Edit User Group": "编辑用户组",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "电子邮箱",
+	"Embark on adventures": "踏上冒险之旅",
+	"Embedding Batch Size": "嵌入层批处理大小 (Embedding Batch Size)",
+	"Embedding Model": "语义向量模型",
+	"Embedding Model Engine": "语义向量模型引擎",
+	"Embedding model set to \"{{embedding_model}}\"": "语义向量模型设置为 \"{{embedding_model}}\"",
+	"Enable API Key Auth": "启用 API 密钥鉴权",
+	"Enable autocomplete generation for chat messages": "启用聊天消息的自动完成生成",
+	"Enable Community Sharing": "启用分享至社区",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "启用内存锁定(mlock)以防止模型数据被交换出RAM。此选项将模型的工作集页面锁定在RAM中,确保它们不会被交换到磁盘。这可以通过避免页面错误和确保快速数据访问来帮助维持性能。",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "启用内存映射(mmap)以加载模型数据。此选项允许系统通过将磁盘文件视为在RAM中来使用磁盘存储作为RAM的扩展。这可以通过更快的数据访问来提高模型性能。然而,它可能无法在所有系统上正常工作,并且可能会消耗大量磁盘空间。",
+	"Enable Message Rating": "启用回复评价",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "启用 Mirostat 采样以控制困惑度。(默认值:0,0 = 禁用,1 = Mirostat,2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "允许新用户注册",
+	"Enable Web Search": "启用联网搜索",
+	"Enabled": "启用",
+	"Engine": "引擎",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
+	"Enter {{role}} message here": "在此处输入 {{role}} 的对话内容",
+	"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
+	"Enter api auth string (e.g. username:password)": "输入 api 鉴权路径 (例如:用户名:密码)",
+	"Enter Application DN": "输入 Application DN",
+	"Enter Application DN Password": "输入 Application DN 密码",
+	"Enter Bing Search V7 Endpoint": "输入 Bing Search V7 端点",
+	"Enter Bing Search V7 Subscription Key": "输入 Bing Search V7 订阅密钥",
+	"Enter Brave Search API Key": "输入 Brave Search API 密钥",
+	"Enter certificate path": "输入证书路径",
+	"Enter CFG Scale (e.g. 7.0)": "输入 CFG Scale (例如:7.0)",
+	"Enter Chunk Overlap": "输入块重叠 (Chunk Overlap)",
+	"Enter Chunk Size": "输入块大小 (Chunk Size)",
+	"Enter description": "输入简介描述",
+	"Enter Github Raw URL": "输入 Github Raw 地址",
+	"Enter Google PSE API Key": "输入 Google PSE API 密钥",
+	"Enter Google PSE Engine Id": "输入 Google PSE 引擎 ID",
+	"Enter Image Size (e.g. 512x512)": "输入图像分辨率 (例如:512x512)",
+	"Enter Jina API Key": "输入 Jina API 密钥",
+	"Enter language codes": "输入语言代码",
+	"Enter Model ID": "输入模型 ID",
+	"Enter model tag (e.g. {{modelTag}})": "输入模型标签 (例如:{{modelTag}})",
+	"Enter Mojeek Search API Key": "输入 Mojeek Search API 密钥",
+	"Enter Number of Steps (e.g. 50)": "输入步骤数 (Steps) (例如:50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "输入代理 URL (例如:https://用户名:密码@主机名:端口)",
+	"Enter Sampler (e.g. Euler a)": "输入 Sampler (例如:Euler a)",
+	"Enter Scheduler (e.g. Karras)": "输入 Scheduler (例如:Karras)",
+	"Enter Score": "输入评分",
+	"Enter SearchApi API Key": "输入 SearchApi API 密钥",
+	"Enter SearchApi Engine": "输入 SearchApi 引擎",
+	"Enter Searxng Query URL": "输入 Searxng 查询地址",
+	"Enter Seed": "输入 Seed",
+	"Enter Serper API Key": "输入 Serper API 密钥",
+	"Enter Serply API Key": "输入 Serply API 密钥",
+	"Enter Serpstack API Key": "输入 Serpstack API 密钥",
+	"Enter server host": "输入服务器主机名 ",
+	"Enter server label": "输入服务器标签",
+	"Enter server port": "输入服务器端口",
+	"Enter stop sequence": "输入停止序列 (Stop Sequence)",
+	"Enter system prompt": "输入系统提示词 (Prompt)",
+	"Enter Tavily API Key": "输入 Tavily API 密钥",
+	"Enter Tika Server URL": "输入 Tika 服务器地址",
+	"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://localhost:11434)": "输入地址 (例如:http://localhost:11434)",
+	"Enter Your Email": "输入您的电子邮箱",
+	"Enter Your Full Name": "输入您的名称",
+	"Enter your message": "输入您的消息",
+	"Enter Your Password": "输入您的密码",
+	"Enter Your Role": "输入您的权限组",
+	"Enter Your Username": "输入您的用户名",
+	"Error": "错误",
+	"ERROR": "错误",
+	"Evaluations": "竞技场评估",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "例如:(&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "例如:ALL",
+	"Example: ou=users,dc=foo,dc=example": "例如:ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "例如:sAMAccountName 或 uid 或 userPrincipalName",
+	"Exclude": "排除",
+	"Experimental": "实验性",
+	"Explore the cosmos": "探索宇宙",
+	"Export": "导出",
+	"Export All Archived Chats": "导出所有已存档对话",
+	"Export All Chats (All Users)": "导出所有用户对话",
+	"Export chat (.json)": "JSON 文件 (.json)",
+	"Export Chats": "导出对话",
+	"Export Config to JSON File": "导出配置信息至 JSON 文件中",
+	"Export Functions": "导出函数",
+	"Export Models": "导出模型",
+	"Export Presets": "导出预设",
+	"Export Prompts": "导出提示词",
+	"Export to CSV": "导出到 CSV",
+	"Export Tools": "导出工具",
+	"External Models": "外部模型",
+	"Failed to add file.": "添加文件失败。",
+	"Failed to create API Key.": "无法创建 API 密钥。",
+	"Failed to read clipboard contents": "无法读取剪贴板内容",
+	"Failed to save models configuration": "无法保存模型配置",
+	"Failed to update settings": "无法更新设置",
+	"Failed to upload file.": "上传文件失败",
+	"February": "二月",
+	"Feedback History": "反馈历史",
+	"Feedbacks": "反馈",
+	"Feel free to add specific details": "欢迎补充具体细节",
+	"File": "文件",
+	"File added successfully.": "文件成功添加",
+	"File content updated successfully.": "文件内容成功更新",
+	"File Mode": "文件模式",
+	"File not found.": "文件未找到。",
+	"File removed successfully.": "文件成功删除",
+	"File size should not exceed {{maxSize}} MB.": "文件大小不应超过 {{maxSize}} MB。",
+	"Files": "文件",
+	"Filter is now globally disabled": "过滤器已全局禁用",
+	"Filter is now globally enabled": "过滤器已全局启用",
+	"Filters": "过滤器",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "检测到指纹伪造:无法使用姓名缩写作为头像。默认使用默认个人形象。",
+	"Fluidly stream large external response chunks": "流畅地传输外部大型响应块数据",
+	"Focus chat input": "聚焦对话输入",
+	"Folder deleted successfully": "分组删除成功",
+	"Folder name cannot be empty": "分组名称不能为空",
+	"Folder name cannot be empty.": "分组名称不能为空。",
+	"Folder name updated successfully": "分组名称更新成功。",
+	"Followed instructions perfectly": "完全按照指示执行",
+	"Forge new paths": "开拓新道路",
+	"Form": "手动创建",
+	"Format your variables using brackets like this:": "使用括号格式化你的变量,如下所示:",
+	"Frequency Penalty": "频率惩罚",
+	"Function": "函数",
+	"Function created successfully": "函数创建成功",
+	"Function deleted successfully": "函数删除成功",
+	"Function Description": "函数描述",
+	"Function ID": "函数ID",
+	"Function is now globally disabled": "函数全局已禁用",
+	"Function is now globally enabled": "函数全局已启用",
+	"Function Name": "函数名称",
+	"Function updated successfully": "函数更新成功",
+	"Functions": "函数",
+	"Functions allow arbitrary code execution": "注意:函数有权执行任意代码",
+	"Functions allow arbitrary code execution.": "注意:函数有权执行任意代码。",
+	"Functions imported successfully": "函数导入成功",
+	"General": "通用",
+	"General Settings": "通用设置",
+	"Generate Image": "生成图像",
+	"Generating search query": "生成搜索查询",
+	"Generation Info": "生成信息",
+	"Get started": "开始使用",
+	"Get started with {{WEBUI_NAME}}": "开始使用 {{WEBUI_NAME}}",
+	"Global": "全局",
+	"Good Response": "点赞此回答",
+	"Google PSE API Key": "Google PSE API 密钥",
+	"Google PSE Engine Id": "Google PSE 引擎 ID",
+	"Group created successfully": "权限组创建成功",
+	"Group deleted successfully": "权限组删除成功",
+	"Group Description": "权限组描述",
+	"Group Name": "权限组名称",
+	"Group updated successfully": "权限组更新成功",
+	"Groups": "权限组",
+	"h:mm a": "HH:mm",
+	"Haptic Feedback": "震动反馈",
+	"has no conversations.": "没有对话。",
+	"Hello, {{name}}": "您好,{{name}}",
+	"Help": "帮助",
+	"Help us create the best community leaderboard by sharing your feedback history!": "分享您的反馈历史记录,共建最佳模型社区排行榜!",
+	"Hex Color": "十六进制颜色代码",
+	"Hex Color - Leave empty for default color": "十六进制颜色代码 - 留空使用默认颜色",
+	"Hide": "隐藏",
+	"Host": "主机",
+	"How can I help you today?": "有什么我能帮您的吗?",
+	"How would you rate this response?": "您如何评价这个回应?",
+	"Hybrid Search": "混合搜索",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "我已阅读并理解我的行为所带来的影响,明白执行任意代码所涉及的风险。且我已验证代码来源可信度。",
+	"ID": "ID",
+	"Ignite curiosity": "点燃好奇心",
+	"Image Generation (Experimental)": "图像生成(实验性)",
+	"Image Generation Engine": "图像生成引擎",
+	"Image Settings": "图像设置",
+	"Images": "图像",
+	"Import Chats": "导入对话记录",
+	"Import Config from JSON File": "导入 JSON 文件中的配置信息",
+	"Import Functions": "导入函数",
+	"Import Models": "导入模型",
+	"Import Presets": "导入预设",
+	"Import Prompts": "导入提示词",
+	"Import Tools": "导入工具",
+	"Include": "包括",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "运行 stable-diffusion-webui 时包含 `--api-auth` 标志",
+	"Include `--api` flag when running stable-diffusion-webui": "运行 stable-diffusion-webui 时包含 `--api` 标志",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "影响算法对生成文本反馈的响应速度。较低的学习率会导致调整速度较慢,而较高的学习率会使算法更具响应性。(默认值:0.1)",
+	"Info": "信息",
+	"Input commands": "输入命令",
+	"Install from Github URL": "从 Github URL 安装",
+	"Instant Auto-Send After Voice Transcription": "语音转录文字后即时自动发送",
+	"Interface": "界面",
+	"Invalid file format.": "无效文件格式。",
+	"Invalid Tag": "无效标签",
+	"January": "一月",
+	"Jina API Key": "Jina API 密钥",
+	"join our Discord for help.": "加入我们的 Discord 寻求帮助。",
+	"JSON": "JSON",
+	"JSON Preview": "JSON 预览",
+	"July": "七月",
+	"June": "六月",
+	"JWT Expiration": "JWT 过期",
+	"JWT Token": "JWT 令牌",
+	"Keep Alive": "保持活动",
+	"Key": "密匙",
+	"Keyboard shortcuts": "键盘快捷键",
+	"Knowledge": "知识库",
+	"Knowledge Access": "访问知识库",
+	"Knowledge created successfully.": "知识成功创建",
+	"Knowledge deleted successfully.": "知识成功删除",
+	"Knowledge reset successfully.": "知识成功重置",
+	"Knowledge updated successfully": "知识成功更新",
+	"Label": "标签",
+	"Landing Page Mode": "默认主页样式",
+	"Language": "语言",
+	"Last Active": "最后在线时间",
+	"Last Modified": "最后修改时间",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP 服务器已更新",
+	"Leaderboard": "排行榜",
+	"Leave empty for unlimited": "留空表示无限制",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "留空表示包含所有来自 \"{{URL}}/api/tags\" 的模型",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "留空表示包含所有来自 \"{{URL}}/models\" 的模型",
+	"Leave empty to include all models or select specific models": "留空表示包含所有模型或请选择模型",
+	"Leave empty to use the default prompt, or enter a custom prompt": "留空以使用默认提示词,或输入自定义提示词。",
+	"Light": "浅色",
+	"Listening...": "正在倾听...",
+	"LLMs can make mistakes. Verify important information.": "大语言模型可能会生成误导性错误信息,请对关键信息加以验证。",
+	"Local": "本地",
+	"Local Models": "本地模型",
+	"Lost": "落败",
+	"LTR": "从左至右",
+	"Made by OpenWebUI Community": "由 OpenWebUI 社区制作",
+	"Make sure to enclose them with": "确保将它们包含在内",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "确保从 ComfyUI 导出 API 格式的 workflow.json 文件。",
+	"Manage": "管理",
+	"Manage Arena Models": "管理竞技场模型",
+	"Manage Ollama": "管理 Ollama",
+	"Manage Ollama API Connections": "管理Ollama API连接",
+	"Manage OpenAI API Connections": "管理OpenAI API连接",
+	"Manage Pipelines": "管理 Pipeline",
+	"March": "三月",
+	"Max Tokens (num_predict)": "最多 Token (num_predict)",
+	"Max Upload Count": "最大上传数量",
+	"Max Upload Size": "最大上传大小",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载 3 个模型,请稍后重试。",
+	"May": "五月",
+	"Memories accessible by LLMs will be shown here.": "大语言模型可访问的记忆将在此显示。",
+	"Memory": "记忆",
+	"Memory added successfully": "记忆添加成功",
+	"Memory cleared successfully": "记忆清除成功",
+	"Memory deleted successfully": "记忆删除成功",
+	"Memory updated successfully": "记忆更新成功",
+	"Merge Responses": "合并回复",
+	"Message rating should be enabled to use this feature": "要使用此功能,应先启用回复评价功能",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "创建链接后发送的消息不会被共享。具有 URL 的用户将能够查看共享对话。",
+	"Min P": "Min P",
+	"Minimum Score": "最低分",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "YYYY年 MM月 DD日",
+	"MMMM DD, YYYY HH:mm": "YYYY年 MM月 DD日 HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "YYYY年 MM月 DD日 hh:mm:ss A",
+	"Model": "模型",
+	"Model '{{modelName}}' has been successfully downloaded.": "模型'{{modelName}}'已成功下载。",
+	"Model '{{modelTag}}' is already in queue for downloading.": "模型'{{modelTag}}'已在下载队列中。",
+	"Model {{modelId}} not found": "未找到模型 {{modelId}}",
+	"Model {{modelName}} is not vision capable": "模型 {{modelName}} 不支持视觉能力",
+	"Model {{name}} is now {{status}}": "模型 {{name}} 现在是 {{status}}",
+	"Model accepts image inputs": "模型接受图像输入",
+	"Model created successfully!": "模型创建成功!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "检测到模型文件系统路径,无法继续进行。更新操作需要提供模型简称。",
+	"Model Filtering": "模型白名单",
+	"Model ID": "模型 ID",
+	"Model IDs": "模型 ID",
+	"Model Name": "模型名称",
+	"Model not selected": "未选择模型",
+	"Model Params": "模型参数",
+	"Model Permissions": "模型权限",
+	"Model updated successfully": "模型更新成功",
+	"Modelfile Content": "模型文件内容",
+	"Models": "模型",
+	"Models Access": "访问模型列表",
+	"Models configuration saved successfully": "模型配置保存成功",
+	"Mojeek Search API Key": "Mojeek Search API 密钥",
+	"more": "更多",
+	"More": "更多",
+	"Name": "名称",
+	"Name your knowledge base": "为您的知识库命名",
+	"New Chat": "新对话",
+	"New folder": "新建分组",
+	"New Password": "新密码",
+	"No content found": "未发现内容",
+	"No content to speak": "没有内容可朗读",
+	"No distance available": "没有可用距离",
+	"No feedbacks found": "暂无任何反馈",
+	"No file selected": "未选中文件",
+	"No files found.": "未找到文件。",
+	"No groups with access, add a group to grant access": "没有权限组,请添加一个权限组以授予访问权限",
+	"No HTML, CSS, or JavaScript content found.": "未找到 HTML、CSS 或 JavaScript 内容。",
+	"No knowledge found": "未找到知识",
+	"No model IDs": "没有模型 ID",
+	"No models found": "未找到任何模型",
+	"No models selected": "未选择任何模型",
+	"No results found": "未找到结果",
+	"No search query generated": "未生成搜索查询",
+	"No source available": "没有可用来源",
+	"No users were found.": "未找到用户",
+	"No valves to update": "没有需要更新的值",
+	"None": "无",
+	"Not factually correct": "事实并非如此",
+	"Not helpful": "无帮助",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:如果设置了最低分数,搜索只会返回分数大于或等于最低分数的文档。",
+	"Notes": "笔记",
+	"Notifications": "桌面通知",
+	"November": "十一月",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread(Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "十月",
+	"Off": "关闭",
+	"Okay, Let's Go!": "确认,开始使用!",
+	"OLED Dark": "黑色",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API 已禁用",
+	"Ollama API settings updated": "Ollama API设置已更新",
+	"Ollama Version": "Ollama 版本",
+	"On": "开启",
+	"Only alphanumeric characters and hyphens are allowed": "只允许使用英文字母,数字 (0-9) 以及连字符 (-)",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用英文字母,数字 (0-9) 以及连字符 (-)。",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "只能编辑文件集,创建一个新的知识库来编辑/添加文件。",
+	"Only select users and groups with permission can access": "只有具有权限的用户和组才能访问",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "此链接似乎为无效链接。请检查后重试。",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "稍等!还有文件正在上传。请等待上传完成。",
+	"Oops! There was an error in the previous response.": "糟糕!有一个错误出现在了之前的回复中。",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "你正在使用不被支持的方法(仅运行前端服务)。需要后端提供 WebUI 服务。",
+	"Open file": "打开文件",
+	"Open in full screen": "全屏打开",
+	"Open new chat": "打开新对话",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI 使用内置 faster-whisper。",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI 使用 SpeechT5 和 CMU Arctic speaker embedding。",
+	"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 API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API 配置",
+	"OpenAI API Key is required.": "需要 OpenAI API 密钥。",
+	"OpenAI API settings updated": "OpenAI API 设置已更新",
+	"OpenAI URL/Key required.": "需要 OpenAI URL/Key",
+	"or": "或",
+	"Organize your users": "组织用户",
+	"Other": "其他",
+	"OUTPUT": "输出",
+	"Output format": "输出格式",
+	"Overview": "概述",
+	"page": "页",
+	"Password": "密码",
+	"Paste Large Text as File": "粘贴大文本为文件",
+	"PDF document (.pdf)": "PDF 文档 (.pdf)",
+	"PDF Extract Images (OCR)": "PDF 图像处理 (使用 OCR)",
+	"pending": "待激活",
+	"Permission denied when accessing media devices": "申请媒体设备权限被拒绝",
+	"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
+	"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
+	"Permissions": "权限",
+	"Personalization": "个性化",
+	"Pin": "置顶",
+	"Pinned": "已置顶",
+	"Pioneer insights": "先锋的见解",
+	"Pipeline deleted successfully": "Pipeline 删除成功",
+	"Pipeline downloaded successfully": "Pipeline 下载成功",
+	"Pipelines": "Pipeline",
+	"Pipelines Not Detected": "未检测到 Pipeline",
+	"Pipelines Valves": "Pipeline 值",
+	"Plain text (.txt)": "TXT 文档 (.txt)",
+	"Playground": "AI 对话游乐场",
+	"Please carefully review the following warnings:": "请仔细阅读以下警告信息:",
+	"Please enter a prompt": "请输出一个 prompt",
+	"Please fill in all fields.": "请填写所有字段。",
+	"Please select a model first.": "请先选择一个模型。",
+	"Please select a reason": "请选择原因",
+	"Port": "端口",
+	"Positive attitude": "积极的态度",
+	"Prefix ID": "Prefix ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "Prefix ID 用于通过为模型 ID 添加前缀来避免与其他连接发生冲突 - 留空则禁用此功能",
+	"Previous 30 days": "过去 30 天",
+	"Previous 7 days": "过去 7 天",
+	"Profile Image": "用户头像",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示(例如:给我讲一个关于罗马帝国的趣事。)",
+	"Prompt Content": "提示词内容",
+	"Prompt created successfully": "提示词创建成功",
+	"Prompt suggestions": "提示词建议",
+	"Prompt updated successfully": "提示词更新成功",
+	"Prompts": "提示词",
+	"Prompts Access": "访问提示词",
+	"Proxy URL": "代理 URL",
+	"Pull \"{{searchValue}}\" from Ollama.com": "从 Ollama.com 拉取 \"{{searchValue}}\"",
+	"Pull a model from Ollama.com": "从 Ollama.com 拉取一个模型",
+	"Query Generation Prompt": "查询生成提示词",
+	"Query Params": "查询参数",
+	"RAG Template": "RAG 提示词模板",
+	"Rating": "评价",
+	"Re-rank models by topic similarity": "根据主题相似性对模型重新排序",
+	"Read Aloud": "朗读",
+	"Record voice": "录音",
+	"Redirecting you to OpenWebUI Community": "正在将您重定向到 OpenWebUI 社区",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "降低产生无意义答案的概率。数值越大(如 100),答案就越多样化,而数值越小(如 10),答案就越保守。(默认值:40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "使用\"User\" (用户) 来指代自己(例如:“User 正在学习西班牙语”)",
+	"References from": "来自",
+	"Refused when it shouldn't have": "无理拒绝",
+	"Regenerate": "重新生成",
+	"Release Notes": "更新日志",
+	"Relevance": "相关性",
+	"Remove": "移除",
+	"Remove Model": "移除模型",
+	"Rename": "重命名",
+	"Reorder Models": "重新排序模型",
+	"Repeat Last N": "重复最后 N 次",
+	"Request Mode": "请求模式",
+	"Reranking Model": "重排模型",
+	"Reranking model disabled": "重排模型已禁用",
+	"Reranking model set to \"{{reranking_model}}\"": "重排模型设置为 \"{{reranking_model}}\"",
+	"Reset": "重置",
+	"Reset All Models": "重置所有模型",
+	"Reset Upload Directory": "重置上传目录",
+	"Reset Vector Storage/Knowledge": "重置向量存储/知识",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "无法激活回复时发送通知。请检查浏览器设置,并授予必要的访问权限。",
+	"Response splitting": "拆分回复",
+	"Result": "结果",
+	"Retrieval Query Generation": "检索查询生成",
+	"Rich Text Input for Chat": "对话富文本输入",
+	"RK": "排名",
+	"Role": "权限组",
+	"Rosé Pine": "玫瑰松木",
+	"Rosé Pine Dawn": "玫瑰松木·晨曦",
+	"RTL": "从右至左",
+	"Run": "运行",
+	"Running": "运行中",
+	"Save": "保存",
+	"Save & Create": "保存并创建",
+	"Save & Update": "保存并更新",
+	"Save As Copy": "另存为副本",
+	"Save Tag": "保存标签",
+	"Saved": "已保存",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "我们不再支持将聊天记录直接保存到浏览器的存储空间。请点击下面的按钮下载并删除您的聊天记录。别担心,您可以轻松地将聊天记录重新导入到后台。",
+	"Scroll to bottom when switching between branches": "在分支间切换时滚动到底部",
+	"Search": "搜索",
+	"Search a model": "搜索模型",
+	"Search Base": "Search Base",
+	"Search Chats": "搜索对话",
+	"Search Collection": "搜索内容",
+	"Search Filters": "Search Filters",
+	"search for tags": "搜索标签",
+	"Search Functions": "搜索函数",
+	"Search Knowledge": "搜索知识",
+	"Search Models": "搜索模型",
+	"Search options": "搜索选项",
+	"Search Prompts": "搜索提示词",
+	"Search Result Count": "搜索结果数量",
+	"Search the web": "从网络搜索",
+	"Search Tools": "搜索工具",
+	"SearchApi API Key": "SearchApi API 密钥",
+	"SearchApi Engine": "SearchApi 引擎",
+	"Searched {{count}} sites_other": "搜索到 {{count}} 个结果",
+	"Searching \"{{searchQuery}}\"": "搜索 \"{{searchQuery}}\" 中",
+	"Searching Knowledge for \"{{searchQuery}}\"": "检索有关 \"{{searchQuery}}\" 的知识中",
+	"Searxng Query URL": "Searxng 查询 URL",
+	"See readme.md for instructions": "查看 readme.md 以获取说明",
+	"See what's new": "查阅最新更新内容",
+	"Seed": "种子 (Seed)",
+	"Select a base model": "选择一个基础模型",
+	"Select a engine": "选择一个搜索引擎",
+	"Select a function": "选择一个函数",
+	"Select a group": "选择一个权限组",
+	"Select a model": "选择一个模型",
+	"Select a pipeline": "选择一个管道",
+	"Select a pipeline url": "选择一个管道 URL",
+	"Select a tool": "选择一个工具",
+	"Select Engine": "选择引擎",
+	"Select Knowledge": "选择知识",
+	"Select model": "选择模型",
+	"Select only one model to call": "请仅选择一个模型来呼叫",
+	"Selected model(s) do not support image inputs": "已选择的模型不支持发送图像",
+	"Semantic distance to query": "语义距离查询",
+	"Send": "发送",
+	"Send a Message": "输入消息",
+	"Send message": "发送消息",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "在请求中发送 `stream_options: { include_usage: true }`。\n设置后支持的接口将在返回响应中包含令牌使用信息。",
+	"September": "九月",
+	"Serper API Key": "Serper API 密钥",
+	"Serply API Key": "Serply API 密钥",
+	"Serpstack API Key": "Serpstack API 密钥",
+	"Server connection verified": "已验证服务器连接",
+	"Set as default": "设为默认",
+	"Set CFG Scale": "设置 CFG Scale",
+	"Set Default Model": "设置默认模型",
+	"Set embedding model": "设置语义向量模型",
+	"Set embedding model (e.g. {{model}})": "设置语义向量模型 (例如:{{model}})",
+	"Set Image Size": "设置图片分辨率",
+	"Set reranking model (e.g. {{model}})": "设置重排模型 (例如:{{model}})",
+	"Set Sampler": "设置 Sampler",
+	"Set Scheduler": "设置 Scheduler",
+	"Set Steps": "设置步骤",
+	"Set Task Model": "设置任务模型",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "设置用于计算的 GPU 设备数量。该选项可控制用于处理传入请求的 GPU 设备(如有)的数量。对于针对 GPU 加速进行了优化的模型,增加该值可以显著提高性能,但也可能消耗更多的电能和 GPU 资源。",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "设置用于计算的工作线程数量。该选项可控制并发处理传入请求的线程数量。增加该值可以提高高并发工作负载下的性能,但也可能消耗更多的 CPU 资源。",
+	"Set Voice": "设置音色",
+	"Set whisper model": "设置 whisper 模型",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "设置模型回溯多远以防止重复。(默认值:64,0 = 禁用,-1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "设置对重复的惩罚力度。数值越大(如 1.5),对重复的惩罚力度越大,而数值越小(如 0.9),惩罚力度越轻。(默认值:1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "设置生成文本时使用的随机数种子。将其设置为一个特定的数字将使模型在同一提示下生成相同的文本。 默认值:随机",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "设置用于生成下一个 Toekn 的上下文大小。(默认值:2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "设置要使用的停止序列。遇到这种模式时,大语言模型将停止生成文本并返回。可以通过在模型文件中指定多个单独的停止参数来设置多个停止模式。",
+	"Settings": "设置",
+	"Settings saved successfully!": "设置已保存",
+	"Share": "分享",
+	"Share Chat": "分享对话",
+	"Share to OpenWebUI Community": "分享到 OpenWebUI 社区",
+	"Show": "显示",
+	"Show \"What's New\" modal on login": "在登录时显示“更新内容”弹窗",
+	"Show Admin Details in Account Pending Overlay": "在用户待激活界面中显示管理员邮箱等详细信息",
+	"Show shortcuts": "显示快捷方式",
+	"Show your support!": "表达你的支持!",
+	"Showcased creativity": "很有创意",
+	"Sign in": "登录",
+	"Sign in to {{WEBUI_NAME}}": "登录 {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "使用 LDAP 登录 {{WEBUI_NAME}}",
+	"Sign Out": "登出",
+	"Sign up": "注册",
+	"Sign up to {{WEBUI_NAME}}": "注册 {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "正在登录 {{WEBUI_NAME}}",
+	"Source": "来源",
+	"Speech Playback Speed": "语音播放速度",
+	"Speech recognition error: {{error}}": "语音识别错误:{{error}}",
+	"Speech-to-Text Engine": "语音转文本引擎",
+	"Stop": "停止",
+	"Stop Sequence": "停止序列 (Stop Sequence)",
+	"Stream Chat Response": "以流式返回对话响应",
+	"STT Model": "语音转文本模型",
+	"STT Settings": "语音转文本设置",
+	"Subtitle (e.g. about the Roman Empire)": "副标题(例如:关于罗马帝国的副标题)",
+	"Success": "成功",
+	"Successfully updated.": "成功更新。",
+	"Suggested": "建议",
+	"Support": "支持",
+	"Support this plugin:": "支持此插件",
+	"Sync directory": "同步目录",
+	"System": "系统",
+	"System Instructions": "系统指令",
+	"System Prompt": "系统提示词 (System Prompt)",
+	"Tags Generation": "标签生成",
+	"Tags Generation Prompt": "标签生成提示词",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "Tail free sampling 用于减少输出中可能性较低的标记的影响。数值越大(如 2.0),影响就越小,而数值为 1.0 则会禁用此设置。(默认值:1)",
+	"Tap to interrupt": "点击以中断",
+	"Tavily API Key": "Tavily API 密钥",
+	"Tell us more:": "请告诉我们更多细节",
+	"Temperature": "温度 (Temperature)",
+	"Template": "模板",
+	"Temporary Chat": "临时对话",
+	"Text Splitter": "文本分切器",
+	"Text-to-Speech Engine": "文本转语音引擎",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "感谢您的反馈!",
+	"The Application Account DN you bind with for search": "您所绑定用于搜索的 Application Account DN",
+	"The base to search for users": "搜索用户的 Base",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "Batch size 决定了同时处理多少个文本请求。Batch size 越大,模型的性能和速度越快,但也需要更多内存。  (默认值:512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "本插件的背后开发者是社区中热情的志愿者。如果此插件有帮助到您,烦请考虑一下为它的开发做出贡献。",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "排行榜基于 Elo 评级系统并实时更新。",
+	"The LDAP attribute that maps to the username that users use to sign in.": "映射到用户登录时使用的用户名的 LDAP 属性。",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "排行榜目前处于 Beta 测试阶段,我们可能会在完善算法后调整评分计算方法。",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "最大文件大小(MB)。如果文件大小超过此限制,则无法上传该文件。",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "在单次对话中可以使用的最大文件数。如果文件数超过此限制,则文件不会上传。",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "分值应介于 0.0(0%)和 1.0(100%)之间。",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "模型的温度。提高温度将使模型更具创造性地回答。(默认值:0.8)",
+	"Theme": "主题",
+	"Thinking...": "正在思考...",
+	"This action cannot be undone. Do you wish to continue?": "此操作无法撤销。是否确认继续?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这将确保您的宝贵对话被安全地保存到后台数据库中。感谢!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "这是一个实验功能,可能不会如预期那样工作,而且可能随时发生变化。",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "该选项控制刷新上下文时保留多少标记。例如,如果设置为 2,就会保留对话上下文的最后 2 个标记。保留上下文有助于保持对话的连续性,但可能会降低回复新话题的能力。(默认值:24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "此选项设置了模型在回答中可以生成的最大 Token 数。增加这个限制可以让模型提供更长的答案,但也可能增加生成无用或不相关内容的可能性。  (默认值:128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "此选项将会删除文件集中所有文件,并用新上传的文件替换。",
+	"This response was generated by \"{{model}}\"": "此回复由 \"{{model}}\" 生成",
+	"This will delete": "这将删除",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "这将删除<strong>{{NAME}}</strong>及其<strong>所有内容</strong>。",
+	"This will delete all models including custom models": "这将删除所有模型,包括自定义模型",
+	"This will delete all models including custom models and cannot be undone.": "这将删除所有模型,包括自定义模型,且无法撤销。",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "这将重置知识库并替换所有文件为目录下文件。确认继续?",
+	"Thorough explanation": "解释较为详细",
+	"Tika": "Tika",
+	"Tika Server URL required.": "请输入 Tika 服务器地址。",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在对话输入中按 Tab 键可以连续更新多个变量。",
+	"Title": "标题",
+	"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
+	"Title Auto-Generation": "自动生成标题",
+	"Title cannot be an empty string.": "标题不能为空。",
+	"Title Generation Prompt": "用于自动生成标题的提示词",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "要访问可下载的模型名称,",
+	"To access the GGUF models available for downloading,": "要访问可下载的 GGUF 模型,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "请联系管理员以访问。管理员可以在后台管理面板中管理用户状态。",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "要在这里附加知识库,请先将其添加到工作空间中的“知识库”。",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "为了保护您的隐私,只有评分、模型ID、标签和元数据会从您的反馈中分享——您的聊天记录将保持私密,不会被包含在内。",
+	"To select actions here, add them to the \"Functions\" workspace first.": "要在这里选择自动化,请先将其添加到工作空间中的“函数”。",
+	"To select filters here, add them to the \"Functions\" workspace first.": "要在这里选择过滤器,请先将其添加到工作空间中的“函数”。",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "要在这里选择工具包,请先将其添加到工作空间中的“工具”。",
+	"Toast notifications for new updates": "新更新的弹窗提示",
+	"Today": "今天",
+	"Toggle settings": "切换设置",
+	"Toggle sidebar": "切换侧边栏",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "在语境刷新时需保留的 Tokens",
+	"Too verbose": "过于冗长",
+	"Tool created successfully": "工具创建成功",
+	"Tool deleted successfully": "工具删除成功",
+	"Tool Description": "工具描述",
+	"Tool ID": "工具 ID",
+	"Tool imported successfully": "工具导入成功",
+	"Tool Name": "工具名称",
+	"Tool updated successfully": "工具更新成功",
+	"Tools": "工具",
+	"Tools Access": "访问工具",
+	"Tools are a function calling system with arbitrary code execution": "工具是一个具有任意代码执行能力的函数调用系统",
+	"Tools have a function calling system that allows arbitrary code execution": "注意:工具有权执行任意代码",
+	"Tools have a function calling system that allows arbitrary code execution.": "注意:工具有权执行任意代码。",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformers",
+	"Trouble accessing Ollama?": "访问 Ollama 时遇到问题?",
+	"TTS Model": "文本转语音模型",
+	"TTS Settings": "文本转语音设置",
+	"TTS Voice": "文本转语音音色",
+	"Type": "类型",
+	"Type Hugging Face Resolve (Download) URL": "输入 Hugging Face 解析(下载)URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
+	"UI": "界面",
+	"Unarchive All": "取消所有存档",
+	"Unarchive All Archived Chats": "取消所有已存档的对话",
+	"Unarchive Chat": "取消存档当前对话",
+	"Unlock mysteries": "揭开神秘面纱",
+	"Unpin": "取消置顶",
+	"Unravel secrets": "解开秘密",
+	"Untagged": "无标签",
+	"Update": "更新",
+	"Update and Copy Link": "更新和复制链接",
+	"Update for the latest features and improvements.": "更新来获得最新功能与改进。",
+	"Update password": "更新密码",
+	"Updated": "已更新",
+	"Updated at": "更新于",
+	"Updated At": "更新于",
+	"Upload": "上传",
+	"Upload a GGUF model": "上传一个 GGUF 模型",
+	"Upload directory": "上传目录",
+	"Upload files": "上传文件",
+	"Upload Files": "上传文件",
+	"Upload Pipeline": "上传 Pipeline",
+	"Upload Progress": "上传进度",
+	"URL": "URL",
+	"URL Mode": "URL 模式",
+	"Use '#' in the prompt input to load and include your knowledge.": "在输入框中输入'#'号来加载你需要的知识库内容。",
+	"Use Gravatar": "使用来自 Gravatar 的头像",
+	"Use groups to group your users and assign permissions.": "使用权限组来组织用户并分配权限。",
+	"Use Initials": "使用首个字符作为头像",
+	"use_mlock (Ollama)": "use_mlock(Ollama)",
+	"use_mmap (Ollama)": "use_mmap (Ollama)",
+	"user": "用户",
+	"User": "用户",
+	"User location successfully retrieved.": "成功检索到用户位置。",
+	"Username": "用户名",
+	"Users": "用户",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "竞技场模型默认使用所有模型。单击加号按钮添加自定义模型。",
+	"Utilize": "利用",
+	"Valid time units:": "有效时间单位:",
+	"Valves": "值",
+	"Valves updated": "已更新值",
+	"Valves updated successfully": "值更新成功",
+	"variable": "变量",
+	"variable to have them replaced with clipboard content.": "变量将被剪贴板内容替换。",
+	"Version": "版本",
+	"Version {{selectedVersion}} of {{totalVersions}}": "版本 {{selectedVersion}}/{{totalVersions}}",
+	"Visibility": "可见性",
+	"Voice": "语音",
+	"Voice Input": "语音输入",
+	"Warning": "警告",
+	"Warning:": "警告:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "警告:启用此功能将允许用户在服务器上上传任意代码。",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果您修改了语义向量模型,则需要重新导入所有文档。",
+	"Web": "网页",
+	"Web API": "网页 API",
+	"Web Loader Settings": "网页爬取设置",
+	"Web Search": "联网搜索",
+	"Web Search Engine": "联网搜索引擎",
+	"Web Search Query Generation": "网页搜索查询生成",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI 设置",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI 将向 \"{{url}}/api/chat\" 发出请求",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI 将向 \"{{url}}/chat/completions\" 发出请求",
+	"What are you trying to achieve?": "你想要达到什么目标?",
+	"What are you working on?": "你在忙于什么?",
+	"What’s New in": "最近更新内容于",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "启用后,模型将实时回复每条聊天信息,在用户发送信息后立即生成回复。这种模式对即时聊天应用非常有用,但可能会影响较慢硬件的性能。",
+	"wherever you are": "无论你在哪里",
+	"Whisper (Local)": "Whisper (本地)",
+	"Why?": "为什么?",
+	"Widescreen Mode": "宽屏模式",
+	"Won": "获胜",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "与 top-k 一起工作。较高的值(例如0.95)将导致更具多样性的文本,而较低的值(例如0.5)将生成更集中和保守的文本。(默认值:0.9)",
+	"Workspace": "工作空间",
+	"Workspace Permissions": "工作空间权限",
+	"Write a prompt suggestion (e.g. Who are you?)": "写一个提示词建议(例如:你是谁?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "用 50 个字写一个总结 [主题或关键词]。",
+	"Write something...": "单击以键入内容...",
+	"Write your model template content here": "在此写入模型模板内容",
+	"Yesterday": "昨天",
+	"You": "你",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "每次对话最多仅能附上 {{maxCount}} 个文件。",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "通过点击下方的“管理”按钮,你可以添加记忆,以个性化大语言模型的互动,使其更有用,更符合你的需求。",
+	"You cannot upload an empty file.": "请勿上传空文件。",
+	"You do not have permission to upload files.": "你没有上传文件的权限。",
+	"You have no archived conversations.": "没有已归档的对话。",
+	"You have shared this chat": "此对话已经分享过",
+	"You're a helpful assistant.": "你是一个有帮助的助手。",
+	"You're now logged in.": "已登录。",
+	"Your account status is currently pending activation.": "您的账号当前状态为待激活。",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "您的全部捐款将直接给到插件开发者,Open WebUI 不会收取任何比例。但众筹平台可能会有服务费、抽成。",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube 爬取设置"
+}
diff --git a/src/lib/i18n/locales/zh-TW/translation.json b/src/lib/i18n/locales/zh-TW/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..dbc9a9adeb3794e0c3b327ffc89cff2ee2012cd4
--- /dev/null
+++ b/src/lib/i18n/locales/zh-TW/translation.json
@@ -0,0 +1,1025 @@
+{
+	"-1 for no limit, or a positive integer for a specific limit": "-1 表示無限制,或正整數表示特定限制",
+	"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s'、'm'、'h'、'd'、'w' 或 '-1' 表示無到期時間。",
+	"(e.g. `sh webui.sh --api --api-auth username_password`)": "(例如 `sh webui.sh --api --api-auth username_password`)",
+	"(e.g. `sh webui.sh --api`)": "(例如 `sh webui.sh --api`)",
+	"(latest)": "(最新版)",
+	"{{ models }}": "{{ models }}",
+	"{{user}}'s Chats": "{{user}} 的對話",
+	"{{webUIName}} Backend Required": "需要 {{webUIName}} 後端",
+	"*Prompt node ID(s) are required for image generation": "* 圖片生成需要提示詞節點 ID",
+	"A new version (v{{LATEST_VERSION}}) is now available.": "新版本 (v{{LATEST_VERSION}}) 現已釋出。",
+	"A task model is used when performing tasks such as generating titles for chats and web search queries": "執行產生對話標題和網頁搜尋查詢等任務時會使用任務模型",
+	"a user": "一位使用者",
+	"About": "關於",
+	"Access": "存取",
+	"Access Control": "存取控制",
+	"Accessible to all users": "所有使用者可存取",
+	"Account": "帳號",
+	"Account Activation Pending": "帳號待啟用",
+	"Accurate information": "準確資訊",
+	"Actions": "動作",
+	"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "在對話輸入框中輸入 \"/{{COMMAND}}\" 來啟用此命令。",
+	"Active Users": "活躍使用者",
+	"Add": "新增",
+	"Add a model ID": "新增模型 ID",
+	"Add a short description about what this model does": "新增這個模型的簡短描述",
+	"Add a tag": "新增標籤",
+	"Add Arena Model": "新增競技模型",
+	"Add Connection": "新增連線",
+	"Add Content": "新增內容",
+	"Add content here": "在此新增內容",
+	"Add custom prompt": "新增自訂提示詞",
+	"Add Files": "新增檔案",
+	"Add Group": "新增群組",
+	"Add Memory": "新增記憶",
+	"Add Model": "新增模型",
+	"Add Tag": "新增標籤",
+	"Add Tags": "新增標籤",
+	"Add text content": "新增文字內容",
+	"Add User": "新增使用者",
+	"Add User Group": "新增使用者群組",
+	"Adjusting these settings will apply changes universally to all users.": "調整這些設定將會影響所有使用者。",
+	"admin": "管理員",
+	"Admin": "管理員",
+	"Admin Panel": "管理員控制台",
+	"Admin Settings": "管理員設定",
+	"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "管理員可以隨時使用所有工具;使用者則需在工作區中為每個模型分配工具。",
+	"Advanced Parameters": "進階參數",
+	"Advanced Params": "進階參數",
+	"All chats": "所有對話",
+	"All Documents": "所有文件",
+	"All models deleted successfully": "成功刪除所有模型",
+	"Allow Chat Delete": "允許刪除對話",
+	"Allow Chat Deletion": "允許刪除對話紀錄",
+	"Allow Chat Edit": "允許編輯對話",
+	"Allow File Upload": "允許上傳檔案",
+	"Allow non-local voices": "允許非本機語音",
+	"Allow Temporary Chat": "允許暫時對話",
+	"Allow User Location": "允許使用者位置",
+	"Allow Voice Interruption in Call": "允許在通話中打斷語音",
+	"Already have an account?": "已經有帳號了嗎?",
+	"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0)": "作為 top_p 的替代方案,旨在確保質量和多樣性的平衡。相對於最可能的 token 機率而言,參數 p 代表一個 token 被考慮在内的最低機率。例如,當 p=0.05 且最可能的 token 機率為 0.9 時,數值低於 0.045 的對數機率會被過濾掉。(預設值:0.0)",
+	"Amazing": "很棒",
+	"an assistant": "一位助手",
+	"and": "和",
+	"and {{COUNT}} more": "和另外 {{COUNT}} 個",
+	"and create a new shared link.": "並建立新的共用連結。",
+	"API Base URL": "API 基礎 URL",
+	"API Key": "API 金鑰",
+	"API Key created.": "API 金鑰已建立。",
+	"API keys": "API 金鑰",
+	"Application DN": "應用程式 DN",
+	"Application DN Password": "應用程式 DN 密碼",
+	"applies to all users with the \"user\" role": "適用於所有具有「使用者」角色的使用者",
+	"April": "4 月",
+	"Archive": "封存",
+	"Archive All Chats": "封存所有對話紀錄",
+	"Archived Chats": "封存的對話紀錄",
+	"archived-chat-export": "archived-chat-export",
+	"Are you sure you want to unarchive all archived chats?": "您確定要解除封存所有封存的對話記錄嗎?",
+	"Are you sure?": "您確定嗎?",
+	"Arena Models": "競技模型",
+	"Artifacts": "成品",
+	"Ask a question": "提出問題",
+	"Assistant": "助手",
+	"Attach file": "附加檔案",
+	"Attention to detail": "注重細節",
+	"Attribute for Username": "使用者名稱屬性",
+	"Audio": "音訊",
+	"August": "8 月",
+	"Authenticate": "驗證",
+	"Auto-Copy Response to Clipboard": "自動將回應複製到剪貼簿",
+	"Auto-playback response": "自動播放回應",
+	"Autocomplete Generation": "自動完成生成",
+	"Autocomplete Generation Input Max Length": "自動完成產生輸入最大長度",
+	"Automatic1111": "Automatic1111",
+	"AUTOMATIC1111 Api Auth String": "AUTOMATIC1111 API 驗證字串",
+	"AUTOMATIC1111 Base URL": "AUTOMATIC1111 基礎 URL",
+	"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基礎 URL。",
+	"Available list": "可用清單",
+	"available!": "可用!",
+	"Awful": "糟糕",
+	"Azure AI Speech": "Azure AI 語音",
+	"Azure Region": "Azure 區域",
+	"Back": "返回",
+	"Bad Response": "錯誤回應",
+	"Banners": "橫幅",
+	"Base Model (From)": "基礎模型(來自)",
+	"Batch Size (num_batch)": "批次大小(num_batch)",
+	"before": "之前",
+	"Being lazy": "懶惰模式",
+	"Bing Search V7 Endpoint": "Bing 搜尋 V7 端點",
+	"Bing Search V7 Subscription Key": "Bing 搜尋 V7 訂閱金鑰",
+	"Brave Search API Key": "Brave 搜尋 API 金鑰",
+	"By {{name}}": "由 {{name}} 製作",
+	"Bypass SSL verification for Websites": "略過網站的 SSL 驗證",
+	"Call": "通話",
+	"Call feature is not supported when using Web STT engine": "使用網頁語音辨識 (Web STT) 引擎時不支援通話功能",
+	"Camera": "相機",
+	"Cancel": "取消",
+	"Capabilities": "功能",
+	"Certificate Path": "憑證路徑",
+	"Change Password": "修改密碼",
+	"Character": "角色",
+	"Character limit for autocomplete generation input": "自動完成產生輸入的字元限制",
+	"Chart new frontiers": "探索新領域",
+	"Chat": "對話",
+	"Chat Background Image": "對話背景圖片",
+	"Chat Bubble UI": "對話氣泡介面",
+	"Chat Controls": "對話控制項",
+	"Chat direction": "對話方向",
+	"Chat Overview": "對話概覽",
+	"Chat Permissions": "對話權限",
+	"Chat Tags Auto-Generation": "對話標籤自動生成",
+	"Chats": "對話",
+	"Check Again": "再次檢查",
+	"Check for updates": "檢查更新",
+	"Checking for updates...": "正在檢查更新...",
+	"Choose a model before saving...": "儲存前請選擇一個模型...",
+	"Chunk Overlap": "區塊重疊",
+	"Chunk Params": "區塊參數",
+	"Chunk Size": "區塊大小",
+	"Ciphers": "加密方式",
+	"Citation": "引用",
+	"Clear memory": "清除記憶",
+	"click here": "點選這裡",
+	"Click here for filter guides.": "點選這裡查看篩選器指南。",
+	"Click here for help.": "點選這裡取得協助。",
+	"Click here to": "點選這裡",
+	"Click here to download user import template file.": "點選這裡下載使用者匯入範本檔案。",
+	"Click here to learn more about faster-whisper and see the available models.": "點選這裡了解更多關於 faster-whisper 的資訊並查看可用的模型。",
+	"Click here to select": "點選這裡選擇",
+	"Click here to select a csv file.": "點選這裡選擇 CSV 檔案。",
+	"Click here to select a py file.": "點選這裡選擇 Python 檔案。",
+	"Click here to upload a workflow.json file.": "點選這裡上傳 workflow.json 檔案。",
+	"click here.": "點選這裡。",
+	"Click on the user role button to change a user's role.": "點選使用者角色按鈕變更使用者的角色。",
+	"Clipboard write permission denied. Please check your browser settings to grant the necessary access.": "剪貼簿寫入權限遭拒。請檢查您的瀏覽器設定,授予必要的存取權限。",
+	"Clone": "複製",
+	"Close": "關閉",
+	"Code execution": "程式碼執行",
+	"Code formatted successfully": "程式碼格式化成功",
+	"Collection": "收藏",
+	"Color": "顏色",
+	"ComfyUI": "ComfyUI",
+	"ComfyUI Base URL": "ComfyUI 基礎 URL",
+	"ComfyUI Base URL is required.": "需要 ComfyUI 基礎 URL。",
+	"ComfyUI Workflow": "ComfyUI 工作流程",
+	"ComfyUI Workflow Nodes": "ComfyUI 工作流程節點",
+	"Command": "命令",
+	"Completions": "補全",
+	"Concurrent Requests": "平行請求",
+	"Configure": "設定",
+	"Configure Models": "設定模型",
+	"Confirm": "確認",
+	"Confirm Password": "確認密碼",
+	"Confirm your action": "確認您的操作",
+	"Connections": "連線",
+	"Contact Admin for WebUI Access": "請聯絡管理員以取得 WebUI 存取權限",
+	"Content": "內容",
+	"Content Extraction": "內容擷取",
+	"Context Length": "上下文長度",
+	"Continue Response": "繼續回應",
+	"Continue with {{provider}}": "使用 {{provider}} 繼續",
+	"Continue with Email": "使用 Email 繼續",
+	"Continue with LDAP": "使用 LDAP 繼續",
+	"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string.": "控制文字轉語音(TTS)請求中如何分割訊息文字。「標點符號」分割為句子,「段落」分割為段落,「無」則保持訊息為單一字串。",
+	"Controls": "控制項",
+	"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)": "控制輸出的連貫性和多樣性之間的平衡。較低的值會產生更專注和連貫的文字。(預設:5.0)",
+	"Copied": "已複製",
+	"Copied shared chat URL to clipboard!": "已複製共用對話 URL 到剪貼簿!",
+	"Copied to clipboard": "已複製到剪貼簿",
+	"Copy": "複製",
+	"Copy last code block": "複製最後一個程式碼區塊",
+	"Copy last response": "複製最後一個回應",
+	"Copy Link": "複製連結",
+	"Copy to clipboard": "複製到剪貼簿",
+	"Copying to clipboard was successful!": "成功複製到剪貼簿!",
+	"Create": "建立",
+	"Create a knowledge base": "建立知識",
+	"Create a model": "建立模型",
+	"Create Account": "建立帳號",
+	"Create Admin Account": "建立管理員賬號",
+	"Create Group": "建立群組",
+	"Create Knowledge": "建立知識",
+	"Create new key": "建立新的金鑰",
+	"Create new secret key": "建立新的金鑰",
+	"Created at": "建立於",
+	"Created At": "建立於",
+	"Created by": "建立者",
+	"CSV Import": "CSV 匯入",
+	"Current Model": "目前模型",
+	"Current Password": "目前密碼",
+	"Custom": "自訂",
+	"Dark": "深色",
+	"Database": "資料庫",
+	"December": "12 月",
+	"Default": "預設",
+	"Default (Open AI)": "預設 (OpenAI)",
+	"Default (SentenceTransformers)": "預設 (SentenceTransformers)",
+	"Default Model": "預設模型",
+	"Default model updated": "預設模型已更新",
+	"Default Models": "預設模型",
+	"Default permissions": "預設權限",
+	"Default permissions updated successfully": "預設權限更新成功",
+	"Default Prompt Suggestions": "預設提示詞建議",
+	"Default to 389 or 636 if TLS is enabled": "如果啓用了 TLS 則預設為 389 或 636",
+	"Default to ALL": "預設到所有",
+	"Default User Role": "預設使用者角色",
+	"Delete": "刪除",
+	"Delete a model": "刪除模型",
+	"Delete All Chats": "刪除所有對話紀錄",
+	"Delete All Models": "刪除所有模型",
+	"Delete chat": "刪除對話紀錄",
+	"Delete Chat": "刪除對話紀錄",
+	"Delete chat?": "刪除對話紀錄?",
+	"Delete folder?": "刪除資料夾?",
+	"Delete function?": "刪除函式?",
+	"Delete prompt?": "刪除提示詞?",
+	"delete this link": "刪除此連結",
+	"Delete tool?": "刪除工具?",
+	"Delete User": "刪除使用者",
+	"Deleted {{deleteModelTag}}": "已刪除 {{deleteModelTag}}",
+	"Deleted {{name}}": "已刪除 {{name}}",
+	"Deleted User": "刪除使用者?",
+	"Describe your knowledge base and objectives": "描述您的知識庫和目標",
+	"Description": "描述",
+	"Didn't fully follow instructions": "未完全遵循指示",
+	"Disabled": "已停用",
+	"Discover a function": "發掘函式",
+	"Discover a model": "發掘模型",
+	"Discover a prompt": "發掘提示詞",
+	"Discover a tool": "發掘工具",
+	"Discover wonders": "發掘奇蹟",
+	"Discover, download, and explore custom functions": "發掘、下載及探索自訂函式",
+	"Discover, download, and explore custom prompts": "發掘、下載及探索自訂提示詞",
+	"Discover, download, and explore custom tools": "發掘、下載及探索自訂工具",
+	"Discover, download, and explore model presets": "發掘、下載及探索模型預設集",
+	"Dismissible": "可忽略",
+	"Display": "顯示",
+	"Display Emoji in Call": "在通話中顯示表情符號",
+	"Display the username instead of You in the Chat": "在對話中顯示使用者名稱,而非「您」",
+	"Displays citations in the response": "在回應中顯示引用",
+	"Dive into knowledge": "深入知識",
+	"Do not install functions from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝函式。",
+	"Do not install tools from sources you do not fully trust.": "請勿從您無法完全信任的來源安裝工具。",
+	"Document": "文件",
+	"Documentation": "文件",
+	"Documents": "文件",
+	"does not make any external connections, and your data stays securely on your locally hosted server.": "不會建立任何外部連線,而且您的資料會安全地儲存在您本機伺服器上。",
+	"Don't have an account?": "還沒註冊帳號嗎?",
+	"don't install random functions from sources you don't trust.": "請勿從您無法信任的來源安裝隨機函式。",
+	"don't install random tools from sources you don't trust.": "請勿從您無法信任的來源安裝隨機工具。",
+	"Don't like the style": "不喜歡這個樣式",
+	"Done": "完成",
+	"Download": "下載",
+	"Download canceled": "已取消下載",
+	"Download Database": "下載資料庫",
+	"Drag and drop a file to upload or select a file to view": "拖放檔案以上傳或選擇檔案以檢視",
+	"Draw": "繪製",
+	"Drop any files here to add to the conversation": "拖拽任意檔案到此處以新增至對話",
+	"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如:'30s'、'10m'。有效的時間單位為 's'、'm'、'h'。",
+	"e.g. A filter to remove profanity from text": "例如:從文字中移除髒話的篩選器",
+	"e.g. My Filter": "例如:我的篩選器",
+	"e.g. My Tools": "例如:我的工具",
+	"e.g. my_filter": "例如:my_filter",
+	"e.g. my_tools": "例如:my_tools",
+	"e.g. Tools for performing various operations": "例如:用於執行各種操作的工具",
+	"Edit": "編輯",
+	"Edit Arena Model": "編輯競技模型",
+	"Edit Connection": "編輯連線",
+	"Edit Default Permissions": "編輯預設權限",
+	"Edit Memory": "編輯記憶",
+	"Edit User": "編輯使用者",
+	"Edit User Group": "編輯使用者群組",
+	"ElevenLabs": "ElevenLabs",
+	"Email": "Email",
+	"Embark on adventures": "展開探險之旅",
+	"Embedding Batch Size": "嵌入批次大小",
+	"Embedding Model": "嵌入模型",
+	"Embedding Model Engine": "嵌入模型引擎",
+	"Embedding model set to \"{{embedding_model}}\"": "嵌入模型已設定為 \"{{embedding_model}}\"",
+	"Enable API Key Auth": "啟用 API 金鑰驗證",
+	"Enable autocomplete generation for chat messages": "啟用聊天訊息的自動完成生成",
+	"Enable Community Sharing": "啟用社群分享",
+	"Enable Memory Locking (mlock) to prevent model data from being swapped out of RAM. This option locks the model's working set of pages into RAM, ensuring that they will not be swapped out to disk. This can help maintain performance by avoiding page faults and ensuring fast data access.": "啟用記憶體鎖定(mlock)以防止模型資料被換出 RAM。此選項會將模型的工作頁面集鎖定在 RAM 中,確保它們不會被換出到磁碟。這可以透過避免頁面錯誤和確保快速資料存取來維持效能。",
+	"Enable Memory Mapping (mmap) to load model data. This option allows the system to use disk storage as an extension of RAM by treating disk files as if they were in RAM. This can improve model performance by allowing for faster data access. However, it may not work correctly with all systems and can consume a significant amount of disk space.": "啟用記憶體映射(mmap)以載入模型資料。此選項允許系統使用磁碟儲存作為 RAM 的延伸,透過將磁碟檔案視為在 RAM 中來處理。這可以透過允許更快的資料存取來改善模型效能。然而,它可能無法在所有系統上正常運作,並且可能會消耗大量磁碟空間。",
+	"Enable Message Rating": "啟用訊息評分",
+	"Enable Mirostat sampling for controlling perplexity. (Default: 0, 0 = Disabled, 1 = Mirostat, 2 = Mirostat 2.0)": "啟用 Mirostat 採樣以控制困惑度。(預設:0,0 = 停用,1 = Mirostat,2 = Mirostat 2.0)",
+	"Enable New Sign Ups": "允許新使用者註冊",
+	"Enable Web Search": "啟用網頁搜尋",
+	"Enabled": "已啟用",
+	"Engine": "引擎",
+	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "請確認您的 CSV 檔案包含以下 4 個欄位,並按照此順序排列:姓名、電子郵件、密碼、角色。",
+	"Enter {{role}} message here": "在此輸入 {{role}} 訊息",
+	"Enter a detail about yourself for your LLMs to recall": "輸入有關您的詳細資訊,讓您的大型語言模型可以回想起來",
+	"Enter api auth string (e.g. username:password)": "輸入 API 驗證字串(例如:username:password)",
+	"Enter Application DN": "輸入應用程式 DN",
+	"Enter Application DN Password": "輸入應用程式 DN 密碼",
+	"Enter Bing Search V7 Endpoint": "輸入 Bing 搜尋 V7 端點",
+	"Enter Bing Search V7 Subscription Key": "輸入 Bing 搜尋 V7 訂閱金鑰",
+	"Enter Brave Search API Key": "輸入 Brave 搜尋 API 金鑰",
+	"Enter certificate path": "輸入憑證路徑",
+	"Enter CFG Scale (e.g. 7.0)": "輸入 CFG 比例(例如:7.0)",
+	"Enter Chunk Overlap": "輸入區塊重疊",
+	"Enter Chunk Size": "輸入區塊大小",
+	"Enter description": "輸入描述",
+	"Enter Github Raw URL": "輸入 GitHub Raw URL",
+	"Enter Google PSE API Key": "輸入 Google PSE API 金鑰",
+	"Enter Google PSE Engine Id": "輸入 Google PSE 引擎 ID",
+	"Enter Image Size (e.g. 512x512)": "輸入圖片大小(例如:512x512)",
+	"Enter Jina API Key": "輸入 Jina API 金鑰",
+	"Enter language codes": "輸入語言代碼",
+	"Enter Model ID": "輸入模型 ID",
+	"Enter model tag (e.g. {{modelTag}})": "輸入模型標籤(例如:{{modelTag}})",
+	"Enter Mojeek Search API Key": "輸入 Mojeek 搜尋 API 金鑰",
+	"Enter Number of Steps (e.g. 50)": "輸入步驟數(例如:50)",
+	"Enter proxy URL (e.g. https://user:password@host:port)": "輸入代理程式 URL(例如:https://user:password@host:port)",
+	"Enter Sampler (e.g. Euler a)": "輸入取樣器(例如:Euler a)",
+	"Enter Scheduler (e.g. Karras)": "輸入排程器(例如:Karras)",
+	"Enter Score": "輸入分數",
+	"Enter SearchApi API Key": "輸入 SearchApi API 金鑰",
+	"Enter SearchApi Engine": "輸入 SearchApi 引擎",
+	"Enter Searxng Query URL": "輸入 SearXNG 查詢 URL",
+	"Enter Seed": "輸入種子值",
+	"Enter Serper API Key": "輸入 Serper API 金鑰",
+	"Enter Serply API Key": "輸入 Serply API 金鑰",
+	"Enter Serpstack API Key": "輸入 Serpstack API 金鑰",
+	"Enter server host": "輸入伺服器主機",
+	"Enter server label": "輸入伺服器標籤",
+	"Enter server port": "輸入伺服器連接埠",
+	"Enter stop sequence": "輸入停止序列",
+	"Enter system prompt": "輸入系統提示詞",
+	"Enter Tavily API Key": "輸入 Tavily API 金鑰",
+	"Enter Tika Server URL": "輸入 Tika 伺服器 URL",
+	"Enter Top K": "輸入 Top K 值",
+	"Enter URL (e.g. http://127.0.0.1:7860/)": "輸入 URL(例如:http://127.0.0.1:7860/)",
+	"Enter URL (e.g. http://localhost:11434)": "輸入 URL(例如:http://localhost:11434)",
+	"Enter Your Email": "輸入您的電子郵件",
+	"Enter Your Full Name": "輸入您的全名",
+	"Enter your message": "輸入您的訊息",
+	"Enter Your Password": "輸入您的密碼",
+	"Enter Your Role": "輸入您的角色",
+	"Enter Your Username": "輸入您的使用者名稱",
+	"Error": "錯誤",
+	"ERROR": "錯誤",
+	"Evaluations": "評估",
+	"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "範例:(&(objectClass=inetOrgPerson)(uid=%s))",
+	"Example: ALL": "範例:ALL",
+	"Example: ou=users,dc=foo,dc=example": "範例:ou=users,dc=foo,dc=example",
+	"Example: sAMAccountName or uid or userPrincipalName": "範例:sAMAccountName 或 uid 或 userPrincipalName",
+	"Exclude": "排除",
+	"Experimental": "實驗性功能",
+	"Explore the cosmos": "探索宇宙",
+	"Export": "匯出",
+	"Export All Archived Chats": "匯出所有已封存的對話",
+	"Export All Chats (All Users)": "匯出所有對話紀錄(所有使用者)",
+	"Export chat (.json)": "匯出對話紀錄(.json)",
+	"Export Chats": "匯出對話紀錄",
+	"Export Config to JSON File": "將設定匯出為 JSON 檔案",
+	"Export Functions": "匯出函式",
+	"Export Models": "匯出模型",
+	"Export Presets": "匯出預設集",
+	"Export Prompts": "匯出提示詞",
+	"Export to CSV": "匯出為 CSV",
+	"Export Tools": "匯出工具",
+	"External Models": "外部模型",
+	"Failed to add file.": "新增檔案失敗。",
+	"Failed to create API Key.": "建立 API 金鑰失敗。",
+	"Failed to read clipboard contents": "讀取剪貼簿內容失敗",
+	"Failed to save models configuration": "儲存模型設定失敗",
+	"Failed to update settings": "更新設定失敗",
+	"Failed to upload file.": "上傳檔案失敗。",
+	"February": "2 月",
+	"Feedback History": "回饋歷史",
+	"Feedbacks": "回饋",
+	"Feel free to add specific details": "歡迎自由新增特定細節",
+	"File": "檔案",
+	"File added successfully.": "檔案新增成功。",
+	"File content updated successfully.": "檔案內容更新成功。",
+	"File Mode": "檔案模式",
+	"File not found.": "找不到檔案。",
+	"File removed successfully.": "成功移除檔案。",
+	"File size should not exceed {{maxSize}} MB.": "檔案大小不應超過 {{maxSize}} MB。",
+	"Files": "檔案",
+	"Filter is now globally disabled": "篩選器現在已全域停用",
+	"Filter is now globally enabled": "篩選器現在已全域啟用",
+	"Filters": "篩選器",
+	"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "偵測到指紋偽造:無法使用姓名縮寫作為大頭貼。將預設為預設個人檔案圖片。",
+	"Fluidly stream large external response chunks": "流暢地串流大型外部回應區塊",
+	"Focus chat input": "聚焦對話輸入",
+	"Folder deleted successfully": "資料夾刪除成功",
+	"Folder name cannot be empty": "資料夾名稱不能為空",
+	"Folder name cannot be empty.": "資料夾名稱不能為空。",
+	"Folder name updated successfully": "資料夾名稱更新成功",
+	"Followed instructions perfectly": "完全遵循指示",
+	"Forge new paths": "開創新路徑",
+	"Form": "表單",
+	"Format your variables using brackets like this:": "使用方括號格式化您的變數,如下所示:",
+	"Frequency Penalty": "頻率懲罰",
+	"Function": "函式",
+	"Function created successfully": "成功建立函式",
+	"Function deleted successfully": "成功刪除函式",
+	"Function Description": "函式描述",
+	"Function ID": "函式 ID",
+	"Function is now globally disabled": "現在已在全域停用函式",
+	"Function is now globally enabled": "現在已在全域啟用函式",
+	"Function Name": "函式名稱",
+	"Function updated successfully": "成功更新函式",
+	"Functions": "函式",
+	"Functions allow arbitrary code execution": "函式允許執行任意程式碼",
+	"Functions allow arbitrary code execution.": "函式允許執行任意程式碼。",
+	"Functions imported successfully": "成功匯入函式",
+	"General": "一般",
+	"General Settings": "一般設定",
+	"Generate Image": "產生圖片",
+	"Generating search query": "正在產生搜尋查詢",
+	"Generation Info": "生成資訊",
+	"Get started": "開始使用",
+	"Get started with {{WEBUI_NAME}}": "開始使用 {{WEBUI_NAME}}",
+	"Global": "全域",
+	"Good Response": "良好回應",
+	"Google PSE API Key": "Google PSE API 金鑰",
+	"Google PSE Engine Id": "Google PSE 引擎 ID",
+	"Group created successfully": "群組建立成功",
+	"Group deleted successfully": "群組刪除成功",
+	"Group Description": "群組描述",
+	"Group Name": "群組名稱",
+	"Group updated successfully": "群組更新成功",
+	"Groups": "群組",
+	"h:mm a": "h:mm a",
+	"Haptic Feedback": "觸覺回饋",
+	"has no conversations.": "沒有對話。",
+	"Hello, {{name}}": "您好,{{name}}",
+	"Help": "說明",
+	"Help us create the best community leaderboard by sharing your feedback history!": "透過分享您的回饋歷史,幫助我們建立最佳的社群排行榜!",
+	"Hex Color": "Hex 顔色",
+	"Hex Color - Leave empty for default color": "Hex 顔色 —— 留空以使用預設顔色",
+	"Hide": "隱藏",
+	"Host": "主機",
+	"How can I help you today?": "今天我能為您做些什麼?",
+	"How would you rate this response?": "您如何評價此回應?",
+	"Hybrid Search": "混合搜尋",
+	"I acknowledge that I have read and I understand the implications of my action. I am aware of the risks associated with executing arbitrary code and I have verified the trustworthiness of the source.": "我確認已閱讀並理解我的操作所帶來的影響。我了解執行任意程式碼的相關風險,並已驗證來源的可信度。",
+	"ID": "ID",
+	"Ignite curiosity": "點燃好奇心",
+	"Image Generation (Experimental)": "圖片生成(實驗性功能)",
+	"Image Generation Engine": "圖片生成引擎",
+	"Image Settings": "圖片設定",
+	"Images": "圖片",
+	"Import Chats": "匯入對話紀錄",
+	"Import Config from JSON File": "從 JSON 檔案匯入設定",
+	"Import Functions": "匯入函式",
+	"Import Models": "匯入模型",
+	"Import Presets": "匯入預設集",
+	"Import Prompts": "匯入提示詞",
+	"Import Tools": "匯入工具",
+	"Include": "包含",
+	"Include `--api-auth` flag when running stable-diffusion-webui": "執行 stable-diffusion-webui 時包含 `--api-auth` 參數",
+	"Include `--api` flag when running stable-diffusion-webui": "執行 stable-diffusion-webui 時包含 `--api` 參數",
+	"Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)": "影響演算法對回饋的反應速度。較低的學習率會導致調整速度較慢,而較高的學習率會使演算法更快回應。(預設:0.1)",
+	"Info": "資訊",
+	"Input commands": "輸入命令",
+	"Install from Github URL": "從 GitHub URL 安裝",
+	"Instant Auto-Send After Voice Transcription": "語音轉錄後立即自動傳送",
+	"Interface": "介面",
+	"Invalid file format.": "無效檔案格式。",
+	"Invalid Tag": "無效標籤",
+	"January": "1 月",
+	"Jina API Key": "Jina API 金鑰",
+	"join our Discord for help.": "加入我們的 Discord 以尋求協助。",
+	"JSON": "JSON",
+	"JSON Preview": "JSON 預覽",
+	"July": "7 月",
+	"June": "6 月",
+	"JWT Expiration": "JWT 過期時間",
+	"JWT Token": "JWT Token",
+	"Keep Alive": "保持連線",
+	"Key": "金鑰",
+	"Keyboard shortcuts": "鍵盤快捷鍵",
+	"Knowledge": "知識",
+	"Knowledge Access": "知識存取",
+	"Knowledge created successfully.": "知識建立成功。",
+	"Knowledge deleted successfully.": "知識刪除成功。",
+	"Knowledge reset successfully.": "知識重設成功。",
+	"Knowledge updated successfully": "知識更新成功",
+	"Label": "標籤",
+	"Landing Page Mode": "首頁模式",
+	"Language": "語言",
+	"Last Active": "上次活動時間",
+	"Last Modified": "上次修改時間",
+	"LDAP": "LDAP",
+	"LDAP server updated": "LDAP 伺服器已更新",
+	"Leaderboard": "排行榜",
+	"Leave empty for unlimited": "留空表示無限制",
+	"Leave empty to include all models from \"{{URL}}/api/tags\" endpoint": "留空以包含來自 \"{{URL}}/api/tags\" 端點的所有模型",
+	"Leave empty to include all models from \"{{URL}}/models\" endpoint": "留空以包含來自 \"{{URL}}/models\" 端點的所有模型",
+	"Leave empty to include all models or select specific models": "留空以包含所有模型或選擇特定模型",
+	"Leave empty to use the default prompt, or enter a custom prompt": "留空以使用預設提示詞,或輸入自訂提示詞",
+	"Light": "淺色",
+	"Listening...": "正在聆聽...",
+	"LLMs can make mistakes. Verify important information.": "大型語言模型可能會出錯。請驗證重要資訊。",
+	"Local": "本機",
+	"Local Models": "本機模型",
+	"Lost": "已遺失",
+	"LTR": "從左到右",
+	"Made by OpenWebUI Community": "由 OpenWebUI 社群製作",
+	"Make sure to enclose them with": "請務必將它們放在",
+	"Make sure to export a workflow.json file as API format from ComfyUI.": "請確保從 ComfyUI 匯出 workflow.json 檔案為 API 格式。",
+	"Manage": "管理",
+	"Manage Arena Models": "管理競技模型",
+	"Manage Ollama": "管理 Ollama",
+	"Manage Ollama API Connections": "管理 Ollama API 連線",
+	"Manage OpenAI API Connections": "管理 OpenAI API 連線",
+	"Manage Pipelines": "管理管線",
+	"March": "3 月",
+	"Max Tokens (num_predict)": "最大 token 數(num_predict)",
+	"Max Upload Count": "最大上傳數量",
+	"Max Upload Size": "最大上傳大小",
+	"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可同時下載 3 個模型。請稍後再試。",
+	"May": "5 月",
+	"Memories accessible by LLMs will be shown here.": "可被大型語言模型存取的記憶將顯示在這裡。",
+	"Memory": "記憶",
+	"Memory added successfully": "成功新增記憶",
+	"Memory cleared successfully": "成功清除記憶",
+	"Memory deleted successfully": "成功刪除記憶",
+	"Memory updated successfully": "成功更新記憶",
+	"Merge Responses": "合併回應",
+	"Message rating should be enabled to use this feature": "需要啟用訊息評分才能使用此功能",
+	"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "建立連結後傳送的訊息不會被分享。擁有網址的使用者可檢視分享的對話內容。",
+	"Min P": "最小 P 值",
+	"Minimum Score": "最低分數",
+	"Mirostat": "Mirostat",
+	"Mirostat Eta": "Mirostat Eta",
+	"Mirostat Tau": "Mirostat Tau",
+	"MMMM DD, YYYY": "YYYY 年 MMMM DD 日",
+	"MMMM DD, YYYY HH:mm": "YYYY 年 MMMM DD 日 HH:mm",
+	"MMMM DD, YYYY hh:mm:ss A": "YYYY 年 MMMM DD 日 hh:mm:ss A",
+	"Model": "模型",
+	"Model '{{modelName}}' has been successfully downloaded.": "模型「{{modelName}}」已成功下載。",
+	"Model '{{modelTag}}' is already in queue for downloading.": "模型「{{modelTag}}」已在下載佇列中。",
+	"Model {{modelId}} not found": "找不到模型 {{modelId}}",
+	"Model {{modelName}} is not vision capable": "模型 {{modelName}} 不具備視覺能力",
+	"Model {{name}} is now {{status}}": "模型 {{name}} 現在狀態為 {{status}}",
+	"Model accepts image inputs": "模型接受影像輸入",
+	"Model created successfully!": "成功建立模型!",
+	"Model filesystem path detected. Model shortname is required for update, cannot continue.": "偵測到模型檔案系統路徑。更新需要模型簡稱,因此無法繼續。",
+	"Model Filtering": "模型篩選",
+	"Model ID": "模型 ID",
+	"Model IDs": "模型 IDs",
+	"Model Name": "模型名稱",
+	"Model not selected": "未選取模型",
+	"Model Params": "模型參數",
+	"Model Permissions": "模型權限",
+	"Model updated successfully": "成功更新模型",
+	"Modelfile Content": "模型檔案內容",
+	"Models": "模型",
+	"Models Access": "模型存取",
+	"Models configuration saved successfully": "模型設定保存成功",
+	"Mojeek Search API Key": "Mojeek 搜尋 API 金鑰",
+	"more": "更多",
+	"More": "更多",
+	"Name": "名稱",
+	"Name your knowledge base": "命名您的知識庫",
+	"New Chat": "新增對話",
+	"New folder": "新增資料夾",
+	"New Password": "新密碼",
+	"No content found": "找不到內容",
+	"No content to speak": "無可朗讀的內容",
+	"No distance available": "無可用距離",
+	"No feedbacks found": "找不到回饋",
+	"No file selected": "未選取檔案",
+	"No files found.": "找不到檔案。",
+	"No groups with access, add a group to grant access": "沒有具有存取權限的群組,新增群組以授予存取權限",
+	"No HTML, CSS, or JavaScript content found.": "找不到 HTML、CSS 或 JavaScript 內容。",
+	"No knowledge found": "找不到知識",
+	"No model IDs": "沒有任何模型 ID",
+	"No models found": "找不到模型",
+	"No models selected": "未選取模型",
+	"No results found": "找不到任何結果",
+	"No search query generated": "未產生搜尋查詢",
+	"No source available": "無可用源",
+	"No users were found.": "找不到任何使用者",
+	"No valves to update": "無閥門可更新",
+	"None": "無",
+	"Not factually correct": "與事實不符",
+	"Not helpful": "沒有幫助",
+	"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "注意:如果您設定了最低分數,則搜尋只會回傳分數大於或等於最低分數的文件。",
+	"Notes": "注意",
+	"Notifications": "通知",
+	"November": "11 月",
+	"num_gpu (Ollama)": "num_gpu (Ollama)",
+	"num_thread (Ollama)": "num_thread (Ollama)",
+	"OAuth ID": "OAuth ID",
+	"October": "10 月",
+	"Off": "關閉",
+	"Okay, Let's Go!": "好的,我們開始吧!",
+	"OLED Dark": "OLED 深色",
+	"Ollama": "Ollama",
+	"Ollama API": "Ollama API",
+	"Ollama API disabled": "Ollama API 已停用",
+	"Ollama API settings updated": "Ollama API 設定已更新",
+	"Ollama Version": "Ollama 版本",
+	"On": "開啟",
+	"Only alphanumeric characters and hyphens are allowed": "只允許使用英文字母、數字和連字號",
+	"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字串中只允許使用英文字母、數字和連字號。",
+	"Only collections can be edited, create a new knowledge base to edit/add documents.": "只能編輯集合,請建立新的知識以編輯或新增文件。",
+	"Only select users and groups with permission can access": "只有具有權限的選定使用者和群組可以存取",
+	"Oops! Looks like the URL is invalid. Please double-check and try again.": "哎呀!這個 URL 似乎無效。請仔細檢查並再試一次。",
+	"Oops! There are files still uploading. Please wait for the upload to complete.": "哎呀!還有檔案正在上傳。請等候上傳完畢。",
+	"Oops! There was an error in the previous response.": "哎呀!之前的回應有一處錯誤。",
+	"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!您使用了不支援的方法(僅限前端)。請從後端提供 WebUI。",
+	"Open file": "開啟檔案",
+	"Open in full screen": "全螢幕開啟",
+	"Open new chat": "開啟新的對話",
+	"Open WebUI uses faster-whisper internally.": "Open WebUI 使用内部 faster-whisper。",
+	"Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.": "Open WebUI 使用 SpeechT5 和 CMU Arctic 說話者嵌入。",
+	"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 API": "OpenAI API",
+	"OpenAI API Config": "OpenAI API 設定",
+	"OpenAI API Key is required.": "需要 OpenAI API 金鑰。",
+	"OpenAI API settings updated": "OpenAI API 設定已更新",
+	"OpenAI URL/Key required.": "需要 OpenAI URL或金鑰。",
+	"or": "或",
+	"Organize your users": "組織您的使用者",
+	"Other": "其他",
+	"OUTPUT": "輸出",
+	"Output format": "輸出格式",
+	"Overview": "概覽",
+	"page": "頁面",
+	"Password": "密碼",
+	"Paste Large Text as File": "將大型文字以檔案貼上",
+	"PDF document (.pdf)": "PDF 文件 (.pdf)",
+	"PDF Extract Images (OCR)": "PDF 影像擷取(OCR 光學文字辨識)",
+	"pending": "待處理",
+	"Permission denied when accessing media devices": "存取媒體裝置時權限遭拒",
+	"Permission denied when accessing microphone": "存取麥克風時權限遭拒",
+	"Permission denied when accessing microphone: {{error}}": "存取麥克風時權限遭拒:{{error}}",
+	"Permissions": "權限",
+	"Personalization": "個人化",
+	"Pin": "釘選",
+	"Pinned": "已釘選",
+	"Pioneer insights": "先驅見解",
+	"Pipeline deleted successfully": "成功刪除管線",
+	"Pipeline downloaded successfully": "成功下載管線",
+	"Pipelines": "管線",
+	"Pipelines Not Detected": "未偵測到管線",
+	"Pipelines Valves": "管線閥門",
+	"Plain text (.txt)": "純文字 (.txt)",
+	"Playground": "遊樂場",
+	"Please carefully review the following warnings:": "請仔細閱讀以下警告:",
+	"Please enter a prompt": "請輸入提示詞",
+	"Please fill in all fields.": "請填寫所有欄位。",
+	"Please select a model first.": "請先選擇型號。",
+	"Please select a reason": "請選擇原因",
+	"Port": "連接埠",
+	"Positive attitude": "積極的態度",
+	"Prefix ID": "前綴 ID",
+	"Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable": "前綴 ID 用於透過為模型 ID 新增前綴以避免與其他連線衝突 - 留空以停用",
+	"Previous 30 days": "過去 30 天",
+	"Previous 7 days": "過去 7 天",
+	"Profile Image": "個人檔案圖片",
+	"Prompt (e.g. Tell me a fun fact about the Roman Empire)": "提示詞(例如:告訴我關於羅馬帝國的一些趣事)",
+	"Prompt Content": "提示詞內容",
+	"Prompt created successfully": "提示詞建立成功",
+	"Prompt suggestions": "提示詞建議",
+	"Prompt updated successfully": "提示詞更新成功",
+	"Prompts": "提示詞",
+	"Prompts Access": "提示詞存取",
+	"Proxy URL": "代理網址",
+	"Pull \"{{searchValue}}\" from Ollama.com": "從 Ollama.com 下載「{{searchValue}}」",
+	"Pull a model from Ollama.com": "從 Ollama.com 下載模型",
+	"Query Generation Prompt": "查詢生成提示詞",
+	"Query Params": "查詢參數",
+	"RAG Template": "RAG 範本",
+	"Rating": "評分",
+	"Re-rank models by topic similarity": "根據主題相似度重新排序模型",
+	"Read Aloud": "大聲朗讀",
+	"Record voice": "錄音",
+	"Redirecting you to OpenWebUI Community": "正在將您重導向至 OpenWebUI 社群",
+	"Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)": "降低產生無意義內容的機率。較高的值(例如 100)會給出更多樣化的答案,而較低的值(例如 10)會更保守。(預設:40)",
+	"Refer to yourself as \"User\" (e.g., \"User is learning Spanish\")": "以「使用者」稱呼自己(例如:「使用者正在學習西班牙文」)",
+	"References from": "引用來源",
+	"Refused when it shouldn't have": "不應拒絕時拒絕了",
+	"Regenerate": "重新產生",
+	"Release Notes": "釋出説明",
+	"Relevance": "相關性",
+	"Remove": "移除",
+	"Remove Model": "移除模型",
+	"Rename": "重新命名",
+	"Reorder Models": "重新排序模型",
+	"Repeat Last N": "重複最後 N 個",
+	"Request Mode": "請求模式",
+	"Reranking Model": "重新排序模型",
+	"Reranking model disabled": "已停用重新排序模型",
+	"Reranking model set to \"{{reranking_model}}\"": "重新排序模型已設定為 \"{{reranking_model}}\"",
+	"Reset": "重設",
+	"Reset All Models": "重設所有模型",
+	"Reset Upload Directory": "重設上傳目錄",
+	"Reset Vector Storage/Knowledge": "重設向量儲存或知識",
+	"Response notifications cannot be activated as the website permissions have been denied. Please visit your browser settings to grant the necessary access.": "無法啟用回應通知,因為網站權限已遭拒。請前往瀏覽器設定以授予必要存取權限。",
+	"Response splitting": "回應分割",
+	"Result": "結果",
+	"Retrieval Query Generation": "檢索查詢生成",
+	"Rich Text Input for Chat": "使用富文本輸入對話",
+	"RK": "RK",
+	"Role": "角色",
+	"Rosé Pine": "玫瑰松",
+	"Rosé Pine Dawn": "黎明玫瑰松",
+	"RTL": "從右到左",
+	"Run": "執行",
+	"Running": "運作中",
+	"Save": "儲存",
+	"Save & Create": "儲存並建立",
+	"Save & Update": "儲存並更新",
+	"Save As Copy": "另存為副本",
+	"Save Tag": "儲存標籤",
+	"Saved": "已儲存",
+	"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "不再支援直接將對話紀錄儲存到您的瀏覽器儲存空間。請點選下方按鈕來下載並刪除您的對話紀錄。別擔心,您可以透過以下方式輕鬆地將對話紀錄重新匯入後端",
+	"Scroll to bottom when switching between branches": "切換分支時捲動到底端",
+	"Search": "搜尋",
+	"Search a model": "搜尋模型",
+	"Search Base": "搜尋基礎",
+	"Search Chats": "搜尋對話",
+	"Search Collection": "搜尋集合",
+	"Search Filters": "搜尋篩選器",
+	"search for tags": "搜尋標籤",
+	"Search Functions": "搜尋函式",
+	"Search Knowledge": "搜尋知識庫",
+	"Search Models": "搜尋模型",
+	"Search options": "搜尋選項",
+	"Search Prompts": "搜尋提示詞",
+	"Search Result Count": "搜尋結果數量",
+	"Search the web": "搜尋網頁",
+	"Search Tools": "搜尋工具",
+	"SearchApi API Key": "SearchApi API 金鑰",
+	"SearchApi Engine": "SearchApi 引擎",
+	"Searched {{count}} sites_one": "已搜尋 {{count}} 個網站",
+	"Searched {{count}} sites_other": "已搜尋 {{count}} 個網站",
+	"Searching \"{{searchQuery}}\"": "正在搜尋「{{searchQuery}}」",
+	"Searching Knowledge for \"{{searchQuery}}\"": "正在搜尋知識庫中的「{{searchQuery}}」",
+	"Searxng Query URL": "Searxng 查詢 URL",
+	"See readme.md for instructions": "檢視 readme.md 以取得說明",
+	"See what's new": "檢視新功能",
+	"Seed": "種子值",
+	"Select a base model": "選擇基礎模型",
+	"Select a engine": "選擇引擎",
+	"Select a function": "選擇函式",
+	"Select a group": "選擇群組",
+	"Select a model": "選擇模型",
+	"Select a pipeline": "選擇管線",
+	"Select a pipeline url": "選擇管線 URL",
+	"Select a tool": "選擇工具",
+	"Select Engine": "選擇引擎",
+	"Select Knowledge": "選擇知識庫",
+	"Select model": "選擇模型",
+	"Select only one model to call": "僅選擇一個模型來呼叫",
+	"Selected model(s) do not support image inputs": "選取的模型不支援圖片輸入",
+	"Semantic distance to query": "與查詢的語義距離",
+	"Send": "傳送",
+	"Send a Message": "傳送訊息",
+	"Send message": "傳送訊息",
+	"Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.": "在請求中傳送 `stream_options: { include_usage: true }`。\n設定後,支援的提供者將在回應中回傳權杖使用資訊。",
+	"September": "9 月",
+	"Serper API Key": "Serper API 金鑰",
+	"Serply API Key": "Serply API 金鑰",
+	"Serpstack API Key": "Serpstack API 金鑰",
+	"Server connection verified": "伺服器連線已驗證",
+	"Set as default": "設為預設",
+	"Set CFG Scale": "設定 CFG 比例",
+	"Set Default Model": "設定預設模型",
+	"Set embedding model": "設定嵌入模型",
+	"Set embedding model (e.g. {{model}})": "設定嵌入模型(例如:{{model}})",
+	"Set Image Size": "設定圖片大小",
+	"Set reranking model (e.g. {{model}})": "設定重新排序模型(例如:{{model}})",
+	"Set Sampler": "設定取樣器",
+	"Set Scheduler": "設定排程器",
+	"Set Steps": "設定步數",
+	"Set Task Model": "設定任務模型",
+	"Set the number of GPU devices used for computation. This option controls how many GPU devices (if available) are used to process incoming requests. Increasing this value can significantly improve performance for models that are optimized for GPU acceleration but may also consume more power and GPU resources.": "設定用於計算的 GPU 裝置數量。此選項控制使用多少個 GPU 裝置(如果可用)來處理傳入的請求。增加此值可以顯著提升針對 GPU 加速優化的模型效能,但也可能消耗更多電力和 GPU 資源。",
+	"Set the number of worker threads used for computation. This option controls how many threads are used to process incoming requests concurrently. Increasing this value can improve performance under high concurrency workloads but may also consume more CPU resources.": "設定用於計算的工作執行緒數量。此選項控制使用多少執行緒來同時處理傳入的請求。增加此值可以在高併發工作負載下提升效能,但也可能消耗更多 CPU 資源。",
+	"Set Voice": "設定語音",
+	"Set whisper model": "設定 whisper 模型",
+	"Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)": "設定模型向後查看以防止重複的距離。(預設:64,0 = 停用,-1 = num_ctx)",
+	"Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)": "設定懲罰重複的強度。較高的值(例如 1.5)會更強烈懲罰重複,而較低的值(例如 0.9)會更寬容。(預設:1.1)",
+	"Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: random)": "設定用於生成的隨機數種子。將其設定為特定數字會使模型對相同的提示詞產生相同的文字。(預設:隨機)",
+	"Sets the size of the context window used to generate the next token. (Default: 2048)": "設定用於生成下一個 token 的上下文視窗大小。(預設:2048)",
+	"Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.": "設定要使用的停止序列。當遇到此模式時,大型語言模型將停止生成文字並返回。可以在模型檔案中指定多個單獨的停止參數來設定多個停止模式。",
+	"Settings": "設定",
+	"Settings saved successfully!": "設定已成功儲存!",
+	"Share": "分享",
+	"Share Chat": "分享對話",
+	"Share to OpenWebUI Community": "分享到 OpenWebUI 社群",
+	"Show": "顯示",
+	"Show \"What's New\" modal on login": "登入時顯示「新功能」對話框",
+	"Show Admin Details in Account Pending Overlay": "在帳號待審覆蓋層中顯示管理員詳細資訊",
+	"Show shortcuts": "顯示快捷鍵",
+	"Show your support!": "表達您的支持!",
+	"Showcased creativity": "展現創意",
+	"Sign in": "登入",
+	"Sign in to {{WEBUI_NAME}}": "登入 {{WEBUI_NAME}}",
+	"Sign in to {{WEBUI_NAME}} with LDAP": "以 LDAP 登入 {{WEBUI_NAME}}",
+	"Sign Out": "登出",
+	"Sign up": "註冊",
+	"Sign up to {{WEBUI_NAME}}": "註冊 {{WEBUI_NAME}}",
+	"Signing in to {{WEBUI_NAME}}": "正在登入 {{WEBUI_NAME}}",
+	"Source": "來源",
+	"Speech Playback Speed": "語音播放速度",
+	"Speech recognition error: {{error}}": "語音辨識錯誤:{{error}}",
+	"Speech-to-Text Engine": "語音轉文字 (STT) 引擎",
+	"Stop": "停止",
+	"Stop Sequence": "停止序列",
+	"Stream Chat Response": "流式對話回應",
+	"STT Model": "語音轉文字 (STT) 模型",
+	"STT Settings": "語音轉文字 (STT) 設定",
+	"Subtitle (e.g. about the Roman Empire)": "副標題(例如:關於羅馬帝國)",
+	"Success": "成功",
+	"Successfully updated.": "更新成功。",
+	"Suggested": "建議",
+	"Support": "支援",
+	"Support this plugin:": "支持這個外掛:",
+	"Sync directory": "同步目錄",
+	"System": "系統",
+	"System Instructions": "系統指令",
+	"System Prompt": "系統提示詞",
+	"Tags Generation": "標籤生成",
+	"Tags Generation Prompt": "標籤生成提示詞",
+	"Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)": "使用無尾採樣來減少較不可能的 token 對輸出的影響。較高的值(例如 2.0)會減少更多影響,而值為 1.0 則停用此設定。(預設:1)",
+	"Tap to interrupt": "點選以中斷",
+	"Tavily API Key": "Tavily API 金鑰",
+	"Tell us more:": "告訴我們更多:",
+	"Temperature": "溫度",
+	"Template": "範本",
+	"Temporary Chat": "臨時對話",
+	"Text Splitter": "文字分割器",
+	"Text-to-Speech Engine": "文字轉語音引擎",
+	"Tfs Z": "Tfs Z",
+	"Thanks for your feedback!": "感謝您的回饋!",
+	"The Application Account DN you bind with for search": "您綁定用於搜尋的應用程式帳號 DN",
+	"The base to search for users": "搜尋使用者的基礎",
+	"The batch size determines how many text requests are processed together at once. A higher batch size can increase the performance and speed of the model, but it also requires more memory.  (Default: 512)": "批次大小決定一次處理多少文字請求。較高的批次大小可以提高模型的效能和速度,但也需要更多記憶體。(預設:512)",
+	"The developers behind this plugin are passionate volunteers from the community. If you find this plugin helpful, please consider contributing to its development.": "這個外掛背後的開發者是來自社群的熱情志願者。如果您覺得這個外掛很有幫助,請考慮為其開發做出貢獻。",
+	"The evaluation leaderboard is based on the Elo rating system and is updated in real-time.": "評估排行榜基於 Elo 評分系統,並即時更新。",
+	"The LDAP attribute that maps to the username that users use to sign in.": "映射到使用者用於登入的使用者名稱的 LDAP 屬性。",
+	"The leaderboard is currently in beta, and we may adjust the rating calculations as we refine the algorithm.": "排行榜目前處於測試階段,我們可能會在改進演算法時調整評分計算方式。",
+	"The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.": "檔案大小上限(MB)。如果檔案大小超過此限制,檔案將不會被上傳。",
+	"The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.": "對話中一次可使用的最大檔案數量。如果檔案數量超過此限制,檔案將不會被上傳。",
+	"The score should be a value between 0.0 (0%) and 1.0 (100%).": "分數應該是介於 0.0(0%)和 1.0(100%)之間的值。",
+	"The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)": "模型的溫度。提高溫度會使模型回答更具創意。(預設:0.8)",
+	"Theme": "主題",
+	"Thinking...": "正在思考...",
+	"This action cannot be undone. Do you wish to continue?": "此操作無法復原。您確定要繼續進行嗎?",
+	"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "這確保您寶貴的對話會安全地儲存到您的後端資料庫。謝謝!",
+	"This is an experimental feature, it may not function as expected and is subject to change at any time.": "這是一個實驗性功能,它可能無法如預期運作,並且可能會隨時變更。",
+	"This option controls how many tokens are preserved when refreshing the context. For example, if set to 2, the last 2 tokens of the conversation context will be retained. Preserving context can help maintain the continuity of a conversation, but it may reduce the ability to respond to new topics. (Default: 24)": "此選項控制重新整理上下文時保留多少 token。例如,如果設定為 2,則會保留對話上下文的最後 2 個 token。保留上下文可以幫助維持對話的連續性,但可能會降低回應新主題的能力。(預設:24)",
+	"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.  (Default: 128)": "此選項設定模型可以在其回應中生成的最大 token 數量。增加此限制允許模型提供更長的答案,但也可能增加產生無用或不相關內容的可能性。(預設:128)",
+	"This option will delete all existing files in the collection and replace them with newly uploaded files.": "此選項將刪除集合中的所有現有檔案,並用新上傳的檔案取代它們。",
+	"This response was generated by \"{{model}}\"": "此回應由「{{model}}」生成",
+	"This will delete": "這將會刪除",
+	"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "這將會刪除 <strong>{{NAME}}</strong> 和<strong>其所有內容</strong>。",
+	"This will delete all models including custom models": "這將刪除所有模型,包括自訂模型",
+	"This will delete all models including custom models and cannot be undone.": "這將刪除所有模型,包括自訂模型,且無法復原。",
+	"This will reset the knowledge base and sync all files. Do you wish to continue?": "這將重設知識庫並同步所有檔案。您確定要繼續嗎?",
+	"Thorough explanation": "詳細解釋",
+	"Tika": "Tika",
+	"Tika Server URL required.": "需要 Tika 伺服器 URL。",
+	"Tiktoken": "Tiktoken",
+	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替換後按下對話輸入框中的 Tab 鍵,即可連續更新多個變數欄位。",
+	"Title": "標題",
+	"Title (e.g. Tell me a fun fact)": "標題(例如:告訴我一個有趣的事實)",
+	"Title Auto-Generation": "自動產生標題",
+	"Title cannot be an empty string.": "標題不能是空字串。",
+	"Title Generation Prompt": "自動產生標題的提示詞",
+	"TLS": "TLS",
+	"To access the available model names for downloading,": "若要存取可供下載的模型名稱,",
+	"To access the GGUF models available for downloading,": "若要存取可供下載的 GGUF 模型,",
+	"To access the WebUI, please reach out to the administrator. Admins can manage user statuses from the Admin Panel.": "若要存取 WebUI,請聯絡管理員。管理員可以從管理面板管理使用者狀態。",
+	"To attach knowledge base here, add them to the \"Knowledge\" workspace first.": "要在此處附加知識庫,請先將它們新增到「知識」工作區。",
+	"To protect your privacy, only ratings, model IDs, tags, and metadata are shared from your feedback—your chat logs remain private and are not included.": "為了保護您的隱私,只會分享您回饋中的評分、模型 ID、標籤和中繼資料 —— 您的對話紀錄仍然是私密的,不會被包含在內。",
+	"To select actions here, add them to the \"Functions\" workspace first.": "若要在此選擇動作,請先將它們新增到「函式」工作區。",
+	"To select filters here, add them to the \"Functions\" workspace first.": "若要在此選擇篩選器,請先將它們新增到「函式」工作區。",
+	"To select toolkits here, add them to the \"Tools\" workspace first.": "若要在此選擇工具包,請先將它們新增到「工具」工作區。",
+	"Toast notifications for new updates": "快顯通知新的更新",
+	"Today": "今天",
+	"Toggle settings": "切換設定",
+	"Toggle sidebar": "切換側邊欄",
+	"Token": "Token",
+	"Tokens To Keep On Context Refresh (num_keep)": "上下文重新整理時要保留的 token 數 (num_keep)",
+	"Too verbose": "太過冗長",
+	"Tool created successfully": "成功建立工具",
+	"Tool deleted successfully": "成功刪除工具",
+	"Tool Description": "工具描述",
+	"Tool ID": "工具 ID",
+	"Tool imported successfully": "成功匯入工具",
+	"Tool Name": "工具名稱",
+	"Tool updated successfully": "成功更新工具",
+	"Tools": "工具",
+	"Tools Access": "工具存取",
+	"Tools are a function calling system with arbitrary code execution": "工具是一個具有任意程式碼執行功能的函式呼叫系統",
+	"Tools have a function calling system that allows arbitrary code execution": "工具具有允許執行任意程式碼的函式呼叫系統",
+	"Tools have a function calling system that allows arbitrary code execution.": "工具具有允許執行任意程式碼的函式呼叫系統。",
+	"Top K": "Top K",
+	"Top P": "Top P",
+	"Transformers": "Transformers",
+	"Trouble accessing Ollama?": "存取 Ollama 時遇到問題?",
+	"TTS Model": "文字轉語音 (TTS) 模型",
+	"TTS Settings": "文字轉語音 (TTS) 設定",
+	"TTS Voice": "文字轉語音 (TTS) 聲音",
+	"Type": "類型",
+	"Type Hugging Face Resolve (Download) URL": "輸入 Hugging Face 的解析(下載)URL",
+	"Uh-oh! There was an issue connecting to {{provider}}.": "哎呀!連線到 {{provider}} 時出現問題。",
+	"UI": "使用者介面",
+	"Unarchive All": "解除封存全部",
+	"Unarchive All Archived Chats": "解除封存全部已封存對話",
+	"Unarchive Chat": "解除封存對話",
+	"Unlock mysteries": "解鎖謎題",
+	"Unpin": "取消釘選",
+	"Unravel secrets": "揭開秘密",
+	"Untagged": "取消標簽的",
+	"Update": "更新",
+	"Update and Copy Link": "更新並複製連結",
+	"Update for the latest features and improvements.": "更新以獲得最新功能和改進。",
+	"Update password": "更新密碼",
+	"Updated": "已更新",
+	"Updated at": "更新於",
+	"Updated At": "更新於",
+	"Upload": "上傳",
+	"Upload a GGUF model": "上傳 GGUF 模型",
+	"Upload directory": "上傳目錄",
+	"Upload files": "上傳檔案",
+	"Upload Files": "上傳檔案",
+	"Upload Pipeline": "上傳管線",
+	"Upload Progress": "上傳進度",
+	"URL": "URL",
+	"URL Mode": "URL 模式",
+	"Use '#' in the prompt input to load and include your knowledge.": "在提示詞輸入中使用 '#' 來載入並包含您的知識。",
+	"Use Gravatar": "使用 Gravatar",
+	"Use groups to group your users and assign permissions.": "使用群組來組織您的使用者並分配權限。",
+	"Use Initials": "使用姓名縮寫",
+	"use_mlock (Ollama)": "使用 mlock (Ollama)",
+	"use_mmap (Ollama)": "使用 mmap (Ollama)",
+	"user": "使用者",
+	"User": "使用者",
+	"User location successfully retrieved.": "成功取得使用者位置。",
+	"Username": "使用者名稱",
+	"Users": "使用者",
+	"Using the default arena model with all models. Click the plus button to add custom models.": "正在使用預設競技模型與所有模型。點選加號按鈕以新增自訂模型。",
+	"Utilize": "使用",
+	"Valid time units:": "有效的時間單位:",
+	"Valves": "閥門",
+	"Valves updated": "閥門已更新",
+	"Valves updated successfully": "閥門更新成功",
+	"variable": "變數",
+	"variable to have them replaced with clipboard content.": "變數,以便將其替換為剪貼簿內容。",
+	"Version": "版本",
+	"Version {{selectedVersion}} of {{totalVersions}}": "第 {{selectedVersion}} 版,共 {{totalVersions}} 版",
+	"Visibility": "可見性",
+	"Voice": "語音",
+	"Voice Input": "語音輸入",
+	"Warning": "警告",
+	"Warning:": "警告:",
+	"Warning: Enabling this will allow users to upload arbitrary code on the server.": "警告:啟用此功能將允許使用者在伺服器上上傳任意程式碼。",
+	"Warning: If you update or change your embedding model, you will need to re-import all documents.": "警告:如果您更新或更改嵌入模型,您將需要重新匯入所有文件。",
+	"Web": "網頁",
+	"Web API": "網頁 API",
+	"Web Loader Settings": "網頁載入器設定",
+	"Web Search": "網頁搜尋",
+	"Web Search Engine": "網頁搜尋引擎",
+	"Web Search Query Generation": "網頁搜尋查詢生成",
+	"Webhook URL": "Webhook URL",
+	"WebUI Settings": "WebUI 設定",
+	"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI 將向 \"{{url}}/api/chat\" 發送請求",
+	"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI 將向 \"{{url}}/chat/completions\" 發送請求",
+	"What are you trying to achieve?": "您正在試圖完成什麽?",
+	"What are you working on?": "您正在工作什麽?",
+	"What’s New in": "新功能",
+	"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "啟用時,模型將即時回應每個對話訊息,在使用者傳送訊息後立即生成回應。此模式適用於即時對話應用程式,但在較慢的硬體上可能會影響效能。",
+	"wherever you are": "無論您在何處",
+	"Whisper (Local)": "Whisper(本機)",
+	"Why?": "為什麼?",
+	"Widescreen Mode": "寬螢幕模式",
+	"Won": "獲勝",
+	"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)": "與 top-k 一起運作。較高的值(例如 0.95)會產生更多樣化的文字,而較低的值(例如 0.5)會產生更專注和保守的文字。(預設:0.9)",
+	"Workspace": "工作區",
+	"Workspace Permissions": "工作區權限",
+	"Write a prompt suggestion (e.g. Who are you?)": "撰寫提示詞建議(例如:你是誰?)",
+	"Write a summary in 50 words that summarizes [topic or keyword].": "用 50 字寫一篇總結 [主題或關鍵字] 的摘要。",
+	"Write something...": "寫一些什麽...",
+	"Write your model template content here": "在此撰寫您的模型範本内容",
+	"Yesterday": "昨天",
+	"You": "您",
+	"You can only chat with a maximum of {{maxCount}} file(s) at a time.": "您一次最多只能與 {{maxCount}} 個檔案進行對話。",
+	"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you.": "您可以透過下方的「管理」按鈕新增記憶,將您與大型語言模型的互動個人化,讓它們更有幫助並更符合您的需求。",
+	"You cannot upload an empty file.": "您無法上傳空檔案",
+	"You do not have permission to upload files.": "您沒有權限上傳檔案",
+	"You have no archived conversations.": "您沒有已封存的對話。",
+	"You have shared this chat": "您已分享此對話",
+	"You're a helpful assistant.": "您是一位樂於助人的助手。",
+	"You're now logged in.": "您已登入。",
+	"Your account status is currently pending activation.": "您的帳號目前正在等待啟用。",
+	"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "您的所有貢獻將會直接交給外掛開發者;Open WebUI 不會收取任何百分比。然而,所選擇的贊助平臺可能有其自身的費用。",
+	"Youtube": "YouTube",
+	"Youtube Loader Settings": "YouTube 載入器設定"
+}
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..856f2b6c38aec1085db88189bcf492dbb49a1c45
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e3976bf906e05a0c4b0831a46142d440d4eb352
--- /dev/null
+++ b/src/lib/stores/index.ts
@@ -0,0 +1,202 @@
+import { APP_NAME } from '$lib/constants';
+import { type Writable, writable } from 'svelte/store';
+import type { GlobalModelConfig, ModelConfig } from '$lib/apis';
+import type { Banner } from '$lib/types';
+import type { Socket } from 'socket.io-client';
+
+// Backend
+export const WEBUI_NAME = writable(APP_NAME);
+export const config: Writable<Config | undefined> = writable(undefined);
+export const user: Writable<SessionUser | undefined> = writable(undefined);
+
+// Frontend
+export const MODEL_DOWNLOAD_POOL = writable({});
+
+export const mobile = writable(false);
+
+export const socket: Writable<null | Socket> = writable(null);
+export const activeUserCount: Writable<null | number> = writable(null);
+export const USAGE_POOL: Writable<null | string[]> = writable(null);
+
+export const theme = writable('system');
+
+export const chatId = writable('');
+export const chatTitle = writable('');
+
+export const chats = writable([]);
+export const pinnedChats = writable([]);
+export const tags = writable([]);
+
+export const models: Writable<Model[]> = writable([]);
+
+export const prompts: Writable<null | Prompt[]> = writable(null);
+export const knowledge: Writable<null | Document[]> = writable(null);
+export const tools = writable(null);
+export const functions = writable(null);
+
+export const banners: Writable<Banner[]> = writable([]);
+
+export const settings: Writable<Settings> = writable({});
+
+export const showSidebar = writable(false);
+export const showSettings = writable(false);
+export const showArchivedChats = writable(false);
+export const showChangelog = writable(false);
+
+export const showControls = writable(false);
+export const showOverview = writable(false);
+export const showArtifacts = writable(false);
+export const showCallOverlay = writable(false);
+
+export const temporaryChatEnabled = writable(false);
+export const scrollPaginationEnabled = writable(false);
+export const currentChatPage = writable(1);
+
+export type Model = OpenAIModel | OllamaModel;
+
+type BaseModel = {
+	id: string;
+	name: string;
+	info?: ModelConfig;
+	owned_by: 'ollama' | 'openai' | 'arena';
+};
+
+export interface OpenAIModel extends BaseModel {
+	owned_by: 'openai';
+	external: boolean;
+	source?: string;
+}
+
+export interface OllamaModel extends BaseModel {
+	owned_by: 'ollama';
+	details: OllamaModelDetails;
+	size: number;
+	description: string;
+	model: string;
+	modified_at: string;
+	digest: string;
+	ollama?: {
+		name?: string;
+		model?: string;
+		modified_at: string;
+		size?: number;
+		digest?: string;
+		details?: {
+			parent_model?: string;
+			format?: string;
+			family?: string;
+			families?: string[];
+			parameter_size?: string;
+			quantization_level?: string;
+		};
+		urls?: number[];
+	};
+}
+
+type OllamaModelDetails = {
+	parent_model: string;
+	format: string;
+	family: string;
+	families: string[] | null;
+	parameter_size: string;
+	quantization_level: string;
+};
+
+type Settings = {
+	models?: string[];
+	conversationMode?: boolean;
+	speechAutoSend?: boolean;
+	responseAutoPlayback?: boolean;
+	audio?: AudioSettings;
+	showUsername?: boolean;
+	notificationEnabled?: boolean;
+	title?: TitleSettings;
+	splitLargeDeltas?: boolean;
+	chatDirection: 'LTR' | 'RTL';
+
+	system?: string;
+	requestFormat?: string;
+	keepAlive?: string;
+	seed?: number;
+	temperature?: string;
+	repeat_penalty?: string;
+	top_k?: string;
+	top_p?: string;
+	num_ctx?: string;
+	num_batch?: string;
+	num_keep?: string;
+	options?: ModelOptions;
+};
+
+type ModelOptions = {
+	stop?: boolean;
+};
+
+type AudioSettings = {
+	STTEngine?: string;
+	TTSEngine?: string;
+	speaker?: string;
+	model?: string;
+	nonLocalVoices?: boolean;
+};
+
+type TitleSettings = {
+	auto?: boolean;
+	model?: string;
+	modelExternal?: string;
+	prompt?: string;
+};
+
+type Prompt = {
+	command: string;
+	user_id: string;
+	title: string;
+	content: string;
+	timestamp: number;
+};
+
+type Document = {
+	collection_name: string;
+	filename: string;
+	name: string;
+	title: string;
+};
+
+type Config = {
+	status: boolean;
+	name: string;
+	version: string;
+	default_locale: string;
+	default_models: string;
+	default_prompt_suggestions: PromptSuggestion[];
+	features: {
+		auth: boolean;
+		auth_trusted_header: boolean;
+		enable_api_key: boolean;
+		enable_signup: boolean;
+		enable_login_form: boolean;
+		enable_web_search?: boolean;
+		enable_image_generation: boolean;
+		enable_admin_export: boolean;
+		enable_admin_chat_access: boolean;
+		enable_community_sharing: boolean;
+	};
+	oauth: {
+		providers: {
+			[key: string]: string;
+		};
+	};
+};
+
+type PromptSuggestion = {
+	content: string;
+	title: [string, string];
+};
+
+type SessionUser = {
+	id: string;
+	email: string;
+	name: string;
+	role: string;
+	profile_image_url: string;
+};
diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..475ead8fd4e384b30ef85766cf5223bcae6b1e82
--- /dev/null
+++ b/src/lib/types/index.ts
@@ -0,0 +1,15 @@
+export type Banner = {
+	id: string;
+	type: string;
+	title?: string;
+	content: string;
+	url?: string;
+	dismissible?: boolean;
+	timestamp: number;
+};
+
+export enum TTS_RESPONSE_SPLIT {
+	PUNCTUATION = 'punctuation',
+	PARAGRAPHS = 'paragraphs',
+	NONE = 'none'
+}
diff --git a/src/lib/utils/_template_old.ts b/src/lib/utils/_template_old.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f69ef226011117f94de622616dcf85596731dc44
--- /dev/null
+++ b/src/lib/utils/_template_old.ts
@@ -0,0 +1,66 @@
+import { titleGenerationTemplate } from '$lib/utils/index';
+import { expect, test } from 'vitest';
+
+test('titleGenerationTemplate correctly replaces {{prompt}} placeholder', () => {
+	const template = 'Hello {{prompt}}!';
+	const prompt = 'world';
+	const expected = 'Hello world!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate correctly replaces {{prompt:start:<length>}} placeholder', () => {
+	const template = 'Hello {{prompt:start:3}}!';
+	const prompt = 'world';
+	const expected = 'Hello wor!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate correctly replaces {{prompt:end:<length>}} placeholder', () => {
+	const template = 'Hello {{prompt:end:3}}!';
+	const prompt = 'world';
+	const expected = 'Hello rld!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate correctly replaces {{prompt:middletruncate:<length>}} placeholder when prompt length is greater than length', () => {
+	const template = 'Hello {{prompt:middletruncate:4}}!';
+	const prompt = 'world';
+	const expected = 'Hello wo...ld!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate correctly replaces {{prompt:middletruncate:<length>}} placeholder when prompt length is less than or equal to length', () => {
+	const template = 'Hello {{prompt:middletruncate:5}}!';
+	const prompt = 'world';
+	const expected = 'Hello world!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate returns original template when no placeholders are present', () => {
+	const template = 'Hello world!';
+	const prompt = 'world';
+	const expected = 'Hello world!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate does not replace placeholders inside of replaced placeholders', () => {
+	const template = 'Hello {{prompt}}!';
+	const prompt = 'World, {{prompt}} injection';
+	const expected = 'Hello World, {{prompt}} injection!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
+
+test('titleGenerationTemplate correctly replaces multiple placeholders', () => {
+	const template = 'Hello {{prompt}}! This is {{prompt:start:3}}!';
+	const prompt = 'world';
+	const expected = 'Hello world! This is wor!';
+	const actual = titleGenerationTemplate(template, prompt);
+	expect(actual).toBe(expected);
+});
diff --git a/src/lib/utils/characters/index.ts b/src/lib/utils/characters/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af3436693a69aa978c4127262894fc5059aeeebd
--- /dev/null
+++ b/src/lib/utils/characters/index.ts
@@ -0,0 +1,197 @@
+import CRC32 from 'crc-32';
+
+export const parseFile = async (file) => {
+	if (file.type === 'application/json') {
+		return await parseJsonFile(file);
+	} else if (file.type === 'image/png') {
+		return await parsePngFile(file);
+	} else {
+		throw new Error('Unsupported file type');
+	}
+};
+
+const parseJsonFile = async (file) => {
+	const text = await file.text();
+	const json = JSON.parse(text);
+
+	const character = extractCharacter(json);
+
+	return {
+		file,
+		json,
+		formats: detectFormats(json),
+		character
+	};
+};
+
+const parsePngFile = async (file) => {
+	const arrayBuffer = await file.arrayBuffer();
+	const text = parsePngText(arrayBuffer);
+	const json = JSON.parse(text);
+
+	const image = URL.createObjectURL(file);
+	const character = extractCharacter(json);
+
+	return {
+		file,
+		json,
+		image,
+		formats: detectFormats(json),
+		character
+	};
+};
+
+const parsePngText = (arrayBuffer) => {
+	const textChunkKeyword = 'chara';
+	const chunks = readPngChunks(new Uint8Array(arrayBuffer));
+
+	const textChunk = chunks
+		.filter((chunk) => chunk.type === 'tEXt')
+		.map((chunk) => decodeTextChunk(chunk.data))
+		.find((entry) => entry.keyword === textChunkKeyword);
+
+	if (!textChunk) {
+		throw new Error(`No PNG text chunk named "${textChunkKeyword}" found`);
+	}
+
+	try {
+		return new TextDecoder().decode(Uint8Array.from(atob(textChunk.text), (c) => c.charCodeAt(0)));
+	} catch (e) {
+		throw new Error('Unable to parse "chara" field as base64', e);
+	}
+};
+
+const readPngChunks = (data) => {
+	const isValidPng =
+		data[0] === 0x89 &&
+		data[1] === 0x50 &&
+		data[2] === 0x4e &&
+		data[3] === 0x47 &&
+		data[4] === 0x0d &&
+		data[5] === 0x0a &&
+		data[6] === 0x1a &&
+		data[7] === 0x0a;
+
+	if (!isValidPng) throw new Error('Invalid PNG file');
+
+	let chunks = [];
+	let offset = 8; // Skip PNG signature
+
+	while (offset < data.length) {
+		let length =
+			(data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3];
+		let type = String.fromCharCode.apply(null, data.slice(offset + 4, offset + 8));
+		let chunkData = data.slice(offset + 8, offset + 8 + length);
+		let crc =
+			(data[offset + 8 + length] << 24) |
+			(data[offset + 8 + length + 1] << 16) |
+			(data[offset + 8 + length + 2] << 8) |
+			data[offset + 8 + length + 3];
+
+		if (CRC32.buf(chunkData, CRC32.str(type)) !== crc) {
+			throw new Error(`Invalid CRC for chunk type "${type}"`);
+		}
+
+		chunks.push({ type, data: chunkData, crc });
+		offset += 12 + length;
+	}
+
+	return chunks;
+};
+
+const decodeTextChunk = (data) => {
+	let i = 0;
+	const keyword = [];
+	const text = [];
+
+	for (; i < data.length && data[i] !== 0; i++) {
+		keyword.push(String.fromCharCode(data[i]));
+	}
+
+	for (i++; i < data.length; i++) {
+		text.push(String.fromCharCode(data[i]));
+	}
+
+	return { keyword: keyword.join(''), text: text.join('') };
+};
+
+const extractCharacter = (json) => {
+	function getTrimmedValue(json, keys) {
+		return keys
+			.map((key) => {
+				const keyParts = key.split('.');
+				let value = json;
+				for (const part of keyParts) {
+					if (value && value[part] != null) {
+						value = value[part];
+					} else {
+						value = null;
+						break;
+					}
+				}
+				return value && value.trim();
+			})
+			.find((value) => value);
+	}
+
+	const name = getTrimmedValue(json, ['char_name', 'name', 'data.name']);
+	const summary = getTrimmedValue(json, ['personality', 'title', 'data.description']);
+	const personality = getTrimmedValue(json, ['char_persona', 'description', 'data.personality']);
+	const scenario = getTrimmedValue(json, ['world_scenario', 'scenario', 'data.scenario']);
+	const greeting = getTrimmedValue(json, [
+		'char_greeting',
+		'greeting',
+		'first_mes',
+		'data.first_mes'
+	]);
+	const examples = getTrimmedValue(json, [
+		'example_dialogue',
+		'mes_example',
+		'definition',
+		'data.mes_example'
+	]);
+
+	return { name, summary, personality, scenario, greeting, examples };
+};
+
+const detectFormats = (json) => {
+	const formats = [];
+
+	if (
+		json.char_name &&
+		json.char_persona &&
+		json.world_scenario &&
+		json.char_greeting &&
+		json.example_dialogue
+	)
+		formats.push('Text Generation Character');
+	if (
+		json.name &&
+		json.personality &&
+		json.description &&
+		json.scenario &&
+		json.first_mes &&
+		json.mes_example
+	)
+		formats.push('TavernAI Character');
+	if (
+		json.character &&
+		json.character.name &&
+		json.character.title &&
+		json.character.description &&
+		json.character.greeting &&
+		json.character.definition
+	)
+		formats.push('CharacterAI Character');
+	if (
+		json.info &&
+		json.info.character &&
+		json.info.character.name &&
+		json.info.character.title &&
+		json.info.character.description &&
+		json.info.character.greeting
+	)
+		formats.push('CharacterAI History');
+
+	return formats;
+};
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..784eef43445c83f348e22948d991f95519d390c5
--- /dev/null
+++ b/src/lib/utils/index.ts
@@ -0,0 +1,928 @@
+import { v4 as uuidv4 } from 'uuid';
+import sha256 from 'js-sha256';
+
+import { WEBUI_BASE_URL } from '$lib/constants';
+import { TTS_RESPONSE_SPLIT } from '$lib/types';
+
+//////////////////////////
+// Helper functions
+//////////////////////////
+
+function escapeRegExp(string: string): string {
+	return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+}
+
+export const replaceTokens = (content, sourceIds, char, user) => {
+	const charToken = /{{char}}/gi;
+	const userToken = /{{user}}/gi;
+	const videoIdToken = /{{VIDEO_FILE_ID_([a-f0-9-]+)}}/gi; // Regex to capture the video ID
+	const htmlIdToken = /{{HTML_FILE_ID_([a-f0-9-]+)}}/gi; // Regex to capture the HTML ID
+
+	// Replace {{char}} if char is provided
+	if (char !== undefined && char !== null) {
+		content = content.replace(charToken, char);
+	}
+
+	// Replace {{user}} if user is provided
+	if (user !== undefined && user !== null) {
+		content = content.replace(userToken, user);
+	}
+
+	// Replace video ID tags with corresponding <video> elements
+	content = content.replace(videoIdToken, (match, fileId) => {
+		const videoUrl = `${WEBUI_BASE_URL}/api/v1/files/${fileId}/content`;
+		return `<video src="${videoUrl}" controls></video>`;
+	});
+
+	// Replace HTML ID tags with corresponding HTML content
+	content = content.replace(htmlIdToken, (match, fileId) => {
+		const htmlUrl = `${WEBUI_BASE_URL}/api/v1/files/${fileId}/content/html`;
+		return `<iframe src="${htmlUrl}" width="100%" frameborder="0" onload="this.style.height=(this.contentWindow.document.body.scrollHeight+20)+'px';"></iframe>`;
+	});
+
+	// Remove sourceIds from the content and replace them with <source_id>...</source_id>
+	if (Array.isArray(sourceIds)) {
+		sourceIds.forEach((sourceId) => {
+			// Escape special characters in the sourceId
+			const escapedSourceId = escapeRegExp(sourceId);
+
+			// Create a token based on the exact `[sourceId]` string
+			const sourceToken = `\\[${escapedSourceId}\\]`; // Escape special characters for RegExp
+			const sourceRegex = new RegExp(sourceToken, 'g'); // Match all occurrences of [sourceId]
+
+			content = content.replace(sourceRegex, `<source_id data="${sourceId}" />`);
+		});
+	}
+
+	return content;
+};
+
+export const sanitizeResponseContent = (content: string) => {
+	return content
+		.replace(/<\|[a-z]*$/, '')
+		.replace(/<\|[a-z]+\|$/, '')
+		.replace(/<$/, '')
+		.replaceAll(/<\|[a-z]+\|>/g, ' ')
+		.replaceAll('<', '&lt;')
+		.replaceAll('>', '&gt;')
+		.trim();
+};
+
+export const processResponseContent = (content: string) => {
+	return content.trim();
+};
+
+export const revertSanitizedResponseContent = (content: string) => {
+	return content.replaceAll('&lt;', '<').replaceAll('&gt;', '>');
+};
+
+export function unescapeHtml(html: string) {
+	const doc = new DOMParser().parseFromString(html, 'text/html');
+	return doc.documentElement.textContent;
+}
+
+export const capitalizeFirstLetter = (string) => {
+	return string.charAt(0).toUpperCase() + string.slice(1);
+};
+
+export const splitStream = (splitOn) => {
+	let buffer = '';
+	return new TransformStream({
+		transform(chunk, controller) {
+			buffer += chunk;
+			const parts = buffer.split(splitOn);
+			parts.slice(0, -1).forEach((part) => controller.enqueue(part));
+			buffer = parts[parts.length - 1];
+		},
+		flush(controller) {
+			if (buffer) controller.enqueue(buffer);
+		}
+	});
+};
+
+export const convertMessagesToHistory = (messages) => {
+	const history = {
+		messages: {},
+		currentId: null
+	};
+
+	let parentMessageId = null;
+	let messageId = null;
+
+	for (const message of messages) {
+		messageId = uuidv4();
+
+		if (parentMessageId !== null) {
+			history.messages[parentMessageId].childrenIds = [
+				...history.messages[parentMessageId].childrenIds,
+				messageId
+			];
+		}
+
+		history.messages[messageId] = {
+			...message,
+			id: messageId,
+			parentId: parentMessageId,
+			childrenIds: []
+		};
+
+		parentMessageId = messageId;
+	}
+
+	history.currentId = messageId;
+	return history;
+};
+
+export const getGravatarURL = (email) => {
+	// Trim leading and trailing whitespace from
+	// an email address and force all characters
+	// to lower case
+	const address = String(email).trim().toLowerCase();
+
+	// Create a SHA256 hash of the final string
+	const hash = sha256(address);
+
+	// Grab the actual image URL
+	return `https://www.gravatar.com/avatar/${hash}`;
+};
+
+export const canvasPixelTest = () => {
+	// Test a 1x1 pixel to potentially identify browser/plugin fingerprint blocking or spoofing
+	// Inspiration: https://github.com/kkapsner/CanvasBlocker/blob/master/test/detectionTest.js
+	const canvas = document.createElement('canvas');
+	const ctx = canvas.getContext('2d');
+	canvas.height = 1;
+	canvas.width = 1;
+	const imageData = new ImageData(canvas.width, canvas.height);
+	const pixelValues = imageData.data;
+
+	// Generate RGB test data
+	for (let i = 0; i < imageData.data.length; i += 1) {
+		if (i % 4 !== 3) {
+			pixelValues[i] = Math.floor(256 * Math.random());
+		} else {
+			pixelValues[i] = 255;
+		}
+	}
+
+	ctx.putImageData(imageData, 0, 0);
+	const p = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+
+	// Read RGB data and fail if unmatched
+	for (let i = 0; i < p.length; i += 1) {
+		if (p[i] !== pixelValues[i]) {
+			console.log(
+				'canvasPixelTest: Wrong canvas pixel RGB value detected:',
+				p[i],
+				'at:',
+				i,
+				'expected:',
+				pixelValues[i]
+			);
+			console.log('canvasPixelTest: Canvas blocking or spoofing is likely');
+			return false;
+		}
+	}
+
+	return true;
+};
+
+export const generateInitialsImage = (name) => {
+	const canvas = document.createElement('canvas');
+	const ctx = canvas.getContext('2d');
+	canvas.width = 100;
+	canvas.height = 100;
+
+	if (!canvasPixelTest()) {
+		console.log(
+			'generateInitialsImage: failed pixel test, fingerprint evasion is likely. Using default image.'
+		);
+		return '/user.png';
+	}
+
+	ctx.fillStyle = '#F39C12';
+	ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+	ctx.fillStyle = '#FFFFFF';
+	ctx.font = '40px Helvetica';
+	ctx.textAlign = 'center';
+	ctx.textBaseline = 'middle';
+
+	const sanitizedName = name.trim();
+	const initials =
+		sanitizedName.length > 0
+			? sanitizedName[0] +
+				(sanitizedName.split(' ').length > 1
+					? sanitizedName[sanitizedName.lastIndexOf(' ') + 1]
+					: '')
+			: '';
+
+	ctx.fillText(initials.toUpperCase(), canvas.width / 2, canvas.height / 2);
+
+	return canvas.toDataURL();
+};
+
+export const copyToClipboard = async (text) => {
+	let result = false;
+	if (!navigator.clipboard) {
+		const textArea = document.createElement('textarea');
+		textArea.value = text;
+
+		// Avoid scrolling to bottom
+		textArea.style.top = '0';
+		textArea.style.left = '0';
+		textArea.style.position = 'fixed';
+
+		document.body.appendChild(textArea);
+		textArea.focus();
+		textArea.select();
+
+		try {
+			const successful = document.execCommand('copy');
+			const msg = successful ? 'successful' : 'unsuccessful';
+			console.log('Fallback: Copying text command was ' + msg);
+			result = true;
+		} catch (err) {
+			console.error('Fallback: Oops, unable to copy', err);
+		}
+
+		document.body.removeChild(textArea);
+		return result;
+	}
+
+	result = await navigator.clipboard
+		.writeText(text)
+		.then(() => {
+			console.log('Async: Copying to clipboard was successful!');
+			return true;
+		})
+		.catch((error) => {
+			console.error('Async: Could not copy text: ', error);
+			return false;
+		});
+
+	return result;
+};
+
+export const compareVersion = (latest, current) => {
+	return current === '0.0.0'
+		? false
+		: current.localeCompare(latest, undefined, {
+				numeric: true,
+				sensitivity: 'case',
+				caseFirst: 'upper'
+			}) < 0;
+};
+
+export const findWordIndices = (text) => {
+	const regex = /\[([^\]]+)\]/g;
+	const matches = [];
+	let match;
+
+	while ((match = regex.exec(text)) !== null) {
+		matches.push({
+			word: match[1],
+			startIndex: match.index,
+			endIndex: regex.lastIndex - 1
+		});
+	}
+
+	return matches;
+};
+
+export const removeLastWordFromString = (inputString, wordString) => {
+	console.log('inputString', inputString);
+	// Split the string by newline characters to handle lines separately
+	const lines = inputString.split('\n');
+
+	// Take the last line to operate only on it
+	const lastLine = lines.pop();
+
+	// Split the last line into an array of words
+	const words = lastLine.split(' ');
+
+	// Conditional to check for the last word removal
+	if (words.at(-1) === wordString || (wordString === '' && words.at(-1) === '\\#')) {
+		words.pop(); // Remove last word if condition is satisfied
+	}
+
+	// Join the remaining words back into a string and handle space correctly
+	let updatedLastLine = words.join(' ');
+
+	// Add a trailing space to the updated last line if there are still words
+	if (updatedLastLine !== '') {
+		updatedLastLine += ' ';
+	}
+
+	// Combine the lines together again, placing the updated last line back in
+	const resultString = [...lines, updatedLastLine].join('\n');
+
+	// Return the final string
+	console.log('resultString', resultString);
+
+	return resultString;
+};
+
+export const removeFirstHashWord = (inputString) => {
+	// Split the string into an array of words
+	const words = inputString.split(' ');
+
+	// Find the index of the first word that starts with #
+	const index = words.findIndex((word) => word.startsWith('#'));
+
+	// Remove the first word with #
+	if (index !== -1) {
+		words.splice(index, 1);
+	}
+
+	// Join the remaining words back into a string
+	const resultString = words.join(' ');
+
+	return resultString;
+};
+
+export const transformFileName = (fileName) => {
+	// Convert to lowercase
+	const lowerCaseFileName = fileName.toLowerCase();
+
+	// Remove special characters using regular expression
+	const sanitizedFileName = lowerCaseFileName.replace(/[^\w\s]/g, '');
+
+	// Replace spaces with dashes
+	const finalFileName = sanitizedFileName.replace(/\s+/g, '-');
+
+	return finalFileName;
+};
+
+export const calculateSHA256 = async (file) => {
+	// Create a FileReader to read the file asynchronously
+	const reader = new FileReader();
+
+	// Define a promise to handle the file reading
+	const readFile = new Promise((resolve, reject) => {
+		reader.onload = () => resolve(reader.result);
+		reader.onerror = reject;
+	});
+
+	// Read the file as an ArrayBuffer
+	reader.readAsArrayBuffer(file);
+
+	try {
+		// Wait for the FileReader to finish reading the file
+		const buffer = await readFile;
+
+		// Convert the ArrayBuffer to a Uint8Array
+		const uint8Array = new Uint8Array(buffer);
+
+		// Calculate the SHA-256 hash using Web Crypto API
+		const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array);
+
+		// Convert the hash to a hexadecimal string
+		const hashArray = Array.from(new Uint8Array(hashBuffer));
+		const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('');
+
+		return `${hashHex}`;
+	} catch (error) {
+		console.error('Error calculating SHA-256 hash:', error);
+		throw error;
+	}
+};
+
+export const getImportOrigin = (_chats) => {
+	// Check what external service chat imports are from
+	if ('mapping' in _chats[0]) {
+		return 'openai';
+	}
+	return 'webui';
+};
+
+export const getUserPosition = async (raw = false) => {
+	// Get the user's location using the Geolocation API
+	const position = await new Promise((resolve, reject) => {
+		navigator.geolocation.getCurrentPosition(resolve, reject);
+	}).catch((error) => {
+		console.error('Error getting user location:', error);
+		throw error;
+	});
+
+	if (!position) {
+		return 'Location not available';
+	}
+
+	// Extract the latitude and longitude from the position
+	const { latitude, longitude } = position.coords;
+
+	if (raw) {
+		return { latitude, longitude };
+	} else {
+		return `${latitude.toFixed(3)}, ${longitude.toFixed(3)} (lat, long)`;
+	}
+};
+
+const convertOpenAIMessages = (convo) => {
+	// Parse OpenAI chat messages and create chat dictionary for creating new chats
+	const mapping = convo['mapping'];
+	const messages = [];
+	let currentId = '';
+	let lastId = null;
+
+	for (const message_id in mapping) {
+		const message = mapping[message_id];
+		currentId = message_id;
+		try {
+			if (
+				messages.length == 0 &&
+				(message['message'] == null ||
+					(message['message']['content']['parts']?.[0] == '' &&
+						message['message']['content']['text'] == null))
+			) {
+				// Skip chat messages with no content
+				continue;
+			} else {
+				const new_chat = {
+					id: message_id,
+					parentId: lastId,
+					childrenIds: message['children'] || [],
+					role: message['message']?.['author']?.['role'] !== 'user' ? 'assistant' : 'user',
+					content:
+						message['message']?.['content']?.['parts']?.[0] ||
+						message['message']?.['content']?.['text'] ||
+						'',
+					model: 'gpt-3.5-turbo',
+					done: true,
+					context: null
+				};
+				messages.push(new_chat);
+				lastId = currentId;
+			}
+		} catch (error) {
+			console.log('Error with', message, '\nError:', error);
+		}
+	}
+
+	const history: Record<PropertyKey, (typeof messages)[number]> = {};
+	messages.forEach((obj) => (history[obj.id] = obj));
+
+	const chat = {
+		history: {
+			currentId: currentId,
+			messages: history // Need to convert this to not a list and instead a json object
+		},
+		models: ['gpt-3.5-turbo'],
+		messages: messages,
+		options: {},
+		timestamp: convo['create_time'],
+		title: convo['title'] ?? 'New Chat'
+	};
+	return chat;
+};
+
+const validateChat = (chat) => {
+	// Because ChatGPT sometimes has features we can't use like DALL-E or might have corrupted messages, need to validate
+	const messages = chat.messages;
+
+	// Check if messages array is empty
+	if (messages.length === 0) {
+		return false;
+	}
+
+	// Last message's children should be an empty array
+	const lastMessage = messages[messages.length - 1];
+	if (lastMessage.childrenIds.length !== 0) {
+		return false;
+	}
+
+	// First message's parent should be null
+	const firstMessage = messages[0];
+	if (firstMessage.parentId !== null) {
+		return false;
+	}
+
+	// Every message's content should be a string
+	for (const message of messages) {
+		if (typeof message.content !== 'string') {
+			return false;
+		}
+	}
+
+	return true;
+};
+
+export const convertOpenAIChats = (_chats) => {
+	// Create a list of dictionaries with each conversation from import
+	const chats = [];
+	let failed = 0;
+	for (const convo of _chats) {
+		const chat = convertOpenAIMessages(convo);
+
+		if (validateChat(chat)) {
+			chats.push({
+				id: convo['id'],
+				user_id: '',
+				title: convo['title'],
+				chat: chat,
+				timestamp: convo['timestamp']
+			});
+		} else {
+			failed++;
+		}
+	}
+	console.log(failed, 'Conversations could not be imported');
+	return chats;
+};
+
+export const isValidHttpUrl = (string: string) => {
+	let url;
+
+	try {
+		url = new URL(string);
+	} catch (_) {
+		return false;
+	}
+
+	return url.protocol === 'http:' || url.protocol === 'https:';
+};
+
+export const removeEmojis = (str: string) => {
+	// Regular expression to match emojis
+	const emojiRegex = /[\uD800-\uDBFF][\uDC00-\uDFFF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g;
+
+	// Replace emojis with an empty string
+	return str.replace(emojiRegex, '');
+};
+
+export const removeFormattings = (str: string) => {
+	return str.replace(/(\*)(.*?)\1/g, '').replace(/(```)(.*?)\1/gs, '');
+};
+
+export const cleanText = (content: string) => {
+	return removeFormattings(removeEmojis(content.trim()));
+};
+
+// This regular expression matches code blocks marked by triple backticks
+const codeBlockRegex = /```[\s\S]*?```/g;
+
+export const extractSentences = (text: string) => {
+	const codeBlocks: string[] = [];
+	let index = 0;
+
+	// Temporarily replace code blocks with placeholders and store the blocks separately
+	text = text.replace(codeBlockRegex, (match) => {
+		const placeholder = `\u0000${index}\u0000`; // Use a unique placeholder
+		codeBlocks[index++] = match;
+		return placeholder;
+	});
+
+	// Split the modified text into sentences based on common punctuation marks, avoiding these blocks
+	let sentences = text.split(/(?<=[.!?])\s+/);
+
+	// Restore code blocks and process sentences
+	sentences = sentences.map((sentence) => {
+		// Check if the sentence includes a placeholder for a code block
+		return sentence.replace(/\u0000(\d+)\u0000/g, (_, idx) => codeBlocks[idx]);
+	});
+
+	return sentences.map(cleanText).filter(Boolean);
+};
+
+export const extractParagraphsForAudio = (text: string) => {
+	const codeBlocks: string[] = [];
+	let index = 0;
+
+	// Temporarily replace code blocks with placeholders and store the blocks separately
+	text = text.replace(codeBlockRegex, (match) => {
+		const placeholder = `\u0000${index}\u0000`; // Use a unique placeholder
+		codeBlocks[index++] = match;
+		return placeholder;
+	});
+
+	// Split the modified text into paragraphs based on newlines, avoiding these blocks
+	let paragraphs = text.split(/\n+/);
+
+	// Restore code blocks and process paragraphs
+	paragraphs = paragraphs.map((paragraph) => {
+		// Check if the paragraph includes a placeholder for a code block
+		return paragraph.replace(/\u0000(\d+)\u0000/g, (_, idx) => codeBlocks[idx]);
+	});
+
+	return paragraphs.map(cleanText).filter(Boolean);
+};
+
+export const extractSentencesForAudio = (text: string) => {
+	return extractSentences(text).reduce((mergedTexts, currentText) => {
+		const lastIndex = mergedTexts.length - 1;
+		if (lastIndex >= 0) {
+			const previousText = mergedTexts[lastIndex];
+			const wordCount = previousText.split(/\s+/).length;
+			const charCount = previousText.length;
+			if (wordCount < 4 || charCount < 50) {
+				mergedTexts[lastIndex] = previousText + ' ' + currentText;
+			} else {
+				mergedTexts.push(currentText);
+			}
+		} else {
+			mergedTexts.push(currentText);
+		}
+		return mergedTexts;
+	}, [] as string[]);
+};
+
+export const getMessageContentParts = (content: string, split_on: string = 'punctuation') => {
+	const messageContentParts: string[] = [];
+
+	switch (split_on) {
+		default:
+		case TTS_RESPONSE_SPLIT.PUNCTUATION:
+			messageContentParts.push(...extractSentencesForAudio(content));
+			break;
+		case TTS_RESPONSE_SPLIT.PARAGRAPHS:
+			messageContentParts.push(...extractParagraphsForAudio(content));
+			break;
+		case TTS_RESPONSE_SPLIT.NONE:
+			messageContentParts.push(cleanText(content));
+			break;
+	}
+
+	return messageContentParts;
+};
+
+export const blobToFile = (blob, fileName) => {
+	// Create a new File object from the Blob
+	const file = new File([blob], fileName, { type: blob.type });
+	return file;
+};
+
+/**
+ * @param {string} template - The template string containing placeholders.
+ * @returns {string} The template string with the placeholders replaced by the prompt.
+ */
+export const promptTemplate = (
+	template: string,
+	user_name?: string,
+	user_location?: string
+): string => {
+	// Get the current date
+	const currentDate = new Date();
+
+	// Format the date to YYYY-MM-DD
+	const formattedDate =
+		currentDate.getFullYear() +
+		'-' +
+		String(currentDate.getMonth() + 1).padStart(2, '0') +
+		'-' +
+		String(currentDate.getDate()).padStart(2, '0');
+
+	// Format the time to HH:MM:SS AM/PM
+	const currentTime = currentDate.toLocaleTimeString('en-US', {
+		hour: 'numeric',
+		minute: 'numeric',
+		second: 'numeric',
+		hour12: true
+	});
+
+	// Get the current weekday
+	const currentWeekday = getWeekday();
+
+	// Get the user's timezone
+	const currentTimezone = getUserTimezone();
+
+	// Get the user's language
+	const userLanguage = localStorage.getItem('locale') || 'en-US';
+
+	// Replace {{CURRENT_DATETIME}} in the template with the formatted datetime
+	template = template.replace('{{CURRENT_DATETIME}}', `${formattedDate} ${currentTime}`);
+
+	// Replace {{CURRENT_DATE}} in the template with the formatted date
+	template = template.replace('{{CURRENT_DATE}}', formattedDate);
+
+	// Replace {{CURRENT_TIME}} in the template with the formatted time
+	template = template.replace('{{CURRENT_TIME}}', currentTime);
+
+	// Replace {{CURRENT_WEEKDAY}} in the template with the current weekday
+	template = template.replace('{{CURRENT_WEEKDAY}}', currentWeekday);
+
+	// Replace {{CURRENT_TIMEZONE}} in the template with the user's timezone
+	template = template.replace('{{CURRENT_TIMEZONE}}', currentTimezone);
+
+	// Replace {{USER_LANGUAGE}} in the template with the user's language
+	template = template.replace('{{USER_LANGUAGE}}', userLanguage);
+
+	if (user_name) {
+		// Replace {{USER_NAME}} in the template with the user's name
+		template = template.replace('{{USER_NAME}}', user_name);
+	}
+
+	if (user_location) {
+		// Replace {{USER_LOCATION}} in the template with the current location
+		template = template.replace('{{USER_LOCATION}}', user_location);
+	}
+
+	return template;
+};
+
+/**
+ * This function is used to replace placeholders in a template string with the provided prompt.
+ * The placeholders can be in the following formats:
+ * - `{{prompt}}`: This will be replaced with the entire prompt.
+ * - `{{prompt:start:<length>}}`: This will be replaced with the first <length> characters of the prompt.
+ * - `{{prompt:end:<length>}}`: This will be replaced with the last <length> characters of the prompt.
+ * - `{{prompt:middletruncate:<length>}}`: This will be replaced with the prompt truncated to <length> characters, with '...' in the middle.
+ *
+ * @param {string} template - The template string containing placeholders.
+ * @param {string} prompt - The string to replace the placeholders with.
+ * @returns {string} The template string with the placeholders replaced by the prompt.
+ */
+export const titleGenerationTemplate = (template: string, prompt: string): string => {
+	template = template.replace(
+		/{{prompt}}|{{prompt:start:(\d+)}}|{{prompt:end:(\d+)}}|{{prompt:middletruncate:(\d+)}}/g,
+		(match, startLength, endLength, middleLength) => {
+			if (match === '{{prompt}}') {
+				return prompt;
+			} else if (match.startsWith('{{prompt:start:')) {
+				return prompt.substring(0, startLength);
+			} else if (match.startsWith('{{prompt:end:')) {
+				return prompt.slice(-endLength);
+			} else if (match.startsWith('{{prompt:middletruncate:')) {
+				if (prompt.length <= middleLength) {
+					return prompt;
+				}
+				const start = prompt.slice(0, Math.ceil(middleLength / 2));
+				const end = prompt.slice(-Math.floor(middleLength / 2));
+				return `${start}...${end}`;
+			}
+			return '';
+		}
+	);
+
+	template = promptTemplate(template);
+
+	return template;
+};
+
+export const approximateToHumanReadable = (nanoseconds: number) => {
+	const seconds = Math.floor((nanoseconds / 1e9) % 60);
+	const minutes = Math.floor((nanoseconds / 6e10) % 60);
+	const hours = Math.floor((nanoseconds / 3.6e12) % 24);
+
+	const results: string[] = [];
+
+	if (seconds >= 0) {
+		results.push(`${seconds}s`);
+	}
+
+	if (minutes > 0) {
+		results.push(`${minutes}m`);
+	}
+
+	if (hours > 0) {
+		results.push(`${hours}h`);
+	}
+
+	return results.reverse().join(' ');
+};
+
+export const getTimeRange = (timestamp) => {
+	const now = new Date();
+	const date = new Date(timestamp * 1000); // Convert Unix timestamp to milliseconds
+
+	// Calculate the difference in milliseconds
+	const diffTime = now.getTime() - date.getTime();
+	const diffDays = diffTime / (1000 * 3600 * 24);
+
+	const nowDate = now.getDate();
+	const nowMonth = now.getMonth();
+	const nowYear = now.getFullYear();
+
+	const dateDate = date.getDate();
+	const dateMonth = date.getMonth();
+	const dateYear = date.getFullYear();
+
+	if (nowYear === dateYear && nowMonth === dateMonth && nowDate === dateDate) {
+		return 'Today';
+	} else if (nowYear === dateYear && nowMonth === dateMonth && nowDate - dateDate === 1) {
+		return 'Yesterday';
+	} else if (diffDays <= 7) {
+		return 'Previous 7 days';
+	} else if (diffDays <= 30) {
+		return 'Previous 30 days';
+	} else if (nowYear === dateYear) {
+		return date.toLocaleString('default', { month: 'long' });
+	} else {
+		return date.getFullYear().toString();
+	}
+};
+
+/**
+ * Extract frontmatter as a dictionary from the specified content string.
+ * @param content {string} - The content string with potential frontmatter.
+ * @returns {Object} - The extracted frontmatter as a dictionary.
+ */
+export const extractFrontmatter = (content) => {
+	const frontmatter = {};
+	let frontmatterStarted = false;
+	let frontmatterEnded = false;
+	const frontmatterPattern = /^\s*([a-z_]+):\s*(.*)\s*$/i;
+
+	// Split content into lines
+	const lines = content.split('\n');
+
+	// Check if the content starts with triple quotes
+	if (lines[0].trim() !== '"""') {
+		return {};
+	}
+
+	frontmatterStarted = true;
+
+	for (let i = 1; i < lines.length; i++) {
+		const line = lines[i];
+
+		if (line.includes('"""')) {
+			if (frontmatterStarted) {
+				frontmatterEnded = true;
+				break;
+			}
+		}
+
+		if (frontmatterStarted && !frontmatterEnded) {
+			const match = frontmatterPattern.exec(line);
+			if (match) {
+				const [, key, value] = match;
+				frontmatter[key.trim()] = value.trim();
+			}
+		}
+	}
+
+	return frontmatter;
+};
+
+// Function to determine the best matching language
+export const bestMatchingLanguage = (supportedLanguages, preferredLanguages, defaultLocale) => {
+	const languages = supportedLanguages.map((lang) => lang.code);
+
+	const match = preferredLanguages
+		.map((prefLang) => languages.find((lang) => lang.startsWith(prefLang)))
+		.find(Boolean);
+
+	return match || defaultLocale;
+};
+
+// Get the date in the format YYYY-MM-DD
+export const getFormattedDate = () => {
+	const date = new Date();
+	return date.toISOString().split('T')[0];
+};
+
+// Get the time in the format HH:MM:SS
+export const getFormattedTime = () => {
+	const date = new Date();
+	return date.toTimeString().split(' ')[0];
+};
+
+// Get the current date and time in the format YYYY-MM-DD HH:MM:SS
+export const getCurrentDateTime = () => {
+	return `${getFormattedDate()} ${getFormattedTime()}`;
+};
+
+// Get the user's timezone
+export const getUserTimezone = () => {
+	return Intl.DateTimeFormat().resolvedOptions().timeZone;
+};
+
+// Get the weekday
+export const getWeekday = () => {
+	const date = new Date();
+	const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+	return weekdays[date.getDay()];
+};
+
+export const createMessagesList = (history, messageId) => {
+	if (messageId === null) {
+		return [];
+	}
+
+	const message = history.messages[messageId];
+	if (message?.parentId) {
+		return [...createMessagesList(history, message.parentId), message];
+	} else {
+		return [message];
+	}
+};
+
+export const formatFileSize = (size) => {
+	if (size == null) return 'Unknown size';
+	if (typeof size !== 'number' || size < 0) return 'Invalid size';
+	if (size === 0) return '0 B';
+	const units = ['B', 'KB', 'MB', 'GB', 'TB'];
+	let unitIndex = 0;
+
+	while (size >= 1024 && unitIndex < units.length - 1) {
+		size /= 1024;
+		unitIndex++;
+	}
+	return `${size.toFixed(1)} ${units[unitIndex]}`;
+};
+
+export const getLineCount = (text) => {
+	console.log(typeof text);
+	return text ? text.split('\n').length : 0;
+};
diff --git a/src/lib/utils/marked/extension.ts b/src/lib/utils/marked/extension.ts
new file mode 100644
index 0000000000000000000000000000000000000000..394b6906a12b041fd5c4f37433f0a169508b9ee9
--- /dev/null
+++ b/src/lib/utils/marked/extension.ts
@@ -0,0 +1,70 @@
+// Helper function to find matching closing tag
+function findMatchingClosingTag(src, openTag, closeTag) {
+	let depth = 1;
+	let index = openTag.length;
+	while (depth > 0 && index < src.length) {
+		if (src.startsWith(openTag, index)) {
+			depth++;
+		} else if (src.startsWith(closeTag, index)) {
+			depth--;
+		}
+		if (depth > 0) {
+			index++;
+		}
+	}
+	return depth === 0 ? index + closeTag.length : -1;
+}
+
+function detailsTokenizer(src) {
+	const detailsRegex = /^<details>\n/;
+	const summaryRegex = /^<summary>(.*?)<\/summary>\n/;
+
+	if (detailsRegex.test(src)) {
+		const endIndex = findMatchingClosingTag(src, '<details>', '</details>');
+		if (endIndex === -1) return;
+
+		const fullMatch = src.slice(0, endIndex);
+		let content = fullMatch.slice(10, -10).trim(); // Remove <details> and </details>
+
+		let summary = '';
+		const summaryMatch = summaryRegex.exec(content);
+		if (summaryMatch) {
+			summary = summaryMatch[1].trim();
+			content = content.slice(summaryMatch[0].length).trim();
+		}
+
+		return {
+			type: 'details',
+			raw: fullMatch,
+			summary: summary,
+			text: content
+		};
+	}
+}
+
+function detailsStart(src) {
+	return src.match(/^<details>/) ? 0 : -1;
+}
+
+function detailsRenderer(token) {
+	return `<details>
+  ${token.summary ? `<summary>${token.summary}</summary>` : ''}
+  ${token.text}
+  </details>`;
+}
+
+function detailsExtension() {
+	return {
+		name: 'details',
+		level: 'block',
+		start: detailsStart,
+		tokenizer: detailsTokenizer,
+		renderer: detailsRenderer
+	};
+}
+
+export default function (options = {}) {
+	return {
+		extensions: [detailsExtension(options)]
+	};
+}
diff --git a/src/lib/utils/marked/katex-extension.ts b/src/lib/utils/marked/katex-extension.ts
new file mode 100644
index 0000000000000000000000000000000000000000..45374f2f340331293abc183a470eb03ca179c230
--- /dev/null
+++ b/src/lib/utils/marked/katex-extension.ts
@@ -0,0 +1,152 @@
+import katex from 'katex';
+
+const DELIMITER_LIST = [
+	{ left: '$$', right: '$$', display: true },
+	{ left: '$', right: '$', display: false },
+	{ left: '\\pu{', right: '}', display: false },
+	{ left: '\\ce{', right: '}', display: false },
+	{ left: '\\(', right: '\\)', display: false },
+	{ left: '\\[', right: '\\]', display: true },
+	{ left: '\\begin{equation}', right: '\\end{equation}', display: true }
+];
+
+// const DELIMITER_LIST = [
+//     { left: '$$', right: '$$', display: false },
+//     { left: '$', right: '$', display: false },
+// ];
+
+// const inlineRule = /^(\${1,2})(?!\$)((?:\\.|[^\\\n])*?(?:\\.|[^\\\n\$]))\1(?=[\s?!\.,:?!。,:]|$)/;
+// const blockRule = /^(\${1,2})\n((?:\\[^]|[^\\])+?)\n\1(?:\n|$)/;
+
+let inlinePatterns = [];
+let blockPatterns = [];
+
+function escapeRegex(string) {
+	return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
+}
+
+function generateRegexRules(delimiters) {
+	delimiters.forEach((delimiter) => {
+		const { left, right, display } = delimiter;
+		// Ensure regex-safe delimiters
+		const escapedLeft = escapeRegex(left);
+		const escapedRight = escapeRegex(right);
+
+		if (!display) {
+			// For inline delimiters, we match everything
+			inlinePatterns.push(`${escapedLeft}((?:\\\\[^]|[^\\\\])+?)${escapedRight}`);
+		} else {
+			// Block delimiters doubles as inline delimiters when not followed by a newline
+			inlinePatterns.push(`${escapedLeft}(?!\\n)((?:\\\\[^]|[^\\\\])+?)(?!\\n)${escapedRight}`);
+			blockPatterns.push(`${escapedLeft}\\n((?:\\\\[^]|[^\\\\])+?)\\n${escapedRight}`);
+		}
+	});
+
+	// Math formulas can end in special characters
+	const inlineRule = new RegExp(
+		`^(${inlinePatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`,
+		'u'
+	);
+	const blockRule = new RegExp(`^(${blockPatterns.join('|')})(?=[\\s?。,!-\/:-@[-\`{-~]|$)`, 'u');
+
+	return { inlineRule, blockRule };
+}
+
+const { inlineRule, blockRule } = generateRegexRules(DELIMITER_LIST);
+
+export default function (options = {}) {
+	return {
+		extensions: [inlineKatex(options), blockKatex(options)]
+	};
+}
+
+function katexStart(src, displayMode: boolean) {
+	let ruleReg = displayMode ? blockRule : inlineRule;
+
+	let indexSrc = src;
+
+	while (indexSrc) {
+		let index = -1;
+		let startIndex = -1;
+		let startDelimiter = '';
+		let endDelimiter = '';
+		for (let delimiter of DELIMITER_LIST) {
+			if (delimiter.display !== displayMode) {
+				continue;
+			}
+
+			startIndex = indexSrc.indexOf(delimiter.left);
+			if (startIndex === -1) {
+				continue;
+			}
+
+			index = startIndex;
+			startDelimiter = delimiter.left;
+			endDelimiter = delimiter.right;
+		}
+
+		if (index === -1) {
+			return;
+		}
+
+		// Check if the delimiter is preceded by a special character.
+		// If it does, then it's potentially a math formula.
+		const f = index === 0 || indexSrc.charAt(index - 1).match(/[\s?。,!-\/:-@[-`{-~]/);
+		if (f) {
+			const possibleKatex = indexSrc.substring(index);
+
+			if (possibleKatex.match(ruleReg)) {
+				return index;
+			}
+		}
+
+		indexSrc = indexSrc.substring(index + startDelimiter.length).replace(endDelimiter, '');
+	}
+}
+
+function katexTokenizer(src, tokens, displayMode: boolean) {
+	let ruleReg = displayMode ? blockRule : inlineRule;
+	let type = displayMode ? 'blockKatex' : 'inlineKatex';
+
+	const match = src.match(ruleReg);
+
+	if (match) {
+		const text = match
+			.slice(2)
+			.filter((item) => item)
+			.find((item) => item.trim());
+
+		return {
+			type,
+			raw: match[0],
+			text: text,
+			displayMode
+		};
+	}
+}
+
+function inlineKatex(options) {
+	return {
+		name: 'inlineKatex',
+		level: 'inline',
+		start(src) {
+			return katexStart(src, false);
+		},
+		tokenizer(src, tokens) {
+			return katexTokenizer(src, tokens, false);
+		}
+	};
+}
+
+function blockKatex(options) {
+	return {
+		name: 'blockKatex',
+		level: 'block',
+		start(src) {
+			return katexStart(src, true);
+		},
+		tokenizer(src, tokens) {
+			return katexTokenizer(src, tokens, true);
+		}
+	};
+}
diff --git a/src/lib/utils/rag/index.ts b/src/lib/utils/rag/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6523bb7dff16fb98ea4c2a25f8969527d1d4f490
--- /dev/null
+++ b/src/lib/utils/rag/index.ts
@@ -0,0 +1,24 @@
+import { getRAGTemplate } from '$lib/apis/retrieval';
+
+export const RAGTemplate = async (token: string, context: string, query: string) => {
+	let template = await getRAGTemplate(token).catch(() => {
+		return `Use the following context as your learned knowledge, inside <context></context> XML tags.
+		<context>
+		  [context]
+		</context>
+		
+		When answer to user:
+		- If you don't know, just say that you don't know.
+		- If you don't know when you are not sure, ask for clarification.
+		Avoid mentioning that you obtained the information from the context.
+		And answer according to the language of the user's question.
+				
+		Given the context information, answer the query.
+		Query: [query]`;
+	});
+
+	template = template.replace(/\[context\]/g, context);
+	template = template.replace(/\[query\]/g, query);
+
+	return template;
+};
diff --git a/src/lib/utils/transitions/index.ts b/src/lib/utils/transitions/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9d919f561e6483ba6b20f2af29c243e904fdee57
--- /dev/null
+++ b/src/lib/utils/transitions/index.ts
@@ -0,0 +1,48 @@
+import { cubicOut } from 'svelte/easing';
+import type { TransitionConfig } from 'svelte/transition';
+
+type FlyAndScaleParams = {
+	y?: number;
+	start?: number;
+	duration?: number;
+};
+
+const defaultFlyAndScaleParams = { y: -8, start: 0.95, duration: 200 };
+
+export const flyAndScale = (node: Element, params?: FlyAndScaleParams): TransitionConfig => {
+	const style = getComputedStyle(node);
+	const transform = style.transform === 'none' ? '' : style.transform;
+	const withDefaults = { ...defaultFlyAndScaleParams, ...params };
+
+	const scaleConversion = (valueA: number, scaleA: [number, number], scaleB: [number, number]) => {
+		const [minA, maxA] = scaleA;
+		const [minB, maxB] = scaleB;
+
+		const percentage = (valueA - minA) / (maxA - minA);
+		const valueB = percentage * (maxB - minB) + minB;
+
+		return valueB;
+	};
+
+	const styleToString = (style: Record<string, number | string | undefined>): string => {
+		return Object.keys(style).reduce((str, key) => {
+			if (style[key] === undefined) return str;
+			return str + `${key}:${style[key]};`;
+		}, '');
+	};
+
+	return {
+		duration: withDefaults.duration ?? 200,
+		delay: 0,
+		css: (t) => {
+			const y = scaleConversion(t, [0, 1], [withDefaults.y, 0]);
+			const scale = scaleConversion(t, [0, 1], [withDefaults.start, 1]);
+
+			return styleToString({
+				transform: `${transform} translate3d(0, ${y}px, 0) scale(${scale})`,
+				opacity: t
+			});
+		},
+		easing: cubicOut
+	};
+};
diff --git a/src/lib/workers/pyodide.worker.ts b/src/lib/workers/pyodide.worker.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b27c00629524df88b80e467d940975a50e14e4d4
--- /dev/null
+++ b/src/lib/workers/pyodide.worker.ts
@@ -0,0 +1,70 @@
+import { loadPyodide, type PyodideInterface } from 'pyodide';
+
+declare global {
+	interface Window {
+		stdout: string | null;
+		stderr: string | null;
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
+		result: any;
+		pyodide: PyodideInterface;
+		packages: string[];
+		// eslint-disable-next-line @typescript-eslint/no-explicit-any
+		[key: string]: any;
+	}
+}
+
+async function loadPyodideAndPackages(packages: string[] = []) {
+	self.stdout = null;
+	self.stderr = null;
+	self.result = null;
+
+	self.pyodide = await loadPyodide({
+		indexURL: '/pyodide/',
+		stdout: (text) => {
+			console.log('Python output:', text);
+
+			if (self.stdout) {
+				self.stdout += `${text}\n`;
+			} else {
+				self.stdout = `${text}\n`;
+			}
+		},
+		stderr: (text) => {
+			console.log('An error occurred:', text);
+			if (self.stderr) {
+				self.stderr += `${text}\n`;
+			} else {
+				self.stderr = `${text}\n`;
+			}
+		},
+		packages: ['micropip']
+	});
+
+	const micropip = self.pyodide.pyimport('micropip');
+
+	// await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
+	await micropip.install(packages);
+}
+
+self.onmessage = async (event) => {
+	const { id, code, ...context } = event.data;
+
+	console.log(event.data);
+
+	// The worker copies the context in its own "memory" (an object mapping name to values)
+	for (const key of Object.keys(context)) {
+		self[key] = context[key];
+	}
+
+	// make sure loading is done
+	await loadPyodideAndPackages(self.packages);
+
+	try {
+		self.result = await self.pyodide.runPythonAsync(code);
+	} catch (error) {
+		self.stderr = error.toString();
+	}
+	self.postMessage({ id, result: self.result, stdout: self.stdout, stderr: self.stderr });
+};
+
+export default {};
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..72ae6229d28560df72f52d5a63c64d3be1b5d9a9
--- /dev/null
+++ b/src/routes/(app)/+layout.svelte
@@ -0,0 +1,343 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { onMount, tick, getContext } from 'svelte';
+	import { openDB, deleteDB } from 'idb';
+	import fileSaver from 'file-saver';
+	const { saveAs } = fileSaver;
+	import mermaid from 'mermaid';
+
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { fade } from 'svelte/transition';
+
+	import { getKnowledgeBases } from '$lib/apis/knowledge';
+	import { getFunctions } from '$lib/apis/functions';
+	import { getModels, getVersionUpdates } from '$lib/apis';
+	import { getAllTags } from '$lib/apis/chats';
+	import { getPrompts } from '$lib/apis/prompts';
+	import { getTools } from '$lib/apis/tools';
+	import { getBanners } from '$lib/apis/configs';
+	import { getUserSettings } from '$lib/apis/users';
+
+	import { WEBUI_VERSION } from '$lib/constants';
+	import { compareVersion } from '$lib/utils';
+
+	import {
+		config,
+		user,
+		settings,
+		models,
+		prompts,
+		knowledge,
+		tools,
+		functions,
+		tags,
+		banners,
+		showSettings,
+		showChangelog,
+		temporaryChatEnabled
+	} from '$lib/stores';
+
+	import Sidebar from '$lib/components/layout/Sidebar.svelte';
+	import SettingsModal from '$lib/components/chat/SettingsModal.svelte';
+	import ChangelogModal from '$lib/components/ChangelogModal.svelte';
+	import AccountPending from '$lib/components/layout/Overlay/AccountPending.svelte';
+	import UpdateInfoToast from '$lib/components/layout/UpdateInfoToast.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+	let DB = null;
+	let localDBChats = [];
+
+	let version;
+
+	onMount(async () => {
+		if ($user === undefined) {
+			await goto('/auth');
+		} else if (['user', 'admin'].includes($user.role)) {
+			try {
+				// Check if IndexedDB exists
+				DB = await openDB('Chats', 1);
+
+				if (DB) {
+					const chats = await DB.getAllFromIndex('chats', 'timestamp');
+					localDBChats = chats.map((item, idx) => chats[chats.length - 1 - idx]);
+
+					if (localDBChats.length === 0) {
+						await deleteDB('Chats');
+					}
+				}
+
+				console.log(DB);
+			} catch (error) {
+				// IndexedDB Not Found
+			}
+
+			const userSettings = await getUserSettings(localStorage.token).catch((error) => {
+				console.error(error);
+				return null;
+			});
+
+			if (userSettings) {
+				settings.set(userSettings.ui);
+			} else {
+				let localStorageSettings = {} as Parameters<(typeof settings)['set']>[0];
+
+				try {
+					localStorageSettings = JSON.parse(localStorage.getItem('settings') ?? '{}');
+				} catch (e: unknown) {
+					console.error('Failed to parse settings from localStorage', e);
+				}
+
+				settings.set(localStorageSettings);
+			}
+
+			models.set(await getModels(localStorage.token));
+			banners.set(await getBanners(localStorage.token));
+			tools.set(await getTools(localStorage.token));
+
+			document.addEventListener('keydown', async function (event) {
+				const isCtrlPressed = event.ctrlKey || event.metaKey; // metaKey is for Cmd key on Mac
+				// Check if the Shift key is pressed
+				const isShiftPressed = event.shiftKey;
+
+				// Check if Ctrl + Shift + O is pressed
+				if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 'o') {
+					event.preventDefault();
+					console.log('newChat');
+					document.getElementById('sidebar-new-chat-button')?.click();
+				}
+
+				// Check if Shift + Esc is pressed
+				if (isShiftPressed && event.key === 'Escape') {
+					event.preventDefault();
+					console.log('focusInput');
+					document.getElementById('chat-input')?.focus();
+				}
+
+				// Check if Ctrl + Shift + ; is pressed
+				if (isCtrlPressed && isShiftPressed && event.key === ';') {
+					event.preventDefault();
+					console.log('copyLastCodeBlock');
+					const button = [...document.getElementsByClassName('copy-code-button')]?.at(-1);
+					button?.click();
+				}
+
+				// Check if Ctrl + Shift + C is pressed
+				if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 'c') {
+					event.preventDefault();
+					console.log('copyLastResponse');
+					const button = [...document.getElementsByClassName('copy-response-button')]?.at(-1);
+					console.log(button);
+					button?.click();
+				}
+
+				// Check if Ctrl + Shift + S is pressed
+				if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === 's') {
+					event.preventDefault();
+					console.log('toggleSidebar');
+					document.getElementById('sidebar-toggle-button')?.click();
+				}
+
+				// Check if Ctrl + Shift + Backspace is pressed
+				if (
+					isCtrlPressed &&
+					isShiftPressed &&
+					(event.key === 'Backspace' || event.key === 'Delete')
+				) {
+					event.preventDefault();
+					console.log('deleteChat');
+					document.getElementById('delete-chat-button')?.click();
+				}
+
+				// Check if Ctrl + . is pressed
+				if (isCtrlPressed && event.key === '.') {
+					event.preventDefault();
+					console.log('openSettings');
+					showSettings.set(!$showSettings);
+				}
+
+				// Check if Ctrl + / is pressed
+				if (isCtrlPressed && event.key === '/') {
+					event.preventDefault();
+					console.log('showShortcuts');
+					document.getElementById('show-shortcuts-button')?.click();
+				}
+
+				// Check if Ctrl + Shift + ' is pressed
+				if (isCtrlPressed && isShiftPressed && event.key.toLowerCase() === `'`) {
+					event.preventDefault();
+					console.log('temporaryChat');
+					temporaryChatEnabled.set(!$temporaryChatEnabled);
+					await goto('/');
+					const newChatButton = document.getElementById('new-chat-button');
+					setTimeout(() => {
+						newChatButton?.click();
+					}, 0);
+				}
+			});
+
+			if ($user.role === 'admin' && ($settings?.showChangelog ?? true)) {
+				showChangelog.set($settings?.version !== $config.version);
+			}
+
+			if ($page.url.searchParams.get('temporary-chat') === 'true') {
+				temporaryChatEnabled.set(true);
+			}
+
+			// Check for version updates
+			if ($user.role === 'admin') {
+				// Check if the user has dismissed the update toast in the last 24 hours
+				if (localStorage.dismissedUpdateToast) {
+					const dismissedUpdateToast = new Date(Number(localStorage.dismissedUpdateToast));
+					const now = new Date();
+
+					if (now - dismissedUpdateToast > 24 * 60 * 60 * 1000) {
+						checkForVersionUpdates();
+					}
+				} else {
+					checkForVersionUpdates();
+				}
+			}
+			await tick();
+		}
+
+		loaded = true;
+	});
+
+	const checkForVersionUpdates = async () => {
+		version = await getVersionUpdates(localStorage.token).catch((error) => {
+			return {
+				current: WEBUI_VERSION,
+				latest: WEBUI_VERSION
+			};
+		});
+	};
+</script>
+
+<SettingsModal bind:show={$showSettings} />
+<ChangelogModal bind:show={$showChangelog} />
+
+{#if version && compareVersion(version.latest, version.current) && ($settings?.showUpdateToast ?? true)}
+	<div class=" absolute bottom-8 right-8 z-50" in:fade={{ duration: 100 }}>
+		<UpdateInfoToast
+			{version}
+			on:close={() => {
+				localStorage.setItem('dismissedUpdateToast', Date.now().toString());
+				version = null;
+			}}
+		/>
+	</div>
+{/if}
+
+<div class="app relative">
+	<div
+		class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900 h-screen max-h-[100dvh] overflow-auto flex flex-row"
+	>
+		{#if loaded}
+			{#if !['user', 'admin'].includes($user.role)}
+				<AccountPending />
+			{:else if localDBChats.length > 0}
+				<div class="fixed w-full h-full flex z-50">
+					<div
+						class="absolute w-full h-full backdrop-blur-md bg-white/20 dark:bg-gray-900/50 flex justify-center"
+					>
+						<div class="m-auto pb-44 flex flex-col justify-center">
+							<div class="max-w-md">
+								<div class="text-center dark:text-white text-2xl font-medium z-50">
+									Important Update<br /> Action Required for Chat Log Storage
+								</div>
+
+								<div class=" mt-4 text-center text-sm dark:text-gray-200 w-full">
+									{$i18n.t(
+										"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through"
+									)}
+									<span class="font-semibold dark:text-white"
+										>{$i18n.t('Settings')} > {$i18n.t('Chats')} > {$i18n.t('Import Chats')}</span
+									>. {$i18n.t(
+										'This ensures that your valuable conversations are securely saved to your backend database. Thank you!'
+									)}
+								</div>
+
+								<div class=" mt-6 mx-auto relative group w-fit">
+									<button
+										class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 transition font-medium text-sm"
+										on:click={async () => {
+											let blob = new Blob([JSON.stringify(localDBChats)], {
+												type: 'application/json'
+											});
+											saveAs(blob, `chat-export-${Date.now()}.json`);
+
+											const tx = DB.transaction('chats', 'readwrite');
+											await Promise.all([tx.store.clear(), tx.done]);
+											await deleteDB('Chats');
+
+											localDBChats = [];
+										}}
+									>
+										Download & Delete
+									</button>
+
+									<button
+										class="text-xs text-center w-full mt-2 text-gray-400 underline"
+										on:click={async () => {
+											localDBChats = [];
+										}}>{$i18n.t('Close')}</button
+									>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			{/if}
+
+			<Sidebar />
+			<slot />
+		{/if}
+	</div>
+</div>
+
+<style>
+	.loading {
+		display: inline-block;
+		clip-path: inset(0 1ch 0 0);
+		animation: l 1s steps(3) infinite;
+		letter-spacing: -0.5px;
+	}
+
+	@keyframes l {
+		to {
+			clip-path: inset(0 -1ch 0 0);
+		}
+	}
+
+	pre[class*='language-'] {
+		position: relative;
+		overflow: auto;
+
+		/* make space  */
+		margin: 5px 0;
+		padding: 1.75rem 0 1.75rem 1rem;
+		border-radius: 10px;
+	}
+
+	pre[class*='language-'] button {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+
+		font-size: 0.9rem;
+		padding: 0.15rem;
+		background-color: #828282;
+
+		border: ridge 1px #7b7b7c;
+		border-radius: 5px;
+		text-shadow: #c4c4c4 0 0 2px;
+	}
+
+	pre[class*='language-'] button:hover {
+		cursor: pointer;
+		background-color: #bcbabb;
+	}
+</style>
diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..08026e7aa09650afbe47a8c0d0efc8cbfb6a56d0
--- /dev/null
+++ b/src/routes/(app)/+page.svelte
@@ -0,0 +1,7 @@
+<script lang="ts">
+	import Chat from '$lib/components/chat/Chat.svelte';
+	import Help from '$lib/components/layout/Help.svelte';
+</script>
+
+<Help />
+<Chat />
diff --git a/src/routes/(app)/admin/+layout.svelte b/src/routes/(app)/admin/+layout.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ace4f810ab17ca9d9e0dfab397e5e60552be115f
--- /dev/null
+++ b/src/routes/(app)/admin/+layout.svelte
@@ -0,0 +1,90 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+
+	import { WEBUI_NAME, showSidebar, user } from '$lib/stores';
+	import MenuLines from '$lib/components/icons/MenuLines.svelte';
+	import { page } from '$app/stores';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			await goto('/');
+		}
+		loaded = true;
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Admin Panel')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<div
+		class=" flex flex-col w-full min-h-screen max-h-screen {$showSidebar
+			? 'md:max-w-[calc(100%-260px)]'
+			: ''}"
+	>
+		<div class=" px-2.5 py-1 backdrop-blur-xl">
+			<div class=" flex items-center gap-1">
+				<div class="{$showSidebar ? 'md:hidden' : ''} flex flex-none items-center">
+					<button
+						id="sidebar-toggle-button"
+						class="cursor-pointer p-1.5 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
+						on:click={() => {
+							showSidebar.set(!$showSidebar);
+						}}
+						aria-label="Toggle Sidebar"
+					>
+						<div class=" m-auto self-center">
+							<MenuLines />
+						</div>
+					</button>
+				</div>
+
+				<div class=" flex w-full">
+					<div
+						class="flex gap-1 scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-full bg-transparent pt-1"
+					>
+						<a
+							class="min-w-fit rounded-full p-1.5 {['/admin/users'].includes($page.url.pathname)
+								? ''
+								: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+							href="/admin">{$i18n.t('Users')}</a
+						>
+
+						<a
+							class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes('/admin/evaluations')
+								? ''
+								: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+							href="/admin/evaluations">{$i18n.t('Evaluations')}</a
+						>
+
+						<a
+							class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes('/admin/functions')
+								? ''
+								: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+							href="/admin/functions">{$i18n.t('Functions')}</a
+						>
+
+						<a
+							class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes('/admin/settings')
+								? ''
+								: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+							href="/admin/settings">{$i18n.t('Settings')}</a
+						>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<div class=" pb-1 px-[16px] flex-1 max-h-full overflow-y-auto">
+			<slot />
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/(app)/admin/+page.svelte b/src/routes/(app)/admin/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2f12bbab80501c3d3a0974dbf6182b08ed8b07d6
--- /dev/null
+++ b/src/routes/(app)/admin/+page.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import { goto } from '$app/navigation';
+	import { onMount } from 'svelte';
+
+	onMount(() => {
+		goto('/admin/users');
+	});
+</script>
diff --git a/src/routes/(app)/admin/evaluations/+page.svelte b/src/routes/(app)/admin/evaluations/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..186697542ce8116349b6275b3e6ee1b9cbfa533e
--- /dev/null
+++ b/src/routes/(app)/admin/evaluations/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Evaluations from '$lib/components/admin/Evaluations.svelte';
+</script>
+
+<Evaluations />
diff --git a/src/routes/(app)/admin/functions/+page.svelte b/src/routes/(app)/admin/functions/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d286c95e19eeba2fea534eff63b1273e047b7d97
--- /dev/null
+++ b/src/routes/(app)/admin/functions/+page.svelte
@@ -0,0 +1,19 @@
+<script>
+	import { onMount } from 'svelte';
+	import { functions } from '$lib/stores';
+
+	import { getFunctions } from '$lib/apis/functions';
+	import Functions from '$lib/components/admin/Functions.svelte';
+
+	onMount(async () => {
+		await Promise.all([
+			(async () => {
+				functions.set(await getFunctions(localStorage.token));
+			})()
+		]);
+	});
+</script>
+
+{#if $functions !== null}
+	<Functions />
+{/if}
diff --git a/src/routes/(app)/admin/functions/create/+page.svelte b/src/routes/(app)/admin/functions/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..c3ac6cb061348af0885792794f91af0e28c268e9
--- /dev/null
+++ b/src/routes/(app)/admin/functions/create/+page.svelte
@@ -0,0 +1,98 @@
+<script>
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+
+	import { functions, models } from '$lib/stores';
+	import { createNewFunction, getFunctions } from '$lib/apis/functions';
+	import FunctionEditor from '$lib/components/admin/Functions/FunctionEditor.svelte';
+	import { getModels } from '$lib/apis';
+	import { compareVersion, extractFrontmatter } from '$lib/utils';
+	import { WEBUI_VERSION } from '$lib/constants';
+
+	const i18n = getContext('i18n');
+
+	let mounted = false;
+	let clone = false;
+	let func = null;
+
+	const saveHandler = async (data) => {
+		console.log(data);
+
+		const manifest = extractFrontmatter(data.content);
+		if (compareVersion(manifest?.required_open_webui_version ?? '0.0.0', WEBUI_VERSION)) {
+			console.log('Version is lower than required');
+			toast.error(
+				$i18n.t(
+					'Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})',
+					{
+						OPEN_WEBUI_VERSION: WEBUI_VERSION,
+						REQUIRED_VERSION: manifest?.required_open_webui_version ?? '0.0.0'
+					}
+				)
+			);
+			return;
+		}
+
+		const res = await createNewFunction(localStorage.token, {
+			id: data.id,
+			name: data.name,
+			meta: data.meta,
+			content: data.content
+		}).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Function created successfully'));
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+
+			await goto('/admin/functions');
+		}
+	};
+
+	onMount(() => {
+		window.addEventListener('message', async (event) => {
+			if (
+				!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
+					event.origin
+				)
+			)
+				return;
+
+			func = JSON.parse(event.data);
+			console.log(func);
+		});
+
+		if (window.opener ?? false) {
+			window.opener.postMessage('loaded', '*');
+		}
+
+		if (sessionStorage.function) {
+			func = JSON.parse(sessionStorage.function);
+			sessionStorage.removeItem('function');
+
+			console.log(func);
+			clone = true;
+		}
+
+		mounted = true;
+	});
+</script>
+
+{#if mounted}
+	{#key func?.content}
+		<FunctionEditor
+			id={func?.id ?? ''}
+			name={func?.name ?? ''}
+			meta={func?.meta ?? { description: '' }}
+			content={func?.content ?? ''}
+			{clone}
+			on:save={(e) => {
+				saveHandler(e.detail);
+			}}
+		/>
+	{/key}
+{/if}
diff --git a/src/routes/(app)/admin/functions/edit/+page.svelte b/src/routes/(app)/admin/functions/edit/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..26989f9b168b0d739e59f1834cab0a89c5e380dc
--- /dev/null
+++ b/src/routes/(app)/admin/functions/edit/+page.svelte
@@ -0,0 +1,88 @@
+<script>
+	import { toast } from 'svelte-sonner';
+	import { onMount, getContext } from 'svelte';
+
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { functions, models } from '$lib/stores';
+	import { updateFunctionById, getFunctions, getFunctionById } from '$lib/apis/functions';
+
+	import FunctionEditor from '$lib/components/admin/Functions/FunctionEditor.svelte';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import { getModels } from '$lib/apis';
+	import { compareVersion, extractFrontmatter } from '$lib/utils';
+	import { WEBUI_VERSION } from '$lib/constants';
+
+	const i18n = getContext('i18n');
+
+	let func = null;
+
+	const saveHandler = async (data) => {
+		console.log(data);
+
+		const manifest = extractFrontmatter(data.content);
+		if (compareVersion(manifest?.required_open_webui_version ?? '0.0.0', WEBUI_VERSION)) {
+			console.log('Version is lower than required');
+			toast.error(
+				$i18n.t(
+					'Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})',
+					{
+						OPEN_WEBUI_VERSION: WEBUI_VERSION,
+						REQUIRED_VERSION: manifest?.required_open_webui_version ?? '0.0.0'
+					}
+				)
+			);
+			return;
+		}
+
+		const res = await updateFunctionById(localStorage.token, func.id, {
+			id: data.id,
+			name: data.name,
+			meta: data.meta,
+			content: data.content
+		}).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Function updated successfully'));
+			functions.set(await getFunctions(localStorage.token));
+			models.set(await getModels(localStorage.token));
+		}
+	};
+
+	onMount(async () => {
+		console.log('mounted');
+		const id = $page.url.searchParams.get('id');
+
+		if (id) {
+			func = await getFunctionById(localStorage.token, id).catch((error) => {
+				toast.error(error);
+				goto('/admin/functions');
+				return null;
+			});
+
+			console.log(func);
+		}
+	});
+</script>
+
+{#if func}
+	<FunctionEditor
+		edit={true}
+		id={func.id}
+		name={func.name}
+		meta={func.meta}
+		content={func.content}
+		on:save={(e) => {
+			saveHandler(e.detail);
+		}}
+	/>
+{:else}
+	<div class="flex items-center justify-center h-full">
+		<div class=" pb-16">
+			<Spinner />
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/(app)/admin/settings/+page.svelte b/src/routes/(app)/admin/settings/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a0a86f43569c15d30cfd6e56582a897d363fc7ae
--- /dev/null
+++ b/src/routes/(app)/admin/settings/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Settings from '$lib/components/admin/Settings.svelte';
+</script>
+
+<Settings />
diff --git a/src/routes/(app)/admin/users/+page.svelte b/src/routes/(app)/admin/users/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..028cfd7fed8fe69cb0709a3fb38c05895b9c4d0e
--- /dev/null
+++ b/src/routes/(app)/admin/users/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Users from '$lib/components/admin/Users.svelte';
+</script>
+
+<Users />
diff --git a/src/routes/(app)/c/[id]/+page.svelte b/src/routes/(app)/c/[id]/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2cc68782eb61c109f9a42843aee429693583ae4c
--- /dev/null
+++ b/src/routes/(app)/c/[id]/+page.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	import { page } from '$app/stores';
+
+	import Chat from '$lib/components/chat/Chat.svelte';
+	import Help from '$lib/components/layout/Help.svelte';
+</script>
+
+<Help />
+<Chat chatIdProp={$page.params.id} />
diff --git a/src/routes/(app)/playground/+layout.svelte b/src/routes/(app)/playground/+layout.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..7528838d9ea083f61f22e3dcb6997b29188764bb
--- /dev/null
+++ b/src/routes/(app)/playground/+layout.svelte
@@ -0,0 +1,76 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+	import { WEBUI_NAME, showSidebar, functions } from '$lib/stores';
+	import MenuLines from '$lib/components/icons/MenuLines.svelte';
+	import { page } from '$app/stores';
+
+	const i18n = getContext('i18n');
+
+	onMount(async () => {});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Playground')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+<div
+	class=" flex flex-col w-full h-screen max-h-[100dvh] {$showSidebar
+		? 'md:max-w-[calc(100%-260px)]'
+		: ''}"
+>
+	<div class=" px-2.5 py-1 backdrop-blur-xl">
+		<div class=" flex items-center">
+			<div class="{$showSidebar ? 'md:hidden' : ''} flex flex-none items-center">
+				<button
+					id="sidebar-toggle-button"
+					class="cursor-pointer p-1.5 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
+					on:click={() => {
+						showSidebar.set(!$showSidebar);
+					}}
+					aria-label="Toggle Sidebar"
+				>
+					<div class=" m-auto self-center">
+						<MenuLines />
+					</div>
+				</button>
+			</div>
+
+			<div class=" flex w-full">
+				<div
+					class="flex gap-1 scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-full bg-transparent pt-1"
+				>
+					<a
+						class="min-w-fit rounded-full p-1.5 {['/playground', '/playground/'].includes(
+							$page.url.pathname
+						)
+							? ''
+							: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+						href="/playground">{$i18n.t('Chat')}</a
+					>
+
+					<!-- <a
+						class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes('/playground/notes')
+							? ''
+							: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+						href="/playground/notes">{$i18n.t('Notes')}</a
+					> -->
+
+					<a
+						class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes(
+							'/playground/completions'
+						)
+							? ''
+							: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+						href="/playground/completions">{$i18n.t('Completions')}</a
+					>
+				</div>
+			</div>
+		</div>
+	</div>
+
+	<div class=" flex-1 max-h-full overflow-y-auto">
+		<slot />
+	</div>
+</div>
diff --git a/src/routes/(app)/playground/+page.svelte b/src/routes/(app)/playground/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2a2f7dd0f4cb170265c1e247715362dbc6ba3606
--- /dev/null
+++ b/src/routes/(app)/playground/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Chat from '$lib/components/playground/Chat.svelte';
+</script>
+
+<Chat />
diff --git a/src/routes/(app)/playground/completions/+page.svelte b/src/routes/(app)/playground/completions/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..fd0f404cfb6ca70376e197ae1f8f6b3497598ea7
--- /dev/null
+++ b/src/routes/(app)/playground/completions/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Completions from '$lib/components/playground/Completions.svelte';
+</script>
+
+<Completions />
diff --git a/src/routes/(app)/playground/notes/+page.svelte b/src/routes/(app)/playground/notes/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..51a4bc51734e99c3d09c5af703c1356ae230635b
--- /dev/null
+++ b/src/routes/(app)/playground/notes/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Notes from '$lib/components/playground/Notes.svelte';
+</script>
+
+<Notes />
diff --git a/src/routes/(app)/workspace/+layout.svelte b/src/routes/(app)/workspace/+layout.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..856247252ec6527516fb736a0d39deb6a9ec5fd8
--- /dev/null
+++ b/src/routes/(app)/workspace/+layout.svelte
@@ -0,0 +1,135 @@
+<script lang="ts">
+	import { onMount, getContext } from 'svelte';
+	import {
+		WEBUI_NAME,
+		showSidebar,
+		functions,
+		user,
+		mobile,
+		models,
+		prompts,
+		knowledge,
+		tools
+	} from '$lib/stores';
+	import { page } from '$app/stores';
+	import { goto } from '$app/navigation';
+
+	import MenuLines from '$lib/components/icons/MenuLines.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	onMount(async () => {
+		if ($user?.role !== 'admin') {
+			if ($page.url.pathname.includes('/models') && !$user?.permissions?.workspace?.models) {
+				goto('/');
+			} else if (
+				$page.url.pathname.includes('/knowledge') &&
+				!$user?.permissions?.workspace?.knowledge
+			) {
+				goto('/');
+			} else if (
+				$page.url.pathname.includes('/prompts') &&
+				!$user?.permissions?.workspace?.prompts
+			) {
+				goto('/');
+			} else if ($page.url.pathname.includes('/tools') && !$user?.permissions?.workspace?.tools) {
+				goto('/');
+			}
+		}
+
+		loaded = true;
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{$i18n.t('Workspace')} | {$WEBUI_NAME}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<div
+		class=" relative flex flex-col w-full h-screen max-h-[100dvh] {$showSidebar
+			? 'md:max-w-[calc(100%-260px)]'
+			: ''}"
+	>
+		<div class="   px-2.5 pt-1 backdrop-blur-xl">
+			<div class=" flex items-center gap-1">
+				<div class="{$showSidebar ? 'md:hidden' : ''} self-center flex flex-none items-center">
+					<button
+						id="sidebar-toggle-button"
+						class="cursor-pointer p-1.5 flex rounded-xl hover:bg-gray-100 dark:hover:bg-gray-850 transition"
+						on:click={() => {
+							showSidebar.set(!$showSidebar);
+						}}
+						aria-label="Toggle Sidebar"
+					>
+						<div class=" m-auto self-center">
+							<MenuLines />
+						</div>
+					</button>
+				</div>
+
+				<div class="">
+					<div
+						class="flex gap-1 scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-full bg-transparent py-1 touch-auto pointer-events-auto"
+					>
+						{#if $user?.role === 'admin' || $user?.permissions?.workspace?.models}
+							<a
+								class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes(
+									'/workspace/models'
+								)
+									? ''
+									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+								href="/workspace/models">{$i18n.t('Models')}</a
+							>
+						{/if}
+
+						{#if $user?.role === 'admin' || $user?.permissions?.workspace?.knowledge}
+							<a
+								class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes(
+									'/workspace/knowledge'
+								)
+									? ''
+									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+								href="/workspace/knowledge"
+							>
+								{$i18n.t('Knowledge')}
+							</a>
+						{/if}
+
+						{#if $user?.role === 'admin' || $user?.permissions?.workspace?.prompts}
+							<a
+								class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes(
+									'/workspace/prompts'
+								)
+									? ''
+									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+								href="/workspace/prompts">{$i18n.t('Prompts')}</a
+							>
+						{/if}
+
+						{#if $user?.role === 'admin' || $user?.permissions?.workspace?.tools}
+							<a
+								class="min-w-fit rounded-full p-1.5 {$page.url.pathname.includes('/workspace/tools')
+									? ''
+									: 'text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'} transition"
+								href="/workspace/tools"
+							>
+								{$i18n.t('Tools')}
+							</a>
+						{/if}
+					</div>
+				</div>
+
+				<!-- <div class="flex items-center text-xl font-semibold">{$i18n.t('Workspace')}</div> -->
+			</div>
+		</div>
+
+		<div class="  pb-1 px-[18px] flex-1 max-h-full overflow-y-auto" id="workspace-container">
+			<slot />
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/(app)/workspace/+page.svelte b/src/routes/(app)/workspace/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..65f9d4cef3641c4749d35ad7aedc8d8b621faf0e
--- /dev/null
+++ b/src/routes/(app)/workspace/+page.svelte
@@ -0,0 +1,23 @@
+<script lang="ts">
+	import { goto } from '$app/navigation';
+	import { user } from '$lib/stores';
+	import { onMount } from 'svelte';
+
+	onMount(() => {
+		if ($user?.role !== 'admin') {
+			if ($user?.permissions?.workspace?.models) {
+				goto('/workspace/models');
+			} else if ($user?.permissions?.workspace?.knowledge) {
+				goto('/workspace/knowledge');
+			} else if ($user?.permissions?.workspace?.prompts) {
+				goto('/workspace/prompts');
+			} else if ($user?.permissions?.workspace?.tools) {
+				goto('/workspace/tools');
+			} else {
+				goto('/');
+			}
+		} else {
+			goto('/workspace/models');
+		}
+	});
+</script>
diff --git a/src/routes/(app)/workspace/functions/create/+page.svelte b/src/routes/(app)/workspace/functions/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..14eec3b675a5db0ef17a6e9e81565991ade82d3f
--- /dev/null
+++ b/src/routes/(app)/workspace/functions/create/+page.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	import { goto } from '$app/navigation';
+	import { onMount } from 'svelte';
+
+	onMount(() => {
+		goto('/admin/functions/create');
+	});
+</script>
diff --git a/src/routes/(app)/workspace/knowledge/+page.svelte b/src/routes/(app)/workspace/knowledge/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..9026a1772f01aef6e5919c9cc01c2441d6445592
--- /dev/null
+++ b/src/routes/(app)/workspace/knowledge/+page.svelte
@@ -0,0 +1,19 @@
+<script>
+	import { onMount } from 'svelte';
+	import { knowledge } from '$lib/stores';
+
+	import { getKnowledgeBases } from '$lib/apis/knowledge';
+	import Knowledge from '$lib/components/workspace/Knowledge.svelte';
+
+	onMount(async () => {
+		await Promise.all([
+			(async () => {
+				knowledge.set(await getKnowledgeBases(localStorage.token));
+			})()
+		]);
+	});
+</script>
+
+{#if $knowledge !== null}
+	<Knowledge />
+{/if}
diff --git a/src/routes/(app)/workspace/knowledge/[id]/+page.svelte b/src/routes/(app)/workspace/knowledge/[id]/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2586707dab66bacb118a575477e58c8a54816854
--- /dev/null
+++ b/src/routes/(app)/workspace/knowledge/[id]/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import KnowledgeBase from '$lib/components/workspace/Knowledge/KnowledgeBase.svelte';
+</script>
+
+<KnowledgeBase />
diff --git a/src/routes/(app)/workspace/knowledge/create/+page.svelte b/src/routes/(app)/workspace/knowledge/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..a99035f82c805daa35b5bd7048e0207810df350e
--- /dev/null
+++ b/src/routes/(app)/workspace/knowledge/create/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import CreateKnowledgeBase from '$lib/components/workspace/Knowledge/CreateKnowledgeBase.svelte';
+</script>
+
+<CreateKnowledgeBase />
diff --git a/src/routes/(app)/workspace/models/+page.svelte b/src/routes/(app)/workspace/models/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ed8474894c0bdecea134041142f55438dbed7c11
--- /dev/null
+++ b/src/routes/(app)/workspace/models/+page.svelte
@@ -0,0 +1,18 @@
+<script>
+	import { onMount } from 'svelte';
+	import { models } from '$lib/stores';
+	import { getModels } from '$lib/apis';
+	import Models from '$lib/components/workspace/Models.svelte';
+
+	onMount(async () => {
+		await Promise.all([
+			(async () => {
+				models.set(await getModels(localStorage.token));
+			})()
+		]);
+	});
+</script>
+
+{#if $models !== null}
+	<Models />
+{/if}
diff --git a/src/routes/(app)/workspace/models/create/+page.svelte b/src/routes/(app)/workspace/models/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..b7280bf3abbf7dc6641b81d9d67081ae933c17a9
--- /dev/null
+++ b/src/routes/(app)/workspace/models/create/+page.svelte
@@ -0,0 +1,78 @@
+<script>
+	import { v4 as uuidv4 } from 'uuid';
+	import { toast } from 'svelte-sonner';
+	import { goto } from '$app/navigation';
+	import { models } from '$lib/stores';
+
+	import { onMount, tick, getContext } from 'svelte';
+	import { createNewModel, getModelById } from '$lib/apis/models';
+	import { getModels } from '$lib/apis';
+
+	import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte';
+
+	const i18n = getContext('i18n');
+
+	const onSubmit = async (modelInfo) => {
+		if ($models.find((m) => m.id === modelInfo.id)) {
+			toast.error(
+				`Error: A model with the ID '${modelInfo.id}' already exists. Please select a different ID to proceed.`
+			);
+			return;
+		}
+
+		if (modelInfo.id === '') {
+			toast.error('Error: Model ID cannot be empty. Please enter a valid ID to proceed.');
+			return;
+		}
+
+		if (modelInfo) {
+			const res = await createNewModel(localStorage.token, {
+				...modelInfo,
+				meta: {
+					...modelInfo.meta,
+					profile_image_url: modelInfo.meta.profile_image_url ?? '/static/favicon.png',
+					suggestion_prompts: modelInfo.meta.suggestion_prompts
+						? modelInfo.meta.suggestion_prompts.filter((prompt) => prompt.content !== '')
+						: null
+				},
+				params: { ...modelInfo.params }
+			}).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (res) {
+				await models.set(await getModels(localStorage.token));
+				toast.success($i18n.t('Model created successfully!'));
+				await goto('/workspace/models');
+			}
+		}
+	};
+
+	let model = null;
+
+	onMount(async () => {
+		window.addEventListener('message', async (event) => {
+			if (
+				!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes(
+					event.origin
+				)
+			)
+				return;
+			model = JSON.parse(event.data);
+		});
+
+		if (window.opener ?? false) {
+			window.opener.postMessage('loaded', '*');
+		}
+
+		if (sessionStorage.model) {
+			model = JSON.parse(sessionStorage.model);
+			sessionStorage.removeItem('model');
+		}
+	});
+</script>
+
+{#key model}
+	<ModelEditor {model} {onSubmit} />
+{/key}
diff --git a/src/routes/(app)/workspace/models/edit/+page.svelte b/src/routes/(app)/workspace/models/edit/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0df4ba67d9ae8266efe6a709d32cc6e969f09c04
--- /dev/null
+++ b/src/routes/(app)/workspace/models/edit/+page.svelte
@@ -0,0 +1,46 @@
+<script>
+	import { toast } from 'svelte-sonner';
+	import { goto } from '$app/navigation';
+
+	import { onMount, getContext } from 'svelte';
+	const i18n = getContext('i18n');
+
+	import { page } from '$app/stores';
+	import { models } from '$lib/stores';
+
+	import { getModelById, updateModelById } from '$lib/apis/models';
+
+	import { getModels } from '$lib/apis';
+	import ModelEditor from '$lib/components/workspace/Models/ModelEditor.svelte';
+
+	let model = null;
+
+	onMount(async () => {
+		const _id = $page.url.searchParams.get('id');
+		if (_id) {
+			model = await getModelById(localStorage.token, _id).catch((e) => {
+				return null;
+			});
+
+			if (!model) {
+				goto('/workspace/models');
+			}
+		} else {
+			goto('/workspace/models');
+		}
+	});
+
+	const onSubmit = async (modelInfo) => {
+		const res = await updateModelById(localStorage.token, modelInfo.id, modelInfo);
+
+		if (res) {
+			await models.set(await getModels(localStorage.token));
+			toast.success($i18n.t('Model updated successfully'));
+			await goto('/workspace/models');
+		}
+	};
+</script>
+
+{#if model}
+	<ModelEditor edit={true} {model} {onSubmit} />
+{/if}
diff --git a/src/routes/(app)/workspace/prompts/+page.svelte b/src/routes/(app)/workspace/prompts/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..48c6e65c6eb5b7a326ce0a23f04fd16ab5031a59
--- /dev/null
+++ b/src/routes/(app)/workspace/prompts/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+	import Prompts from '$lib/components/workspace/Prompts.svelte';
+</script>
+
+<Prompts />
diff --git a/src/routes/(app)/workspace/prompts/create/+page.svelte b/src/routes/(app)/workspace/prompts/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..8152805300dd9d0afba9773fd20d31956cda85b9
--- /dev/null
+++ b/src/routes/(app)/workspace/prompts/create/+page.svelte
@@ -0,0 +1,66 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { goto } from '$app/navigation';
+	import { prompts } from '$lib/stores';
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { createNewPrompt, getPrompts } from '$lib/apis/prompts';
+	import PromptEditor from '$lib/components/workspace/Prompts/PromptEditor.svelte';
+
+	let prompt = null;
+	const onSubmit = async (_prompt) => {
+		const prompt = await createNewPrompt(localStorage.token, _prompt).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (prompt) {
+			toast.success($i18n.t('Prompt created successfully'));
+
+			await prompts.set(await getPrompts(localStorage.token));
+			await goto('/workspace/prompts');
+		}
+	};
+
+	onMount(async () => {
+		window.addEventListener('message', async (event) => {
+			if (
+				!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes(
+					event.origin
+				)
+			)
+				return;
+			const _prompt = JSON.parse(event.data);
+			console.log(_prompt);
+
+			prompt = {
+				title: _prompt.title,
+				command: _prompt.command,
+				content: _prompt.content,
+				access_control: null
+			};
+		});
+
+		if (window.opener ?? false) {
+			window.opener.postMessage('loaded', '*');
+		}
+
+		if (sessionStorage.prompt) {
+			const _prompt = JSON.parse(sessionStorage.prompt);
+
+			prompt = {
+				title: _prompt.title,
+				command: _prompt.command,
+				content: _prompt.content,
+				access_control: null
+			};
+			sessionStorage.removeItem('prompt');
+		}
+	});
+</script>
+
+{#key prompt}
+	<PromptEditor {prompt} {onSubmit} />
+{/key}
diff --git a/src/routes/(app)/workspace/prompts/edit/+page.svelte b/src/routes/(app)/workspace/prompts/edit/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5f8d41d3de669c6e6d5774454ec85cf1daa5ce8d
--- /dev/null
+++ b/src/routes/(app)/workspace/prompts/edit/+page.svelte
@@ -0,0 +1,58 @@
+<script lang="ts">
+	import { toast } from 'svelte-sonner';
+	import { goto } from '$app/navigation';
+	import { prompts } from '$lib/stores';
+	import { onMount, tick, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	import { getPromptByCommand, getPrompts, updatePromptByCommand } from '$lib/apis/prompts';
+	import { page } from '$app/stores';
+
+	import PromptEditor from '$lib/components/workspace/Prompts/PromptEditor.svelte';
+
+	let prompt = null;
+	const onSubmit = async (_prompt) => {
+		console.log(_prompt);
+		const prompt = await updatePromptByCommand(localStorage.token, _prompt).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (prompt) {
+			toast.success($i18n.t('Prompt updated successfully'));
+			await prompts.set(await getPrompts(localStorage.token));
+			await goto('/workspace/prompts');
+		}
+	};
+
+	onMount(async () => {
+		const command = $page.url.searchParams.get('command');
+		if (command) {
+			const _prompt = await getPromptByCommand(
+				localStorage.token,
+				command.replace(/\//g, '')
+			).catch((error) => {
+				toast.error(error);
+				return null;
+			});
+
+			if (_prompt) {
+				prompt = {
+					title: _prompt.title,
+					command: _prompt.command,
+					content: _prompt.content,
+					access_control: _prompt?.access_control ?? null
+				};
+			} else {
+				goto('/workspace/prompts');
+			}
+		} else {
+			goto('/workspace/prompts');
+		}
+	});
+</script>
+
+{#if prompt}
+	<PromptEditor {prompt} {onSubmit} edit />
+{/if}
diff --git a/src/routes/(app)/workspace/tools/+page.svelte b/src/routes/(app)/workspace/tools/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..86b1b2b7c3b4769411c3508834a8b3e7a13a1d0a
--- /dev/null
+++ b/src/routes/(app)/workspace/tools/+page.svelte
@@ -0,0 +1,7 @@
+<script>
+	import { onMount } from 'svelte';
+
+	import Tools from '$lib/components/workspace/Tools.svelte';
+</script>
+
+<Tools />
diff --git a/src/routes/(app)/workspace/tools/create/+page.svelte b/src/routes/(app)/workspace/tools/create/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..5eeb261d15235130dcbd663301b8b9ce55471bbb
--- /dev/null
+++ b/src/routes/(app)/workspace/tools/create/+page.svelte
@@ -0,0 +1,97 @@
+<script>
+	import { goto } from '$app/navigation';
+	import { createNewTool, getTools } from '$lib/apis/tools';
+	import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
+	import { WEBUI_VERSION } from '$lib/constants';
+	import { tools } from '$lib/stores';
+	import { compareVersion, extractFrontmatter } from '$lib/utils';
+	import { onMount, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	let mounted = false;
+	let clone = false;
+	let tool = null;
+
+	const saveHandler = async (data) => {
+		console.log(data);
+
+		const manifest = extractFrontmatter(data.content);
+		if (compareVersion(manifest?.required_open_webui_version ?? '0.0.0', WEBUI_VERSION)) {
+			console.log('Version is lower than required');
+			toast.error(
+				$i18n.t(
+					'Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})',
+					{
+						OPEN_WEBUI_VERSION: WEBUI_VERSION,
+						REQUIRED_VERSION: manifest?.required_open_webui_version ?? '0.0.0'
+					}
+				)
+			);
+			return;
+		}
+
+		const res = await createNewTool(localStorage.token, {
+			id: data.id,
+			name: data.name,
+			meta: data.meta,
+			content: data.content,
+			access_control: data.access_control
+		}).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Tool created successfully'));
+			tools.set(await getTools(localStorage.token));
+
+			await goto('/workspace/tools');
+		}
+	};
+
+	onMount(() => {
+		window.addEventListener('message', async (event) => {
+			if (
+				!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
+					event.origin
+				)
+			)
+				return;
+
+			tool = JSON.parse(event.data);
+			console.log(tool);
+		});
+
+		if (window.opener ?? false) {
+			window.opener.postMessage('loaded', '*');
+		}
+
+		if (sessionStorage.tool) {
+			tool = JSON.parse(sessionStorage.tool);
+			sessionStorage.removeItem('tool');
+
+			console.log(tool);
+			clone = true;
+		}
+
+		mounted = true;
+	});
+</script>
+
+{#if mounted}
+	{#key tool?.content}
+		<ToolkitEditor
+			id={tool?.id ?? ''}
+			name={tool?.name ?? ''}
+			meta={tool?.meta ?? { description: '' }}
+			content={tool?.content ?? ''}
+			access_control={null}
+			{clone}
+			on:save={(e) => {
+				saveHandler(e.detail);
+			}}
+		/>
+	{/key}
+{/if}
diff --git a/src/routes/(app)/workspace/tools/edit/+page.svelte b/src/routes/(app)/workspace/tools/edit/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..ffda6dc12d4b80b9ac0aab088c1bd3ee95876080
--- /dev/null
+++ b/src/routes/(app)/workspace/tools/edit/+page.svelte
@@ -0,0 +1,88 @@
+<script>
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { getToolById, getTools, updateToolById } from '$lib/apis/tools';
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
+	import { WEBUI_VERSION } from '$lib/constants';
+	import { tools } from '$lib/stores';
+	import { compareVersion, extractFrontmatter } from '$lib/utils';
+	import { onMount, getContext } from 'svelte';
+	import { toast } from 'svelte-sonner';
+
+	const i18n = getContext('i18n');
+
+	let tool = null;
+
+	const saveHandler = async (data) => {
+		console.log(data);
+
+		const manifest = extractFrontmatter(data.content);
+		if (compareVersion(manifest?.required_open_webui_version ?? '0.0.0', WEBUI_VERSION)) {
+			console.log('Version is lower than required');
+			toast.error(
+				$i18n.t(
+					'Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})',
+					{
+						OPEN_WEBUI_VERSION: WEBUI_VERSION,
+						REQUIRED_VERSION: manifest?.required_open_webui_version ?? '0.0.0'
+					}
+				)
+			);
+			return;
+		}
+
+		const res = await updateToolById(localStorage.token, tool.id, {
+			id: data.id,
+			name: data.name,
+			meta: data.meta,
+			content: data.content,
+			access_control: data.access_control
+		}).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		if (res) {
+			toast.success($i18n.t('Tool updated successfully'));
+			tools.set(await getTools(localStorage.token));
+
+			// await goto('/workspace/tools');
+		}
+	};
+
+	onMount(async () => {
+		console.log('mounted');
+		const id = $page.url.searchParams.get('id');
+
+		if (id) {
+			tool = await getToolById(localStorage.token, id).catch((error) => {
+				toast.error(error);
+				goto('/workspace/tools');
+				return null;
+			});
+
+			console.log(tool);
+		}
+	});
+</script>
+
+{#if tool}
+	<ToolkitEditor
+		edit={true}
+		id={tool.id}
+		name={tool.name}
+		meta={tool.meta}
+		content={tool.content}
+		accessControl={tool.access_control}
+		on:save={(e) => {
+			saveHandler(e.detail);
+		}}
+	/>
+{:else}
+	<div class="flex items-center justify-center h-full">
+		<div class=" pb-16">
+			<Spinner />
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..76748a1d3139141c43c101cfa5b74758e78233f2
--- /dev/null
+++ b/src/routes/+error.svelte
@@ -0,0 +1,11 @@
+<script>
+	import { page } from '$app/stores';
+</script>
+
+<div class=" bg-white dark:bg-gray-800 min-h-screen">
+	<div class=" flex h-full">
+		<div class="m-auto my-10 dark:text-gray-300 text-3xl font-semibold">
+			{$page.status}: {$page.error.message}
+		</div>
+	</div>
+</div>
diff --git a/src/routes/+layout.js b/src/routes/+layout.js
new file mode 100644
index 0000000000000000000000000000000000000000..b49c52809497f1c37096bafc5ea6919e0438c2d9
--- /dev/null
+++ b/src/routes/+layout.js
@@ -0,0 +1,16 @@
+// if you want to generate a static html file
+// for your page.
+// Documentation: https://kit.svelte.dev/docs/page-options#prerender
+// export const prerender = true;
+
+// if you want to Generate a SPA
+// you have to set ssr to false.
+// This is not the case (so set as true or comment the line)
+// Documentation: https://kit.svelte.dev/docs/page-options#ssr
+export const ssr = false;
+
+// How to manage the trailing slashes in the URLs
+// the URL for about page witll be /about with 'ignore' (default)
+// the URL for about page witll be /about/ with 'always'
+// https://kit.svelte.dev/docs/page-options#trailingslash
+export const trailingSlash = 'ignore';
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..d1c30a96b6a07087307fb3f9fcc4ddde80cfce4b
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,222 @@
+<script>
+	import { io } from 'socket.io-client';
+	import { spring } from 'svelte/motion';
+
+	let loadingProgress = spring(0, {
+		stiffness: 0.05
+	});
+
+	import { onMount, tick, setContext } from 'svelte';
+	import {
+		config,
+		user,
+		theme,
+		WEBUI_NAME,
+		mobile,
+		socket,
+		activeUserCount,
+		USAGE_POOL
+	} from '$lib/stores';
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+	import { Toaster, toast } from 'svelte-sonner';
+
+	import { getBackendConfig } from '$lib/apis';
+	import { getSessionUser } from '$lib/apis/auths';
+
+	import '../tailwind.css';
+	import '../app.css';
+
+	import 'tippy.js/dist/tippy.css';
+
+	import { WEBUI_BASE_URL, WEBUI_HOSTNAME } from '$lib/constants';
+	import i18n, { initI18n, getLanguages } from '$lib/i18n';
+	import { bestMatchingLanguage } from '$lib/utils';
+
+	setContext('i18n', i18n);
+
+	let loaded = false;
+	const BREAKPOINT = 768;
+
+	const setupSocket = () => {
+		const _socket = io(`${WEBUI_BASE_URL}` || undefined, {
+			reconnection: true,
+			reconnectionDelay: 1000,
+			reconnectionDelayMax: 5000,
+			randomizationFactor: 0.5,
+			path: '/ws/socket.io',
+			auth: { token: localStorage.token }
+		});
+
+		socket.set(_socket);
+
+		_socket.on('connect_error', (err) => {
+			console.log('connect_error', err);
+		});
+
+		_socket.on('connect', () => {
+			console.log('connected', _socket.id);
+		});
+
+		_socket.on('reconnect_attempt', (attempt) => {
+			console.log('reconnect_attempt', attempt);
+		});
+
+		_socket.on('reconnect_failed', () => {
+			console.log('reconnect_failed');
+		});
+
+		_socket.on('disconnect', (reason, details) => {
+			console.log(`Socket ${_socket.id} disconnected due to ${reason}`);
+			if (details) {
+				console.log('Additional details:', details);
+			}
+		});
+
+		_socket.on('user-count', (data) => {
+			console.log('user-count', data);
+			activeUserCount.set(data.count);
+		});
+
+		_socket.on('usage', (data) => {
+			console.log('usage', data);
+			USAGE_POOL.set(data['models']);
+		});
+	};
+
+	onMount(async () => {
+		theme.set(localStorage.theme);
+
+		mobile.set(window.innerWidth < BREAKPOINT);
+		const onResize = () => {
+			if (window.innerWidth < BREAKPOINT) {
+				mobile.set(true);
+			} else {
+				mobile.set(false);
+			}
+		};
+
+		window.addEventListener('resize', onResize);
+
+		let backendConfig = null;
+		try {
+			backendConfig = await getBackendConfig();
+			console.log('Backend config:', backendConfig);
+		} catch (error) {
+			console.error('Error loading backend config:', error);
+		}
+		// Initialize i18n even if we didn't get a backend config,
+		// so `/error` can show something that's not `undefined`.
+
+		initI18n();
+		if (!localStorage.locale) {
+			const languages = await getLanguages();
+			const browserLanguages = navigator.languages
+				? navigator.languages
+				: [navigator.language || navigator.userLanguage];
+			const lang = backendConfig.default_locale
+				? backendConfig.default_locale
+				: bestMatchingLanguage(languages, browserLanguages, 'en-US');
+			$i18n.changeLanguage(lang);
+		}
+
+		if (backendConfig) {
+			// Save Backend Status to Store
+			await config.set(backendConfig);
+			await WEBUI_NAME.set(backendConfig.name);
+
+			if ($config) {
+				setupSocket();
+
+				if (localStorage.token) {
+					// Get Session User Info
+					const sessionUser = await getSessionUser(localStorage.token).catch((error) => {
+						toast.error(error);
+						return null;
+					});
+
+					if (sessionUser) {
+						// Save Session User to Store
+						await user.set(sessionUser);
+						await config.set(await getBackendConfig());
+					} else {
+						// Redirect Invalid Session User to /auth Page
+						localStorage.removeItem('token');
+						await goto('/auth');
+					}
+				} else {
+					// Don't redirect if we're already on the auth page
+					// Needed because we pass in tokens from OAuth logins via URL fragments
+					if ($page.url.pathname !== '/auth') {
+						await goto('/auth');
+					}
+				}
+			}
+		} else {
+			// Redirect to /error when Backend Not Detected
+			await goto(`/error`);
+		}
+
+		await tick();
+
+		if (
+			document.documentElement.classList.contains('her') &&
+			document.getElementById('progress-bar')
+		) {
+			loadingProgress.subscribe((value) => {
+				const progressBar = document.getElementById('progress-bar');
+
+				if (progressBar) {
+					progressBar.style.width = `${value}%`;
+				}
+			});
+
+			await loadingProgress.set(100);
+
+			document.getElementById('splash-screen')?.remove();
+
+			const audio = new Audio(`/audio/greeting.mp3`);
+			const playAudio = () => {
+				audio.play();
+				document.removeEventListener('click', playAudio);
+			};
+
+			document.addEventListener('click', playAudio);
+
+			loaded = true;
+		} else {
+			document.getElementById('splash-screen')?.remove();
+			loaded = true;
+		}
+
+		return () => {
+			window.removeEventListener('resize', onResize);
+		};
+	});
+</script>
+
+<svelte:head>
+	<title>{$WEBUI_NAME}</title>
+	<link crossorigin="anonymous" rel="icon" href="{WEBUI_BASE_URL}/static/favicon.png" />
+
+	<!-- rosepine themes have been disabled as it's not up to date with our latest version. -->
+	<!-- feel free to make a PR to fix if anyone wants to see it return -->
+	<!-- <link rel="stylesheet" type="text/css" href="/themes/rosepine.css" />
+	<link rel="stylesheet" type="text/css" href="/themes/rosepine-dawn.css" /> -->
+</svelte:head>
+
+{#if loaded}
+	<slot />
+{/if}
+
+<Toaster
+	theme={$theme.includes('dark')
+		? 'dark'
+		: $theme === 'system'
+			? window.matchMedia('(prefers-color-scheme: dark)').matches
+				? 'dark'
+				: 'light'
+			: 'light'}
+	richColors
+	position="top-center"
+/>
diff --git a/src/routes/auth/+page.svelte b/src/routes/auth/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..2148061e71b22a0791636821976adf6da6b4b5b1
--- /dev/null
+++ b/src/routes/auth/+page.svelte
@@ -0,0 +1,431 @@
+<script>
+	import { toast } from 'svelte-sonner';
+
+	import { onMount, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+
+	import { getBackendConfig } from '$lib/apis';
+	import { ldapUserSignIn, getSessionUser, userSignIn, userSignUp } from '$lib/apis/auths';
+
+	import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
+	import { WEBUI_NAME, config, user, socket } from '$lib/stores';
+
+	import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
+
+	import Spinner from '$lib/components/common/Spinner.svelte';
+	import OnBoarding from '$lib/components/OnBoarding.svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	let mode = $config?.features.enable_ldap ? 'ldap' : 'signin';
+
+	let name = '';
+	let email = '';
+	let password = '';
+
+	let ldapUsername = '';
+
+	const setSessionUser = async (sessionUser) => {
+		if (sessionUser) {
+			console.log(sessionUser);
+			toast.success($i18n.t(`You're now logged in.`));
+			if (sessionUser.token) {
+				localStorage.token = sessionUser.token;
+			}
+
+			$socket.emit('user-join', { auth: { token: sessionUser.token } });
+			await user.set(sessionUser);
+			await config.set(await getBackendConfig());
+			goto('/');
+		}
+	};
+
+	const signInHandler = async () => {
+		const sessionUser = await userSignIn(email, password).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+
+		await setSessionUser(sessionUser);
+	};
+
+	const signUpHandler = async () => {
+		const sessionUser = await userSignUp(name, email, password, generateInitialsImage(name)).catch(
+			(error) => {
+				toast.error(error);
+				return null;
+			}
+		);
+
+		await setSessionUser(sessionUser);
+	};
+
+	const ldapSignInHandler = async () => {
+		const sessionUser = await ldapUserSignIn(ldapUsername, password).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+		await setSessionUser(sessionUser);
+	};
+
+	const submitHandler = async () => {
+		if (mode === 'ldap') {
+			await ldapSignInHandler();
+		} else if (mode === 'signin') {
+			await signInHandler();
+		} else {
+			await signUpHandler();
+		}
+	};
+
+	const checkOauthCallback = async () => {
+		if (!$page.url.hash) {
+			return;
+		}
+		const hash = $page.url.hash.substring(1);
+		if (!hash) {
+			return;
+		}
+		const params = new URLSearchParams(hash);
+		const token = params.get('token');
+		if (!token) {
+			return;
+		}
+		const sessionUser = await getSessionUser(token).catch((error) => {
+			toast.error(error);
+			return null;
+		});
+		if (!sessionUser) {
+			return;
+		}
+		localStorage.token = token;
+		await setSessionUser(sessionUser);
+	};
+
+	let onboarding = false;
+
+	onMount(async () => {
+		if ($user !== undefined) {
+			await goto('/');
+		}
+		await checkOauthCallback();
+
+		loaded = true;
+		if (($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false) {
+			await signInHandler();
+		} else {
+			onboarding = $config?.onboarding ?? false;
+		}
+	});
+</script>
+
+<svelte:head>
+	<title>
+		{`${$WEBUI_NAME}`}
+	</title>
+</svelte:head>
+
+<OnBoarding
+	bind:show={onboarding}
+	getStartedHandler={() => {
+		onboarding = false;
+		mode = $config?.features.enable_ldap ? 'ldap' : 'signup';
+	}}
+/>
+
+<div class="w-full h-screen max-h-[100dvh] text-white relative">
+	<div class="w-full h-full absolute top-0 left-0 bg-white dark:bg-black"></div>
+
+	{#if loaded}
+		<div class="fixed m-10 z-50">
+			<div class="flex space-x-2">
+				<div class=" self-center">
+					<img
+						crossorigin="anonymous"
+						src="{WEBUI_BASE_URL}/static/favicon.png"
+						class=" w-6 rounded-full"
+						alt="logo"
+					/>
+				</div>
+			</div>
+		</div>
+
+		<div
+			class="fixed bg-transparent min-h-screen w-full flex justify-center font-primary z-50 text-black dark:text-white"
+		>
+			<div class="w-full sm:max-w-md px-10 min-h-screen flex flex-col text-center">
+				{#if ($config?.features.auth_trusted_header ?? false) || $config?.features.auth === false}
+					<div class=" my-auto pb-10 w-full">
+						<div
+							class="flex items-center justify-center gap-3 text-xl sm:text-2xl text-center font-semibold dark:text-gray-200"
+						>
+							<div>
+								{$i18n.t('Signing in to {{WEBUI_NAME}}', { WEBUI_NAME: $WEBUI_NAME })}
+							</div>
+
+							<div>
+								<Spinner />
+							</div>
+						</div>
+					</div>
+				{:else}
+					<div class="  my-auto pb-10 w-full dark:text-gray-100">
+						<form
+							class=" flex flex-col justify-center"
+							on:submit={(e) => {
+								e.preventDefault();
+								submitHandler();
+							}}
+						>
+							<div class="mb-1">
+								<div class=" text-2xl font-medium">
+									{#if $config?.onboarding ?? false}
+										{$i18n.t(`Get started with {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })}
+									{:else if mode === 'ldap'}
+										{$i18n.t(`Sign in to {{WEBUI_NAME}} with LDAP`, { WEBUI_NAME: $WEBUI_NAME })}
+									{:else if mode === 'signin'}
+										{$i18n.t(`Sign in to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })}
+									{:else}
+										{$i18n.t(`Sign up to {{WEBUI_NAME}}`, { WEBUI_NAME: $WEBUI_NAME })}
+									{/if}
+								</div>
+
+								{#if $config?.onboarding ?? false}
+									<div class=" mt-1 text-xs font-medium text-gray-500">
+										ⓘ {$WEBUI_NAME}
+										{$i18n.t(
+											'does not make any external connections, and your data stays securely on your locally hosted server.'
+										)}
+									</div>
+								{/if}
+							</div>
+
+							{#if $config?.features.enable_login_form || $config?.features.enable_ldap}
+								<div class="flex flex-col mt-4">
+									{#if mode === 'signup'}
+										<div class="mb-2">
+											<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Name')}</div>
+											<input
+												bind:value={name}
+												type="text"
+												class="my-0.5 w-full text-sm outline-none bg-transparent"
+												autocomplete="name"
+												placeholder={$i18n.t('Enter Your Full Name')}
+												required
+											/>
+										</div>
+									{/if}
+
+									{#if mode === 'ldap'}
+										<div class="mb-2">
+											<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Username')}</div>
+											<input
+												bind:value={ldapUsername}
+												type="text"
+												class="my-0.5 w-full text-sm outline-none bg-transparent"
+												autocomplete="username"
+												name="username"
+												placeholder={$i18n.t('Enter Your Username')}
+												required
+											/>
+										</div>
+									{:else}
+										<div class="mb-2">
+											<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Email')}</div>
+											<input
+												bind:value={email}
+												type="email"
+												class="my-0.5 w-full text-sm outline-none bg-transparent"
+												autocomplete="email"
+												name="email"
+												placeholder={$i18n.t('Enter Your Email')}
+												required
+											/>
+										</div>
+									{/if}
+
+									<div>
+										<div class=" text-sm font-medium text-left mb-1">{$i18n.t('Password')}</div>
+
+										<input
+											bind:value={password}
+											type="password"
+											class="my-0.5 w-full text-sm outline-none bg-transparent"
+											placeholder={$i18n.t('Enter Your Password')}
+											autocomplete="current-password"
+											name="current-password"
+											required
+										/>
+									</div>
+								</div>
+							{/if}
+							<div class="mt-5">
+								{#if $config?.features.enable_login_form || $config?.features.enable_ldap}
+									{#if mode === 'ldap'}
+										<button
+											class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-medium text-sm py-2.5"
+											type="submit"
+										>
+											{$i18n.t('Authenticate')}
+										</button>
+									{:else}
+										<button
+											class="bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-medium text-sm py-2.5"
+											type="submit"
+										>
+											{mode === 'signin'
+												? $i18n.t('Sign in')
+												: ($config?.onboarding ?? false)
+													? $i18n.t('Create Admin Account')
+													: $i18n.t('Create Account')}
+										</button>
+
+										{#if $config?.features.enable_signup && !($config?.onboarding ?? false)}
+											<div class=" mt-4 text-sm text-center">
+												{mode === 'signin'
+													? $i18n.t("Don't have an account?")
+													: $i18n.t('Already have an account?')}
+
+												<button
+													class=" font-medium underline"
+													type="button"
+													on:click={() => {
+														if (mode === 'signin') {
+															mode = 'signup';
+														} else {
+															mode = 'signin';
+														}
+													}}
+												>
+													{mode === 'signin' ? $i18n.t('Sign up') : $i18n.t('Sign in')}
+												</button>
+											</div>
+										{/if}
+									{/if}
+								{/if}
+							</div>
+						</form>
+
+						{#if Object.keys($config?.oauth?.providers ?? {}).length > 0}
+							<div class="inline-flex items-center justify-center w-full">
+								<hr class="w-32 h-px my-4 border-0 dark:bg-gray-100/10 bg-gray-700/10" />
+								{#if $config?.features.enable_login_form || $config?.features.enable_ldap}
+									<span
+										class="px-3 text-sm font-medium text-gray-900 dark:text-white bg-transparent"
+										>{$i18n.t('or')}</span
+									>
+								{/if}
+
+								<hr class="w-32 h-px my-4 border-0 dark:bg-gray-100/10 bg-gray-700/10" />
+							</div>
+							<div class="flex flex-col space-y-2">
+								{#if $config?.oauth?.providers?.google}
+									<button
+										class="flex justify-center items-center bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-medium text-sm py-2.5"
+										on:click={() => {
+											window.location.href = `${WEBUI_BASE_URL}/oauth/google/login`;
+										}}
+									>
+										<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="size-6 mr-3">
+											<path
+												fill="#EA4335"
+												d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"
+											/><path
+												fill="#4285F4"
+												d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"
+											/><path
+												fill="#FBBC05"
+												d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"
+											/><path
+												fill="#34A853"
+												d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"
+											/><path fill="none" d="M0 0h48v48H0z" />
+										</svg>
+										<span>{$i18n.t('Continue with {{provider}}', { provider: 'Google' })}</span>
+									</button>
+								{/if}
+								{#if $config?.oauth?.providers?.microsoft}
+									<button
+										class="flex justify-center items-center bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-medium text-sm py-2.5"
+										on:click={() => {
+											window.location.href = `${WEBUI_BASE_URL}/oauth/microsoft/login`;
+										}}
+									>
+										<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21" class="size-6 mr-3">
+											<rect x="1" y="1" width="9" height="9" fill="#f25022" /><rect
+												x="1"
+												y="11"
+												width="9"
+												height="9"
+												fill="#00a4ef"
+											/><rect x="11" y="1" width="9" height="9" fill="#7fba00" /><rect
+												x="11"
+												y="11"
+												width="9"
+												height="9"
+												fill="#ffb900"
+											/>
+										</svg>
+										<span>{$i18n.t('Continue with {{provider}}', { provider: 'Microsoft' })}</span>
+									</button>
+								{/if}
+								{#if $config?.oauth?.providers?.oidc}
+									<button
+										class="flex justify-center items-center bg-gray-700/5 hover:bg-gray-700/10 dark:bg-gray-100/5 dark:hover:bg-gray-100/10 dark:text-gray-300 dark:hover:text-white transition w-full rounded-full font-medium text-sm py-2.5"
+										on:click={() => {
+											window.location.href = `${WEBUI_BASE_URL}/oauth/oidc/login`;
+										}}
+									>
+										<svg
+											xmlns="http://www.w3.org/2000/svg"
+											fill="none"
+											viewBox="0 0 24 24"
+											stroke-width="1.5"
+											stroke="currentColor"
+											class="size-6 mr-3"
+										>
+											<path
+												stroke-linecap="round"
+												stroke-linejoin="round"
+												d="M15.75 5.25a3 3 0 0 1 3 3m3 0a6 6 0 0 1-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1 1 21.75 8.25Z"
+											/>
+										</svg>
+
+										<span
+											>{$i18n.t('Continue with {{provider}}', {
+												provider: $config?.oauth?.providers?.oidc ?? 'SSO'
+											})}</span
+										>
+									</button>
+								{/if}
+							</div>
+						{/if}
+
+						{#if $config?.features.enable_ldap && $config?.features.enable_login_form}
+							<div class="mt-2">
+								<button
+									class="flex justify-center items-center text-xs w-full text-center underline"
+									type="button"
+									on:click={() => {
+										if (mode === 'ldap')
+											mode = ($config?.onboarding ?? false) ? 'signup' : 'signin';
+										else mode = 'ldap';
+									}}
+								>
+									<span
+										>{mode === 'ldap'
+											? $i18n.t('Continue with Email')
+											: $i18n.t('Continue with LDAP')}</span
+									>
+								</button>
+							</div>
+						{/if}
+					</div>
+				{/if}
+			</div>
+		</div>
+	{/if}
+</div>
diff --git a/src/routes/error/+page.svelte b/src/routes/error/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..955186600f72144c1db4666dbe8a6396796e6bfa
--- /dev/null
+++ b/src/routes/error/+page.svelte
@@ -0,0 +1,60 @@
+<script>
+	import { goto } from '$app/navigation';
+	import { WEBUI_NAME, config } from '$lib/stores';
+	import { onMount, getContext } from 'svelte';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	onMount(async () => {
+		if ($config) {
+			await goto('/');
+		}
+
+		loaded = true;
+	});
+</script>
+
+{#if loaded}
+	<div class="absolute w-full h-full flex z-50">
+		<div class="absolute rounded-xl w-full h-full backdrop-blur flex justify-center">
+			<div class="m-auto pb-44 flex flex-col justify-center">
+				<div class="max-w-md">
+					<div class="text-center text-2xl font-medium z-50">
+						{$i18n.t('{{webUIName}} Backend Required', { webUIName: $WEBUI_NAME })}
+					</div>
+
+					<div class=" mt-4 text-center text-sm w-full">
+						{$i18n.t(
+							"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend."
+						)}
+
+						<br class=" " />
+						<br class=" " />
+						<a
+							class=" font-semibold underline"
+							href="https://github.com/open-webui/open-webui#how-to-install-"
+							target="_blank">{$i18n.t('See readme.md for instructions')}</a
+						>
+						{$i18n.t('or')}
+						<a class=" font-semibold underline" href="https://discord.gg/5rJgQTnV4s" target="_blank"
+							>{$i18n.t('join our Discord for help.')}</a
+						>
+					</div>
+
+					<div class=" mt-6 mx-auto relative group w-fit">
+						<button
+							class="relative z-20 flex px-5 py-2 rounded-full bg-gray-100 hover:bg-gray-200 transition font-medium text-sm"
+							on:click={() => {
+								location.href = '/';
+							}}
+						>
+							{$i18n.t('Check Again')}
+						</button>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/s/[id]/+page.svelte b/src/routes/s/[id]/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..0d4579838a0281e0d5f01cf29868733bfe7f55e8
--- /dev/null
+++ b/src/routes/s/[id]/+page.svelte
@@ -0,0 +1,154 @@
+<script lang="ts">
+	import { onMount, tick, getContext } from 'svelte';
+	import { goto } from '$app/navigation';
+	import { page } from '$app/stores';
+
+	import dayjs from 'dayjs';
+
+	import { settings, chatId, WEBUI_NAME, models } from '$lib/stores';
+	import { convertMessagesToHistory, createMessagesList } from '$lib/utils';
+
+	import { getChatByShareId } from '$lib/apis/chats';
+
+	import Messages from '$lib/components/chat/Messages.svelte';
+	import Navbar from '$lib/components/layout/Navbar.svelte';
+	import { getUserById } from '$lib/apis/users';
+	import { error } from '@sveltejs/kit';
+	import { getModels } from '$lib/apis';
+
+	const i18n = getContext('i18n');
+
+	let loaded = false;
+
+	let autoScroll = true;
+	let processing = '';
+	let messagesContainerElement: HTMLDivElement;
+
+	// let chatId = $page.params.id;
+	let showModelSelector = false;
+	let selectedModels = [''];
+
+	let chat = null;
+	let user = null;
+
+	let title = '';
+	let files = [];
+
+	let messages = [];
+	let history = {
+		messages: {},
+		currentId: null
+	};
+
+	$: messages = createMessagesList(history, history.currentId);
+
+	$: if ($page.params.id) {
+		(async () => {
+			if (await loadSharedChat()) {
+				await tick();
+				loaded = true;
+			} else {
+				await goto('/');
+			}
+		})();
+	}
+
+	//////////////////////////
+	// Web functions
+	//////////////////////////
+
+	const loadSharedChat = async () => {
+		await models.set(await getModels(localStorage.token));
+		await chatId.set($page.params.id);
+		chat = await getChatByShareId(localStorage.token, $chatId).catch(async (error) => {
+			await goto('/');
+			return null;
+		});
+
+		if (chat) {
+			user = await getUserById(localStorage.token, chat.user_id).catch((error) => {
+				console.error(error);
+				return null;
+			});
+
+			const chatContent = chat.chat;
+
+			if (chatContent) {
+				console.log(chatContent);
+
+				selectedModels =
+					(chatContent?.models ?? undefined) !== undefined
+						? chatContent.models
+						: [chatContent.models ?? ''];
+				history =
+					(chatContent?.history ?? undefined) !== undefined
+						? chatContent.history
+						: convertMessagesToHistory(chatContent.messages);
+				title = chatContent.title;
+
+				autoScroll = true;
+				await tick();
+
+				if (messages.length > 0) {
+					history.messages[messages.at(-1).id].done = true;
+				}
+				await tick();
+
+				return true;
+			} else {
+				return null;
+			}
+		}
+	};
+</script>
+
+<svelte:head>
+	<title>
+		{title
+			? `${title.length > 30 ? `${title.slice(0, 30)}...` : title} | ${$WEBUI_NAME}`
+			: `${$WEBUI_NAME}`}
+	</title>
+</svelte:head>
+
+{#if loaded}
+	<div
+		class="min-h-screen max-h-screen w-full flex flex-col text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900"
+	>
+		<div class="flex flex-col flex-auto justify-center py-8">
+			<div class="px-3 w-full max-w-5xl mx-auto">
+				<div>
+					<div class=" text-3xl font-semibold line-clamp-1">
+						{title}
+					</div>
+
+					<div class=" mt-1 text-gray-400">
+						{dayjs(chat.chat.timestamp).format($i18n.t('MMMM DD, YYYY'))}
+					</div>
+				</div>
+
+				<hr class="border-gray-50 dark:border-gray-850 mt-6 mb-2" />
+			</div>
+
+			<div class=" flex flex-col w-full flex-auto overflow-auto h-0" id="messages-container">
+				<div class=" h-full w-full flex flex-col py-4">
+					<div class="py-2">
+						<Messages
+							{user}
+							chatId={$chatId}
+							readOnly={true}
+							{selectedModels}
+							{processing}
+							bind:history
+							bind:messages
+							bind:autoScroll
+							bottomPadding={files.length > 0}
+							sendPrompt={() => {}}
+							continueResponse={() => {}}
+							regenerateResponse={() => {}}
+						/>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+{/if}
diff --git a/src/routes/watch/+page.svelte b/src/routes/watch/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..78eab5be3196feb63b1e44a51c8ade7710ffa510
--- /dev/null
+++ b/src/routes/watch/+page.svelte
@@ -0,0 +1,22 @@
+<script>
+	import { onMount } from 'svelte';
+	import { goto } from '$app/navigation';
+
+	onMount(() => {
+		// Get the current URL search parameters
+		const params = new URLSearchParams(window.location.search);
+
+		// Check if 'v' parameter exists
+		if (params.has('v')) {
+			// Get the value of 'v' parameter
+			const videoId = params.get('v');
+
+			// Redirect to root with 'youtube' parameter
+
+			goto(`/?youtube=${encodeURIComponent(videoId)}`);
+		} else {
+			// Redirect to root if 'v' parameter doesn't exist
+			goto('/');
+		}
+	});
+</script>
diff --git a/src/tailwind.css b/src/tailwind.css
new file mode 100644
index 0000000000000000000000000000000000000000..998ab8433d9a2253069aafbfecb7ba632c01253c
--- /dev/null
+++ b/src/tailwind.css
@@ -0,0 +1,16 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+	html,
+	pre {
+		font-family: -apple-system, BlinkMacSystemFont, 'Inter', ui-sans-serif, system-ui, 'Segoe UI',
+			Roboto, Ubuntu, Cantarell, 'Noto Sans', sans-serif, 'Helvetica Neue', Arial,
+			'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
+	}
+
+	pre {
+		white-space: pre-wrap;
+	}
+}
diff --git a/static/assets/fonts/Archivo-Variable.ttf b/static/assets/fonts/Archivo-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..99dc9e5bc7b39e8e989462bef7b624972818da11
--- /dev/null
+++ b/static/assets/fonts/Archivo-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ed648e6308957e7b2d45b755d8a0461c4d10cd99bc501ee859f91935cb3d8727
+size 652084
diff --git a/static/assets/fonts/InstrumentSerif-Italic.ttf b/static/assets/fonts/InstrumentSerif-Italic.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..099e57256d90ff697052dce1fe520d52557873a1
--- /dev/null
+++ b/static/assets/fonts/InstrumentSerif-Italic.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3edbfddd920b3f79b6a4b63288f375e9ff38e17b13d3a395ff0d602910bd372
+size 70868
diff --git a/static/assets/fonts/InstrumentSerif-Regular.ttf b/static/assets/fonts/InstrumentSerif-Regular.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..2e104d40cca70b6f011faa1952babb80da6c260b
--- /dev/null
+++ b/static/assets/fonts/InstrumentSerif-Regular.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f3f7e5b35df7986803b7608f92bc2e72b35649cf1031a6107285fd5beb2f652
+size 69312
diff --git a/static/assets/fonts/Inter-Variable.ttf b/static/assets/fonts/Inter-Variable.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..13713530a909f056dd9fa8cfc3dd0d3acf107a92
--- /dev/null
+++ b/static/assets/fonts/Inter-Variable.ttf
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cf3cb43b0366e2dc6df60e1132b1c9a4c15777f0cd8e5a53e0c15124003e9ed4
+size 804612
diff --git a/static/assets/fonts/Mona-Sans.woff2 b/static/assets/fonts/Mona-Sans.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..d88d5ff27bea51dfc9954392acd7933d642415c9
Binary files /dev/null and b/static/assets/fonts/Mona-Sans.woff2 differ
diff --git a/static/assets/images/adam.jpg b/static/assets/images/adam.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..6ec6c2d8037c7dd5c39bd0658e9160d01dd14524
--- /dev/null
+++ b/static/assets/images/adam.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:514a5efb27cb206809029abacbeffe18e0fcc5ff4bdcebafd2f8da9b39d14967
+size 1318500
diff --git a/static/assets/images/earth.jpg b/static/assets/images/earth.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..c74dd6eef3f2e512b8ba8194ae7a7e2f4b664778
--- /dev/null
+++ b/static/assets/images/earth.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0dd114d9800383923b8a25f584d4a95a306a1acf9672bd6649e7ec5d6325935e
+size 800226
diff --git a/static/assets/images/galaxy.jpg b/static/assets/images/galaxy.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..2a098e65913fdb0c6a1a3bf887588c8c6a26e9ca
--- /dev/null
+++ b/static/assets/images/galaxy.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a37af52935de1a9c37add24d2fd53cf6698b6413ebd462019a9b53d3a95eeb12
+size 2981700
diff --git a/static/assets/images/space.jpg b/static/assets/images/space.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..a9d49c18a42cf6939c14396b603ab557e38d765a
--- /dev/null
+++ b/static/assets/images/space.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:142db8533dc923b6c732b7ceb78b9ae9d6e7ec823ebb80cfb15171096b7af438
+size 441911
diff --git a/static/audio/greeting.mp3 b/static/audio/greeting.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..8ded2a86a568459c861f280b20064b2c8943a0bf
Binary files /dev/null and b/static/audio/greeting.mp3 differ
diff --git a/static/doge.png b/static/doge.png
new file mode 100644
index 0000000000000000000000000000000000000000..66723c6c1f20b5fbee8fdf52ccbbf91280a763d3
Binary files /dev/null and b/static/doge.png differ
diff --git a/static/favicon.png b/static/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b2074780847581edf9cf2ed0d2e9ebd8ff08c56
Binary files /dev/null and b/static/favicon.png differ
diff --git a/static/favicon/apple-touch-icon.png b/static/favicon/apple-touch-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..77ae5bd6cd31404046dd669f0b69c47e49c81b2d
Binary files /dev/null and b/static/favicon/apple-touch-icon.png differ
diff --git a/static/favicon/favicon-96x96.png b/static/favicon/favicon-96x96.png
new file mode 100644
index 0000000000000000000000000000000000000000..197a07131709e72f1b456226411594a5770125e7
Binary files /dev/null and b/static/favicon/favicon-96x96.png differ
diff --git a/static/favicon/favicon.ico b/static/favicon/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..3d8002cd4d1f180a5920e79fb946a73af3fcc6ec
Binary files /dev/null and b/static/favicon/favicon.ico differ
diff --git a/static/favicon/favicon.svg b/static/favicon/favicon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..af01b22282740e3160b25cc1cc26219c9f4b0bb7
--- /dev/null
+++ b/static/favicon/favicon.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="500" height="500" viewBox="0 0 500 500"><image width="500" height="500" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAYAAADL1t+KAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAACo0SURBVHgB7d3rWRTLFsbxpc/5vt0ROEQARMAYARgBEIEagRqBGoFDBEIEjhGIEdhEsDUCT78zVdjAzDCXvtSq+v+epx1UzkXBfmetWlX9xAAk48+fP6Pw4Shc1nh9Hl6fhav5e7bg9x7zK1zLfl6F15vGz381rurJkye/DEASnhiAztVBHYP2ILyObB7Q8eP46tEs3BuvN+F19mt16F8bgM4R6EBLQmiPbB7aen1udwO8ZAr76/D6o/HxNVU+0A4CHdjQveDet7shjs3FsNd1Ez8m6IHNEOjACiG8xzYP6yMjuPukYK9sXtFPjZAHViLQgSCE90G4CO80VTYPekIeuIdAR7HCRPnY5m1zvR4YPIrt+iubB3xlQIEIdBSjEeBH9reNjvxUNq/ev+mVgEcpCHRkK7TQT2xeget1ZChRZfOAVwU/pUWPXBHoyEod4mP7W4GPDXhoan+r96kBmSDQ4VpjCv3Y5lX4uqekAVIZ1TsyQaDDnRDiZzYP8XhwC9CGaX1dGGvvcIhAhwv3QnxsQPemRrjDEQIdySLEkZCpEe5IHIGOpDQm00+NEEeapjYP90vW3JESAh1JCNPpqsTPjDVx+KAwv6yvC6blkQICHYMJ1fir+npthDh8q+prYvNwrwwYAIGO3oVq/K3RUkeepjYP9okBPSLQ0QuqcRSosnm4v6dqRx8IdHSKahyY0Vr7J9ba0SUCHa1rbDfTpDpPMAP+qmxesU8MaBmBjtbQVgfWVhnteLSMQMfOQltd1fiZAdjUxAh2tIBAx9ZYHwdaxTo7dkKgY2MEOdCpqbHtDVsg0LE2ghzoVWUM0GEDBDoeRZADg6qMYMcaCHQsRZADSamMYMcKBDoeIMiBpF3X1xuG53AfgY5bBDngiqbi37DdDdFTQ/HqIB/V1+f6w69GmANenNTXT/3b1b9hQ/Go0AvGyW5AVt4Zj28tGoFeqDrMFeTvjCAHclIZg3PFItALE9bJPxgPTQFyVtl8ff3SUAwCvRBhjU3r5GMDUIqJcU58MQj0zLFODqD2rg7194asEegZC+11VeUjA1C6ylhfzxqBnqFQlSvITwwA7poYbfgssQ89M2F6/acR5gAWO6uvr/W94syQFSr0TDD0BmALVX29oFrPAxV6Buow13Gt340wB7CZkc1Pm3trcI8K3bH6H6H2kqsqZ085gF1VRrXuGhW6U42qnDAH0IaRUa27RoXuTFgr/2IEOYDuVEa17g4VuiNhgp2qHEDXRka17g4VugNMsAMY0LS+zqnW00eFnrg6zLWfnAl2AEMZG/vWXSDQE6XT3upLT0XTejlnsAMY0qi+PuueFE6iRIJouScotNi/GmewA0hPZQzMJYkKPTGNwbeRAUB6RvX1vb5XvTYkhQo9EaGNpRb7mQGADx9t/qCXX4bBEegJoMUOwLHKaMEngZb7wOowPzVa7AD8Gtm8BX9mGBSBPqAwxT4xptgB+KZ72GcOohkWLfcBcFAMgIxd19dLWvD9I9B7Fp6Qpr3lIwOAPFXGunrvaLn3KKyXM/wGIHcjY2tb7wj0noS1pYmxXg6gDLOtuKyr94eWe8fYXw4AdmnzB7ywX71DBHqHeHY5ANyqjHX1ThHoHeGwGAB4oDJCvTOsoXegDvOxcVgMANw3svmw3ImhdQR6y8LDVVSZM/wGAA/p3viFCfj2EegtCtOcHw0A8Bgm4FvGGnpLwjGuvOMEgM28q9fU3xt2RqC3oA5zHeN6ZgCAbUzqUD837IRA30HYY671cralAcBudAb8C/aqb4819C0R5gDQKt1Lv4Ytv9gCFfoW2GMOAJ2pjL3qWyHQN0SYA0DnKiPUN0agb4AwB4DeVEaob4RAXxNhDgC9q4xQXxuBvgbCHAAGUxmhvhYC/RGEOQAMrjJC/VEE+gqEOQAkozJCfSUCfQnCHACSUxmhvhSBvgBhDgDJqoxQX4hAv4cwB4DkVUaoP0CgNxDmAOBGVV+HnP3+F2e5B+Fs9i9GmAOAByObn/3+zDBDhR7U3xTfjQetAIA313WVfmigQpfwPHPCHAD8OQj38OIVH+j1N8Lb+uXMAABendX38g9WuKIDPYT5OwMAePc63NOLVewaev2Ff1W/fDQAQE7e1GvqRd7biwz0OsxPbD7RDgDIj/aoT60wxQV62GuuiXa2OgBAnrQ3/bC0g2eKWkNvHBxDmANAvnSP/xru+cUopkIPhw+oMh8ZAKAE1zZvvxdxmlxJFbr2KY4MAFAKnS9SzHa2IgI9bGU4MQBAac5K2c6Wfcud7WkAgNpZ3Xq/sIxlHehMtAMAAq2jaz392jKVbcudiXYAQMPsiZo5T75nW6Hz9DQAwALTukp/YRnKskIPAxCEOQDgvnGuD3LJrkKvv1BnNt+iBgDAMtkNyWUV6AzBAQDWlN3xsNkEOifBAQA2VNk81LM4SS6nNXStm48MAID1jCyjk+SyCPRweMxrAwBgMzpJLov8cN9yZ90cALCjLNbTXQc66+YAgJZU5nw93XvLnXVzAEAbRjbPFLfcVujsNwcAdOBlXaVfmkMuA71xTvvIAABoj9v1dK8td1XmIwMAoF2azXLZ/XUX6OGc9rEBANCNscetbK5a7qHV/tMAAOiWu9a7twr9qwEA0L3Z89PNETeBHlrtIwMAoB8Hdfa8MydctNxptQMABqTW+7UlzkuFTqsdADAUF1PvyQc6rXYAwMBctN6TbrnTagcAJCL5qffUK3Ra7QCAFCR/4EyygR7Oah8ZAABpSPrAmSRb7pzVDgBIlFrveyk+ZjXVCp1BOABAipJtvSdXofNYVACAAy/qKn1qCUkx0DXVPjIAANJV2XzqPZnWe1Itd/acAwCcGNVXUgNyyVTo7DkHADiT1N70lCr0twYAgB9JDcglUaHX1fmJOXtMHQAAQRIDcqkEOoNwAACvrutAP7SBDd5y50Q4AIBzBymcIDdohc6JcACATAx+gtzQFfqpEeYAAP80IDdolT5Yhc42NQBAZgbdxjZkhc42NQBATlSlD5Ztg1ToVOcAgIwNso1tqAr9gwEAkKdBqvTeK/S6Oh/bfLIdAIBc9V6lDxHo3+uXAwMAIF/TOtBfWI96bbmHQ2QIcwBA7sahI92bXit0jngFABSk1yq9twqdI14BAIXptUrvrUKnOgcAFKi3Kr2XQA/VeTLPjEW5qqqyX79+2fX1tf3+/fv25/HSz5ufu8yzZ89ml4xGo9vX+OvPnz+//fnBAWMjMJtMJnZ+fm6e1PduQyt6mXj/n/WDU+HQK4XxdDq1Hz9+zD5WgMfQbkPzv2tV8EcK9RjuCvr9/f3bXwOQPWXg1DrWeaCzdo6uNcNbr7HqToneUIj+/zXFgB+Px3Z0dEQ1D+RptpbedZXeR4V+akCLFNhXV1ezkLy8vEwuvDehP0P8c0is4k9OTgh4IC+dV+mdBnqY7hsbsCNVtgpxBd86LW6v9OZEf9ZYycfqXdfx8TEtesCvzqv0Tofi6v/zX+qXEwM2FAfXLi4u3FfhbVKwn52dzar3OIwHHxiKg3U88d5ZhR6eqEaYYyOxEtfNjxB/qFm9x3A/PWVVC3Ci0yq9y4NlmGzHWhTc79+/t3///ddevHhhHz9+JMzXoGBXoOvvTZVfHLwDkLTO3oF3EuihOj8zYAUFkgJcgfTu3TtCfEv6e1NH4/DwcHZpmQJAss7qjOxkGKarCp3qHAspfD59+jQLHoX5/W1c2I2qdFXte3t7s6o95wFCwLHX1oGuAn1sQENsqytoXr9+TXu4YwpyVe0EO5CkV11U6a0HOgfJoKkZ5LTVh0GwA8lRmJ9Zy7qo0Gm3gyBPEMEOJKX14bhWAz0cJDMyFE1r5AR5ugh2IAkHbT9ate0K/ZWhWBpwi2vkBHn6FOwaTFQnBcAgWu1otxboHCRTLlV5CgZdVHy+6OulToreiLHdDejduM3huDYrdNbOCxPXybUFje1nvinYtd2NNjzQu9a2sLUZ6GNDMRTgCnLWyfMSD6jRHASAXrS2VN1KoLNVrRwK7zdv3tBez5i+xpqDUBuerzHQuWdtDce1VaHzdIgCxKpcZ60jfwpzqnWgF60sWe8c6GEYbmzIGlV5mWK1/vLlS772QHdaGY5ro0I/M2QrTrBTlZdNz6TX94FeAXRi5+G4NgKddnumdPNmgh2R3typUmffOtCJnYfjdgp0TobLl1rsunkzwY77tLOB5RegdTsPx+1aoVOdZ0YBTosdj4nPsifUgVbtdDjb1oEeFvA5GS4jcaqZFjvWwRQ80LrTXYbjdqnQFeatP88Vw4hb0qi4sIk4Bc+6OtCKnQrlXQKddnsmVGGpfcp6ObaldXXNXQDY2dbZulWgs/c8H6qsVGEBu9LcBYOUwM623pO+bYU+NrinMFdlBbQl7ldn6QbYyZltYdtAp93unJ6qRZijC9fX14Q6sJtj28LGgU673T+FuZ6qBXQlnjBIqANb2artvk2FPja4RZijL4Q6sJMz29A2gU673SnCHH0j1IGtbdx23yjQabf7pS1FhDmGQKgDW9m47b5phT42uKNpdo5yxZDig13Y0gZs5GyTT9400Gm3O8PWNKQiTr8DWNtGbfe1Az2U/mODGzoBjjBHShTqmuUAsJaDTdrum1ToY4MbOpudE+CQIs1ycPY7sJaNCulNAn2rje7oX1yvBFKlzhFPaQPWsnb2UqFnJk4UM3yE1KmDpBY8gJXWfvraWoFe9/DH9cvIkDytT7I9CF6ok8T3K7DSs5DBj1q3Qh8bkqd1Sa2dA16wPASsZbzOJ60b6EeGpGnQiIl2eKS2O89SB1ZaK4P/99gnsF0tfapymBre3mg0ur2ePXtmz58/n73G37tP8wnx+v379+zvX5d+zprwdnTw0cHBgZ2ectQFsMDs1LgnT56sHI56NNCNME8ex2quRyGt0NC1v78/e40h3iaFur4eP378mC2B6OcMKT5OQ3JHR0cL30QBmA3HTVZ9wjqBzna1hKkyJ8yXG4/Hs5DQqwK87fBeJL5pODk5sbdv385+TaGu6+rqahbyBPxD+jvRevr3798NwAMHj33Ck8c+oS7zfxoT7klSQBweHhr+ilW4WrcK1D4CfBsK9YuLi9krb8juUqX+4cMHy43mXLydklff/w3JqOqW+96qT1gZ6OHpaj8NyVE1ozAnDOZUgR8fH9vZ2VmyIb7M5eXlrHLnaXh/ff36dfY1zQmBjhbs1aFeLfvNx6bcx4Yk0WqfV+Oq5nTz16WPvYW5qJPw+fNn+/nz5+yVNeT5eQosSwAPjFf95mOBzna1BKlNW/LjUBXaWptWAKo1m0slpyBXh4Fgn+/c4GhY4IGVmUyF7lCpT6tqBrn23HusxtdFsM/Pe2cbIHDHeNVvLg30sH4+MiSlxFZ7SUF+X+nBzoEzwB2jkM0LrarQHx2RR78U5KWdBqd2urYxlRbk9ynY9fcQt8GVQstLtN6BO8bLfmNVoLP/PDElnQanajQOuzEkNqc3NHpjo4o9twnwVfRnZkAOuLW02KZCd0JbXkrZ1vTq1atZNVpSaG0ivtnRQGAJXQuFOUcbA7eWDsYt3Icezm//z5CMvb297NfOFU5fvnwhyDeg74lSjv7VmzwdGuQV+9DRon8Xneu+rEKnOk+IbgS537DjWjlhvhlV62rBl7C2zoAccGthRi8L9LEhCSU8SU0tdtbKd6N1Zk3C59yC14CcLgCbBfq+IQk67zvX6lzhoxAq+ZCcNsVJ+JzfGLGWDsyMF/0iLfeEKchzHYSLg10KIbQn/r16XmtehSodmFlYdD8IdA6USUeu1XnuoTO0+PerJ87liCodmB0w82B9bVGFPjIMLtfqPIYN6+Xd0nKGvn9yDHWqdGBmfP8Xnq7zSehfjtU5Yd4/hXqOE/BU6cDD4ntRoDMQl4DcqnPCfDiagNejZXNClQ6sV6GPDIPKbd85YT48nSqXW/udKh2Fe1B83wn0sMjOpNLAcrpREebp0BvFnAYRVaGX9uRBoOHBYNz9Cp0wH1huNykd5UqYpyO3N1eaNQEKNmr+hEBPTE43KLV52ZqWFk2/K9RzOVFOhxLxJDYU7M4N9n6gjwyDyWmrmiarcxvEyoUqdHVOcqAwp0pHwVYGOhPuA8rlxqSqXJPVSJcegqMOSg4uLy8NKNSo+RNa7gnJoTrPqfrLnTooOTzdTnMn19fXBhToThF+G+hhWi7fRzUlLpdhOLXaGYLzI5cntF1dXRlQoDuT7s0Knep8QDm02/WgFR624ovefCnUveOJfSjYKH7QDHSq8wF5P/VKwZDjEaMlODk5mV2eaTiOk+NQqNtinAo9AWoXem+302r3LYfWO213FGoUP2gGOhPuA/E+pUur3T+FufcOS45PJwTW8Dx+QMs9Ad5bhbTa8+B96p22OwpFyz0V3qfbX716Ras9I97fnNF2R4EWTrlToQ/A8w1IQc5pcHlRhe65SueQGRToduvaLNDrn1CdD8Rzi1CP46Q6z4/nKl3dLp7AhgL9DXSjOh+EbjxeT7hSkDMIlyfvVTptdxRoVpTHQB8Zekd1jlR5rtIZjEOB7lToI0PvPFcSVOd581ylE+go0Eg/EOgD8tpuV5hTnefPa5Wu7Ws8rAWFme1Ff9r8CfrjeXiHfedlUIXu9fS4b9++GVCQf/UDQ3ED+fHjh3mkmzzVeTm8bkukQkdh7lToI0OvvK7zaRgO5dDBQR6xjo7CsG1tSF4rCM/bmbA5tdw9fs21nKW1dKAQI/3wtPlwdPTHYwWhR2zSbi+P10er0nZHSeosH6lCHxl65fVGc3x8bCiP12UWr3MqwLYU6FToPbu5uTGPaLeXyWvbnQodhRkR6APweKNhur1sBDqQPAJ9CF4DHeU6Ojoyb3hIC0rDGvoAPN5oPN7Q0R6Ph8xoyp1QR0FGTw29o0KHR7TdgbRRoffMY8VAmEM8fh/8/v3bgEI8p0LvmcdAPzg4MGB/f9+8oeWOkijQeTBLjzxWDKyfQzy+sSPQUZB/qdB75vEGw3Y1iIbivH0vEOgoyD8Ees88ni9Nyx2Rt+8FAh0lYR96z7zdYKjO0eT1+ehAAThYpm8EOjyjQgfSRcsdKxHoaPrnn3/MGx6jilIQ6D3zdnMh0NHk8fuBQEcpaLn3jJsLPOMNHpCsZwQ6VuIGDu9YR0chntFy75m3m4vHNVN0hzd4QLoIdKzENiUA8IFABwAgAwQ6AAAZINABAMgAgQ4AQAYIdKx0c3NjAID0Eeg9Y2ocnnEwEpAuAr1n3gKdGziaPH4/8CYapVCgc8fGUgQ6mjyeukagoxC/CPSeebu5cGwmmn7//m3eEOgoxC9a7j2j5Q7PqNCBdBHoPfN2Fvb19bUBkbdAJ8xREgK9Z7Tc4RmBDiRr1nKvDL3xeIMh1BF5+17g6XAoCGvofXv+/Ll5Q9sdkbfvBSp0lIQp9555rBi+fftmwHQ6NW+o0FESBbq/fSiOebzB0HKH/Pjxw7wh0FGQipZ7zzzeYDxWZmifx++D/f19A0rBUNwAvIW69qJTpcPjLAVr6CjIDRX6AA4ODsybq6srQ7n0hs7jmzqP/9aAbVGhD4C2O7zx+PUnzFEY1tCH4HFdTzd0joEtl8cODQNxKAwHywzBY+WgMGc/ermo0IHkEehD8Fo5sI5eJn3dPXZnmHBHYTgpbgiavPUY6pPJxFCey8tL84iWOwpTPX3y5Ell6N14PDZvVKUxHFcWfc09vpHTm2Za7ihJneW3FXpl6JXXm8379+8N5fBanRPmKMxsTexp8yfoz9HRkXnEtHtZLi4uzCOPHTBgB5V+oEIfiCoIr6dYffr0yZA/HSTjdYnF6xtmYEt3KnQe0DIAr23Bjx8/GvLneXmFCh2FqfQDFfqATk5OzCO13L22YrEeVededzUQ5ijQjX4g0AfkeZ8sW9jy5rk69/pGGdhBpR8YihuQKgmv6+haW2ULW548V+fCgTIoUKUfYqBzpudAPFcT5+fnhvx4rs51mAwtdxSIbWsp8DyNq0qOtfS8eK/OCXOU6MmTJ7Oi/Gn4iQK9MvTO+3rf69ev2ZeeEe8HBx0fHxtQmNsb8NNFv4j+aA3dc1WhMGdfeh70ND3vw45U6CjQ7ZJ5M9B/GAbhvUp/9+7drFUL316+fGme6d+R1yFTYAe358g0A70yDOL09NS8Y0DON7Xavb8po92OQi2s0CvDILy33UVb2Gi9+6QgV5fFM/0bYv85CrUw0Nm6NqAcqnRa7z69ePHCvKPdjoItHIqrDIPJ4YakATnv67ClyaHVLjm8IQa29LBCZ+vasBTmZ2dn5p0mpd+8eWNIn5ZJvLfahcNkULBfIbtnnt77TdruA8plqEdPY7u8vDSkS1V5LoOMObwRBrZ0J7PvB/qNYTCqMnKpNBQWrKenS+vmuXx9aLejYHe2m1OhJyaXSV2tp+cUGjnRkkguXxdV52q5A4Wqmj8h0BOjaiOXaV2FhobkOBo2HRqC05JILqjOUbiVLffKMCiFuc5HzwVDculQmOcwBBcxDAesCHQm3dPw6tWrrPbU6nxwTpIblp6Kl1OYy9u3bw0o2HVzwl2eLvokw6ByPPVKoe79SV5eKcxzmwRXdc50Owr3YIh9UaB/Mwwux+pDFSLt937lGOZCdQ48LL4XBXplGJwqkBzPptZAFu33fqgjkmOYU50DM9P7v/B0nU/CMHKtQtR+Pzw8ZPq9Q7kNwDVRnQMz1f1feBDoDMal4+DgINtKRNPvCnX2qbdLb5LUAck1zKnOgZmqzurq/i8+XfLJDMYlIudqRGGuw2d0pjh2F/8+1QHJFdU5MPNj0S8uC3QG4xKhiiSnfen3xRBiAn43elOkjoc6H7miOgduLfyHToXugKqS3J/1rBYxR8VuRzsH9HeX+0wC1Tlwa7roFwl0B3I7PW4ZVZkKJm21wuP05kdVeU5HuS6jE+GozoFb61foYTCOUE+ITo8r4SEUCinduHla22paosi9xd70+fNnAzDz4IS46OmK/xDr6AlRlV7STU2DXVTrD8W1ci1RlLLtjyeqAXcsfRf/dJv/EIaR0/PS1xGr9b29veIn4eOT6/Qmp5SqXBTkrJ0Dd1wt+41VgT41JEdVeu4DcvfFSfgS2/CqwmN7/fLy0kqjMKc6B+7YvEIPm9YrQ1JKrljUhle1rmDPvUqNQa4/b0nt9SZ1ZxiEA+5YeKBMtKpCF9bRE6SJ95KfAx2Pjs1xjV1LC3rDUnKQi7pQtNqBB6arfvOxQJ8aklRi6/0+hV9cY1c167Udr9D+9OnT7A1KPOmt9HPuabUDC60ssp+s+s0/f/6M6pefhiRp/zGPI70r7lc+OjpKOhAU2Fo2UIdBa+M8qOYvff1y3NGhN2renjRYZ4AhKXurWu4rA13qL6gCfWRIEmehL6dw1yNo9/f3k1iiUAfh6upqFuAKc0L8Ib0J+/r1a5bVOYGOHWn9fG/VJ/zPHqcR+VeGJKmS4VGki+mNTnyzo+UJPb1Owa7qXR93uWShr4cC/Nu3b7Pw1v8PDsp5HK12YKnpY5+wTqCzHz1huvl9+fJlVqljOQVsM+AlhnwMd1Xyeo3XqmCJ4axXXb9//779WAFOeG9OYc5UO7DU1WOfsE7LXWXMf4akaS29hDO9kSe9efr5M+9xHVru2NG/y458jR6bco/nuk8NSfvw4UPRW9ngV1w3B7DU9LEwl0cDPWA/ugNaT2f9Ed7wfQs8aq0MXjfQp4bkxfX00venww+tm9NZAh41XeeT1gr0utSf1i+MUTugAS+134HU6ZHAOg0PwEpVyOBHrVuhy6MTdkiDJoU5NhMp0xtPhjiBtUzX/cRNAr28Rz05pspHZ74DqYlLQwDWsnYxvUmgT422uytMviM1OZ8EB3Rkuu4nrh3oYWSeQ2acUSWk9iYwNA1rEubARtbarhZtUqEL6+jO6CaqUOcmiiER5sBWNno+9KaBPjG4Q5sTQ4phTqcI2Nh0k0/eKNA5Nc4vQh1D0SwHYQ5sbLrqUamLbFqhC213pwh19EmVuU6B44ErwFY2arfLNoE+MbhFqKMPsc1OmANbm9qGNg502u7+EeroEmvmwM42brfLNhW60HZ3TmH+/ft3brpoFd9XQCs2brfLtoE+MbinSko339PTUwN2RecHaM3UtrBVoNN2z8tkMuHsd+xEFbneHBLmwM62arfLthW6bNUSQJp09jtPacM29NQ0hTmP7QVasXW27hLoelgLZ7tnRA9zocrCJvQmkKemAa1Rpm79ILStAz203anSM6PWKeugeEycZOeJfkCrLjc5u/2+XSr02f+4ITsK858/f3KzxkJxvZwn+QGt26lI3inQ63cSU6Ptni21U3XSF2ujiOJ6OR0coHVVyNSt7VqhyydDtnTSFzdwxBY76+VAZ3Zewm4j0PkXnrnYgmdrW5nUWqfFDnRuYjvaOdDZk14ObW1TsFOtl0FVuZZdGJIEOrf13vOmNip0eW8oAtV6GWJVzmAk0ItWdoy1EugMx5WHaj1Pqsq/fPlCVQ70R8NwE2tBWxW6MBxXmFitaxKem79/mmDX1/Pk5MQA9GZqLWkz0BmOK5Qm4Xn2tV9qr8cJdrYoAr1rbcm6tUBnOK5sqtBVqavCI9h9iE9H08UEOzCIqzaG4aI2K3RhOK5wMdhZg01XnF7Xmy+CHBhUq53tVgM9DMddG4qnoGB9PS0Kcu1O4FhfIAk7nwx3X9sVuvDAFtxS+z0GO9XgMJpBrt0JrJMDSWi9o91FoE+MLWy4Jw7OMTzXH72B0hup//77jyAH0tLaVrWm1gM9DMexhQ0LxZCJw3O049sXp9Z58wQka2od6KJCF7awYaXmVDzt+N3FtrqqcabWgeR1MkDeSaCHKp21dKwltuOp2jejEI9/d7TVATcmbW5Va/qfdWdSX6cGrClW7TKdTu3i4mL2WlWVYU6BfXBwYKenp7MT3QhwwJ3Otnd3Fugax//z58+0/nBswIbUMo5tY4X61dXV7PX6urxdkQpthffx8fHs74QQB9y66qo6ly4rdNE7kbEBO2iGu6r1ZsD/+pXfhopYhR8dHd35swNwr9P5sifWsbpK/2qEOjqiil3B/u3bt1nYe6zgFeAKbQW4glwXVXh+JpOJnZ+fmyf1/dvQGj3z/IV1qI9AH9cvXw3ogSp2hbquFENecwIKbL3GAGcIsAwEevHOu9h73tR5oAtVOoamYI/Xzc3N7cd6AxBfd6WqWpcCWpc+fv78+Z0QR7kI9KLpIJk961jXa+gRa+kYVAzZVRTqzWud/85FHwOL6HuEg36K1cuDy3qp0IUqHQBQoF6qc+nqpLhFeLQqAKA0vWVfbxW6UKUDAArSW3UufVboQpUOAChFr5nXa4UuVOkAgAJc19X5ofWo7wpdqNIBALl7Yz3rPdB1xrt19CxYAAAScBmyrle9t9yF0+MAABnb6/IhLMsM0XKPVTrPSwcA5GYyRJjLIBW61FX6qH75Xl88hQIAkIu9oQJ9kApdwh/4kwEAkIf3Q4W5DFahS12lqzr/aVTpAADfqvp6MWSgD1ahS/0H1xMw2MYGAPBu0OpcBq3Qo7pS11r6gQEA4E+vR7wuM2iF3tD7BnwAAFqSRIYlEegcNgMAcErb1C4tAUm03IVtbAAAh/aGXjuPUmm5s40NAODN4INwTclU6BK2salKHxkAAOlKYhCuKZkKXcI2tnMDACBtyW25TirQJQzIJTFgAADAAhqEm1hikmq5R5wgBwBIVGUDnwi3THIVunCCHAAgUUkNwjUlWaFHdaWuZ6aPDQCA4SU3CNeUeqCPjL3pAIA0JLPnfJEkW+4Re9MBAIlIttUeJV2hRzy8BQAwoKRb7VHSFXoDe9MBAEN5YQ64CPT6ndG1MfUOAOhf8q32yEXLPaL1DgDokYtWe+Sl5R69rK9fBgBA91y02iNXgR7aHrTeAQBdc9Nqj1y13CMOnAEAdGhah7mr6ly8BvrIOHAGANC+yhI9q/0x3tbQZ8JfNFvZAABtc9dqj1wGutR/4XrEKqfIAQDa8inFx6Kuy2XLPQqPWVXrfWQAAGyvqq/D8LRPl1wHurCeDgDYkUL80GurPXLbco/YygYA2JHbdfMm9xV6VFfqk/rl1AAAWJ/WzV9bBnIKdNbTAQCbqMz5unlTNoEurKcDANaUxbp5k/s19KbwhXljAACs9ianMJesAl3CHkL2pwMAlnnveb/5Mlm13Js47x0AsMB1HeaHlqHsKvQGHQ1bGQAAc5XNH8OdpWwrdKmr9IP6RZU6Q3IAULbshuDuy7lC13r6tTEkBwDI5PCYVbIOdAmDD5wkBwDlUph/tMxl3XJv4iQ5ACjSZR3m2a6bN5UU6FpH13r6gQEASlBZRifBPSb7lnsUvqB6l1YZACB3VX29KCXMpZgKPeJ4WADIXvYT7YsUU6FH4QtcxHoKABTqvLQwl+ICXeov9NTYzgYAOdIZ7ZdWoCIDXcIWBrazAUA+itietkxxa+j31Wvq+uK/MgCAZwrzd1aw4gNd2KMOAK5d1GF+ZoUj0IM61DX5zh51APAl26enbarYNfQFXtTXtQEAvNA9+4Vhhgq9IZwmp0p9ZACAlFU2PzimMsxQoTeEE4X0bq8yAECqKiPMH6BCXyCcJqdz30cGAEhJZYT5QgT6EoQ6ACSnMsJ8KQJ9BUIdAJJRGWG+EoH+CEIdAAZXGWH+KAJ9DYQ6AAymMsJ8LQT6mgh1AOhdZYT52gj0DRDqANCbygjzjRDoGyLUAaBzlRHmGyPQt0CoA0BnKiPMt8JJcVsI32ic/Q4A7dI99ZAw3w4V+g7C2e+q1HlKGwDsZvaglXAEN7ZAhb4DfeOFx/ZdGABgW3qe+SFhvhsCvQX1N+FZ/fLeAACb+hTuodgRgd6S+hvynRHqALCJ9/W987WhFayht6xeV9c35wcDAKzypg7zj4bWEOgdqEP9pH75XF/PDADQpHXyl3WYTw2tItA7wl51AHigMvaYd4Y19I409qpXBgCI29IqQycI9A6Fb1xta7s0ACiXtvYS5h0j0DsW9qq/NCbgAZRJk+xn7DHvHmvoPQoT8G+NYTkA+VOAa5J9YugFgd4zhuUAFKCy+SQ7z7voES33nvFgFwCZm9p8vZx7XM8I9AEo1MMZ8KyrA8iJjnFl+G0gtNwHVrfgz2x+shzr6gC8Yr08AQR6AlhXB+BYZWxJSwIt9wQ09qt/MgDwQ/vLDwnzNBDoiQj71bWt7Y3N21cAkKrYYmd/eUJouSeIFjyAhFVGiz1JVOgJogUPIFG6J9FiTxQVeuLCFLxOlxsZAAxDbfXzOsh5LkXCCHQHQgtez1cfGwD0a2rzMK8MSaPl7kA4iEany3EQDYC+xME31sudoEJ3hoE5AD3Qsa0vCXJfqNCdCdX6nlGtA+iGHnfK4JtDVOiOUa0DaJGq8nMequIXFbpjVOsAWqC18liVE+aOUaFngmodwBamxgR7NqjQM9Go1s9tfpITACzDBHuGqNAzFKr1d/V1agBwlw6HOecM9vwQ6BnjlDkADZXNg3xqyBKBXoA62N/ZPNgBlEeVuM5g/0hVnjcCvRC04YEiTY2ht2IQ6IWpg/2kfvlgtOGBnGn72Rva62Vhyr0weloS0/BAtuL0+iFhXh4q9IKFNvyZsb4OeMc6OQh0sL4OODex+UlvlaFoBDpuhWDX+vqJAUjd1OZBPjXACHQsUAf72ObBfmAAUjM1ghwLMBSHB3Sj0FCNMTgHpGRaXy/Cca1TA+6hQsejOHEOGNTUqMixBgIdayPYgV5NjSDHBgh0bCwEuybixwagbVMjyLEFAh1bC8Nzr4ypeKANUyPIsQMCHTtjHzuwk0l9XRDk2BWBjtY0gv3IWGcHVuFkN7SOQEcnGKADFtJDUy7qa0KQo20EOjrFOjswMzXWx9ExAh29oB2PAtFWR68IdPSObW/I3NSoxjEAAh2DaTy+VeE+MsAvqnEMjkBHEsJa+1l9HdfXMwPSp+DWgNsl1ThSQKAjKXWwK8w1QEdLHqma2t8gpxpHMgh0JCu05MdGuGN40/q6MrabIWEEOlwg3DGAqRHicIRAhzuEOzqi0NbBL4Q4XCLQ4VpYcx/bfN2dPe7YlEL70uYhPiXE4RmBjqyEaXldR0b1jsWm9fXN5gE+NSATBDqyRfWOoLJ5Ba52OpPpyBaBjmI01t51EfD5quxuFV4ZUAACHcUKAX9g8wp+P3wMf1R5fwuvBDiKRaADQWjRK9TH9jfgR4aUVHY3wK9poQNzBDqwQqjidY1tHvL6mEq+H5X9DW99zBQ6sAKBDmyoUcnHCj5W85xBv53K5sF9E151VYQ3sBkCHWhJI+jj637j49LDvrK/B7fc2N8QJ7iBlhDoQE/qwI/BPgrX8/D6rPHqUWXzsI6vN3Y3wH8R2kD3CHQgIaHKH9k83OM1Cr/9PLyOGv+R+DnLfr7Kr3A1VQt+7+be71XxYybKgXT8H/v+bQl8AB+nAAAAAElFTkSuQmCC"></image><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
+@media (prefers-color-scheme: dark) { :root { filter: none; } }
+</style></svg>
\ No newline at end of file
diff --git a/static/favicon/site.webmanifest b/static/favicon/site.webmanifest
new file mode 100644
index 0000000000000000000000000000000000000000..0e59bbb28234b069baf19cb76cc38380d4821d3b
--- /dev/null
+++ b/static/favicon/site.webmanifest
@@ -0,0 +1,21 @@
+{
+  "name": "Open WebUI",
+  "short_name": "WebUI",
+  "icons": [
+    {
+      "src": "/favicon/web-app-manifest-192x192.png",
+      "sizes": "192x192",
+      "type": "image/png",
+      "purpose": "maskable"
+    },
+    {
+      "src": "/favicon/web-app-manifest-512x512.png",
+      "sizes": "512x512",
+      "type": "image/png",
+      "purpose": "maskable"
+    }
+  ],
+  "theme_color": "#ffffff",
+  "background_color": "#ffffff",
+  "display": "standalone"
+}
\ No newline at end of file
diff --git a/static/favicon/web-app-manifest-192x192.png b/static/favicon/web-app-manifest-192x192.png
new file mode 100644
index 0000000000000000000000000000000000000000..3fd0833f4fe0a3a238ce17446313e33df412681d
Binary files /dev/null and b/static/favicon/web-app-manifest-192x192.png differ
diff --git a/static/favicon/web-app-manifest-512x512.png b/static/favicon/web-app-manifest-512x512.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c4c24d129447f8d6ded3823b734b8c8b235d124
Binary files /dev/null and b/static/favicon/web-app-manifest-512x512.png differ
diff --git a/static/manifest.json b/static/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/static/manifest.json
@@ -0,0 +1 @@
+{}
diff --git a/static/opensearch.xml b/static/opensearch.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ce47e39ae988ff08b4d47eab9f7af325929d4599
--- /dev/null
+++ b/static/opensearch.xml
@@ -0,0 +1,8 @@
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
+<ShortName>Open WebUI</ShortName>
+<Description>Search Open WebUI</Description>
+<InputEncoding>UTF-8</InputEncoding>
+<Image width="16" height="16" type="image/x-icon">http://localhost:5137/favicon.png</Image>
+<Url type="text/html" method="get" template="http://localhost:5137/?q={searchTerms}"/>
+<moz:SearchForm>http://localhost:5137</moz:SearchForm>
+</OpenSearchDescription>
\ No newline at end of file
diff --git a/static/pyodide/pyodide-lock.json b/static/pyodide/pyodide-lock.json
new file mode 100644
index 0000000000000000000000000000000000000000..a651b04459c26ad27ac598e03e5acc802cba867a
--- /dev/null
+++ b/static/pyodide/pyodide-lock.json
@@ -0,0 +1 @@
+{"info": {"abi_version": "2024_0", "arch": "wasm32", "platform": "emscripten_3_1_58", "python": "3.12.1", "version": "0.26.1"}, "packages": {"aiohttp": {"depends": ["aiosignal", "async-timeout", "attrs", "charset-normalizer", "frozenlist", "multidict", "yarl"], "file_name": "aiohttp-3.9.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["aiohttp"], "install_dir": "site", "name": "aiohttp", "package_type": "package", "sha256": "c9a381c45ca9d6f16f6ec269c27a82bcbcaa3075f7f8e5de271ef5c256aa4ea4", "shared_library": false, "unvendored_tests": true, "version": "3.9.5"}, "aiohttp-tests": {"depends": ["aiohttp"], "file_name": "aiohttp-tests.tar", "imports": [], "install_dir": "site", "name": "aiohttp-tests", "package_type": "package", "sha256": "dc42d3bea4ede411be4cd43059f224832438b00f076fbe4d9d1ef516e2eab250", "shared_library": false, "unvendored_tests": false, "version": "3.9.5"}, "aiosignal": {"depends": ["frozenlist"], "file_name": "aiosignal-1.3.1-py3-none-any.whl", "imports": ["aiosignal"], "install_dir": "site", "name": "aiosignal", "package_type": "package", "sha256": "e091282e280a5940e759c5c849dfcd3169f8eea21d5838c872ec1265ef825f1e", "shared_library": false, "unvendored_tests": false, "version": "1.3.1"}, "altair": {"depends": ["typing-extensions", "jinja2", "jsonschema", "numpy", "pandas", "toolz", "packaging"], "file_name": "altair-5.3.0-py3-none-any.whl", "imports": ["altair"], "install_dir": "site", "name": "altair", "package_type": "package", "sha256": "1d2f248506ab81f13292d42464faa60c4940c1ef5da9013dc3a97ed398d6f7f7", "shared_library": false, "unvendored_tests": false, "version": "5.3.0"}, "annotated-types": {"depends": [], "file_name": "annotated_types-0.6.0-py3-none-any.whl", "imports": ["annotated_types"], "install_dir": "site", "name": "annotated-types", "package_type": "package", "sha256": "337d2a3e1b65926cd6560d0c9a33f8be55d6b8a21e846091bc7ab8fdb2f421a3", "shared_library": false, "unvendored_tests": true, "version": "0.6.0"}, "annotated-types-tests": {"depends": ["annotated-types"], "file_name": "annotated-types-tests.tar", "imports": [], "install_dir": "site", "name": "annotated-types-tests", "package_type": "package", "sha256": "0eecd674a295f84758689100739cca7d54f73cd6288c41f2269f78a01e465a8d", "shared_library": false, "unvendored_tests": false, "version": "0.6.0"}, "asciitree": {"depends": [], "file_name": "asciitree-0.3.3-py3-none-any.whl", "imports": ["asciitree"], "install_dir": "site", "name": "asciitree", "package_type": "package", "sha256": "15d47009b9cacdddacbc7ef306e64d103ea96d4fdc0fbb2d579e43c8fe8666bf", "shared_library": false, "unvendored_tests": false, "version": "0.3.3"}, "astropy": {"depends": ["packaging", "numpy", "pyerfa", "pyyaml", "astropy_iers_data"], "file_name": "astropy-6.0.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["astropy"], "install_dir": "site", "name": "astropy", "package_type": "package", "sha256": "94be600bfff4973c962112913bf86b5d1c4ba8b162b6ef409a03f70a0cefb9e9", "shared_library": false, "unvendored_tests": false, "version": "6.0.1"}, "astropy-iers-data": {"depends": [], "file_name": "astropy_iers_data-0.2024.4.22.0.29.50-py3-none-any.whl", "imports": ["astropy_iers_data"], "install_dir": "site", "name": "astropy_iers_data", "package_type": "package", "sha256": "b9989c71b05bc8a550d74d7e49d5e041f192f420bdf8b71553adb225e286f478", "shared_library": false, "unvendored_tests": true, "version": "0.2024.4.22.0.29.50"}, "astropy-iers-data-tests": {"depends": ["astropy_iers_data"], "file_name": "astropy_iers_data-tests.tar", "imports": [], "install_dir": "site", "name": "astropy_iers_data-tests", "package_type": "package", "sha256": "3cbaffbea097e4d5f206a7f357b59871a67594cd3f899ad45a28c293c271d4f1", "shared_library": false, "unvendored_tests": false, "version": "0.2024.4.22.0.29.50"}, "asttokens": {"depends": ["six"], "file_name": "asttokens-2.4.1-py2.py3-none-any.whl", "imports": ["asttokens"], "install_dir": "site", "name": "asttokens", "package_type": "package", "sha256": "4f62a79cfd557b35cd1f1e4809c61eef0b6e54e0dea5270653bbde3fc341d05a", "shared_library": false, "unvendored_tests": false, "version": "2.4.1"}, "async-timeout": {"depends": [], "file_name": "async_timeout-4.0.3-py3-none-any.whl", "imports": ["async_timeout"], "install_dir": "site", "name": "async-timeout", "package_type": "package", "sha256": "39d42f0d92a009c9205d74a01ff194d89ea9f1741af36998bc405c615993779e", "shared_library": false, "unvendored_tests": false, "version": "4.0.3"}, "atomicwrites": {"depends": [], "file_name": "atomicwrites-1.4.1-py2.py3-none-any.whl", "imports": ["atomicwrites"], "install_dir": "site", "name": "atomicwrites", "package_type": "package", "sha256": "7ac6f1fb3dde1c23246b08b6d2a380fb9e1b344e57126f00bd9364ad1104bc82", "shared_library": false, "unvendored_tests": false, "version": "1.4.1"}, "attrs": {"depends": ["six"], "file_name": "attrs-23.2.0-py3-none-any.whl", "imports": ["attr", "attrs"], "install_dir": "site", "name": "attrs", "package_type": "package", "sha256": "8e0e26b04b67bd3f09a60f289a0673aadb45daef63c8a6ceca762159d41bdea8", "shared_library": false, "unvendored_tests": false, "version": "23.2.0"}, "autograd": {"depends": ["numpy", "future"], "file_name": "autograd-1.6.2-py3-none-any.whl", "imports": ["autograd"], "install_dir": "site", "name": "autograd", "package_type": "package", "sha256": "4fd6746b144d95de5e8dceae6d2d4fa2a962810b93e420430df74506e915af67", "shared_library": false, "unvendored_tests": true, "version": "1.6.2"}, "autograd-tests": {"depends": ["autograd"], "file_name": "autograd-tests.tar", "imports": [], "install_dir": "site", "name": "autograd-tests", "package_type": "package", "sha256": "4869f8c5b9bbdedfb84ebe2f866934dd9204f62d65b8e2e1c87feb5f769afec3", "shared_library": false, "unvendored_tests": false, "version": "1.6.2"}, "awkward-cpp": {"depends": ["numpy"], "file_name": "awkward_cpp-33-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["awkward_cpp"], "install_dir": "site", "name": "awkward-cpp", "package_type": "package", "sha256": "6dba5ce80f904ade19758b4e0b1e3bc4681aa0bb5e414d6a243251bc3ff9fd10", "shared_library": false, "unvendored_tests": false, "version": "33"}, "b2d": {"depends": ["numpy", "pydantic", "setuptools", "annotated-types"], "file_name": "b2d-0.7.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["b2d"], "install_dir": "site", "name": "b2d", "package_type": "package", "sha256": "6a2c8d02641ff9a65b19adfbe0f0aeef1ad1a8a8915a91122d554c3335c82faa", "shared_library": false, "unvendored_tests": false, "version": "0.7.4"}, "bcrypt": {"depends": [], "file_name": "bcrypt-4.1.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["bcrypt"], "install_dir": "site", "name": "bcrypt", "package_type": "package", "sha256": "8981e7922af6f1f6e2ad31ef120c67bfee047916e0b764ed5cd2a1ed8eaadbc7", "shared_library": false, "unvendored_tests": false, "version": "4.1.2"}, "beautifulsoup4": {"depends": ["soupsieve"], "file_name": "beautifulsoup4-4.12.3-py3-none-any.whl", "imports": ["bs4"], "install_dir": "site", "name": "beautifulsoup4", "package_type": "package", "sha256": "94a8052bb54628e76229fe6a1d1d16aa360f265c15abbca97497dad5370f7a7b", "shared_library": false, "unvendored_tests": true, "version": "4.12.3"}, "beautifulsoup4-tests": {"depends": ["beautifulsoup4"], "file_name": "beautifulsoup4-tests.tar", "imports": [], "install_dir": "site", "name": "beautifulsoup4-tests", "package_type": "package", "sha256": "55ec265dec8d21577aad92c4f7f2e5b22f3edecdc68fe003e525946bb088c44d", "shared_library": false, "unvendored_tests": false, "version": "4.12.3"}, "biopython": {"depends": ["numpy"], "file_name": "biopython-1.83-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["Bio", "BioSQL"], "install_dir": "site", "name": "biopython", "package_type": "package", "sha256": "4102d8fa77feca014bb2d49bebbae50cb6b0583737aff4ad02a3efaa6accd55c", "shared_library": false, "unvendored_tests": false, "version": "1.83"}, "bitarray": {"depends": [], "file_name": "bitarray-2.9.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["bitarray"], "install_dir": "site", "name": "bitarray", "package_type": "package", "sha256": "c7cfd44b70d8e6d5e26f81dbd7c7ac96cb562f6dceed02d6af3fdd23e6f74888", "shared_library": false, "unvendored_tests": true, "version": "2.9.2"}, "bitarray-tests": {"depends": ["bitarray"], "file_name": "bitarray-tests.tar", "imports": [], "install_dir": "site", "name": "bitarray-tests", "package_type": "package", "sha256": "3378a1981df0a26a423dde6ab332a4965447e35af539ceabfe4e330bed05f933", "shared_library": false, "unvendored_tests": false, "version": "2.9.2"}, "bitstring": {"depends": ["bitarray"], "file_name": "bitstring-4.1.4-py3-none-any.whl", "imports": ["bitstring"], "install_dir": "site", "name": "bitstring", "package_type": "package", "sha256": "fdea8060e5d10fe019b702aba16c3b0a349abca3230640dd70d57e49e824d127", "shared_library": false, "unvendored_tests": false, "version": "4.1.4"}, "bleach": {"depends": ["webencodings", "packaging", "six"], "file_name": "bleach-6.1.0-py3-none-any.whl", "imports": ["bleach"], "install_dir": "site", "name": "bleach", "package_type": "package", "sha256": "5e12c4669d0caeb23584e9fd5115a7c81c7d8cd10915c64e6f3357e549e3179d", "shared_library": false, "unvendored_tests": false, "version": "6.1.0"}, "bokeh": {"depends": ["contourpy", "numpy", "jinja2", "pandas", "pillow", "python-dateutil", "six", "typing-extensions", "pyyaml", "xyzservices"], "file_name": "bokeh-3.4.1-py3-none-any.whl", "imports": ["bokeh"], "install_dir": "site", "name": "bokeh", "package_type": "package", "sha256": "5617ba021385e030d02822fee0f10d2a58e97951472ddd6eaae4fc8682ff276f", "shared_library": false, "unvendored_tests": false, "version": "3.4.1"}, "boost-histogram": {"depends": ["numpy"], "file_name": "boost_histogram-1.4.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["boost_histogram"], "install_dir": "site", "name": "boost-histogram", "package_type": "package", "sha256": "76cb6e74ed49b53043fa55a0896279c3f83a8eb9b749f7a710360d08f2e324a4", "shared_library": false, "unvendored_tests": false, "version": "1.4.1"}, "brotli": {"depends": [], "file_name": "Brotli-1.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["brotli"], "install_dir": "site", "name": "brotli", "package_type": "package", "sha256": "60f96da7ec930a6c71d92f9f4c811ac96748d80c7f83f0664f2b59c08052f173", "shared_library": false, "unvendored_tests": false, "version": "1.1.0"}, "cachetools": {"depends": [], "file_name": "cachetools-5.3.3-py3-none-any.whl", "imports": ["cachetools"], "install_dir": "site", "name": "cachetools", "package_type": "package", "sha256": "89aabb74b24badd4557a1e4b2d6e2ac000089aeb4083b1c62afed95a40c88d90", "shared_library": false, "unvendored_tests": false, "version": "5.3.3"}, "cartopy": {"depends": ["shapely", "pyshp", "pyproj", "geos", "matplotlib", "scipy"], "file_name": "Cartopy-0.23.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cartopy"], "install_dir": "site", "name": "Cartopy", "package_type": "package", "sha256": "d202ba6909b904b13d2013ab791651e24658dae3c7dfb930ae14b2522a6064b9", "shared_library": false, "unvendored_tests": true, "version": "0.23.0"}, "cartopy-tests": {"depends": ["cartopy"], "file_name": "Cartopy-tests.tar", "imports": [], "install_dir": "site", "name": "Cartopy-tests", "package_type": "package", "sha256": "e817f56f9745cbe6a20df6aa41c03967fd82508354e9f5b8ddda9886be3be903", "shared_library": false, "unvendored_tests": false, "version": "0.23.0"}, "cbor-diag": {"depends": [], "file_name": "cbor_diag-1.0.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cbor_diag"], "install_dir": "site", "name": "cbor-diag", "package_type": "package", "sha256": "e4f0f8e870c80e76a50edc2a12883a90b87c8c8a6296444cd32c58157aebef2a", "shared_library": false, "unvendored_tests": false, "version": "1.0.1"}, "certifi": {"depends": [], "file_name": "certifi-2024.2.2-py3-none-any.whl", "imports": ["certifi"], "install_dir": "site", "name": "certifi", "package_type": "package", "sha256": "60307c886a375d40cf3ba444151c347b4271e9dcd7f432517896dcd692dafc62", "shared_library": false, "unvendored_tests": false, "version": "2024.2.2"}, "cffi": {"depends": ["pycparser"], "file_name": "cffi-1.16.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cffi"], "install_dir": "site", "name": "cffi", "package_type": "package", "sha256": "4a993a87e8a955d817656931640ee3a5e2abb887e73397def631613b1ee91273", "shared_library": false, "unvendored_tests": false, "version": "1.16.0"}, "cffi-example": {"depends": ["cffi"], "file_name": "cffi_example-0.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cffi_example"], "install_dir": "site", "name": "cffi_example", "package_type": "package", "sha256": "0419901f15584f393072aabafdaf30b9469fdd515771b15fa0348ad4145303de", "shared_library": false, "unvendored_tests": false, "version": "0.1"}, "cftime": {"depends": ["numpy"], "file_name": "cftime-1.6.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cftime"], "install_dir": "site", "name": "cftime", "package_type": "package", "sha256": "af2ffd030f317b437c9d8b51348d5203c06c6d62363b29780d83c0f0b37f7912", "shared_library": false, "unvendored_tests": false, "version": "1.6.3"}, "charset-normalizer": {"depends": [], "file_name": "charset_normalizer-3.3.2-py3-none-any.whl", "imports": ["charset_normalizer"], "install_dir": "site", "name": "charset-normalizer", "package_type": "package", "sha256": "b7159d1583fd0938540590346a637e8f4661d9a31461c1a7bc57893a71089acb", "shared_library": false, "unvendored_tests": false, "version": "3.3.2"}, "clarabel": {"depends": ["numpy", "scipy"], "file_name": "clarabel-0.7.1-cp37-abi3-pyodide_2024_0_wasm32.whl", "imports": ["clarabel"], "install_dir": "site", "name": "clarabel", "package_type": "package", "sha256": "2065e3eae13ee52440cadd32e373bd748536a572698f2e722315948f1bf1800c", "shared_library": false, "unvendored_tests": false, "version": "0.7.1"}, "click": {"depends": [], "file_name": "click-8.1.7-py3-none-any.whl", "imports": ["click"], "install_dir": "site", "name": "click", "package_type": "package", "sha256": "a91efee37121d94b0a1584676a08fba266acf76ca821274cbd7bf6abbb7984df", "shared_library": false, "unvendored_tests": false, "version": "8.1.7"}, "cligj": {"depends": ["click"], "file_name": "cligj-0.7.2-py3-none-any.whl", "imports": ["cligj"], "install_dir": "site", "name": "cligj", "package_type": "package", "sha256": "b938b3aa1e035663db0077f98b889af0f6e3a1fcba727392ef0fdb999c061a76", "shared_library": false, "unvendored_tests": false, "version": "0.7.2"}, "cloudpickle": {"depends": [], "file_name": "cloudpickle-3.0.0-py3-none-any.whl", "imports": ["cloudpickle"], "install_dir": "site", "name": "cloudpickle", "package_type": "package", "sha256": "c658a09163430185fcc8b091cccec3b7809a66a89ce366ddcea721bae39082f6", "shared_library": false, "unvendored_tests": false, "version": "3.0.0"}, "cmyt": {"depends": ["colorspacious", "matplotlib", "more-itertools", "numpy"], "file_name": "cmyt-2.0.0-py3-none-any.whl", "imports": ["cmyt"], "install_dir": "site", "name": "cmyt", "package_type": "package", "sha256": "5e99f7f3922eebd62e1570a254cd6aba140efadc5f5dab743ad16859f84071d3", "shared_library": false, "unvendored_tests": true, "version": "2.0.0"}, "cmyt-tests": {"depends": ["cmyt"], "file_name": "cmyt-tests.tar", "imports": [], "install_dir": "site", "name": "cmyt-tests", "package_type": "package", "sha256": "8cabd34a691476a4c425459e8ca0cf39c34d552a34e23ced302edc3c3257a671", "shared_library": false, "unvendored_tests": false, "version": "2.0.0"}, "colorspacious": {"depends": ["numpy"], "file_name": "colorspacious-1.1.2-py2.py3-none-any.whl", "imports": ["colorspacious"], "install_dir": "site", "name": "colorspacious", "package_type": "package", "sha256": "acee2628cc1eafc4ef7456563bb3dbe0f1578e95dd96a02d8127b09a552dbd33", "shared_library": false, "unvendored_tests": false, "version": "1.1.2"}, "contourpy": {"depends": ["numpy"], "file_name": "contourpy-1.2.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["contourpy"], "install_dir": "site", "name": "contourpy", "package_type": "package", "sha256": "8cc70108fea11bc60feb5d38fa4546155f00407c8ffcd082600c62bf0278b0a5", "shared_library": false, "unvendored_tests": false, "version": "1.2.1"}, "coolprop": {"depends": ["numpy", "matplotlib"], "file_name": "CoolProp-6.6.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["CoolProp"], "install_dir": "site", "name": "coolprop", "package_type": "package", "sha256": "aa0083a1067adc59aa8741e10ea67b677f2693e0fe95574ca4589169cef2d03b", "shared_library": false, "unvendored_tests": true, "version": "6.6.0"}, "coolprop-tests": {"depends": ["coolprop"], "file_name": "coolprop-tests.tar", "imports": [], "install_dir": "site", "name": "coolprop-tests", "package_type": "package", "sha256": "82b2574e6b4ee184156367481db9b9479f7f3d6cf5880940b0e726f866f3677c", "shared_library": false, "unvendored_tests": false, "version": "6.6.0"}, "coverage": {"depends": ["sqlite3"], "file_name": "coverage-7.4.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["coverage"], "install_dir": "site", "name": "coverage", "package_type": "package", "sha256": "606a1a385c30766260b5b7d2f7ded768a5f91e29b0c090c5bbac7b2bb2b2c2e7", "shared_library": false, "unvendored_tests": false, "version": "7.4.4"}, "cpp-exceptions-test": {"depends": [], "file_name": "cpp-exceptions-test-0.1.zip", "imports": [], "install_dir": "dynlib", "name": "cpp-exceptions-test", "package_type": "shared_library", "sha256": "23ee6f17609635436bc8cced518a3adec8bdf095f0dda8062166e39077cf8005", "shared_library": true, "unvendored_tests": false, "version": "0.1"}, "cpp-exceptions-test2": {"depends": [], "file_name": "cpp_exceptions_test2-1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cpp-exceptions-test2"], "install_dir": "site", "name": "cpp-exceptions-test2", "package_type": "package", "sha256": "5d5c8a58219d050452190b4fbe3a0577c9da2ac557822a977d34c08b1ff4cac9", "shared_library": false, "unvendored_tests": false, "version": "1.0"}, "cramjam": {"depends": [], "file_name": "cramjam-2.8.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cramjam"], "install_dir": "site", "name": "cramjam", "package_type": "package", "sha256": "42c509a8c042b90f8d39b2b428d7512e40110b6bc8bff417ff5f345fba3860bd", "shared_library": false, "unvendored_tests": false, "version": "2.8.3"}, "crc32c": {"depends": [], "file_name": "crc32c-2.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["crc32c"], "install_dir": "site", "name": "crc32c", "package_type": "package", "sha256": "c0703c9b40d31758e7d836a2f0657c343a8a60d2e09bb2826e0d01febe41b6a4", "shared_library": false, "unvendored_tests": false, "version": "2.4"}, "cryptography": {"depends": ["openssl", "six", "cffi"], "file_name": "cryptography-42.0.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cryptography"], "install_dir": "site", "name": "cryptography", "package_type": "package", "sha256": "c05ea45f7aa36950b2f7c4216147e51abd6e6899656c512e106c7043bc7772d9", "shared_library": false, "unvendored_tests": false, "version": "42.0.5"}, "cssselect": {"depends": [], "file_name": "cssselect-1.2.0-py2.py3-none-any.whl", "imports": ["cssselect"], "install_dir": "site", "name": "cssselect", "package_type": "package", "sha256": "a835c843cac791f67be9d6ad2eabe0d2690cab0c147902ffe604018271bc557c", "shared_library": false, "unvendored_tests": false, "version": "1.2.0"}, "cvxpy-base": {"depends": ["numpy", "scipy", "clarabel"], "file_name": "cvxpy_base-1.5.1-py3-none-any.whl", "imports": ["cvxpy"], "install_dir": "site", "name": "cvxpy-base", "package_type": "package", "sha256": "43dcbeee1e7875516cdf6d3a227cc9d6720b91dc0765c2f4a2dfe1a297607324", "shared_library": false, "unvendored_tests": true, "version": "1.5.1"}, "cvxpy-base-tests": {"depends": ["cvxpy-base"], "file_name": "cvxpy-base-tests.tar", "imports": [], "install_dir": "site", "name": "cvxpy-base-tests", "package_type": "package", "sha256": "a18ac6cec0d4a36e5297d9920717bfdab1db27b56ae9aae245c04051b8bf6990", "shared_library": false, "unvendored_tests": false, "version": "1.5.1"}, "cycler": {"depends": ["six"], "file_name": "cycler-0.12.1-py3-none-any.whl", "imports": ["cycler"], "install_dir": "site", "name": "cycler", "package_type": "package", "sha256": "62243ceb501f58ad420395814f88d259d7546d115bd652e67d281b476b17c96e", "shared_library": false, "unvendored_tests": false, "version": "0.12.1"}, "cysignals": {"depends": [], "file_name": "cysignals-1.11.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cysignals"], "install_dir": "site", "name": "cysignals", "package_type": "package", "sha256": "ff6b27611229d4fffd3ff5f9a8d88854e5181441a2e9c2e337e6bc79ee14de5e", "shared_library": false, "unvendored_tests": false, "version": "1.11.4"}, "cytoolz": {"depends": ["toolz"], "file_name": "cytoolz-0.12.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cytoolz"], "install_dir": "site", "name": "cytoolz", "package_type": "package", "sha256": "3db082ceb3c2e49842ad8065d5d9a48cadfa58752d0ee00cf0d9b86cb4b3cf8c", "shared_library": false, "unvendored_tests": true, "version": "0.12.3"}, "cytoolz-tests": {"depends": ["cytoolz"], "file_name": "cytoolz-tests.tar", "imports": [], "install_dir": "site", "name": "cytoolz-tests", "package_type": "package", "sha256": "9b8bae8b847080f2530f764d63b4a87df456f96a63eadb2eefc8884a47beee2b", "shared_library": false, "unvendored_tests": false, "version": "0.12.3"}, "decorator": {"depends": [], "file_name": "decorator-5.1.1-py3-none-any.whl", "imports": ["decorator"], "install_dir": "site", "name": "decorator", "package_type": "package", "sha256": "e7b2576b7ab0f7fab4a8260ce091e610dedeb2f7874c328db3920c5b1d06a57e", "shared_library": false, "unvendored_tests": false, "version": "5.1.1"}, "demes": {"depends": ["attrs", "ruamel.yaml"], "file_name": "demes-0.2.3-py3-none-any.whl", "imports": ["demes"], "install_dir": "site", "name": "demes", "package_type": "package", "sha256": "8be66f0c28a713b8efc6626b4aeb7cdeea70818b2ff3877ebb731a0e6a638fdd", "shared_library": false, "unvendored_tests": false, "version": "0.2.3"}, "deprecation": {"depends": ["packaging"], "file_name": "deprecation-2.1.0-py2.py3-none-any.whl", "imports": ["deprecation"], "install_dir": "site", "name": "deprecation", "package_type": "package", "sha256": "7e29e9de88b1bb7319e512cf74e474e47fe216978df72fe696504c464f564088", "shared_library": false, "unvendored_tests": false, "version": "2.1.0"}, "distlib": {"depends": [], "file_name": "distlib-0.3.8-py2.py3-none-any.whl", "imports": ["distlib"], "install_dir": "site", "name": "distlib", "package_type": "package", "sha256": "c38f21e1668748621aa187e1d77017a072cad344df5b17e855c87d7b29cbeb56", "shared_library": false, "unvendored_tests": false, "version": "0.3.8"}, "docutils": {"depends": [], "file_name": "docutils-0.21.1-py3-none-any.whl", "imports": ["docutils"], "install_dir": "site", "name": "docutils", "package_type": "package", "sha256": "820ce56834fd002d80f0512070f7d71565f076de184287a491cbad6273e5a76c", "shared_library": false, "unvendored_tests": false, "version": "0.21.1"}, "ewah-bool-utils": {"depends": [], "file_name": "ewah_bool_utils-1.2.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["ewah_bool_utils"], "install_dir": "site", "name": "ewah_bool_utils", "package_type": "package", "sha256": "2d64e4cc33e22bc6e54a3cea53f6b7b156f9b5d24c81a012eaab449edb64b2bd", "shared_library": false, "unvendored_tests": true, "version": "1.2.0"}, "ewah-bool-utils-tests": {"depends": ["ewah_bool_utils"], "file_name": "ewah_bool_utils-tests.tar", "imports": [], "install_dir": "site", "name": "ewah_bool_utils-tests", "package_type": "package", "sha256": "1bb3b4036603e5e7a1209236f78105e7036af3b0b615dd38b2bc2c92b60232e8", "shared_library": false, "unvendored_tests": false, "version": "1.2.0"}, "exceptiongroup": {"depends": [], "file_name": "exceptiongroup-1.2.1-py3-none-any.whl", "imports": ["exceptiongroup"], "install_dir": "site", "name": "exceptiongroup", "package_type": "package", "sha256": "92b9f147b8f6e461cf0caf268db6a6e2ee7efb228d60d80f7dd92d521d59e469", "shared_library": false, "unvendored_tests": false, "version": "1.2.1"}, "executing": {"depends": [], "file_name": "executing-2.0.1-py2.py3-none-any.whl", "imports": ["executing"], "install_dir": "site", "name": "executing", "package_type": "package", "sha256": "9d446583b446b5795bc02ae5e92eebd5ffba061287b3940f45462f2612f47b6a", "shared_library": false, "unvendored_tests": false, "version": "2.0.1"}, "fastparquet": {"depends": ["cramjam", "numpy", "pandas", "fsspec", "packaging"], "file_name": "fastparquet-2024.2.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["fastparquet"], "install_dir": "site", "name": "fastparquet", "package_type": "package", "sha256": "ea0392fe11e9fbdea79ad8199d13a38798eb9981413e08539e6bbd33d17b85be", "shared_library": false, "unvendored_tests": false, "version": "2024.2.0"}, "fiona": {"depends": ["attrs", "certifi", "setuptools", "six", "click", "cligj"], "file_name": "fiona-1.9.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["fiona"], "install_dir": "site", "name": "fiona", "package_type": "package", "sha256": "0f55d6c5d5b09d420e2b58d010967b5e2e43c1ffdf3aa1d559951ac10d893655", "shared_library": false, "unvendored_tests": true, "version": "1.9.5"}, "fiona-tests": {"depends": ["fiona"], "file_name": "fiona-tests.tar", "imports": [], "install_dir": "site", "name": "fiona-tests", "package_type": "package", "sha256": "6b82496111cea9551fa09174c70c47c12ceb6e5885c8511683e3bbf7b7e29ff5", "shared_library": false, "unvendored_tests": false, "version": "1.9.5"}, "fonttools": {"depends": [], "file_name": "fonttools-4.51.0-py3-none-any.whl", "imports": ["fontTools"], "install_dir": "site", "name": "fonttools", "package_type": "package", "sha256": "e27bd9df5b39f6210c66571b12aa2372310e9e96aee0b605fc5021716b5f3249", "shared_library": false, "unvendored_tests": false, "version": "4.51.0"}, "fpcast-test": {"depends": [], "file_name": "fpcast_test-0.1.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["fpcast_test"], "install_dir": "site", "name": "fpcast-test", "package_type": "package", "sha256": "0f72430fbccb73c6690a931413032f9c1892bd07b7af2ba59f32374ec4970756", "shared_library": false, "unvendored_tests": false, "version": "0.1.1"}, "freesasa": {"depends": [], "file_name": "freesasa-2.2.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["freesasa"], "install_dir": "site", "name": "freesasa", "package_type": "package", "sha256": "d3d478c7aa17ca7b6caf6609cc3152929f4cb43d20fdda7217e648aad96acf0e", "shared_library": false, "unvendored_tests": false, "version": "2.2.1"}, "frozenlist": {"depends": [], "file_name": "frozenlist-1.4.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["frozenlist"], "install_dir": "site", "name": "frozenlist", "package_type": "package", "sha256": "481ab66c7f7b932d8fadfadece1bd9928c0ee471da75cff2baf22845baea2a2c", "shared_library": false, "unvendored_tests": false, "version": "1.4.1"}, "fsspec": {"depends": [], "file_name": "fsspec-2024.3.1-py3-none-any.whl", "imports": ["fsspec"], "install_dir": "site", "name": "fsspec", "package_type": "package", "sha256": "2f1439e76d695b8414d89c7c85cdb0ce0f227be1ef083f07e8c9452e0718d9c8", "shared_library": false, "unvendored_tests": true, "version": "2024.3.1"}, "fsspec-tests": {"depends": ["fsspec"], "file_name": "fsspec-tests.tar", "imports": [], "install_dir": "site", "name": "fsspec-tests", "package_type": "package", "sha256": "0f777d658d32926b150c87c88b5e839545d6ad88320818027908ac83cf7bd8de", "shared_library": false, "unvendored_tests": false, "version": "2024.3.1"}, "future": {"depends": [], "file_name": "future-1.0.0-py3-none-any.whl", "imports": ["future"], "install_dir": "site", "name": "future", "package_type": "package", "sha256": "445e96936c4f8ae76ddb2fa505308eeda42af28db4584037634318c3ebaf9d03", "shared_library": false, "unvendored_tests": true, "version": "1.0.0"}, "future-tests": {"depends": ["future"], "file_name": "future-tests.tar", "imports": [], "install_dir": "site", "name": "future-tests", "package_type": "package", "sha256": "eb8fedb6b9848fa3ab6fa1362f81b7d1c5e8c89773783d923584b95a3b97547f", "shared_library": false, "unvendored_tests": false, "version": "1.0.0"}, "galpy": {"depends": ["numpy", "scipy", "matplotlib", "astropy", "future", "setuptools"], "file_name": "galpy-1.9.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["galpy"], "install_dir": "site", "name": "galpy", "package_type": "package", "sha256": "8f5af7d9d1c7a558fe0ac137ad88f59185bdf30dec8649f291403eceff248b98", "shared_library": false, "unvendored_tests": false, "version": "1.9.2"}, "gdal": {"depends": ["geos"], "file_name": "gdal-3.8.3.zip", "imports": [], "install_dir": "dynlib", "name": "gdal", "package_type": "shared_library", "sha256": "30401f5d6894f73556a95de723c278de64c617dc35a10894b77154c94292b572", "shared_library": true, "unvendored_tests": false, "version": "3.8.3"}, "gensim": {"depends": ["numpy", "scipy", "six", "smart_open", "wrapt"], "file_name": "gensim-4.3.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["gensim"], "install_dir": "site", "name": "gensim", "package_type": "package", "sha256": "3c4b346db7bc0fd0174881be499333eef4aade2aca690beeef4a1a5fd05b6815", "shared_library": false, "unvendored_tests": true, "version": "4.3.2"}, "gensim-tests": {"depends": ["gensim"], "file_name": "gensim-tests.tar", "imports": [], "install_dir": "site", "name": "gensim-tests", "package_type": "package", "sha256": "5ceb546ac41bb973d08be7c44a5732ce29f5720b5f35102149e4f6961223e19d", "shared_library": false, "unvendored_tests": false, "version": "4.3.2"}, "geopandas": {"depends": ["shapely", "fiona", "pyproj", "packaging", "pandas"], "file_name": "geopandas-0.14.3-py3-none-any.whl", "imports": ["geopandas"], "install_dir": "site", "name": "geopandas", "package_type": "package", "sha256": "ad8eade2c6f45ba17c878a302910ebc3d6f7cecd009cdb5d63d140781afb4a21", "shared_library": false, "unvendored_tests": true, "version": "0.14.3"}, "geopandas-tests": {"depends": ["geopandas"], "file_name": "geopandas-tests.tar", "imports": [], "install_dir": "site", "name": "geopandas-tests", "package_type": "package", "sha256": "40703d011365f2763e7dd7514be85e65af8f488671ab4f1aaaacc7ad1a14e23e", "shared_library": false, "unvendored_tests": false, "version": "0.14.3"}, "geos": {"depends": [], "file_name": "geos-3.12.1.zip", "imports": [], "install_dir": "dynlib", "name": "geos", "package_type": "shared_library", "sha256": "150ef1387663f8ddebb9e2b2d49c556f60ccfe3d6a8c3b2334f22bd2e3ad9e52", "shared_library": true, "unvendored_tests": false, "version": "3.12.1"}, "gmpy2": {"depends": [], "file_name": "gmpy2-2.1.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["gmpy2"], "install_dir": "site", "name": "gmpy2", "package_type": "package", "sha256": "60b06b4d6ad774cd76ce94dea287cab3d27fe846477ce5f1e5db58e9ebe7596b", "shared_library": false, "unvendored_tests": false, "version": "2.1.5"}, "gsw": {"depends": ["numpy"], "file_name": "gsw-3.6.17-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["gsw"], "install_dir": "site", "name": "gsw", "package_type": "package", "sha256": "abba76d5a7da89cd64646e7856599f31621ae9f48951cad5eac9ed75ab3ee031", "shared_library": false, "unvendored_tests": true, "version": "3.6.17"}, "gsw-tests": {"depends": ["gsw"], "file_name": "gsw-tests.tar", "imports": [], "install_dir": "site", "name": "gsw-tests", "package_type": "package", "sha256": "c1dcfb0baf3f2891f68ed569ed7f08264bca69cc210e1681d9628b64ce42b02d", "shared_library": false, "unvendored_tests": false, "version": "3.6.17"}, "h5py": {"depends": ["numpy", "pkgconfig"], "file_name": "h5py-3.11.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["h5py"], "install_dir": "site", "name": "h5py", "package_type": "package", "sha256": "5d7604354ff24428521620d75352946656375e7a725e7040c976f7be0a908055", "shared_library": false, "unvendored_tests": true, "version": "3.11.0"}, "h5py-tests": {"depends": ["h5py"], "file_name": "h5py-tests.tar", "imports": [], "install_dir": "site", "name": "h5py-tests", "package_type": "package", "sha256": "6f4d260fb05e96386f5091941bb520c804447436983e24901d4e7ba831ed15e7", "shared_library": false, "unvendored_tests": false, "version": "3.11.0"}, "hashlib": {"depends": ["openssl"], "file_name": "hashlib-1.0.0.zip", "imports": ["_hashlib"], "install_dir": "stdlib", "name": "hashlib", "package_type": "cpython_module", "sha256": "2608deda99041715fec1152b2d2d377f591035fcab44a76e270a898b9f70f580", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "html5lib": {"depends": ["webencodings", "six"], "file_name": "html5lib-1.1-py2.py3-none-any.whl", "imports": ["html5lib"], "install_dir": "site", "name": "html5lib", "package_type": "package", "sha256": "58e4002edf04a42f3ec887c5b68c432b21427603b4d1090e379259f6678a3d58", "shared_library": false, "unvendored_tests": false, "version": "1.1"}, "idna": {"depends": [], "file_name": "idna-3.7-py3-none-any.whl", "imports": ["idna"], "install_dir": "site", "name": "idna", "package_type": "package", "sha256": "9fda8aaea4574988fcd20f729a3b6df5b22488245ef347ec63bfe794325d4def", "shared_library": false, "unvendored_tests": false, "version": "3.7"}, "igraph": {"depends": ["texttable"], "file_name": "igraph-0.11.4-cp39-abi3-pyodide_2024_0_wasm32.whl", "imports": ["igraph"], "install_dir": "site", "name": "igraph", "package_type": "package", "sha256": "d6030d268459626e1db94e26ec6f8d372e808e9a9cdf4d684cbe6bb2e6afa866", "shared_library": false, "unvendored_tests": false, "version": "0.11.4"}, "imageio": {"depends": ["numpy", "pillow"], "file_name": "imageio-2.34.1-py3-none-any.whl", "imports": ["imageio"], "install_dir": "site", "name": "imageio", "package_type": "package", "sha256": "052dc45002387ef8b9fd79693c99e67591bcaaf1ca9a82cb67f0de348325122b", "shared_library": false, "unvendored_tests": false, "version": "2.34.1"}, "iniconfig": {"depends": [], "file_name": "iniconfig-2.0.0-py3-none-any.whl", "imports": ["iniconfig"], "install_dir": "site", "name": "iniconfig", "package_type": "package", "sha256": "f9cab05fb8a8d99890626831d140b0f9c26c44ed38a520b89c42ff07afef929d", "shared_library": false, "unvendored_tests": false, "version": "2.0.0"}, "ipython": {"depends": ["asttokens", "decorator", "executing", "matplotlib-inline", "prompt_toolkit", "pure_eval", "pygments", "six", "stack_data", "traitlets", "sqlite3", "wcwidth"], "file_name": "ipython-8.23.0-py3-none-any.whl", "imports": ["IPython"], "install_dir": "site", "name": "ipython", "package_type": "package", "sha256": "cff64d70b073fb2dcf718ee4abd47767373171b864e3d30f890f1573f0654c22", "shared_library": false, "unvendored_tests": true, "version": "8.23.0"}, "ipython-tests": {"depends": ["ipython"], "file_name": "ipython-tests.tar", "imports": [], "install_dir": "site", "name": "ipython-tests", "package_type": "package", "sha256": "b5a49455fa364805cfd9b765a1c68df0dc12cedd79e25ba4096cbe94f04bf10d", "shared_library": false, "unvendored_tests": false, "version": "8.23.0"}, "jedi": {"depends": ["parso"], "file_name": "jedi-0.19.1-py2.py3-none-any.whl", "imports": ["jedi"], "install_dir": "site", "name": "jedi", "package_type": "package", "sha256": "5ee59883b4346dcc0c88adc8973f1dc635b38779f17595a7abec794cc09e8840", "shared_library": false, "unvendored_tests": true, "version": "0.19.1"}, "jedi-tests": {"depends": ["jedi"], "file_name": "jedi-tests.tar", "imports": [], "install_dir": "site", "name": "jedi-tests", "package_type": "package", "sha256": "ca8d52714ffade10e237eed44cb8d359ade5ae6b92d8c7ba0ff66d00161f9dff", "shared_library": false, "unvendored_tests": false, "version": "0.19.1"}, "jinja2": {"depends": ["markupsafe"], "file_name": "Jinja2-3.1.3-py3-none-any.whl", "imports": ["jinja2"], "install_dir": "site", "name": "Jinja2", "package_type": "package", "sha256": "ed3b38b217a040a4dd6dd5f62fca8d0a18ab68f3d5c5a8fef12dcdfeaa9a4373", "shared_library": false, "unvendored_tests": false, "version": "3.1.3"}, "joblib": {"depends": [], "file_name": "joblib-1.4.0-py3-none-any.whl", "imports": ["joblib"], "install_dir": "site", "name": "joblib", "package_type": "package", "sha256": "e913d6bb8a168be5a89e76764eacbb56f8e93f99c8e19e93197da2a4f0331e3f", "shared_library": false, "unvendored_tests": true, "version": "1.4.0"}, "joblib-tests": {"depends": ["joblib"], "file_name": "joblib-tests.tar", "imports": [], "install_dir": "site", "name": "joblib-tests", "package_type": "package", "sha256": "a0e85b342cc9fdb888eef3328a7e99a0740f6a774953276f0a082793a3e10981", "shared_library": false, "unvendored_tests": false, "version": "1.4.0"}, "jsonschema": {"depends": ["attrs", "pyrsistent", "referencing", "jsonschema_specifications"], "file_name": "jsonschema-4.21.1-py3-none-any.whl", "imports": ["jsonschema"], "install_dir": "site", "name": "jsonschema", "package_type": "package", "sha256": "8e103c1dd50ecd40e385b2167ebb00182ed3aa3b455cd88d1edab5243c521503", "shared_library": false, "unvendored_tests": true, "version": "4.21.1"}, "jsonschema-specifications": {"depends": [], "file_name": "jsonschema_specifications-2023.12.1-py3-none-any.whl", "imports": ["jsonschema_specifications"], "install_dir": "site", "name": "jsonschema_specifications", "package_type": "package", "sha256": "40589d9387803ddaf07c51e0a935f6fd0af653826182dfc2eda0e6171320f83b", "shared_library": false, "unvendored_tests": true, "version": "2023.12.1"}, "jsonschema-specifications-tests": {"depends": ["jsonschema_specifications"], "file_name": "jsonschema_specifications-tests.tar", "imports": [], "install_dir": "site", "name": "jsonschema_specifications-tests", "package_type": "package", "sha256": "1aa663daca70e3b7560afb0097d1bcfd7cbea9f5fcd6cb290a020e66e1726f88", "shared_library": false, "unvendored_tests": false, "version": "2023.12.1"}, "jsonschema-tests": {"depends": ["jsonschema"], "file_name": "jsonschema-tests.tar", "imports": [], "install_dir": "site", "name": "jsonschema-tests", "package_type": "package", "sha256": "4f2b82f097f9379d0029c442fa67ed87fc37e3d53045dff293bb9f8491f96857", "shared_library": false, "unvendored_tests": false, "version": "4.21.1"}, "kiwisolver": {"depends": [], "file_name": "kiwisolver-1.4.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["kiwisolver"], "install_dir": "site", "name": "kiwisolver", "package_type": "package", "sha256": "93324048b98508b7c87c8827f7fb810803f4c19e1216d3f44ea39ebefce5d977", "shared_library": false, "unvendored_tests": false, "version": "1.4.5"}, "lakers-python": {"depends": [], "file_name": "lakers_python-0.3.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["lakers"], "install_dir": "site", "name": "lakers-python", "package_type": "package", "sha256": "a3ff1dd2c6b808ef58c25b889309a1183780faae47bc30a3d0c3fcf565225c46", "shared_library": false, "unvendored_tests": false, "version": "0.3.0"}, "lazy-loader": {"depends": [], "file_name": "lazy_loader-0.4-py3-none-any.whl", "imports": ["lazy_loader"], "install_dir": "site", "name": "lazy_loader", "package_type": "package", "sha256": "791a725032a6950412e9af99ea11e1c33767616f62fff82038dc5a1e011a2a15", "shared_library": false, "unvendored_tests": true, "version": "0.4"}, "lazy-loader-tests": {"depends": ["lazy_loader"], "file_name": "lazy_loader-tests.tar", "imports": [], "install_dir": "site", "name": "lazy_loader-tests", "package_type": "package", "sha256": "bb75854213b518faf51a33cf774b28c62db019a863e0c8b82336e15118402f07", "shared_library": false, "unvendored_tests": false, "version": "0.4"}, "lazy-object-proxy": {"depends": [], "file_name": "lazy_object_proxy-1.10.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["lazy_object_proxy"], "install_dir": "site", "name": "lazy-object-proxy", "package_type": "package", "sha256": "630987c74e6a8715032001b486625f6b5e319299e447b7ca9730bff8a476b49a", "shared_library": false, "unvendored_tests": false, "version": "1.10.0"}, "libcst": {"depends": ["pyyaml"], "file_name": "libcst-1.3.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["libcst"], "install_dir": "site", "name": "libcst", "package_type": "package", "sha256": "f503a45b3855bb1a008caf8f6ac1a71443bf66369cd6168dccc9f5331400a970", "shared_library": false, "unvendored_tests": true, "version": "1.3.1"}, "libcst-tests": {"depends": ["libcst"], "file_name": "libcst-tests.tar", "imports": [], "install_dir": "site", "name": "libcst-tests", "package_type": "package", "sha256": "61555281e0724435cf9ccf1722dfeb75776795c5d07dc143e06f0d8086e42700", "shared_library": false, "unvendored_tests": false, "version": "1.3.1"}, "libhdf5": {"depends": [], "file_name": "libhdf5-1.12.1.zip", "imports": [], "install_dir": "dynlib", "name": "libhdf5", "package_type": "shared_library", "sha256": "7cd1ee833910ded4bca697b2881f49e26d185bf84d5921baecdf84c6f3e78b8d", "shared_library": true, "unvendored_tests": false, "version": "1.12.1"}, "libheif": {"depends": [], "file_name": "libheif-1.12.0.zip", "imports": [], "install_dir": "dynlib", "name": "libheif", "package_type": "shared_library", "sha256": "ed954f71cd78d1fcf4f9aa93e34f598bbc9a87cc6a288c12f43b5dad800a85b2", "shared_library": true, "unvendored_tests": false, "version": "1.12.0"}, "libmagic": {"depends": [], "file_name": "libmagic-5.42.zip", "imports": [], "install_dir": "dynlib", "name": "libmagic", "package_type": "shared_library", "sha256": "b8c3aee549fcdb7484f24b5dd804192efc75f1de315523f44b626662cc807b67", "shared_library": true, "unvendored_tests": false, "version": "5.42"}, "libnetcdf": {"depends": [], "file_name": "libnetcdf-4.9.2.zip", "imports": [], "install_dir": "dynlib", "name": "libnetcdf", "package_type": "shared_library", "sha256": "25e03271a4ccc228f092d03d6e7001b622d87069f82b255781aaea09f793adaa", "shared_library": true, "unvendored_tests": false, "version": "4.9.2"}, "lightgbm": {"depends": ["numpy", "scipy", "scikit-learn"], "file_name": "lightgbm-4.3.0-py3-none-pyodide_2024_0_wasm32.whl", "imports": ["lightgbm"], "install_dir": "site", "name": "lightgbm", "package_type": "package", "sha256": "3bf6ce78f0341aa9f2ba194f61222bc1e91b1a60e2e2c46bc7bbd6fb5e8534b0", "shared_library": false, "unvendored_tests": false, "version": "4.3.0"}, "logbook": {"depends": ["ssl"], "file_name": "Logbook-1.7.0.post0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["logbook"], "install_dir": "site", "name": "logbook", "package_type": "package", "sha256": "ebdfee44251e45c6a3b7804f8742a477d374c732ec7851552e4f25eb267c1aef", "shared_library": false, "unvendored_tests": false, "version": "1.7.0.post0"}, "lxml": {"depends": [], "file_name": "lxml-5.2.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["lxml"], "install_dir": "site", "name": "lxml", "package_type": "package", "sha256": "71420ee4af4188f59c3e7acdf1d7cab4f4e2d10f9ec1bf4b1742f9075d4b36a2", "shared_library": false, "unvendored_tests": false, "version": "5.2.1"}, "lzma": {"depends": [], "file_name": "lzma-1.0.0.zip", "imports": ["lzma", "_lzma"], "install_dir": "stdlib", "name": "lzma", "package_type": "cpython_module", "sha256": "95f2169b9c57112556c5fc907c3429b572a204cf210048a9d65456ad8e3f4c14", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "markupsafe": {"depends": [], "file_name": "MarkupSafe-2.1.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["markupsafe"], "install_dir": "site", "name": "MarkupSafe", "package_type": "package", "sha256": "f2e79b5b66389a43f8381cece4b7dc03df1a28f9d083abe4e964f33bf9e9b72c", "shared_library": false, "unvendored_tests": false, "version": "2.1.5"}, "matplotlib": {"depends": ["cycler", "fonttools", "kiwisolver", "numpy", "packaging", "pillow", "pyparsing", "python-dateutil", "pytz", "matplotlib-pyodide"], "file_name": "matplotlib-3.5.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pylab", "mpl_toolkits", "matplotlib"], "install_dir": "site", "name": "matplotlib", "package_type": "package", "sha256": "670024b6ec06a27fe933dda6cbca5140497ff67d80c70f70b6e6a2d4ccef9b5d", "shared_library": false, "unvendored_tests": true, "version": "3.5.2"}, "matplotlib-inline": {"depends": ["traitlets"], "file_name": "matplotlib_inline-0.1.7-py3-none-any.whl", "imports": ["matplotlib-inline"], "install_dir": "site", "name": "matplotlib-inline", "package_type": "package", "sha256": "01eae0298c3b9d145a69317bbf9622c145130683bd5419b7112f6c2dcaed988a", "shared_library": false, "unvendored_tests": false, "version": "0.1.7"}, "matplotlib-pyodide": {"depends": [], "file_name": "matplotlib_pyodide-0.2.2-py3-none-any.whl", "imports": ["matplotlib_pyodide"], "install_dir": "site", "name": "matplotlib-pyodide", "package_type": "package", "sha256": "07d56729d9625b2b9bb9778a92c88c5c750476abb80f58d71b1386fed434eb8c", "shared_library": false, "unvendored_tests": false, "version": "0.2.2"}, "matplotlib-tests": {"depends": ["matplotlib"], "file_name": "matplotlib-tests.tar", "imports": [], "install_dir": "site", "name": "matplotlib-tests", "package_type": "package", "sha256": "47377f81b9aa221e30824c3edb8cf81268603a578fb6351aab69ec71da799b20", "shared_library": false, "unvendored_tests": false, "version": "3.5.2"}, "memory-allocator": {"depends": [], "file_name": "memory_allocator-0.1.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["memory_allocator"], "install_dir": "site", "name": "memory-allocator", "package_type": "package", "sha256": "b1e90dba5ac8b79c66a9677d52dd2c4ba15320151196c7cc9f30aacafb8752f0", "shared_library": false, "unvendored_tests": false, "version": "0.1.4"}, "micropip": {"depends": ["packaging"], "file_name": "micropip-0.6.0-py3-none-any.whl", "imports": ["micropip"], "install_dir": "site", "name": "micropip", "package_type": "package", "sha256": "1a3c889a69e6b2a15456f183e7711bd54175930cbe7ba09e91bec92cac8c418a", "shared_library": false, "unvendored_tests": false, "version": "0.6.0"}, "mmh3": {"depends": [], "file_name": "mmh3-4.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["mmh3"], "install_dir": "site", "name": "mmh3", "package_type": "package", "sha256": "ebd23d16d23dd0b2471857f6aa239bd68bfd1fe88a35072dce95ad9c0fb377df", "shared_library": false, "unvendored_tests": false, "version": "4.1.0"}, "mne": {"depends": ["numpy", "scipy", "setuptools", "decorator", "lazy_loader", "packaging"], "file_name": "mne-1.7.0-py3-none-any.whl", "imports": ["mne"], "install_dir": "site", "name": "mne", "package_type": "package", "sha256": "8086f8a7cb973332e29c253c9d60f9807c2384fba15b47b8d875786f6191366f", "shared_library": false, "unvendored_tests": true, "version": "1.7.0"}, "mne-tests": {"depends": ["mne"], "file_name": "mne-tests.tar", "imports": [], "install_dir": "site", "name": "mne-tests", "package_type": "package", "sha256": "7e5c8b54e62641797d2dfed22aed6cf8336135cf65008a3df2b7addb0c1e5015", "shared_library": false, "unvendored_tests": false, "version": "1.7.0"}, "more-itertools": {"depends": [], "file_name": "more_itertools-10.2.0-py3-none-any.whl", "imports": ["more_itertools"], "install_dir": "site", "name": "more-itertools", "package_type": "package", "sha256": "31e7d9b869d4cc1507cbd8ab9883dc52f04cfc60edeb867faa8a43199ee2dfd4", "shared_library": false, "unvendored_tests": false, "version": "10.2.0"}, "mpmath": {"depends": [], "file_name": "mpmath-1.3.0-py3-none-any.whl", "imports": ["mpmath"], "install_dir": "site", "name": "mpmath", "package_type": "package", "sha256": "9fd7b1f3a8a915006c2b48071c89ba620780c714d7e60560d9095f38bb440d9d", "shared_library": false, "unvendored_tests": true, "version": "1.3.0"}, "mpmath-tests": {"depends": ["mpmath"], "file_name": "mpmath-tests.tar", "imports": [], "install_dir": "site", "name": "mpmath-tests", "package_type": "package", "sha256": "c05a226349ebd546b5295ff3a43205e2180cfd552a1fa93d10cbea8763ac8053", "shared_library": false, "unvendored_tests": false, "version": "1.3.0"}, "msgpack": {"depends": [], "file_name": "msgpack-1.0.8-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["msgpack"], "install_dir": "site", "name": "msgpack", "package_type": "package", "sha256": "1e1b95c34b429d9ce044703050035fe1afed561fcbafb5615f9d0ce6d0b243d6", "shared_library": false, "unvendored_tests": false, "version": "1.0.8"}, "msgspec": {"depends": [], "file_name": "msgspec-0.18.6-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["msgspec"], "install_dir": "site", "name": "msgspec", "package_type": "package", "sha256": "8a3ada2ad782421c91e46dfe3bdc88279a9f9640874c774c7eb664f49b4f546e", "shared_library": false, "unvendored_tests": false, "version": "0.18.6"}, "msprime": {"depends": ["numpy", "newick", "tskit", "demes", "rpds-py"], "file_name": "msprime-1.3.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["msprime"], "install_dir": "site", "name": "msprime", "package_type": "package", "sha256": "a5f301b280e9821ae0994fb8dfbc547b41e12a6897e51139c4195de3682f922b", "shared_library": false, "unvendored_tests": false, "version": "1.3.1"}, "multidict": {"depends": [], "file_name": "multidict-6.0.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["multidict"], "install_dir": "site", "name": "multidict", "package_type": "package", "sha256": "01a44293972db77a7d74872a37dc32fc57e127ac6c3e92d6c70b039f4656d1f5", "shared_library": false, "unvendored_tests": false, "version": "6.0.5"}, "munch": {"depends": ["setuptools", "six"], "file_name": "munch-4.0.0-py2.py3-none-any.whl", "imports": ["munch"], "install_dir": "site", "name": "munch", "package_type": "package", "sha256": "107c09e48dd9dab92ced1bb625eb714396188b747775ae08f0b289839ac9d08e", "shared_library": false, "unvendored_tests": false, "version": "4.0.0"}, "mypy": {"depends": [], "file_name": "mypy-1.9.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["mypyc", "mypy"], "install_dir": "site", "name": "mypy", "package_type": "package", "sha256": "a0959e7167e1c1d2ca3f015a3b37462c8469a5846960e71db85c6f9700966b09", "shared_library": false, "unvendored_tests": true, "version": "1.9.0"}, "mypy-tests": {"depends": ["mypy"], "file_name": "mypy-tests.tar", "imports": [], "install_dir": "site", "name": "mypy-tests", "package_type": "package", "sha256": "901440be6cfa6a63bd22258515f8997855bf9e358846cbc3424eb630a29f987a", "shared_library": false, "unvendored_tests": false, "version": "1.9.0"}, "netcdf4": {"depends": ["numpy", "packaging", "h5py", "cftime", "certifi"], "file_name": "netCDF4-1.6.5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["netCDF4"], "install_dir": "site", "name": "netcdf4", "package_type": "package", "sha256": "2b7af5d41dded09350659179b4e5e119cb23652952e08eba560629b38dad52c4", "shared_library": false, "unvendored_tests": false, "version": "1.6.5"}, "networkx": {"depends": ["decorator", "setuptools", "matplotlib", "numpy"], "file_name": "networkx-3.3-py3-none-any.whl", "imports": ["networkx"], "install_dir": "site", "name": "networkx", "package_type": "package", "sha256": "0a39e7e4000d95224d4baf7d9e22c4e837fc67da2e0d39a9bb68bfca50860131", "shared_library": false, "unvendored_tests": true, "version": "3.3"}, "networkx-tests": {"depends": ["networkx"], "file_name": "networkx-tests.tar", "imports": [], "install_dir": "site", "name": "networkx-tests", "package_type": "package", "sha256": "d8bfd615ab6db81a3c8db5afabe78c6c61c80a7994b273aa69d26fdabc2675e8", "shared_library": false, "unvendored_tests": false, "version": "3.3"}, "newick": {"depends": [], "file_name": "newick-1.9.0-py2.py3-none-any.whl", "imports": ["newick"], "install_dir": "site", "name": "newick", "package_type": "package", "sha256": "fd7c551780ac51fbf27e0b1b1efada288a79bfc8b66afc5ea21cd42a7906085e", "shared_library": false, "unvendored_tests": false, "version": "1.9.0"}, "nh3": {"depends": [], "file_name": "nh3-0.2.17-cp37-abi3-pyodide_2024_0_wasm32.whl", "imports": ["nh3"], "install_dir": "site", "name": "nh3", "package_type": "package", "sha256": "ed8c980917c2e5cae0e37fd037fee7115119c27d6eab471662efb1ab6660a284", "shared_library": false, "unvendored_tests": false, "version": "0.2.17"}, "nlopt": {"depends": ["numpy"], "file_name": "nlopt-2.7.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["nlopt"], "install_dir": "site", "name": "nlopt", "package_type": "package", "sha256": "d7b47dd14f8a4d64d7f5a23ebe2d92597fa37492886dc91ce3433cf08d0af1d5", "shared_library": false, "unvendored_tests": false, "version": "2.7.0"}, "nltk": {"depends": ["regex", "sqlite3"], "file_name": "nltk-3.8.1-py3-none-any.whl", "imports": ["nltk"], "install_dir": "site", "name": "nltk", "package_type": "package", "sha256": "063ce4e517cf219b990c6ea452cf46f28b798fd46d7641c74d07a6d49ed8d29c", "shared_library": false, "unvendored_tests": true, "version": "3.8.1"}, "nltk-tests": {"depends": ["nltk"], "file_name": "nltk-tests.tar", "imports": [], "install_dir": "site", "name": "nltk-tests", "package_type": "package", "sha256": "83ea5fe1cc7ff21cd0e7ab455eadd2a69e207955640ac7e7274eddf5df01625e", "shared_library": false, "unvendored_tests": false, "version": "3.8.1"}, "numcodecs": {"depends": ["numpy", "msgpack"], "file_name": "numcodecs-0.11.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["numcodecs"], "install_dir": "site", "name": "numcodecs", "package_type": "package", "sha256": "9c7b7254d00fd62b7effd5b31786bb19f1509530df671a090a5913ff5c3e1e44", "shared_library": false, "unvendored_tests": true, "version": "0.11.0"}, "numcodecs-tests": {"depends": ["numcodecs"], "file_name": "numcodecs-tests.tar", "imports": [], "install_dir": "site", "name": "numcodecs-tests", "package_type": "package", "sha256": "ec0ab47c1ac88f3fb08eb7291e2c8bbd90339809dac5066c744fb0a7c2300859", "shared_library": false, "unvendored_tests": false, "version": "0.11.0"}, "numpy": {"depends": [], "file_name": "numpy-1.26.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["numpy"], "install_dir": "site", "name": "numpy", "package_type": "package", "sha256": "4aeba609614f88fbb49d31bdafbc3f8e18e3ae4ba6d2b710c5281e5d22b64f9a", "shared_library": false, "unvendored_tests": true, "version": "1.26.4"}, "numpy-tests": {"depends": ["numpy"], "file_name": "numpy-tests.tar", "imports": [], "install_dir": "site", "name": "numpy-tests", "package_type": "package", "sha256": "95ee5fe4134dc7b045637150374154a2034430e0b48ae522d59496a6c81a16df", "shared_library": false, "unvendored_tests": false, "version": "1.26.4"}, "openblas": {"depends": [], "file_name": "openblas-0.3.26.zip", "imports": [], "install_dir": "dynlib", "name": "openblas", "package_type": "shared_library", "sha256": "bdc0f6c43169c8cd80a6f708d873fd441e6e475f3615476907f8865c3efd5668", "shared_library": true, "unvendored_tests": false, "version": "0.3.26"}, "opencv-python": {"depends": ["numpy"], "file_name": "opencv_python-4.9.0.80-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["cv2"], "install_dir": "site", "name": "opencv-python", "package_type": "package", "sha256": "50b8041c16f52608e17132ca5149bbc88495a030a19d88ce5502a8cf6abbf618", "shared_library": false, "unvendored_tests": false, "version": "4.9.0.80"}, "openssl": {"depends": [], "file_name": "openssl-1.1.1n.zip", "imports": [], "install_dir": "dynlib", "name": "openssl", "package_type": "shared_library", "sha256": "59303d428657ee5466221b3f778fc425149387a89d3330859af53be2796ff95d", "shared_library": true, "unvendored_tests": false, "version": "1.1.1n"}, "optlang": {"depends": ["sympy", "six", "swiglpk"], "file_name": "optlang-1.8.1-py2.py3-none-any.whl", "imports": ["optlang"], "install_dir": "site", "name": "optlang", "package_type": "package", "sha256": "c0f07f5eaf66e64a6c42d0061032f86c670b4e1af96514196f2d2c3f3a71e70a", "shared_library": false, "unvendored_tests": true, "version": "1.8.1"}, "optlang-tests": {"depends": ["optlang"], "file_name": "optlang-tests.tar", "imports": [], "install_dir": "site", "name": "optlang-tests", "package_type": "package", "sha256": "cf7f754efeb1bf3195e16c5cce5eb13c1716db18dda20d4fc88683c9e49427a6", "shared_library": false, "unvendored_tests": false, "version": "1.8.1"}, "orjson": {"depends": [], "file_name": "orjson-3.10.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["orjson"], "install_dir": "site", "name": "orjson", "package_type": "package", "sha256": "b12cded1997656c848609705f31a1d0d91ab4e53000365acec899c7cb9488deb", "shared_library": false, "unvendored_tests": false, "version": "3.10.1"}, "packaging": {"depends": [], "file_name": "packaging-23.2-py3-none-any.whl", "imports": ["packaging"], "install_dir": "site", "name": "packaging", "package_type": "package", "sha256": "34c09d7d17c4b584b10edca9255281c11c4713f77c76945d918d7ffa0455c9fa", "shared_library": false, "unvendored_tests": false, "version": "23.2"}, "pandas": {"depends": ["numpy", "python-dateutil", "pytz"], "file_name": "pandas-2.2.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pandas"], "install_dir": "site", "name": "pandas", "package_type": "package", "sha256": "57257d1fe41081ec6aa88499a6eab6e06b1cee1e54fbc05fc1365c739017e4d6", "shared_library": false, "unvendored_tests": true, "version": "2.2.0"}, "pandas-tests": {"depends": ["pandas"], "file_name": "pandas-tests.tar", "imports": [], "install_dir": "site", "name": "pandas-tests", "package_type": "package", "sha256": "eba78f89a53a4898e0ae87e7571fbfe7a540d65d53fda17935b115e1c767c444", "shared_library": false, "unvendored_tests": false, "version": "2.2.0"}, "parso": {"depends": [], "file_name": "parso-0.8.4-py2.py3-none-any.whl", "imports": ["parso"], "install_dir": "site", "name": "parso", "package_type": "package", "sha256": "36e589cee5c2aaf94489df8f30f4db7d89b42b260a6d739583fa00a3e42d157a", "shared_library": false, "unvendored_tests": false, "version": "0.8.4"}, "patsy": {"depends": ["numpy", "six"], "file_name": "patsy-0.5.6-py2.py3-none-any.whl", "imports": ["patsy"], "install_dir": "site", "name": "patsy", "package_type": "package", "sha256": "760833a100a66baafd1e4b0a726eea87e6c9edcd7c6ba3e7473ca1dfe593e7f8", "shared_library": false, "unvendored_tests": true, "version": "0.5.6"}, "patsy-tests": {"depends": ["patsy"], "file_name": "patsy-tests.tar", "imports": [], "install_dir": "site", "name": "patsy-tests", "package_type": "package", "sha256": "6a3632284ca095eb87cec8a79d06183399f85c21af4ff986cb7d9a37ebd3e13f", "shared_library": false, "unvendored_tests": false, "version": "0.5.6"}, "peewee": {"depends": ["sqlite3", "cffi"], "file_name": "peewee-3.17.3-py3-none-any.whl", "imports": ["peewee"], "install_dir": "site", "name": "peewee", "package_type": "package", "sha256": "b3aac565a0a5e345fa0c069c41a0b696c9c8292e9970405ce9fb7fded0a3cf34", "shared_library": false, "unvendored_tests": true, "version": "3.17.3"}, "peewee-tests": {"depends": ["peewee"], "file_name": "peewee-tests.tar", "imports": [], "install_dir": "site", "name": "peewee-tests", "package_type": "package", "sha256": "bcda11ebbf1e3b0326845039c48106fd56843a1ff1cf33375b075f9b615ae233", "shared_library": false, "unvendored_tests": false, "version": "3.17.3"}, "pillow": {"depends": [], "file_name": "pillow-10.2.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["PIL"], "install_dir": "site", "name": "Pillow", "package_type": "package", "sha256": "b1ef215cdd2abc38fab5d821a86d730bc495e0450d34ed99d90c74f319f90f55", "shared_library": false, "unvendored_tests": false, "version": "10.2.0"}, "pillow-heif": {"depends": ["cffi", "pillow", "libheif"], "file_name": "pillow_heif-0.8.0-cp36-abi3-pyodide_2024_0_wasm32.whl", "imports": ["pillow_heif"], "install_dir": "site", "name": "pillow_heif", "package_type": "package", "sha256": "a2adfb50f218b2609b2be0d48d7fa0e4f03f66b7c8b69a80a83fab2bd62c8472", "shared_library": false, "unvendored_tests": false, "version": "0.8.0"}, "pkgconfig": {"depends": [], "file_name": "pkgconfig-1.5.5-py3-none-any.whl", "imports": ["pkgconfig"], "install_dir": "site", "name": "pkgconfig", "package_type": "package", "sha256": "b1772cd7ef35b74e2fae52ee83f70bc1c51c46e962c3ce8d514be3927b57a37b", "shared_library": false, "unvendored_tests": false, "version": "1.5.5"}, "pluggy": {"depends": [], "file_name": "pluggy-1.5.0-py3-none-any.whl", "imports": ["pluggy"], "install_dir": "site", "name": "pluggy", "package_type": "package", "sha256": "41726a2e85006e36c5d1b584b01674aeaba798f7f27eabc68dc1350ebdebd7e1", "shared_library": false, "unvendored_tests": false, "version": "1.5.0"}, "pplpy": {"depends": ["gmpy2", "cysignals"], "file_name": "pplpy-0.8.10-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["ppl"], "install_dir": "site", "name": "pplpy", "package_type": "package", "sha256": "325d38df6ddd86ba3e0b8fc02e77fcb520033c3211589550b1c0f30638644f22", "shared_library": false, "unvendored_tests": false, "version": "0.8.10"}, "primecountpy": {"depends": ["cysignals"], "file_name": "primecountpy-0.1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["primecountpy"], "install_dir": "site", "name": "primecountpy", "package_type": "package", "sha256": "dbbd9dd54cdde6f191cb615875f8a8edf6ec4f548def141d99b4ee5e9804d44e", "shared_library": false, "unvendored_tests": false, "version": "0.1.0"}, "prompt-toolkit": {"depends": [], "file_name": "prompt_toolkit-3.0.43-py3-none-any.whl", "imports": ["prompt_toolkit"], "install_dir": "site", "name": "prompt_toolkit", "package_type": "package", "sha256": "954ac31a47ce598daec1757c13f9d4ae5e3081f5762bcaaadbea62cef593ca3e", "shared_library": false, "unvendored_tests": false, "version": "3.0.43"}, "protobuf": {"depends": [], "file_name": "protobuf-4.24.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["google"], "install_dir": "site", "name": "protobuf", "package_type": "package", "sha256": "fedaad25244cd92d3ae1cce9dc2bfc30186efde7d979146aa6c2e5c322f3f441", "shared_library": false, "unvendored_tests": false, "version": "4.24.4"}, "pure-eval": {"depends": [], "file_name": "pure_eval-0.2.2-py3-none-any.whl", "imports": ["pure_eval"], "install_dir": "site", "name": "pure_eval", "package_type": "package", "sha256": "2a02f1b49cb9b405f7fa300695be174fc4f8b4da220dcace1daa66ab252ba027", "shared_library": false, "unvendored_tests": false, "version": "0.2.2"}, "py": {"depends": [], "file_name": "py-1.11.0-py2.py3-none-any.whl", "imports": ["py"], "install_dir": "site", "name": "py", "package_type": "package", "sha256": "8027d2e090352a65167983ec14b187b0c281dc31e98cb6e17885f57d3cf2b407", "shared_library": false, "unvendored_tests": false, "version": "1.11.0"}, "pyclipper": {"depends": [], "file_name": "pyclipper-1.3.0.post5-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyclipper"], "install_dir": "site", "name": "pyclipper", "package_type": "package", "sha256": "c6f8d4b9749600cb8443076258ccf293465fbe0bd62c448316ef3613c9dd2c05", "shared_library": false, "unvendored_tests": false, "version": "1.3.0.post5"}, "pycparser": {"depends": [], "file_name": "pycparser-2.22-py3-none-any.whl", "imports": ["pycparser"], "install_dir": "site", "name": "pycparser", "package_type": "package", "sha256": "2a807b142ffd51086282d4776576ca61b0fd870ea6babc7212d75607357279be", "shared_library": false, "unvendored_tests": false, "version": "2.22"}, "pycryptodome": {"depends": [], "file_name": "pycryptodome-3.20.0-cp35-abi3-pyodide_2024_0_wasm32.whl", "imports": ["Crypto"], "install_dir": "site", "name": "pycryptodome", "package_type": "package", "sha256": "bea845208b260b9081f5ec1cd9833b111b1642af04ec5824e3c83fd0d2c30a4f", "shared_library": false, "unvendored_tests": true, "version": "3.20.0"}, "pycryptodome-tests": {"depends": ["pycryptodome"], "file_name": "pycryptodome-tests.tar", "imports": [], "install_dir": "site", "name": "pycryptodome-tests", "package_type": "package", "sha256": "8b37e84e9df0bae3d6459d5ac64669d88e73015d11918c6c57adeb052f5d0c59", "shared_library": false, "unvendored_tests": false, "version": "3.20.0"}, "pydantic": {"depends": ["typing-extensions", "pydantic_core", "annotated-types"], "file_name": "pydantic-2.7.0-py3-none-any.whl", "imports": ["pydantic"], "install_dir": "site", "name": "pydantic", "package_type": "package", "sha256": "7c866a4a9071ca25279c06c0e4871d6e5a7c611f2e1bcfadc47ba2b3336016ca", "shared_library": false, "unvendored_tests": false, "version": "2.7.0"}, "pydantic-core": {"depends": [], "file_name": "pydantic_core-2.18.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pydantic_core"], "install_dir": "site", "name": "pydantic_core", "package_type": "package", "sha256": "be7f83ad55493a9ffb94777d30fe3e6d3334cb08e2ebdaf0e7a799eef0174866", "shared_library": false, "unvendored_tests": false, "version": "2.18.1"}, "pydecimal": {"depends": [], "file_name": "pydecimal-1.0.0.zip", "imports": ["_pydecimal"], "install_dir": "stdlib", "name": "pydecimal", "package_type": "cpython_module", "sha256": "c0661bafc675196901c67b4f883abdb0dffb44b97aec91bec0a03bfebeb12668", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "pydoc-data": {"depends": [], "file_name": "pydoc_data-1.0.0.zip", "imports": ["pydoc_data"], "install_dir": "stdlib", "name": "pydoc_data", "package_type": "cpython_module", "sha256": "b15bf763b1158e7757c7265a1340c92ce23c753f983e4d487e2bea8cc749300f", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "pyerfa": {"depends": ["numpy"], "file_name": "pyerfa-2.0.1.4-cp39-abi3-pyodide_2024_0_wasm32.whl", "imports": ["erfa"], "install_dir": "site", "name": "pyerfa", "package_type": "package", "sha256": "423bd62931e5244edd860856e47eb6b5122f1ec369fdd797112b6676ab60922a", "shared_library": false, "unvendored_tests": true, "version": "2.0.1.4"}, "pyerfa-tests": {"depends": ["pyerfa"], "file_name": "pyerfa-tests.tar", "imports": [], "install_dir": "site", "name": "pyerfa-tests", "package_type": "package", "sha256": "8bf7d38d8aa679833a5af53c9729bcd28c69a028fefe7f751d06d2e92f93981f", "shared_library": false, "unvendored_tests": false, "version": "2.0.1.4"}, "pygame-ce": {"depends": [], "file_name": "pygame_ce-2.4.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pygame"], "install_dir": "site", "name": "pygame-ce", "package_type": "package", "sha256": "ffeae5c40f989d0c208711b552fa2e2b41b2df6077a4d3941f208fc97d4bf9a1", "shared_library": false, "unvendored_tests": true, "version": "2.4.1"}, "pygame-ce-tests": {"depends": ["pygame-ce"], "file_name": "pygame-ce-tests.tar", "imports": [], "install_dir": "site", "name": "pygame-ce-tests", "package_type": "package", "sha256": "ece1ba607e541cea41d29ad516327e58853cb1908f9fbf338058ee40bdc683c0", "shared_library": false, "unvendored_tests": false, "version": "2.4.1"}, "pygments": {"depends": [], "file_name": "pygments-2.17.2-py3-none-any.whl", "imports": ["pygments"], "install_dir": "site", "name": "Pygments", "package_type": "package", "sha256": "58dfa90582044080f780d8f1b71e9e52c320fb7b0231c7191d32d456650f6cee", "shared_library": false, "unvendored_tests": false, "version": "2.17.2"}, "pyheif": {"depends": ["cffi"], "file_name": "pyheif-0.7.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyheif"], "install_dir": "site", "name": "pyheif", "package_type": "package", "sha256": "7b1837edd9ae8bb8e1f1d16a65c3f260ca42751db17dd6652a02c92c12201936", "shared_library": false, "unvendored_tests": false, "version": "0.7.1"}, "pyiceberg": {"depends": ["click", "fsspec", "mmh3", "pydantic", "pyparsing", "requests", "rich", "sortedcontainers", "sqlalchemy", "strictyaml"], "file_name": "pyiceberg-0.6.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyiceberg"], "install_dir": "site", "name": "pyiceberg", "package_type": "package", "sha256": "aa291e29362604cc8acf7ae035b7f044ab5660014087a7464f75b9fbf36a566d", "shared_library": false, "unvendored_tests": false, "version": "0.6.0"}, "pyinstrument": {"depends": [], "file_name": "pyinstrument-4.4.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyinstrument"], "install_dir": "site", "name": "pyinstrument", "package_type": "package", "sha256": "1ac73667303af9889b8787b46413084b55cb3b2420825cc68bc6afd529fe1996", "shared_library": false, "unvendored_tests": false, "version": "4.4.0"}, "pynacl": {"depends": ["cffi"], "file_name": "PyNaCl-1.5.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["nacl"], "install_dir": "site", "name": "pynacl", "package_type": "package", "sha256": "c3ab7e374410b93609bc53212a8faf8cd617f45bdfd9cb6cdf8ecce60e3ba9e9", "shared_library": false, "unvendored_tests": false, "version": "1.5.0"}, "pyodide-http": {"depends": [], "file_name": "pyodide_http-0.2.1-py3-none-any.whl", "imports": ["pyodide_http"], "install_dir": "site", "name": "pyodide-http", "package_type": "package", "sha256": "aa2cc7e585e98a84208bc042dd36140cb4213ec6112915a1cc86f7101e38d47a", "shared_library": false, "unvendored_tests": false, "version": "0.2.1"}, "pyparsing": {"depends": [], "file_name": "pyparsing-3.1.2-py3-none-any.whl", "imports": ["pyparsing"], "install_dir": "site", "name": "pyparsing", "package_type": "package", "sha256": "8db11035317ae75a36c2b4dea44eeba5630c8bd2f501e74a243dbbe93f511895", "shared_library": false, "unvendored_tests": false, "version": "3.1.2"}, "pyproj": {"depends": ["certifi", "sqlite3"], "file_name": "pyproj-3.6.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyproj"], "install_dir": "site", "name": "pyproj", "package_type": "package", "sha256": "109d9c5c99442a89c40453571c1337589bcfdeb66d683645973ee8bc5d14ff85", "shared_library": false, "unvendored_tests": false, "version": "3.6.1"}, "pyrsistent": {"depends": [], "file_name": "pyrsistent-0.20.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["_pyrsistent_version", "pyrsistent"], "install_dir": "site", "name": "pyrsistent", "package_type": "package", "sha256": "5d464ec51462568599539a111c431c7612e4acefa8d86a4d18280cfa3bc4d3ec", "shared_library": false, "unvendored_tests": false, "version": "0.20.0"}, "pysam": {"depends": [], "file_name": "pysam-0.22.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pysam"], "install_dir": "site", "name": "pysam", "package_type": "package", "sha256": "c6374ae788806ea721c6588dd8e297c214262e95e42839ef4ade44b649f8a278", "shared_library": false, "unvendored_tests": false, "version": "0.22.0"}, "pyshp": {"depends": [], "file_name": "pyshp-2.3.1-py2.py3-none-any.whl", "imports": ["shapefile"], "install_dir": "site", "name": "pyshp", "package_type": "package", "sha256": "592a39ee27138b454d767f6620c6895f29ae360c4a47ab3fd00ad6875e432566", "shared_library": false, "unvendored_tests": false, "version": "2.3.1"}, "pytest": {"depends": ["atomicwrites", "attrs", "more-itertools", "pluggy", "py", "setuptools", "six", "iniconfig", "exceptiongroup"], "file_name": "pytest-8.1.1-py3-none-any.whl", "imports": ["_pytest", "pytest"], "install_dir": "site", "name": "pytest", "package_type": "package", "sha256": "132f51fe851ed5735e6f6b7729c566e44cb6ac488b7d0040ca247d1cb9638d1a", "shared_library": false, "unvendored_tests": false, "version": "8.1.1"}, "pytest-asyncio": {"depends": ["pytest"], "file_name": "pytest_asyncio-0.23.7-py3-none-any.whl", "imports": ["pytest_asyncio"], "install_dir": "site", "name": "pytest-asyncio", "package_type": "package", "sha256": "80597e5a925462a351645d48af5806fd5ccf95f269c0690eae4df772aa66d424", "shared_library": false, "unvendored_tests": false, "version": "0.23.7"}, "pytest-benchmark": {"depends": [], "file_name": "pytest_benchmark-4.0.0-py3-none-any.whl", "imports": ["pytest_benchmark"], "install_dir": "site", "name": "pytest-benchmark", "package_type": "package", "sha256": "4752b0087dc78a3909a472998646266e834f2af2111ba7cf4a6c9659e737a957", "shared_library": false, "unvendored_tests": false, "version": "4.0.0"}, "python-dateutil": {"depends": ["six"], "file_name": "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", "imports": ["dateutil"], "install_dir": "site", "name": "python-dateutil", "package_type": "package", "sha256": "1adf6847a0ae4a09bef1fc9954089766800ce6d9688a5329d04ad54bcf7d1f2c", "shared_library": false, "unvendored_tests": false, "version": "2.9.0.post0"}, "python-flint": {"depends": [], "file_name": "python_flint-0.6.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["flint"], "install_dir": "site", "name": "python-flint", "package_type": "package", "sha256": "95eac4de7c679cd78071e8d17f4e6ed2f373082f3bddba775d5a017dd9bfdc69", "shared_library": false, "unvendored_tests": false, "version": "0.6.0"}, "python-magic": {"depends": ["libmagic"], "file_name": "python_magic-0.4.27-py2.py3-none-any.whl", "imports": ["magic"], "install_dir": "site", "name": "python-magic", "package_type": "package", "sha256": "7353b45205f8d79530c37367321ae511e84dec5f8a39c441082f3b2f0fff70d3", "shared_library": false, "unvendored_tests": false, "version": "0.4.27"}, "python-sat": {"depends": ["six"], "file_name": "python_sat-1.8.dev13-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pysat"], "install_dir": "site", "name": "python-sat", "package_type": "package", "sha256": "210f18f6d80f9670a9d5d53973b73facfbf2fcc71a87d6583db816862be0ba23", "shared_library": false, "unvendored_tests": false, "version": "1.8.dev13"}, "python-solvespace": {"depends": [], "file_name": "python_solvespace-3.0.8-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["python_solvespace"], "install_dir": "site", "name": "python_solvespace", "package_type": "package", "sha256": "b88c85292a771f365664a9c4d044d87db62c201ee8f7a97bd4fca94ddbee242c", "shared_library": false, "unvendored_tests": false, "version": "3.0.8"}, "pytz": {"depends": [], "file_name": "pytz-2024.1-py2.py3-none-any.whl", "imports": ["pytz"], "install_dir": "site", "name": "pytz", "package_type": "package", "sha256": "62dfa923a4643d5d56371fe08a8aa2ea63854d154cf46c09e7c91c167207eb1a", "shared_library": false, "unvendored_tests": false, "version": "2024.1"}, "pywavelets": {"depends": ["numpy", "matplotlib", "scipy"], "file_name": "pywavelets-1.6.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pywt"], "install_dir": "site", "name": "pywavelets", "package_type": "package", "sha256": "32626f1fd288714f500b961b7358f5dc7b2fabfbd90b458514055dd95f7965d3", "shared_library": false, "unvendored_tests": true, "version": "1.6.0"}, "pywavelets-tests": {"depends": ["pywavelets"], "file_name": "pywavelets-tests.tar", "imports": [], "install_dir": "site", "name": "pywavelets-tests", "package_type": "package", "sha256": "6a6b63b515e6285b7b90bc94dd138a94f1771fa1dfb550816c61c6b1b7bcb4bf", "shared_library": false, "unvendored_tests": false, "version": "1.6.0"}, "pyxel": {"depends": [], "file_name": "pyxel-1.9.10-cp37-abi3-pyodide_2024_0_wasm32.whl", "imports": ["pyxel"], "install_dir": "site", "name": "pyxel", "package_type": "package", "sha256": "46e15678938bb23be6a5e89ee2566bbe6457fa7ede38a0c26e3ea86bdb642b09", "shared_library": false, "unvendored_tests": false, "version": "1.9.10"}, "pyxirr": {"depends": [], "file_name": "pyxirr-0.10.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["pyxirr"], "install_dir": "site", "name": "pyxirr", "package_type": "package", "sha256": "ccb91ae101e3ee001aec040e1423c86b25832deddbaf3efbb6ca4557064a7ced", "shared_library": false, "unvendored_tests": false, "version": "0.10.3"}, "pyyaml": {"depends": [], "file_name": "PyYAML-6.0.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["_yaml", "yaml"], "install_dir": "site", "name": "pyyaml", "package_type": "package", "sha256": "f8e7882442554c1648a62c1f47e4ef3c1c4756834c9d42cd3ea89b01e92738c6", "shared_library": false, "unvendored_tests": false, "version": "6.0.1"}, "rebound": {"depends": ["numpy"], "file_name": "rebound-3.24.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["rebound"], "install_dir": "site", "name": "rebound", "package_type": "package", "sha256": "e85c1670d5a67564fa313e55c176a946db7646fb6ad4faa4bc8c32464e7f30de", "shared_library": false, "unvendored_tests": false, "version": "3.24.2"}, "reboundx": {"depends": ["rebound", "numpy"], "file_name": "reboundx-3.10.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["reboundx"], "install_dir": "site", "name": "reboundx", "package_type": "package", "sha256": "2acea849f2e63eaf09ec8ea3e0cc676f3c7930ecdaf372660a72aed514acdb40", "shared_library": false, "unvendored_tests": false, "version": "3.10.1"}, "referencing": {"depends": ["attrs", "rpds-py"], "file_name": "referencing-0.34.0-py3-none-any.whl", "imports": ["referencing"], "install_dir": "site", "name": "referencing", "package_type": "package", "sha256": "6912d699247bc5c38391859c922308465a6a6beb27b4d6a48ade6cf7f66db561", "shared_library": false, "unvendored_tests": true, "version": "0.34.0"}, "referencing-tests": {"depends": ["referencing"], "file_name": "referencing-tests.tar", "imports": [], "install_dir": "site", "name": "referencing-tests", "package_type": "package", "sha256": "5f7bceb247a09327cf0912eca4d6a3c90cb6b4436a864bc82a333ecf3e11159f", "shared_library": false, "unvendored_tests": false, "version": "0.34.0"}, "regex": {"depends": [], "file_name": "regex-2024.4.16-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["regex"], "install_dir": "site", "name": "regex", "package_type": "package", "sha256": "826a6da49b07843236888dd29c8ce0cab1f588315ccf4c55321ecd135fd21093", "shared_library": false, "unvendored_tests": true, "version": "2024.4.16"}, "regex-tests": {"depends": ["regex"], "file_name": "regex-tests.tar", "imports": [], "install_dir": "site", "name": "regex-tests", "package_type": "package", "sha256": "37d33d6e7fb2d1c605bd72d78236ef4e324665c474aea4db737a6e6444159942", "shared_library": false, "unvendored_tests": false, "version": "2024.4.16"}, "requests": {"depends": ["charset-normalizer", "idna", "urllib3", "certifi"], "file_name": "requests-2.31.0-py3-none-any.whl", "imports": ["requests"], "install_dir": "site", "name": "requests", "package_type": "package", "sha256": "001c81f2678aa7dbd3f6884e53997b26c948d0b52603933d953d6f835c397965", "shared_library": false, "unvendored_tests": false, "version": "2.31.0"}, "retrying": {"depends": ["six"], "file_name": "retrying-1.3.4-py3-none-any.whl", "imports": ["retrying"], "install_dir": "site", "name": "retrying", "package_type": "package", "sha256": "c3dba397227a4f7eb4eb95fe627f4d2d03a4c50adf6f25edc1dbc691ee5622c9", "shared_library": false, "unvendored_tests": false, "version": "1.3.4"}, "rich": {"depends": [], "file_name": "rich-13.7.1-py3-none-any.whl", "imports": ["rich"], "install_dir": "site", "name": "rich", "package_type": "package", "sha256": "ea0e850b047833799b03e521dd8eb7d21118056183cf6957c336bf414c265282", "shared_library": false, "unvendored_tests": false, "version": "13.7.1"}, "river": {"depends": ["numpy", "pandas", "pytest", "scipy"], "file_name": "river-0.19.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["river"], "install_dir": "site", "name": "river", "package_type": "package", "sha256": "aaac213ea3f3176ee84654399e6b397f69f0bf2d1632d72c66a3232499dca938", "shared_library": false, "unvendored_tests": true, "version": "0.19.0"}, "river-tests": {"depends": ["river"], "file_name": "river-tests.tar", "imports": [], "install_dir": "site", "name": "river-tests", "package_type": "package", "sha256": "9b2bee4cfa8e9c91c3587ec24657def30e8a569b329b52d1acb64f078f309e22", "shared_library": false, "unvendored_tests": false, "version": "0.19.0"}, "robotraconteur": {"depends": ["numpy"], "file_name": "RobotRaconteur-1.2.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["RobotRaconteur"], "install_dir": "site", "name": "RobotRaconteur", "package_type": "package", "sha256": "77753ca8a261756c7821f7b2cf7be4db90e2a2281d7d89e2c952f68e69eb3711", "shared_library": false, "unvendored_tests": false, "version": "1.2.0"}, "rpds-py": {"depends": [], "file_name": "rpds_py-0.18.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["rpds"], "install_dir": "site", "name": "rpds-py", "package_type": "package", "sha256": "abbcc63b95769dd804106e191eaf0ac462bd81acc45faeb6e83a8d72f8353b2d", "shared_library": false, "unvendored_tests": false, "version": "0.18.0"}, "ruamel-yaml": {"depends": [], "file_name": "ruamel.yaml-0.18.6-py3-none-any.whl", "imports": ["ruamel"], "install_dir": "site", "name": "ruamel.yaml", "package_type": "package", "sha256": "2ebfdf7175e37b832f191a7e11452e32c900618382b6bba4e9b16265d5e56f57", "shared_library": false, "unvendored_tests": false, "version": "0.18.6"}, "rust-panic-test": {"depends": [], "file_name": "rust_panic_test-1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["rust-panic-test"], "install_dir": "site", "name": "rust-panic-test", "package_type": "package", "sha256": "c97425311fffebd46631b3e0666d4a4a6d4a84b5143e54c58d663b2fe6c122e9", "shared_library": false, "unvendored_tests": false, "version": "1.0"}, "scikit-image": {"depends": ["packaging", "numpy", "scipy", "networkx", "pillow", "imageio", "pywavelets", "lazy_loader"], "file_name": "scikit_image-0.23.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["skimage"], "install_dir": "site", "name": "scikit-image", "package_type": "package", "sha256": "ac530cc5e2cb757079e691415e096fe14b476267ccb0d14be9cd65c714cea96e", "shared_library": false, "unvendored_tests": true, "version": "0.23.2"}, "scikit-image-tests": {"depends": ["scikit-image"], "file_name": "scikit-image-tests.tar", "imports": [], "install_dir": "site", "name": "scikit-image-tests", "package_type": "package", "sha256": "292dda6f83cc184f2332fd05659a55214e9c269ada38472da9338d9e51ee500a", "shared_library": false, "unvendored_tests": false, "version": "0.23.2"}, "scikit-learn": {"depends": ["scipy", "joblib", "threadpoolctl"], "file_name": "scikit_learn-1.4.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["sklearn"], "install_dir": "site", "name": "scikit-learn", "package_type": "package", "sha256": "53302f72d375c6c110e1f50c9d70376884b22ba87eab53e440bd68f2a96016cd", "shared_library": false, "unvendored_tests": true, "version": "1.4.2"}, "scikit-learn-tests": {"depends": ["scikit-learn"], "file_name": "scikit-learn-tests.tar", "imports": [], "install_dir": "site", "name": "scikit-learn-tests", "package_type": "package", "sha256": "e57544bda608c3c48f613197bf1853d6d58d24e56ac70020f7c3c10e905e1b76", "shared_library": false, "unvendored_tests": false, "version": "1.4.2"}, "scipy": {"depends": ["numpy", "openblas"], "file_name": "scipy-1.12.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["scipy"], "install_dir": "site", "name": "scipy", "package_type": "package", "sha256": "3eb24e2939b223bc946db8cd193b33ad2ebbd6c98004cf17f26e2bd4e5bad92c", "shared_library": false, "unvendored_tests": true, "version": "1.12.0"}, "scipy-tests": {"depends": ["scipy"], "file_name": "scipy-tests.tar", "imports": [], "install_dir": "site", "name": "scipy-tests", "package_type": "package", "sha256": "999bf6b9449c64b74a08f4b40840c1f0a9e690cd8714a75554d552fd4cded727", "shared_library": false, "unvendored_tests": false, "version": "1.12.0"}, "screed": {"depends": [], "file_name": "screed-1.1.3-py2.py3-none-any.whl", "imports": ["bigtests", "screed"], "install_dir": "site", "name": "screed", "package_type": "package", "sha256": "1f7bd864f01e97056e0c06a11d0c302726f4d9bcd2cf378961dad6336cedfebf", "shared_library": false, "unvendored_tests": true, "version": "1.1.3"}, "screed-tests": {"depends": ["screed"], "file_name": "screed-tests.tar", "imports": [], "install_dir": "site", "name": "screed-tests", "package_type": "package", "sha256": "8d3ce3a84b6efcd306853e35bffb027b1b21b0cdf8f60be25c7c906193df07bf", "shared_library": false, "unvendored_tests": false, "version": "1.1.3"}, "setuptools": {"depends": ["pyparsing"], "file_name": "setuptools-69.5.1-py3-none-any.whl", "imports": ["_distutils_hack", "pkg_resources", "setuptools"], "install_dir": "site", "name": "setuptools", "package_type": "package", "sha256": "8d109163f2597f5353f0e9089636de4031a310245443e8a1f2af6afe18b8c41c", "shared_library": false, "unvendored_tests": false, "version": "69.5.1"}, "shapely": {"depends": ["numpy"], "file_name": "shapely-2.0.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["shapely"], "install_dir": "site", "name": "shapely", "package_type": "package", "sha256": "2ca8a9a020893e3ce747c006908bd3ff8ca14f5ecf7201bc240d7e2bcdae53a5", "shared_library": false, "unvendored_tests": true, "version": "2.0.2"}, "shapely-tests": {"depends": ["shapely"], "file_name": "shapely-tests.tar", "imports": [], "install_dir": "site", "name": "shapely-tests", "package_type": "package", "sha256": "cd2eca2e5c2aa75fe98a012eebbc52beda9dc13bc9ddfe455955f79d28d1972f", "shared_library": false, "unvendored_tests": false, "version": "2.0.2"}, "sharedlib-test": {"depends": [], "file_name": "sharedlib-test-1.0.zip", "imports": [], "install_dir": "dynlib", "name": "sharedlib-test", "package_type": "shared_library", "sha256": "7478123361942519d05cc921696d597a2ba75806d52ddb5973c0f19b21ee22fd", "shared_library": true, "unvendored_tests": false, "version": "1.0"}, "sharedlib-test-py": {"depends": ["sharedlib-test"], "file_name": "sharedlib_test_py-1.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["sharedlib_test"], "install_dir": "site", "name": "sharedlib-test-py", "package_type": "package", "sha256": "f1e7add98c4d1aebced3e6ccbfe973ff83ec5e8642a00f16975351d0518411cf", "shared_library": false, "unvendored_tests": false, "version": "1.0"}, "simplejson": {"depends": [], "file_name": "simplejson-3.19.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["simplejson"], "install_dir": "site", "name": "simplejson", "package_type": "package", "sha256": "c4f8f531952ab9d402d01ce89106edfbcc243173f968f2e60efdb3618930b3df", "shared_library": false, "unvendored_tests": true, "version": "3.19.2"}, "simplejson-tests": {"depends": ["simplejson"], "file_name": "simplejson-tests.tar", "imports": [], "install_dir": "site", "name": "simplejson-tests", "package_type": "package", "sha256": "4c8ff471020ca4a7d115672f1d2aca34dd7bd7db53b38d45e6b24c3d71f7c2b5", "shared_library": false, "unvendored_tests": false, "version": "3.19.2"}, "sisl": {"depends": ["pyparsing", "numpy", "scipy", "tqdm", "xarray", "pandas", "matplotlib"], "file_name": "sisl-0.14.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["sisl_toolbox", "sisl"], "install_dir": "site", "name": "sisl", "package_type": "package", "sha256": "a026d869059702221c0b093650bbd62b96d1c7d87bc42188b5a4a328a2bdb39d", "shared_library": false, "unvendored_tests": true, "version": "0.14.3"}, "sisl-tests": {"depends": ["sisl"], "file_name": "sisl-tests.tar", "imports": [], "install_dir": "site", "name": "sisl-tests", "package_type": "package", "sha256": "35a35535a2ae2ea220c97042753c7c10e3a76932cbb773bb3a8166a833e03d0b", "shared_library": false, "unvendored_tests": false, "version": "0.14.3"}, "six": {"depends": [], "file_name": "six-1.16.0-py2.py3-none-any.whl", "imports": ["six"], "install_dir": "site", "name": "six", "package_type": "package", "sha256": "176a02f2e3c155246b96ff0a8e8a35c35e74f666923a50e47d719f21e2d3b7f2", "shared_library": false, "unvendored_tests": false, "version": "1.16.0"}, "smart-open": {"depends": [], "file_name": "smart_open-7.0.4-py3-none-any.whl", "imports": ["smart_open"], "install_dir": "site", "name": "smart_open", "package_type": "package", "sha256": "8217625b7117f7fe3bd295a6a41c5a5253b2471a0668e3d28453fd5375920690", "shared_library": false, "unvendored_tests": false, "version": "7.0.4"}, "sortedcontainers": {"depends": [], "file_name": "sortedcontainers-2.4.0-py2.py3-none-any.whl", "imports": ["sortedcontainers"], "install_dir": "site", "name": "sortedcontainers", "package_type": "package", "sha256": "56194229f40e8f5d813b68ce87595483b408ead2c4552439265069545b20d753", "shared_library": false, "unvendored_tests": false, "version": "2.4.0"}, "soupsieve": {"depends": [], "file_name": "soupsieve-2.5-py3-none-any.whl", "imports": ["soupsieve"], "install_dir": "site", "name": "soupsieve", "package_type": "package", "sha256": "cc4fbcb87488b840a835be9f568435dba297c6914f1edfa32b3bbf74a8a348cf", "shared_library": false, "unvendored_tests": false, "version": "2.5"}, "sourmash": {"depends": ["screed", "cffi", "deprecation", "cachetools", "numpy", "matplotlib", "scipy", "sqlite3", "bitstring"], "file_name": "sourmash-4.8.8-py3-none-pyodide_2024_0_wasm32.whl", "imports": ["sourmash"], "install_dir": "site", "name": "sourmash", "package_type": "package", "sha256": "2f1a1b6906816867b95e61dea826e7a5cd9344380324a2a90bae8bb33753927d", "shared_library": false, "unvendored_tests": false, "version": "4.8.8"}, "sparseqr": {"depends": ["pycparser", "cffi", "numpy", "scipy", "suitesparse"], "file_name": "sparseqr-1.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["sparseqr"], "install_dir": "site", "name": "sparseqr", "package_type": "package", "sha256": "88acb940cfadf513b4d72c1ccb0adaf7001f0d7da9ca101d450e0321cef08116", "shared_library": false, "unvendored_tests": false, "version": "1.2"}, "sqlalchemy": {"depends": ["sqlite3", "typing-extensions"], "file_name": "SQLAlchemy-2.0.29-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["sqlalchemy"], "install_dir": "site", "name": "sqlalchemy", "package_type": "package", "sha256": "e3e485820b9782d2ce98bf4fdbaa6dee12314d3fd66751fd9ec6edb13065e75b", "shared_library": false, "unvendored_tests": true, "version": "2.0.29"}, "sqlalchemy-tests": {"depends": ["sqlalchemy"], "file_name": "sqlalchemy-tests.tar", "imports": [], "install_dir": "site", "name": "sqlalchemy-tests", "package_type": "package", "sha256": "a4335234473c85da12709a05d651940564acd344f62bf91cbf13cdac888f9604", "shared_library": false, "unvendored_tests": false, "version": "2.0.29"}, "sqlite3": {"depends": [], "file_name": "sqlite3-1.0.0.zip", "imports": ["sqlite3", "_sqlite3"], "install_dir": "stdlib", "name": "sqlite3", "package_type": "cpython_module", "sha256": "29586a9ec94786c6385d3ef46aa65f484bb3f9f61b1bdbd18a0aa396e551cd9c", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "ssl": {"depends": ["openssl"], "file_name": "ssl-1.0.0.zip", "imports": ["ssl", "_ssl"], "install_dir": "stdlib", "name": "ssl", "package_type": "cpython_module", "sha256": "c57eed8de854b8d1ba4911c98390e5a6810d2b88ce84e030598d9a23c98346bf", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "stack-data": {"depends": [], "file_name": "stack_data-0.6.3-py3-none-any.whl", "imports": ["stack_data"], "install_dir": "site", "name": "stack_data", "package_type": "package", "sha256": "2f71f5dd0878678c74d89dfafe6b6d343b4ad5a1036f444df8b0208b8dd2576a", "shared_library": false, "unvendored_tests": false, "version": "0.6.3"}, "statsmodels": {"depends": ["numpy", "scipy", "pandas", "patsy", "packaging"], "file_name": "statsmodels-0.14.2-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["statsmodels"], "install_dir": "site", "name": "statsmodels", "package_type": "package", "sha256": "663c63c8ec52595437705125b2312c334ade7d805278c4484a64db14d6c0e5cf", "shared_library": false, "unvendored_tests": true, "version": "0.14.2"}, "statsmodels-tests": {"depends": ["statsmodels"], "file_name": "statsmodels-tests.tar", "imports": [], "install_dir": "site", "name": "statsmodels-tests", "package_type": "package", "sha256": "6c90ea3d8b162c1557a557145bc8fcdf3437da3d5be74d710b0824b6f5ad49ec", "shared_library": false, "unvendored_tests": false, "version": "0.14.2"}, "strictyaml": {"depends": ["python-dateutil"], "file_name": "strictyaml-1.7.3-py3-none-any.whl", "imports": ["strictyaml"], "install_dir": "site", "name": "strictyaml", "package_type": "package", "sha256": "7e0306417f802210ada75ea0eb506d7a60b150d366f3144b7e8bc18f5ff75d29", "shared_library": false, "unvendored_tests": false, "version": "1.7.3"}, "suitesparse": {"depends": ["openblas"], "file_name": "suitesparse-5.11.0.zip", "imports": [], "install_dir": "dynlib", "name": "suitesparse", "package_type": "shared_library", "sha256": "c14ddfb6b7c3f5fdc86177b9f96cdceacdad3d970bc53a1b22be5c1dd244db8d", "shared_library": true, "unvendored_tests": false, "version": "5.11.0"}, "svgwrite": {"depends": [], "file_name": "svgwrite-1.4.3-py3-none-any.whl", "imports": ["svgwrite"], "install_dir": "site", "name": "svgwrite", "package_type": "package", "sha256": "2f708f13e85580beff1adb8e1b178ab83c3f41161aec6afbdcc418fc6297daa7", "shared_library": false, "unvendored_tests": false, "version": "1.4.3"}, "swiglpk": {"depends": [], "file_name": "swiglpk-5.0.10-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["swiglpk"], "install_dir": "site", "name": "swiglpk", "package_type": "package", "sha256": "47c0fb4a7692d01155b2b15af095ad3505a2ba2c5a5d31bf46197293533515d9", "shared_library": false, "unvendored_tests": false, "version": "5.0.10"}, "sympy": {"depends": ["mpmath"], "file_name": "sympy-1.12-py3-none-any.whl", "imports": ["isympy", "sympy"], "install_dir": "site", "name": "sympy", "package_type": "package", "sha256": "7a4792ab8d537fa71e1a0ee316e106132212e7ef1473fb6a31b325ad868f1802", "shared_library": false, "unvendored_tests": true, "version": "1.12"}, "sympy-tests": {"depends": ["sympy"], "file_name": "sympy-tests.tar", "imports": [], "install_dir": "site", "name": "sympy-tests", "package_type": "package", "sha256": "5a1619b61ee0615c73cafbf492c5a9875b4af5411264c3eca2201d346e86a093", "shared_library": false, "unvendored_tests": false, "version": "1.12"}, "tblib": {"depends": [], "file_name": "tblib-3.0.0-py3-none-any.whl", "imports": ["tblib"], "install_dir": "site", "name": "tblib", "package_type": "package", "sha256": "ad99c66810817b58e92c3be9ceb9ebc3762f51d7ecb6c06a2c8753a170c4e662", "shared_library": false, "unvendored_tests": false, "version": "3.0.0"}, "termcolor": {"depends": [], "file_name": "termcolor-2.4.0-py3-none-any.whl", "imports": ["termcolor"], "install_dir": "site", "name": "termcolor", "package_type": "package", "sha256": "f7eea924f89bf4f79713eb1253d6f3560247cee070e0c5531256e93d7d21e666", "shared_library": false, "unvendored_tests": false, "version": "2.4.0"}, "test": {"depends": [], "file_name": "test-1.0.0.zip", "imports": ["test"], "install_dir": "stdlib", "name": "test", "package_type": "cpython_module", "sha256": "0e1c1c9a3fab52f55d1ee52242962d3e83c4b25aa33aa4c0a9d3e628cedcb67c", "shared_library": true, "unvendored_tests": false, "version": "1.0.0"}, "texttable": {"depends": [], "file_name": "texttable-1.7.0-py2.py3-none-any.whl", "imports": ["texttable"], "install_dir": "site", "name": "texttable", "package_type": "package", "sha256": "c2c44fd89aff617e8211c9e531ee654de3af7dba3cc8edb8415129555e349199", "shared_library": false, "unvendored_tests": false, "version": "1.7.0"}, "threadpoolctl": {"depends": [], "file_name": "threadpoolctl-3.4.0-py3-none-any.whl", "imports": ["threadpoolctl"], "install_dir": "site", "name": "threadpoolctl", "package_type": "package", "sha256": "42744246ef195dcf1a4e1387ef93b52fdbbb1e838c970d8303a970c9cea886c5", "shared_library": false, "unvendored_tests": false, "version": "3.4.0"}, "tomli": {"depends": [], "file_name": "tomli-2.0.1-py3-none-any.whl", "imports": ["tomli"], "install_dir": "site", "name": "tomli", "package_type": "package", "sha256": "96c7e72ef2b9a75c5a5fe81e1e79f8c0dcb2f95cefe5c2b4406083e41bb922be", "shared_library": false, "unvendored_tests": false, "version": "2.0.1"}, "tomli-w": {"depends": [], "file_name": "tomli_w-1.0.0-py3-none-any.whl", "imports": ["tomli_w"], "install_dir": "site", "name": "tomli-w", "package_type": "package", "sha256": "bc217efbfd5909c997ee2ffcbbedce498b235f89d3f8bd97dc91bce38f8222f6", "shared_library": false, "unvendored_tests": false, "version": "1.0.0"}, "toolz": {"depends": [], "file_name": "toolz-0.12.1-py3-none-any.whl", "imports": ["tlz", "toolz"], "install_dir": "site", "name": "toolz", "package_type": "package", "sha256": "7fdec44c6bf2ec5e200f88d34616ffa37a612d59e79a3c7650e1651f43830ad1", "shared_library": false, "unvendored_tests": true, "version": "0.12.1"}, "toolz-tests": {"depends": ["toolz"], "file_name": "toolz-tests.tar", "imports": [], "install_dir": "site", "name": "toolz-tests", "package_type": "package", "sha256": "f8284c76aa221ae218fc32b9c8e79f2002d762d4b29fc418bf661ed1aaaebbbf", "shared_library": false, "unvendored_tests": false, "version": "0.12.1"}, "tqdm": {"depends": [], "file_name": "tqdm-4.66.2-py3-none-any.whl", "imports": ["tqdm"], "install_dir": "site", "name": "tqdm", "package_type": "package", "sha256": "a320470e3ebe4427ba9d5ebc55ba32e115602768a3c3839ffc3b6734cdc5c332", "shared_library": false, "unvendored_tests": false, "version": "4.66.2"}, "traitlets": {"depends": [], "file_name": "traitlets-5.14.3-py3-none-any.whl", "imports": ["traitlets"], "install_dir": "site", "name": "traitlets", "package_type": "package", "sha256": "eb436d3e1a6fc61c765112cb14ef88a97e17af1c1776530d152031d03d616625", "shared_library": false, "unvendored_tests": true, "version": "5.14.3"}, "traitlets-tests": {"depends": ["traitlets"], "file_name": "traitlets-tests.tar", "imports": [], "install_dir": "site", "name": "traitlets-tests", "package_type": "package", "sha256": "ede0f6fe9beada3195925d17172d7bd6007bb32704528658d858b696b204bb66", "shared_library": false, "unvendored_tests": false, "version": "5.14.3"}, "traits": {"depends": [], "file_name": "traits-6.4.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["traits"], "install_dir": "site", "name": "traits", "package_type": "package", "sha256": "2c44ca2179d175a1321158821ea4c1950a661ea4d68df0c7c00994ac2b8bb1f5", "shared_library": false, "unvendored_tests": true, "version": "6.4.3"}, "traits-tests": {"depends": ["traits"], "file_name": "traits-tests.tar", "imports": [], "install_dir": "site", "name": "traits-tests", "package_type": "package", "sha256": "281021428f44bc41d9b312740eb2c5081434d14be76ab56dc872011156b0ad01", "shared_library": false, "unvendored_tests": false, "version": "6.4.3"}, "tskit": {"depends": ["numpy", "svgwrite", "jsonschema", "rpds-py"], "file_name": "tskit-0.5.6-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["tskit"], "install_dir": "site", "name": "tskit", "package_type": "package", "sha256": "357a4e1bd6e3221f492300564910dea0745c796d4bba3327244e3bd766a900d7", "shared_library": false, "unvendored_tests": false, "version": "0.5.6"}, "typing-extensions": {"depends": [], "file_name": "typing_extensions-4.11.0-py3-none-any.whl", "imports": ["typing_extensions"], "install_dir": "site", "name": "typing-extensions", "package_type": "package", "sha256": "27413666e546c12f1fd74234fcd3ff44b3b11dd82b4becb2dfd697d199a01373", "shared_library": false, "unvendored_tests": false, "version": "4.11.0"}, "tzdata": {"depends": [], "file_name": "tzdata-2024.1-py2.py3-none-any.whl", "imports": ["tzdata"], "install_dir": "site", "name": "tzdata", "package_type": "package", "sha256": "7bcb4600ba97f6aae14e4bdd9d3f03f16e0b8136f9532a9af3f4c0e9d0403bea", "shared_library": false, "unvendored_tests": false, "version": "2024.1"}, "uncertainties": {"depends": ["future"], "file_name": "uncertainties-3.1.7-py2.py3-none-any.whl", "imports": ["uncertainties"], "install_dir": "site", "name": "uncertainties", "package_type": "package", "sha256": "7d976cd8383c7a5494588ef8c753aa3bc0cf58b85c13d35ef32a28258bc51dca", "shared_library": false, "unvendored_tests": true, "version": "3.1.7"}, "uncertainties-tests": {"depends": ["uncertainties"], "file_name": "uncertainties-tests.tar", "imports": [], "install_dir": "site", "name": "uncertainties-tests", "package_type": "package", "sha256": "31b8ae8b62bfac15cd420ddf7aecb67a11ea6c4911b43ac181763fb0c8c21279", "shared_library": false, "unvendored_tests": false, "version": "3.1.7"}, "unyt": {"depends": ["numpy", "packaging", "sympy"], "file_name": "unyt-3.0.2-py3-none-any.whl", "imports": ["unyt"], "install_dir": "site", "name": "unyt", "package_type": "package", "sha256": "0266261f277eb5ee959c6ddc12b912808ede8f4fc65550474fa52345072adca8", "shared_library": false, "unvendored_tests": true, "version": "3.0.2"}, "unyt-tests": {"depends": ["unyt"], "file_name": "unyt-tests.tar", "imports": [], "install_dir": "site", "name": "unyt-tests", "package_type": "package", "sha256": "dc62293a0d6b7548dea6cffce3fc6560c9efd62fd828038ef9e650a3352bc1fb", "shared_library": false, "unvendored_tests": false, "version": "3.0.2"}, "urllib3": {"depends": [], "file_name": "urllib3-2.2.1-py3-none-any.whl", "imports": ["urllib3"], "install_dir": "site", "name": "urllib3", "package_type": "package", "sha256": "db8c61a24d25a4902704fce3b4dfdd369141859cbeb5b8dd553e22634875f912", "shared_library": false, "unvendored_tests": false, "version": "2.2.1"}, "wcwidth": {"depends": [], "file_name": "wcwidth-0.2.13-py2.py3-none-any.whl", "imports": ["wcwidth"], "install_dir": "site", "name": "wcwidth", "package_type": "package", "sha256": "3384f09b43b9618624cb57500076aa54372a1c6160bf5069f6309c9f9e11a914", "shared_library": false, "unvendored_tests": false, "version": "0.2.13"}, "webencodings": {"depends": [], "file_name": "webencodings-0.5.1-py2.py3-none-any.whl", "imports": ["webencodings"], "install_dir": "site", "name": "webencodings", "package_type": "package", "sha256": "bd558d5be03feae048c02a9b5875f7b7dc5eac1e1c8b167fac3a9575bd13178c", "shared_library": false, "unvendored_tests": false, "version": "0.5.1"}, "wordcloud": {"depends": ["matplotlib"], "file_name": "wordcloud-1.9.3-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["wordcloud"], "install_dir": "site", "name": "wordcloud", "package_type": "package", "sha256": "e35767ded44ad7a2565633e60a011cdf63c9c40ce8755127c80ab12bd71c340b", "shared_library": false, "unvendored_tests": false, "version": "1.9.3"}, "wrapt": {"depends": [], "file_name": "wrapt-1.16.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["wrapt"], "install_dir": "site", "name": "wrapt", "package_type": "package", "sha256": "218ecd2503e989f9ff5e0c44b3270f3faed9e0c1cc989dd30e555840833aba7a", "shared_library": false, "unvendored_tests": false, "version": "1.16.0"}, "xarray": {"depends": ["numpy", "packaging", "pandas"], "file_name": "xarray-2024.3.0-py3-none-any.whl", "imports": ["xarray"], "install_dir": "site", "name": "xarray", "package_type": "package", "sha256": "811da0060896ccf83a10b575e3b73519c2ea6c4f1c53a7cc4b7ec7367f8297b2", "shared_library": false, "unvendored_tests": true, "version": "2024.3.0"}, "xarray-tests": {"depends": ["xarray"], "file_name": "xarray-tests.tar", "imports": [], "install_dir": "site", "name": "xarray-tests", "package_type": "package", "sha256": "ad0d1a70e79d8fd8059283d0e3cc56ddbf0282f64f252b84d9aa18bf183c8f5a", "shared_library": false, "unvendored_tests": false, "version": "2024.3.0"}, "xgboost": {"depends": ["numpy", "scipy", "setuptools"], "file_name": "xgboost-2.1.0.dev0-py3-none-pyodide_2024_0_wasm32.whl", "imports": ["xgboost"], "install_dir": "site", "name": "xgboost", "package_type": "package", "sha256": "4628575b6ceb2e986bbba4356c4864fb32bcbcc60786d3f26d5730012a48712b", "shared_library": false, "unvendored_tests": false, "version": "2.1.0.dev0"}, "xlrd": {"depends": [], "file_name": "xlrd-2.0.1-py2.py3-none-any.whl", "imports": ["xlrd"], "install_dir": "site", "name": "xlrd", "package_type": "package", "sha256": "5dce9ae1baf5f1bca143ae6bc664618ed3cd69d15b7be8d59901296dfd5f316d", "shared_library": false, "unvendored_tests": false, "version": "2.0.1"}, "xxhash": {"depends": [], "file_name": "xxhash-3.4.1-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["xxhash"], "install_dir": "site", "name": "xxhash", "package_type": "package", "sha256": "d7552b7b502fed9a814536ef4192f615c72293c03bc3b4f0abb8a910b025d22b", "shared_library": false, "unvendored_tests": false, "version": "3.4.1"}, "xyzservices": {"depends": [], "file_name": "xyzservices-2024.4.0-py3-none-any.whl", "imports": ["xyzservices"], "install_dir": "site", "name": "xyzservices", "package_type": "package", "sha256": "d83a3998a7ebdaf867608ed25ff2225d735ef165d7d637a53eae60a4019be77c", "shared_library": false, "unvendored_tests": true, "version": "2024.4.0"}, "xyzservices-tests": {"depends": ["xyzservices"], "file_name": "xyzservices-tests.tar", "imports": [], "install_dir": "site", "name": "xyzservices-tests", "package_type": "package", "sha256": "5478c9cfc55e20112bd9786d5170e03498715987db905508288723ce5bd015ba", "shared_library": false, "unvendored_tests": false, "version": "2024.4.0"}, "yarl": {"depends": ["multidict", "idna"], "file_name": "yarl-1.9.4-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["yarl"], "install_dir": "site", "name": "yarl", "package_type": "package", "sha256": "970a80d724db012e4832adbe5f7f25d3070dddec20eac496063ec4c615ba0f5b", "shared_library": false, "unvendored_tests": false, "version": "1.9.4"}, "yt": {"depends": ["ewah_bool_utils", "numpy", "matplotlib", "sympy", "setuptools", "packaging", "unyt", "cmyt", "colorspacious", "tqdm", "tomli", "tomli-w"], "file_name": "yt-4.3.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["yt"], "install_dir": "site", "name": "yt", "package_type": "package", "sha256": "8064f02ce5685790bfa3030a538a5808086ffdd84d4591cb2caaf6d959125564", "shared_library": false, "unvendored_tests": false, "version": "4.3.0"}, "zarr": {"depends": ["numpy", "asciitree", "numcodecs"], "file_name": "zarr-2.16.1-py3-none-any.whl", "imports": ["zarr"], "install_dir": "site", "name": "zarr", "package_type": "package", "sha256": "e8d9f3f619de2b6ab029535822e2ac7d215533c175b10dda90744cd0584edc4b", "shared_library": false, "unvendored_tests": true, "version": "2.16.1"}, "zarr-tests": {"depends": ["zarr"], "file_name": "zarr-tests.tar", "imports": [], "install_dir": "site", "name": "zarr-tests", "package_type": "package", "sha256": "559d33fa87d95d2da09bc0f84fa387ee6e9bf193eecb55b103825659065d804f", "shared_library": false, "unvendored_tests": false, "version": "2.16.1"}, "zengl": {"depends": [], "file_name": "zengl-2.4.1-cp311-abi3-pyodide_2024_0_wasm32.whl", "imports": ["zengl", "_zengl"], "install_dir": "site", "name": "zengl", "package_type": "package", "sha256": "84ca8691ab1b7ac6892fb546f1bb954a9cd97f46b39b25f33e3ee5c68ce44cb5", "shared_library": false, "unvendored_tests": false, "version": "2.4.1"}, "zstandard": {"depends": ["cffi"], "file_name": "zstandard-0.22.0-cp312-cp312-pyodide_2024_0_wasm32.whl", "imports": ["zstandard"], "install_dir": "site", "name": "zstandard", "package_type": "package", "sha256": "2132e35bc2775019e64bb1a88ae0f09c4bb46eb6516681ff7e0f27ef38fade54", "shared_library": false, "unvendored_tests": false, "version": "0.22.0"}}}
\ No newline at end of file
diff --git a/static/robots.txt b/static/robots.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1f53798bb4fe33c86020be7f10c44f29486fd190
--- /dev/null
+++ b/static/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/static/static/favicon.png b/static/static/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2b2074780847581edf9cf2ed0d2e9ebd8ff08c56
Binary files /dev/null and b/static/static/favicon.png differ
diff --git a/static/static/splash-dark.png b/static/static/splash-dark.png
new file mode 100644
index 0000000000000000000000000000000000000000..202c03f8e46e189025b204b5bedc0552aec4ac82
Binary files /dev/null and b/static/static/splash-dark.png differ
diff --git a/static/static/splash.png b/static/static/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..389196ca6a364b9e4b7daa0fc13be463b914b251
Binary files /dev/null and b/static/static/splash.png differ
diff --git a/static/themes/rosepine-dawn.css b/static/themes/rosepine-dawn.css
new file mode 100644
index 0000000000000000000000000000000000000000..cc2b5de57d9f5a69d4295513f2dea889310549dd
--- /dev/null
+++ b/static/themes/rosepine-dawn.css
@@ -0,0 +1,140 @@
+.rose-pine-dawn * {
+	color: #575279 !important;
+	stroke: #d7827e !important;
+}
+
+.rose-pine-dawn .app > * {
+	background-color: #faf4ed !important;
+}
+
+.rose-pine-dawn #nav {
+	background-color: #fffaf3;
+}
+
+.rose-pine-dawn .py-2\.5.my-auto.flex.flex-col.justify-between.h-screen {
+	background: #f2e9e1;
+}
+
+.rose-pine-dawn .bg-white.dark\:bg-gray-800 {
+	background: #f2e9e1;
+}
+
+.rose-pine-dawn .w-4.h-4 {
+	fill: #ebbcba;
+}
+
+.rose-pine-dawn #chat-input {
+	background: #cecacd;
+	margin: 0.3rem;
+	padding: 0.5rem;
+}
+
+.rose-pine-dawn .bg-gradient-to-t.from-white.dark\:from-gray-800.from-40\%.pb-2 {
+	background: #f2e9e1 !important;
+	padding-top: 0.6rem;
+}
+
+.rose-pine-dawn
+	.text-white.bg-gray-100.dark\:text-gray-800.dark\:bg-gray-600.disabled.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center {
+	background-color: #cecacd;
+	transition: background-color 0.2s ease-out linear;
+}
+
+.rose-pine-dawn
+	.bg-black.text-white.hover\:bg-gray-900.dark\:bg-white.dark\:text-black.dark\:hover\:bg-gray-100.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center {
+	background-color: #286983;
+	transition: background-color 0.2s ease-out linear;
+}
+
+.rose-pine-dawn
+	.bg-black.text-white.hover\:bg-gray-900.dark\:bg-white.dark\:text-black.dark\:hover\:bg-gray-100.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center
+	> * {
+	fill: #56949f !important;
+	transition: fill 0.2s ease-out linear;
+}
+
+.rose-pine-dawn
+	.w-full.flex.justify-between.rounded-md.px-3.py-2.hover\:bg-gray-900.bg-gray-900.transition.whitespace-nowrap.text-ellipsis {
+	background-color: #56526e;
+	font-weight: bold;
+}
+
+.rose-pine-dawn .hover\:bg-gray-900:hover {
+	--tw-bg-opacity: 1;
+	background-color: rgb(152 147 165 / var(--tw-bg-opacity));
+}
+
+.rose-pine-dawn .text-xs.text-gray-700.uppercase.bg-gray-50.dark\:bg-gray-700.dark\:text-gray-400 {
+	background-color: #403d52;
+}
+
+.rose-pine-dawn .scrollbar-hidden.relative.overflow-x-auto.whitespace-nowrap.svelte-3g4avz {
+	border-radius: 16px 16px 0 0;
+}
+
+.rose-pine-dawn .base.enter.svelte-ug60r4 {
+	background-color: #286983;
+}
+
+.rose-pine-dawn .message.svelte-1nauejd {
+	color: #e0def4 !important;
+}
+
+.rose-pine-dawn #dropdownDots {
+	background-color: #dfdad9;
+}
+
+.rose-pine-dawn .flex.py-2\.5.px-3\.5.w-full.hover\:bg-gray-800.transition:hover {
+	background: #cecacd;
+}
+
+.rose-pine-dawn #dropdownDots {
+	background-color: #dfdad9;
+}
+
+.rose-pine-dawn .flex.py-2\.5.px-3\.5.w-full.hover\:bg-gray-800.transition:hover {
+	background: #cecacd;
+}
+
+.rose-pine-dawn
+	.m-auto.rounded-xl.max-w-full.w-\[40rem\].mx-2.bg-gray-50.dark\:bg-gray-900.shadow-3xl {
+	background-color: #f2e9e1;
+}
+
+.rose-pine-dawn
+	.w-full.rounded.p-4.text-sm.dark\:text-gray-300.dark\:bg-gray-800.outline-none.resize-none {
+	background-color: #cecacd;
+}
+
+.rose-pine-dawn
+	.w-full.rounded.py-2.px-4.text-sm.dark\:text-gray-300.dark\:bg-gray-800.outline-none.svelte-1vx7r9s {
+	background-color: #cecacd;
+}
+
+.rose-pine-dawn
+	.px-2\.5.py-2\.5.min-w-fit.rounded-lg.flex-1.md\:flex-none.flex.text-right.transition.bg-gray-200.dark\:bg-gray-700 {
+	background-color: #dfdad9;
+}
+
+.rose-pine-dawn
+	.px-2\.5.py-2\.5.min-w-fit.rounded-lg.flex-1.md\:flex-none.flex.text-right.transition.hover\:bg-gray-300.dark\:hover\:bg-gray-800:hover {
+	background-color: #cecacd;
+}
+
+.rose-pine-dawn .px-4.py-2.bg-emerald-600.hover\:bg-emerald-700.text-gray-100.transition.rounded {
+	background-color: #56949f;
+}
+
+.rose-pine-dawn #chat-search > * {
+	background-color: #dfdad9 !important;
+}
+
+.rose-pine-dawn .svelte-1ee93ns {
+	--primary: #b4637a !important;
+	--secondary: #fffaf3 !important;
+}
+
+.rose-pine-dawn .svelte-11kvm4p {
+	--primary: #56949f !important;
+	--secondary: #fffaf3 !important;
+}
diff --git a/static/themes/rosepine.css b/static/themes/rosepine.css
new file mode 100644
index 0000000000000000000000000000000000000000..494fd29e06b8e89f322b775a7f6a8d39e8c8c90a
--- /dev/null
+++ b/static/themes/rosepine.css
@@ -0,0 +1,131 @@
+.rose-pine * {
+	color: #e0def4 !important;
+	stroke: #907aa9 !important;
+}
+
+.rose-pine .app > * {
+	background-color: #1f1d2e !important;
+}
+
+.rose-pine #nav {
+	background-color: #191724;
+}
+
+.rose-pine .py-2\.5.my-auto.flex.flex-col.justify-between.h-screen {
+	background: #191724;
+}
+
+.rose-pine .bg-white.dark\:bg-gray-800 {
+	background: #26233a;
+}
+
+.rose-pine .w-4.h-4 {
+	fill: #c4a7e7;
+}
+
+.rose-pine #chat-input {
+	background: #393552;
+	margin: 0.3rem;
+	padding: 0.5rem;
+}
+
+.rose-pine .bg-gradient-to-t.from-white.dark\:from-gray-800.from-40\%.pb-2 {
+	background: #26233a !important;
+	padding-top: 0.6rem;
+}
+
+.rose-pine
+	.text-white.bg-gray-100.dark\:text-gray-800.dark\:bg-gray-600.disabled.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center {
+	background-color: #6e6a86;
+	transition: background-color 0.2s ease-out linear;
+}
+
+.rose-pine
+	.bg-black.text-white.hover\:bg-gray-900.dark\:bg-white.dark\:text-black.dark\:hover\:bg-gray-100.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center {
+	background-color: #286983;
+	transition: background-color 0.2s ease-out linear;
+}
+
+.rose-pine
+	.bg-black.text-white.hover\:bg-gray-900.dark\:bg-white.dark\:text-black.dark\:hover\:bg-gray-100.transition.rounded-lg.p-1.mr-0\.5.w-7.h-7.self-center
+	> * {
+	fill: #9ccfd8 !important;
+	transition: fill 0.2s ease-out linear;
+}
+
+.rose-pine
+	.w-full.flex.justify-between.rounded-md.px-3.py-2.hover\:bg-gray-900.bg-gray-900.transition.whitespace-nowrap.text-ellipsis {
+	background-color: #56526e;
+	font-weight: bold;
+}
+
+.rose-pine .hover\:bg-gray-900:hover {
+	--tw-bg-opacity: 1;
+	background-color: rgb(57 53 82 / var(--tw-bg-opacity));
+}
+
+.rose-pine .text-xs.text-gray-700.uppercase.bg-gray-50.dark\:bg-gray-700.dark\:text-gray-400 {
+	background-color: #403d52;
+}
+
+.rose-pine .scrollbar-hidden.relative.overflow-x-auto.whitespace-nowrap.svelte-3g4avz {
+	border-radius: 16px 16px 0 0;
+}
+
+.rose-pine .base.enter.svelte-ug60r4 {
+	background-color: #393552;
+}
+
+.rose-pine .message.svelte-1nauejd {
+	color: #e0def4 !important;
+}
+
+.rose-pine #dropdownDots {
+	background-color: #403d52;
+}
+
+.rose-pine .flex.py-2\.5.px-3\.5.w-full.hover\:bg-gray-800.transition:hover {
+	background: #524f67;
+}
+
+.rose-pine .m-auto.rounded-xl.max-w-full.w-\[40rem\].mx-2.bg-gray-50.dark\:bg-gray-900.shadow-3xl {
+	background-color: #26233a;
+}
+
+.rose-pine
+	.w-full.rounded.p-4.text-sm.dark\:text-gray-300.dark\:bg-gray-800.outline-none.resize-none {
+	background-color: #524f67;
+}
+
+.rose-pine
+	.w-full.rounded.py-2.px-4.text-sm.dark\:text-gray-300.dark\:bg-gray-800.outline-none.svelte-1vx7r9s {
+	background-color: #524f67;
+}
+
+.rose-pine
+	.px-2\.5.py-2\.5.min-w-fit.rounded-lg.flex-1.md\:flex-none.flex.text-right.transition.bg-gray-200.dark\:bg-gray-700 {
+	background-color: #403d52;
+}
+
+.rose-pine
+	.px-2\.5.py-2\.5.min-w-fit.rounded-lg.flex-1.md\:flex-none.flex.text-right.transition.hover\:bg-gray-300.dark\:hover\:bg-gray-800:hover {
+	background-color: #524f67;
+}
+
+.rose-pine .px-4.py-2.bg-emerald-600.hover\:bg-emerald-700.text-gray-100.transition.rounded {
+	background-color: #31748f;
+}
+
+.rose-pine #chat-search > * {
+	background-color: #403d52 !important;
+}
+
+.rose-pine .svelte-1ee93ns {
+	--primary: #eb6f92 !important;
+	--secondary: #e0def4 !important;
+}
+
+.rose-pine .svelte-11kvm4p {
+	--primary: #9ccfd8 !important;
+	--secondary: #1f1d2e !important;
+}
diff --git a/static/user.png b/static/user.png
new file mode 100644
index 0000000000000000000000000000000000000000..2771e7286684e0935f952ef671dfe1f75f422256
Binary files /dev/null and b/static/user.png differ
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..d756191d3068dbf6c7cf37f9a305357aec4de522
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,27 @@
+import adapter from '@sveltejs/adapter-static';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+	// Consult https://kit.svelte.dev/docs/integrations#preprocessors
+	// for more information about preprocessors
+	preprocess: vitePreprocess(),
+	kit: {
+		// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
+		// If your environment is not supported or you settled on a specific environment, switch out the adapter.
+		// See https://kit.svelte.dev/docs/adapters for more information about adapters.
+		adapter: adapter({
+			pages: 'build',
+			assets: 'build',
+			fallback: 'index.html'
+		})
+	},
+	onwarn: (warning, handler) => {
+		const { code, _ } = warning;
+		if (code === 'css-unused-selector') return;
+
+		handler(warning);
+	}
+};
+
+export default config;
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..937c0ab564fee58c43f1714dea7132f4cbc1091d
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,42 @@
+import typography from '@tailwindcss/typography';
+
+/** @type {import('tailwindcss').Config} */
+export default {
+	darkMode: 'class',
+	content: ['./src/**/*.{html,js,svelte,ts}'],
+	theme: {
+		extend: {
+			colors: {
+				gray: {
+					50: '#f9f9f9',
+					100: '#ececec',
+					200: '#e3e3e3',
+					300: '#cdcdcd',
+					400: '#b4b4b4',
+					500: '#9b9b9b',
+					600: '#676767',
+					700: '#4e4e4e',
+					800: 'var(--color-gray-800, #333)',
+					850: 'var(--color-gray-850, #262626)',
+					900: 'var(--color-gray-900, #171717)',
+					950: 'var(--color-gray-950, #0d0d0d)'
+				}
+			},
+			typography: {
+				DEFAULT: {
+					css: {
+						pre: false,
+						code: false,
+						'pre code': false,
+						'code::before': false,
+						'code::after': false
+					}
+				}
+			},
+			padding: {
+				'safe-bottom': 'env(safe-area-inset-bottom)'
+			}
+		}
+	},
+	plugins: [typography]
+};
diff --git a/test/test_files/image_gen/sd-empty.pt b/test/test_files/image_gen/sd-empty.pt
new file mode 100644
index 0000000000000000000000000000000000000000..c6ac59eb01fcb778290a85f12bdb7867de3dfdd1
Binary files /dev/null and b/test/test_files/image_gen/sd-empty.pt differ
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..6ae0c8c44d08a78140c9c62c1b0f745edd05e804
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,17 @@
+{
+	"extends": "./.svelte-kit/tsconfig.json",
+	"compilerOptions": {
+		"allowJs": true,
+		"checkJs": true,
+		"esModuleInterop": true,
+		"forceConsistentCasingInFileNames": true,
+		"resolveJsonModule": true,
+		"skipLibCheck": true,
+		"sourceMap": true,
+		"strict": true
+	}
+	// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
+	//
+	// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
+	// from the referenced tsconfig.json - TypeScript does not merge them in
+}
diff --git a/update_ollama_models.sh b/update_ollama_models.sh
new file mode 100644
index 0000000000000000000000000000000000000000..bde11b4b248694fcb1a224f7c193e75a88ee156f
--- /dev/null
+++ b/update_ollama_models.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# update_llm.sh
+
+# Retrieves the list of LLMs installed in the Docker container
+llm_list=$(docker exec ollama ollama list | tail -n +2 | awk '{print $1}')
+
+# Loop over each LLM to update it
+for llm in $llm_list; do
+  docker exec ollama ollama pull $llm
+done
diff --git a/uv.lock b/uv.lock
new file mode 100644
index 0000000000000000000000000000000000000000..1b0e7c6ddea7bcc2f19e24771c524684f013d942
--- /dev/null
+++ b/uv.lock
@@ -0,0 +1,4659 @@
+version = 1
+requires-python = ">=3.11, <3.12.0"
+resolution-markers = [
+    "python_full_version < '3.12' and platform_system == 'Darwin'",
+    "python_full_version < '3.12' and platform_machine == 'aarch64' and platform_system == 'Linux'",
+    "(python_full_version < '3.12' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version < '3.12' and platform_system != 'Darwin' and platform_system != 'Linux')",
+    "python_full_version < '3.12' and platform_system == 'Darwin'",
+    "python_full_version >= '3.12' and python_full_version < '3.12.4' and platform_system == 'Darwin'",
+    "python_full_version >= '3.12.4' and python_full_version < '3.13' and platform_system == 'Darwin'",
+    "python_full_version >= '3.13' and platform_system == 'Darwin'",
+    "python_full_version < '3.12' and platform_machine == 'aarch64' and platform_system == 'Linux'",
+    "python_full_version >= '3.12' and python_full_version < '3.12.4' and platform_machine == 'aarch64' and platform_system == 'Linux'",
+    "python_full_version >= '3.12.4' and python_full_version < '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'",
+    "python_full_version >= '3.13' and platform_machine == 'aarch64' and platform_system == 'Linux'",
+    "(python_full_version < '3.12' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version < '3.12' and platform_system != 'Darwin' and platform_system != 'Linux')",
+    "(python_full_version >= '3.12' and python_full_version < '3.12.4' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.12' and python_full_version < '3.12.4' and platform_system != 'Darwin' and platform_system != 'Linux')",
+    "(python_full_version >= '3.12.4' and python_full_version < '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.12.4' and python_full_version < '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')",
+    "(python_full_version >= '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version >= '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')",
+]
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f7/22bba300a16fd1cad99da1a23793fe43963ee326d012fdf852d0b4035955/aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2", size = 16786 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/18/b6/58ea188899950d759a837f9a58b2aee1d1a380ea4d6211ce9b1823748851/aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd", size = 12155 },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.10.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "aiohappyeyeballs" },
+    { name = "aiosignal" },
+    { name = "attrs" },
+    { name = "frozenlist" },
+    { name = "multidict" },
+    { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ca/28/ca549838018140b92a19001a8628578b0f2a3b38c16826212cc6f706e6d4/aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691", size = 7524360 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f1/90/54ccb1e4eadfb6c95deff695582453f6208584431d69bf572782e9ae542b/aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2", size = 586455 },
+    { url = "https://files.pythonhosted.org/packages/c3/7a/95e88c02756e7e718f054e1bb3ec6ad5d0ee4a2ca2bb1768c5844b3de30a/aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf", size = 397255 },
+    { url = "https://files.pythonhosted.org/packages/07/4f/767387b39990e1ee9aba8ce642abcc286d84d06e068dc167dab983898f18/aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e", size = 388973 },
+    { url = "https://files.pythonhosted.org/packages/61/46/0df41170a4d228c07b661b1ba9d87101d99a79339dc93b8b1183d8b20545/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77", size = 1326126 },
+    { url = "https://files.pythonhosted.org/packages/af/20/da0d65e07ce49d79173fed41598f487a0a722e87cfbaa8bb7e078a7c1d39/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061", size = 1364538 },
+    { url = "https://files.pythonhosted.org/packages/aa/20/b59728405114e57541ba9d5b96033e69d004e811ded299537f74237629ca/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697", size = 1399896 },
+    { url = "https://files.pythonhosted.org/packages/2a/92/006690c31b830acbae09d2618e41308fe4c81c0679b3b33a3af859e0b7bf/aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7", size = 1312914 },
+    { url = "https://files.pythonhosted.org/packages/d4/71/1a253ca215b6c867adbd503f1e142117527ea8775e65962bc09b2fad1d2c/aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0", size = 1271301 },
+    { url = "https://files.pythonhosted.org/packages/0a/ab/5d1d9ff9ce6cce8fa54774d0364e64a0f3cd50e512ff09082ced8e5217a1/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5", size = 1291652 },
+    { url = "https://files.pythonhosted.org/packages/75/5f/f90510ea954b9ae6e7a53d2995b97a3e5c181110fdcf469bc9238445871d/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e", size = 1286289 },
+    { url = "https://files.pythonhosted.org/packages/be/9e/1f523414237798660921817c82b9225a363af436458caf584d2fa6a2eb4a/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1", size = 1341848 },
+    { url = "https://files.pythonhosted.org/packages/f6/36/443472ddaa85d7d80321fda541d9535b23ecefe0bf5792cc3955ea635190/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277", size = 1361619 },
+    { url = "https://files.pythonhosted.org/packages/19/f6/3ecbac0bc4359c7d7ba9e85c6b10f57e20edaf1f97751ad2f892db231ad0/aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058", size = 1320869 },
+    { url = "https://files.pythonhosted.org/packages/34/7e/ed74ffb36e3a0cdec1b05d8fbaa29cb532371d5a20058b3a8052fc90fe7c/aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072", size = 359271 },
+    { url = "https://files.pythonhosted.org/packages/98/1b/718901f04bc8c886a742be9e83babb7b93facabf7c475cc95e2b3ab80b4d/aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff", size = 379143 },
+    { url = "https://files.pythonhosted.org/packages/d9/1c/74f9dad4a2fc4107e73456896283d915937f48177b99867b63381fadac6e/aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487", size = 583468 },
+    { url = "https://files.pythonhosted.org/packages/12/29/68d090551f2b58ce76c2b436ced8dd2dfd32115d41299bf0b0c308a5483c/aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a", size = 394066 },
+    { url = "https://files.pythonhosted.org/packages/8f/f7/971f88b4cdcaaa4622925ba7d86de47b48ec02a9040a143514b382f78da4/aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d", size = 389098 },
+    { url = "https://files.pythonhosted.org/packages/f1/5a/fe3742efdce551667b2ddf1158b27c5b8eb1edc13d5e14e996e52e301025/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75", size = 1332742 },
+    { url = "https://files.pythonhosted.org/packages/1a/52/a25c0334a1845eb4967dff279151b67ca32a948145a5812ed660ed900868/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178", size = 1372134 },
+    { url = "https://files.pythonhosted.org/packages/96/3d/33c1d8efc2d8ec36bff9a8eca2df9fdf8a45269c6e24a88e74f2aa4f16bd/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e", size = 1414413 },
+    { url = "https://files.pythonhosted.org/packages/64/74/0f1ddaa5f0caba1d946f0dd0c31f5744116e4a029beec454ec3726d3311f/aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f", size = 1328107 },
+    { url = "https://files.pythonhosted.org/packages/0a/32/c10118f0ad50e4093227234f71fd0abec6982c29367f65f32ee74ed652c4/aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73", size = 1280126 },
+    { url = "https://files.pythonhosted.org/packages/c6/c9/77e3d648d97c03a42acfe843d03e97be3c5ef1b4d9de52e5bd2d28eed8e7/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf", size = 1292660 },
+    { url = "https://files.pythonhosted.org/packages/7e/5d/99c71f8e5c8b64295be421b4c42d472766b263a1fe32e91b64bf77005bf2/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820", size = 1300988 },
+    { url = "https://files.pythonhosted.org/packages/8f/2c/76d2377dd947f52fbe8afb19b18a3b816d66c7966755c04030f93b1f7b2d/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca", size = 1339268 },
+    { url = "https://files.pythonhosted.org/packages/fd/e6/3d9d935cc705d57ed524d82ec5d6b678a53ac1552720ae41282caa273584/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91", size = 1366993 },
+    { url = "https://files.pythonhosted.org/packages/fe/c2/f7eed4d602f3f224600d03ab2e1a7734999b0901b1c49b94dc5891340433/aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6", size = 1329459 },
+    { url = "https://files.pythonhosted.org/packages/ce/8f/27f205b76531fc592abe29e1ad265a16bf934a9f609509c02d765e6a8055/aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12", size = 356968 },
+    { url = "https://files.pythonhosted.org/packages/39/8c/4f6c0b2b3629f6be6c81ab84d9d577590f74f01d4412bfc4067958eaa1e1/aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc", size = 377650 },
+    { url = "https://files.pythonhosted.org/packages/7b/b9/03b4327897a5b5d29338fa9b514f1c2f66a3e4fc88a4e40fad478739314d/aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092", size = 576994 },
+    { url = "https://files.pythonhosted.org/packages/67/1b/20c2e159cd07b8ed6dde71c2258233902fdf415b2fe6174bd2364ba63107/aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77", size = 390684 },
+    { url = "https://files.pythonhosted.org/packages/4d/6b/ff83b34f157e370431d8081c5d1741963f4fb12f9aaddb2cacbf50305225/aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385", size = 386176 },
+    { url = "https://files.pythonhosted.org/packages/4d/a1/6e92817eb657de287560962df4959b7ddd22859c4b23a0309e2d3de12538/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972", size = 1303310 },
+    { url = "https://files.pythonhosted.org/packages/04/29/200518dc7a39c30ae6d5bc232d7207446536e93d3d9299b8e95db6e79c54/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16", size = 1340445 },
+    { url = "https://files.pythonhosted.org/packages/8e/20/53f7bba841ba7b5bb5dea580fea01c65524879ba39cb917d08c845524717/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6", size = 1385121 },
+    { url = "https://files.pythonhosted.org/packages/f1/b4/d99354ad614c48dd38fb1ee880a1a54bd9ab2c3bcad3013048d4a1797d3a/aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa", size = 1299669 },
+    { url = "https://files.pythonhosted.org/packages/51/39/ca1de675f2a5729c71c327e52ac6344e63f036bd37281686ae5c3fb13bfb/aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689", size = 1252638 },
+    { url = "https://files.pythonhosted.org/packages/54/cf/a3ae7ff43138422d477348e309ef8275779701bf305ff6054831ef98b782/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57", size = 1266889 },
+    { url = "https://files.pythonhosted.org/packages/6e/7a/c6027ad70d9fb23cf254a26144de2723821dade1a624446aa22cd0b6d012/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f", size = 1266249 },
+    { url = "https://files.pythonhosted.org/packages/64/fd/ed136d46bc2c7e3342fed24662b4827771d55ceb5a7687847aae977bfc17/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599", size = 1311036 },
+    { url = "https://files.pythonhosted.org/packages/76/9a/43eeb0166f1119256d6f43468f900db1aed7fbe32069d2a71c82f987db4d/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5", size = 1338756 },
+    { url = "https://files.pythonhosted.org/packages/d5/bc/d01ff0810b3f5e26896f76d44225ed78b088ddd33079b85cd1a23514318b/aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987", size = 1299976 },
+    { url = "https://files.pythonhosted.org/packages/3e/c9/50a297c4f7ab57a949f4add2d3eafe5f3e68bb42f739e933f8b32a092bda/aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04", size = 355609 },
+    { url = "https://files.pythonhosted.org/packages/65/28/aee9d04fb0b3b1f90622c338a08e54af5198e704a910e20947c473298fd0/aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022", size = 375697 },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "frozenlist" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ae/67/0952ed97a9793b4958e5736f6d2b346b414a2cd63e82d05940032f45b32f/aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc", size = 19422 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/76/ac/a7305707cb852b7e16ff80eaf5692309bde30e2b1100a1fcacdc8f731d97/aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17", size = 7617 },
+]
+
+[[package]]
+name = "alembic"
+version = "1.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "mako" },
+    { name = "sqlalchemy" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/e2/efa88e86029cada2da5941ec664d50d9a3b2a91f5066405c6f90e5016c16/alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef", size = 1206463 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/df/ed/c884465c33c25451e4a5cd4acad154c29e5341e3214e220e7f3478aa4b0d/alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953", size = 232990 },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
+]
+
+[[package]]
+name = "anthropic"
+version = "0.34.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+    { name = "distro" },
+    { name = "httpx" },
+    { name = "jiter" },
+    { name = "pydantic" },
+    { name = "sniffio" },
+    { name = "tokenizers" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/87/e2/98ff733ff75c1d371c029fb27eb9308f9c8e694749cea70382338a8e7e88/anthropic-0.34.1.tar.gz", hash = "sha256:69e822bd7a31ec11c2edb85f2147e8f0ee0cfd3288fea70b0ca8808b2f9bf91d", size = 901462 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a4/1c/1ce9edec76885badebacb4e31d42acffbdfd30dbaa839d5c378d57ac9aa9/anthropic-0.34.1-py3-none-any.whl", hash = "sha256:2fa26710809d0960d970f26cd0be3686437250a481edb95c33d837aa5fa24158", size = 891537 },
+]
+
+[[package]]
+name = "anyio"
+version = "4.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "idna" },
+    { name = "sniffio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e6/e3/c4c8d473d6780ef1853d630d581f70d655b4f8d7553c6997958c283039a2/anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94", size = 163930 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7b/a2/10639a79341f6c019dedc95bd48a4928eed9f1d1197f4c04f546fc7ae0ff/anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7", size = 86780 },
+]
+
+[[package]]
+name = "apscheduler"
+version = "3.10.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pytz" },
+    { name = "six" },
+    { name = "tzlocal" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/34/5dcb368cf89f93132d9a31bd3747962a9dc874480e54333b0c09fa7d56ac/APScheduler-3.10.4.tar.gz", hash = "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a", size = 100832 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/13/b5/7af0cb920a476dccd612fbc9a21a3745fb29b1fcd74636078db8f7ba294c/APScheduler-3.10.4-py3-none-any.whl", hash = "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661", size = 59303 },
+]
+
+[[package]]
+name = "argon2-cffi"
+version = "23.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "argon2-cffi-bindings" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/31/fa/57ec2c6d16ecd2ba0cf15f3c7d1c3c2e7b5fcb83555ff56d7ab10888ec8f/argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08", size = 42798 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea", size = 15124 },
+]
+
+[[package]]
+name = "argon2-cffi-bindings"
+version = "21.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b9/e9/184b8ccce6683b0aa2fbb7ba5683ea4b9c5763f1356347f1312c32e3c66e/argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3", size = 1779911 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d4/13/838ce2620025e9666aa8f686431f67a29052241692a3dd1ae9d3692a89d3/argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367", size = 29658 },
+    { url = "https://files.pythonhosted.org/packages/b3/02/f7f7bb6b6af6031edb11037639c697b912e1dea2db94d436e681aea2f495/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d", size = 80583 },
+    { url = "https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae", size = 86168 },
+    { url = "https://files.pythonhosted.org/packages/74/f6/4a34a37a98311ed73bb80efe422fed95f2ac25a4cacc5ae1d7ae6a144505/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c", size = 82709 },
+    { url = "https://files.pythonhosted.org/packages/74/2b/73d767bfdaab25484f7e7901379d5f8793cccbb86c6e0cbc4c1b96f63896/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86", size = 83613 },
+    { url = "https://files.pythonhosted.org/packages/4f/fd/37f86deef67ff57c76f137a67181949c2d408077e2e3dd70c6c42912c9bf/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f", size = 84583 },
+    { url = "https://files.pythonhosted.org/packages/6f/52/5a60085a3dae8fded8327a4f564223029f5f54b0cb0455a31131b5363a01/argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e", size = 88475 },
+    { url = "https://files.pythonhosted.org/packages/8b/95/143cd64feb24a15fa4b189a3e1e7efbaeeb00f39a51e99b26fc62fbacabd/argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082", size = 27698 },
+    { url = "https://files.pythonhosted.org/packages/37/2c/e34e47c7dee97ba6f01a6203e0383e15b60fb85d78ac9a15cd066f6fe28b/argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f", size = 30817 },
+    { url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104 },
+]
+
+[[package]]
+name = "asgiref"
+version = "3.8.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/29/38/b3395cc9ad1b56d2ddac9970bc8f4141312dbaec28bc7c218b0dfafd0f42/asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590", size = 35186 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828 },
+]
+
+[[package]]
+name = "async-timeout"
+version = "4.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", size = 8345 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 },
+]
+
+[[package]]
+name = "attrs"
+version = "24.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 },
+]
+
+[[package]]
+name = "authlib"
+version = "1.3.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "cryptography" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/75/47dbab150ef6f9298e227a40c93c7fed5f3ffb67c9fb62cd49f66285e46e/authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2", size = 147313 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/df/4c/9aa0416a403d5cc80292cb030bcd2c918cce2755e314d8c1aa18656e1e12/Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc", size = 225111 },
+]
+
+[[package]]
+name = "av"
+version = "12.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/f8/5adeeae0c42a7130933d168b8d84a21c98a32cb9fcf9222e2541ed0d9c7b/av-12.3.0.tar.gz", hash = "sha256:04b1892562aff3277efc79f32bd8f1d0cbb64ed011241cb3e96f9ad471816c22", size = 3833953 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5d/20/256fa4fc4ef9bb46fdc4be4662e13a30b0334487c955961f3816d94db04b/av-12.3.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:cc06a806419fddc7102150ffe353c7d96b99b95fd12864280c91c851603fd4cb", size = 24658122 },
+    { url = "https://files.pythonhosted.org/packages/5d/45/a9d0475539b4f49deb34f3da558de31cefc6be867d5c0603d575a8485069/av-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e2130ff622a574d3d5d6e88ac335efcdd98c375bb341f87d9fe540830a746f5", size = 19923068 },
+    { url = "https://files.pythonhosted.org/packages/af/27/1f2b3e46059c6618fd76ba12a96b49dc8515a426cd477032cd33f80505e8/av-12.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e8b9bd99f916ff4d1278654e94658e6ace7ca60f6321f254d09c8cd81d9095b", size = 32555100 },
+    { url = "https://files.pythonhosted.org/packages/28/34/759741d397a8bdbb8a359b8b5d49832a444b26c9a7f79c0f88be76a6b979/av-12.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e375d1d89a5c6edfd9f66701fdb6cc9161cc1ff99d15ff0bda21ee1ad38e9e0", size = 31936355 },
+    { url = "https://files.pythonhosted.org/packages/b4/6e/77426cb92117c941b0f759908bc83f34f259b11b353acb5de95972b452f7/av-12.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef9066fd8d86548e12d587cbfe7b852159e48ff3c732271c3032668d4bd7c599", size = 34416598 },
+    { url = "https://files.pythonhosted.org/packages/ff/d3/4b0fddcd54d0a88ee7e035f239ebb56ce139fac8e02ee0942c43746a66ff/av-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bfaa9864560e43d45d254ed95f70ab1aab24a2fa0cc35ac99eef362f1453bec0", size = 25975217 },
+    { url = "https://files.pythonhosted.org/packages/e4/c1/0636bccf5a1a2c935952614b9d34d8d8aae078c9773a60efb5376702f499/av-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5174e995772ebe33561980dca625f830aea8d39a4338728dedb41ae7dc2605af", size = 24669628 },
+    { url = "https://files.pythonhosted.org/packages/ef/7d/9126abdafe20fa73d2c19fd108450363253cfea283c350618cc1434f473c/av-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:028d8b40308536f740dace3efd0178eb96825b414897c9594fb74136532901cb", size = 19928928 },
+    { url = "https://files.pythonhosted.org/packages/27/75/c1b9e0aa4bd0d8b8311f366b6b38f6c6600d66baddfe2888accc7f76b1f5/av-12.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b030791ecc6185776d832d19ce196f61daf3e17e591a9bb6fd181280e1754138", size = 32793461 },
+    { url = "https://files.pythonhosted.org/packages/5a/06/1364c445f8a8ab4870f0f5c4530b496257ae09de7fa01b6108525abea8b9/av-12.3.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3703a35481fda5798a27bf6208c1ec3b61c18931625771fb3c9fd870539c7d7", size = 32217647 },
+    { url = "https://files.pythonhosted.org/packages/27/08/220d5a1ae7e7830d66d041c71e607c1f5df2e3598b12fb406b0d7c2defa7/av-12.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32f3eef56b2df289db6105f9fe2ebc9a8134a8adbd62190daeb8e22c4ff47794", size = 34746451 },
+    { url = "https://files.pythonhosted.org/packages/96/67/9f1c444864d4f3e3773100b9ed20e670f80d5575b7a8fd53cca20de9d681/av-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:62d036ee8321d67190887012c3dbcd1ad83248603cc29ea75fbb75835b8d6e6e", size = 25977611 },
+]
+
+[[package]]
+name = "backoff"
+version = "2.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 },
+]
+
+[[package]]
+name = "bcrypt"
+version = "4.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/7e/d95e7d96d4828e965891af92e43b52a4cd3395dc1c1ef4ee62748d0471d0/bcrypt-4.2.0.tar.gz", hash = "sha256:cf69eaf5185fd58f268f805b505ce31f9b9fc2d64b376642164e9244540c1221", size = 24294 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a9/81/4e8f5bc0cd947e91fb720e1737371922854da47a94bc9630454e7b2845f8/bcrypt-4.2.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:096a15d26ed6ce37a14c1ac1e48119660f21b24cba457f160a4b830f3fe6b5cb", size = 471568 },
+    { url = "https://files.pythonhosted.org/packages/05/d2/1be1e16aedec04bcf8d0156e01b987d16a2063d38e64c3f28030a3427d61/bcrypt-4.2.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c02d944ca89d9b1922ceb8a46460dd17df1ba37ab66feac4870f6862a1533c00", size = 277372 },
+    { url = "https://files.pythonhosted.org/packages/e3/96/7a654027638ad9b7589effb6db77eb63eba64319dfeaf9c0f4ca953e5f76/bcrypt-4.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d84cf6d877918620b687b8fd1bf7781d11e8a0998f576c7aa939776b512b98d", size = 273488 },
+    { url = "https://files.pythonhosted.org/packages/46/54/dc7b58abeb4a3d95bab653405935e27ba32f21b812d8ff38f271fb6f7f55/bcrypt-4.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1bb429fedbe0249465cdd85a58e8376f31bb315e484f16e68ca4c786dcc04291", size = 277759 },
+    { url = "https://files.pythonhosted.org/packages/ac/be/da233c5f11fce3f8adec05e8e532b299b64833cc962f49331cdd0e614fa9/bcrypt-4.2.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:655ea221910bcac76ea08aaa76df427ef8625f92e55a8ee44fbf7753dbabb328", size = 273796 },
+    { url = "https://files.pythonhosted.org/packages/b0/b8/8b4add88d55a263cf1c6b8cf66c735280954a04223fcd2880120cc767ac3/bcrypt-4.2.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:1ee38e858bf5d0287c39b7a1fc59eec64bbf880c7d504d3a06a96c16e14058e7", size = 311082 },
+    { url = "https://files.pythonhosted.org/packages/7b/76/2aa660679abbdc7f8ee961552e4bb6415a81b303e55e9374533f22770203/bcrypt-4.2.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:0da52759f7f30e83f1e30a888d9163a81353ef224d82dc58eb5bb52efcabc399", size = 305912 },
+    { url = "https://files.pythonhosted.org/packages/00/03/2af7c45034aba6002d4f2b728c1a385676b4eab7d764410e34fd768009f2/bcrypt-4.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3698393a1b1f1fd5714524193849d0c6d524d33523acca37cd28f02899285060", size = 325185 },
+    { url = "https://files.pythonhosted.org/packages/dc/5d/6843443ce4ab3af40bddb6c7c085ed4a8418b3396f7a17e60e6d9888416c/bcrypt-4.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:762a2c5fb35f89606a9fde5e51392dad0cd1ab7ae64149a8b935fe8d79dd5ed7", size = 335188 },
+    { url = "https://files.pythonhosted.org/packages/cb/4c/ff8ca83d816052fba36def1d24e97d9a85739b9bbf428c0d0ecd296a07c8/bcrypt-4.2.0-cp37-abi3-win32.whl", hash = "sha256:5a1e8aa9b28ae28020a3ac4b053117fb51c57a010b9f969603ed885f23841458", size = 156481 },
+    { url = "https://files.pythonhosted.org/packages/65/f1/e09626c88a56cda488810fb29d5035f1662873777ed337880856b9d204ae/bcrypt-4.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:8f6ede91359e5df88d1f5c1ef47428a4420136f3ce97763e31b86dd8280fbdf5", size = 151336 },
+    { url = "https://files.pythonhosted.org/packages/96/86/8c6a84daed4dd878fbab094400c9174c43d9b838ace077a2f8ee8bc3ae12/bcrypt-4.2.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:c52aac18ea1f4a4f65963ea4f9530c306b56ccd0c6f8c8da0c06976e34a6e841", size = 472414 },
+    { url = "https://files.pythonhosted.org/packages/f6/05/e394515f4e23c17662e5aeb4d1859b11dc651be01a3bd03c2e919a155901/bcrypt-4.2.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3bbbfb2734f0e4f37c5136130405332640a1e46e6b23e000eeff2ba8d005da68", size = 277599 },
+    { url = "https://files.pythonhosted.org/packages/4b/3b/ad784eac415937c53da48983756105d267b91e56aa53ba8a1b2014b8d930/bcrypt-4.2.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3413bd60460f76097ee2e0a493ccebe4a7601918219c02f503984f0a7ee0aebe", size = 273491 },
+    { url = "https://files.pythonhosted.org/packages/cc/14/b9ff8e0218bee95e517b70e91130effb4511e8827ac1ab00b4e30943a3f6/bcrypt-4.2.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8d7bb9c42801035e61c109c345a28ed7e84426ae4865511eb82e913df18f58c2", size = 277934 },
+    { url = "https://files.pythonhosted.org/packages/3e/d0/31938bb697600a04864246acde4918c4190a938f891fd11883eaaf41327a/bcrypt-4.2.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3d3a6d28cb2305b43feac298774b997e372e56c7c7afd90a12b3dc49b189151c", size = 273804 },
+    { url = "https://files.pythonhosted.org/packages/e7/c3/dae866739989e3f04ae304e1201932571708cb292a28b2f1b93283e2dcd8/bcrypt-4.2.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9c1c4ad86351339c5f320ca372dfba6cb6beb25e8efc659bedd918d921956bae", size = 311275 },
+    { url = "https://files.pythonhosted.org/packages/5d/2c/019bc2c63c6125ddf0483ee7d914a405860327767d437913942b476e9c9b/bcrypt-4.2.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:27fe0f57bb5573104b5a6de5e4153c60814c711b29364c10a75a54bb6d7ff48d", size = 306355 },
+    { url = "https://files.pythonhosted.org/packages/75/fe/9e137727f122bbe29771d56afbf4e0dbc85968caa8957806f86404a5bfe1/bcrypt-4.2.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8ac68872c82f1add6a20bd489870c71b00ebacd2e9134a8aa3f98a0052ab4b0e", size = 325381 },
+    { url = "https://files.pythonhosted.org/packages/1a/d4/586b9c18a327561ea4cd336ff4586cca1a7aa0f5ee04e23a8a8bb9ca64f1/bcrypt-4.2.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:cb2a8ec2bc07d3553ccebf0746bbf3d19426d1c6d1adbd4fa48925f66af7b9e8", size = 335685 },
+    { url = "https://files.pythonhosted.org/packages/24/55/1a7127faf4576138bb278b91e9c75307490178979d69c8e6e273f74b974f/bcrypt-4.2.0-cp39-abi3-win32.whl", hash = "sha256:77800b7147c9dc905db1cba26abe31e504d8247ac73580b4aa179f98e6608f34", size = 155857 },
+    { url = "https://files.pythonhosted.org/packages/1c/2a/c74052e54162ec639266d91539cca7cbf3d1d3b8b36afbfeaee0ea6a1702/bcrypt-4.2.0-cp39-abi3-win_amd64.whl", hash = "sha256:61ed14326ee023917ecd093ee6ef422a72f3aec6f07e21ea5f10622b735538a9", size = 151717 },
+]
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.12.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "soupsieve" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/ca/824b1195773ce6166d388573fc106ce56d4a805bd7427b624e063596ec58/beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", size = 581181 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 },
+]
+
+[[package]]
+name = "bidict"
+version = "0.23.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/6e/026678aa5a830e07cd9498a05d3e7e650a4f56a42f267a53d22bcda1bdc9/bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71", size = 29093 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/99/37/e8730c3587a65eb5645d4aba2d27aae48e8003614d6aaf15dda67f702f1f/bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5", size = 32764 },
+]
+
+[[package]]
+name = "bitarray"
+version = "2.9.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/bf/25cf92a83e1fe4948d7935ae3c02f4c9ff9cb9c13e977fba8af11a5f642c/bitarray-2.9.2.tar.gz", hash = "sha256:a8f286a51a32323715d77755ed959f94bef13972e9a2fe71b609e40e6d27957e", size = 132825 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/32/86/a02960105c0a40e7e4cbc74933f070ab476312d20aa25f6959f4abe5095b/bitarray-2.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fe71fd4b76380c2772f96f1e53a524da7063645d647a4fcd3b651bdd80ca0f2e", size = 177175 },
+    { url = "https://files.pythonhosted.org/packages/8d/fd/ce16db75d5470f9676089428500ef0d473f4e2ff1dcbcf1f856bcd351ea2/bitarray-2.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d527172919cdea1e13994a66d9708a80c3d33dedcf2f0548e4925e600fef3a3a", size = 128273 },
+    { url = "https://files.pythonhosted.org/packages/06/60/c1a419f8abd0c9d2641e3e570fc63ad3a87a63ef88a362900e3254f780bc/bitarray-2.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:052c5073bdcaa9dd10628d99d37a2f33ec09364b86dd1f6281e2d9f8d3db3060", size = 124642 },
+    { url = "https://files.pythonhosted.org/packages/9e/af/bba89e6f9499fb9dba04b701c8106a1dcc94b5913f35ed20f089da8bea99/bitarray-2.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e064caa55a6ed493aca1eda06f8b3f689778bc780a75e6ad7724642ba5dc62f7", size = 296283 },
+    { url = "https://files.pythonhosted.org/packages/8f/44/19e91ffc42a2ded4f1ee73f7923186cf1606cab1119b1d5df24c9cea763e/bitarray-2.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:508069a04f658210fdeee85a7a0ca84db4bcc110cbb1d21f692caa13210f24a7", size = 311309 },
+    { url = "https://files.pythonhosted.org/packages/9f/76/eedaa1fcb60af30536af70f6659e3a86dcfdce3e413b188f7864513e4923/bitarray-2.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4da73ebd537d75fa7bccfc2228fcaedea0803f21dd9d0bf0d3b67fef3c4af294", size = 314220 },
+    { url = "https://files.pythonhosted.org/packages/21/fa/9fb7266b28ce1c8778aaea650c75855640ea1ada91a80c47c90376994a59/bitarray-2.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb378eaa65cd43098f11ff5d27e48ee3b956d2c00d2d6b5bfc2a09fe183be47", size = 296519 },
+    { url = "https://files.pythonhosted.org/packages/c4/ee/c9a92c123f9b0438498d0a8f9470439a43bdafbf042cbdceab7968e5bb1c/bitarray-2.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d14c790b91f6cbcd9b718f88ed737c78939980c69ac8c7f03dd7e60040c12951", size = 286784 },
+    { url = "https://files.pythonhosted.org/packages/81/7f/0d9c16a7e321f5cb1d6c634acf4f8620513634776ceeee6f8f732b992fae/bitarray-2.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eea9318293bc0ea6447e9ebfba600a62f3428bea7e9c6d42170ae4f481dbab3", size = 328319 },
+    { url = "https://files.pythonhosted.org/packages/2e/98/0730518cf071366633dd027e14e3fba91dc91dd8b4e60d6ae249cde3f53f/bitarray-2.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b76ffec27c7450b8a334f967366a9ebadaea66ee43f5b530c12861b1a991f503", size = 315614 },
+    { url = "https://files.pythonhosted.org/packages/4c/b3/8f198444cd2312d520e0372f933932fff68b5900eb2dbab91725924aa7a1/bitarray-2.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:76b76a07d4ee611405045c6950a1e24c4362b6b44808d4ad6eea75e0dbc59af4", size = 340143 },
+    { url = "https://files.pythonhosted.org/packages/b9/1a/78841fa855ea2dc13d8d61f231bd3a619732738d7d840b4b35d9d8f93e6e/bitarray-2.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c7d16beeaaab15b075990cd26963d6b5b22e8c5becd131781514a00b8bdd04bd", size = 345958 },
+    { url = "https://files.pythonhosted.org/packages/b7/9f/aac87cd45cc4d7b7e50dde590327bde525601088e710c8ba00e8743ddb2b/bitarray-2.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60df43e868a615c7e15117a1e1c2e5e11f48f6457280eba6ddf8fbefbec7da99", size = 327122 },
+    { url = "https://files.pythonhosted.org/packages/78/3e/5df523037f80cf95f99d0155ec921298f4fa2b1f7be10cb0c4daffb632da/bitarray-2.9.2-cp311-cp311-win32.whl", hash = "sha256:e788608ed7767b7b3bbde6d49058bccdf94df0de9ca75d13aa99020cc7e68095", size = 118627 },
+    { url = "https://files.pythonhosted.org/packages/9c/0e/af070131ed7a4fd15cadc84e018d3c6d3b58070513934462b48a5ff9eb1e/bitarray-2.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:a23397da092ef0a8cfe729571da64c2fc30ac18243caa82ac7c4f965087506ff", size = 126013 },
+    { url = "https://files.pythonhosted.org/packages/ef/7d/f489f2136cf5ea1af201be12d55bfc57b45731c4b7e3cc6e001efe62effb/bitarray-2.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:90e3a281ffe3897991091b7c46fca38c2675bfd4399ffe79dfeded6c52715436", size = 176636 },
+    { url = "https://files.pythonhosted.org/packages/b8/b6/3e64b19e45b52837e0c4ec1c220bf24dd70dcbcd27e073e07837973f8c16/bitarray-2.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bed637b674db5e6c8a97a4a321e3e4d73e72d50b5c6b29950008a93069cc64cd", size = 127916 },
+    { url = "https://files.pythonhosted.org/packages/23/c7/12b1e5cdd8678a6a47610a013fafdbe80d62226d49b73185619bd7361c53/bitarray-2.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e49066d251dbbe4e6e3a5c3937d85b589e40e2669ad0eef41a00f82ec17d844b", size = 124475 },
+    { url = "https://files.pythonhosted.org/packages/77/e1/02dc3f03348808a77b26556a6c299f68dbbf0c4a27f340a69d574d2d2432/bitarray-2.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4344e96642e2211fb3a50558feff682c31563a4c64529a931769d40832ca79", size = 299121 },
+    { url = "https://files.pythonhosted.org/packages/ea/f1/9cdb006c352b47b26532e7ee7798bd2dacf42774eb75e4a353a38a3ff2b3/bitarray-2.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aeb60962ec4813c539a59fbd4f383509c7222b62c3fb1faa76b54943a613e33a", size = 313486 },
+    { url = "https://files.pythonhosted.org/packages/bb/79/98bdfea0f390d313fa04546578a3eee3c3a6dbba94973246438ea8c880aa/bitarray-2.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f7982f10581bb16553719e5e8f933e003f5b22f7d25a68bdb30fac630a6ff", size = 317527 },
+    { url = "https://files.pythonhosted.org/packages/58/02/21a2038ee856649f03738828e3bc6c4cd9bfd31125a250c6e30d378067ff/bitarray-2.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c71d1cabdeee0cdda4669168618f0e46b7dace207b29da7b63aaa1adc2b54081", size = 299885 },
+    { url = "https://files.pythonhosted.org/packages/49/97/82c350256a22689fb50ed86af1a3a5509410332cfe55d644fd7ff5e46dbf/bitarray-2.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0ef2d0a6f1502d38d911d25609b44c6cc27bee0a4363dd295df78b075041b60", size = 289796 },
+    { url = "https://files.pythonhosted.org/packages/09/69/ca799419b576d015331b19b42c70086b1208ba363f744c8bb7dc31a0bb6a/bitarray-2.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6f71d92f533770fb027388b35b6e11988ab89242b883f48a6fe7202d238c61f8", size = 334912 },
+    { url = "https://files.pythonhosted.org/packages/fb/8a/dad9d48c72367f76b117957e3d718de07254667f33838d061856b238b576/bitarray-2.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba0734aa300757c924f3faf8148e1b8c247176a0ac8e16aefdf9c1eb19e868f7", size = 322790 },
+    { url = "https://files.pythonhosted.org/packages/b7/f1/a4f723153e6b4c56a90275a1d6ad04860bd72d9966196eb331cd18b50a12/bitarray-2.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:d91406f413ccbf4af6ab5ae7bc78f772a95609f9ddd14123db36ef8c37116d95", size = 346212 },
+    { url = "https://files.pythonhosted.org/packages/7f/4f/55301544e90df8a7b798959bffa94e5694b29c964ed9e9e2a52d6cfac9c7/bitarray-2.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:87abb7f80c0a042f3fe8e5264da1a2756267450bb602110d5327b8eaff7682e7", size = 353044 },
+    { url = "https://files.pythonhosted.org/packages/16/8b/363fdc21aff37ac99dba4ed41c0d535c37b416cd002351a9848173c738f1/bitarray-2.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b558ce85579b51a2e38703877d1e93b7728a7af664dd45a34e833534f0b755d", size = 334380 },
+    { url = "https://files.pythonhosted.org/packages/66/58/f57a6420b363d2f0517d79b9af9fd608360ef174eb5d1d82cc5a26dbdbde/bitarray-2.9.2-cp312-cp312-win32.whl", hash = "sha256:dac2399ee2889fbdd3472bfc2ede74c34cceb1ccf29a339964281a16eb1d3188", size = 118738 },
+    { url = "https://files.pythonhosted.org/packages/93/a9/b9462e2a3b4ee020c6caa1dccece324d6b0c7643b9f0a43d9ac8cd15c9d9/bitarray-2.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:48a30d718d1a6dfc22a49547450107abe8f4afdf2abdcbe76eb9ed88edc49498", size = 126202 },
+]
+
+[[package]]
+name = "black"
+version = "24.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "mypy-extensions" },
+    { name = "packaging" },
+    { name = "pathspec" },
+    { name = "platformdirs" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/04/b0/46fb0d4e00372f4a86a6f8efa3cb193c9f64863615e39010b1477e010578/black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", size = 644810 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/08/a6/0a3aa89de9c283556146dc6dbda20cd63a9c94160a6fbdebaf0918e4a3e1/black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1", size = 1615080 },
+    { url = "https://files.pythonhosted.org/packages/db/94/b803d810e14588bb297e565821a947c108390a079e21dbdcb9ab6956cd7a/black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", size = 1438143 },
+    { url = "https://files.pythonhosted.org/packages/a5/b5/f485e1bbe31f768e2e5210f52ea3f432256201289fd1a3c0afda693776b0/black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", size = 1738774 },
+    { url = "https://files.pythonhosted.org/packages/a8/69/a000fc3736f89d1bdc7f4a879f8aaf516fb03613bb51a0154070383d95d9/black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", size = 1427503 },
+    { url = "https://files.pythonhosted.org/packages/a2/a8/05fb14195cfef32b7c8d4585a44b7499c2a4b205e1662c427b941ed87054/black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", size = 1646132 },
+    { url = "https://files.pythonhosted.org/packages/41/77/8d9ce42673e5cb9988f6df73c1c5c1d4e9e788053cccd7f5fb14ef100982/black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", size = 1448665 },
+    { url = "https://files.pythonhosted.org/packages/cc/94/eff1ddad2ce1d3cc26c162b3693043c6b6b575f538f602f26fe846dfdc75/black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", size = 1762458 },
+    { url = "https://files.pythonhosted.org/packages/28/ea/18b8d86a9ca19a6942e4e16759b2fa5fc02bbc0eb33c1b866fcd387640ab/black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", size = 1436109 },
+    { url = "https://files.pythonhosted.org/packages/27/1e/83fa8a787180e1632c3d831f7e58994d7aaf23a0961320d21e84f922f919/black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", size = 206504 },
+]
+
+[[package]]
+name = "blinker"
+version = "1.8.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/57/a6a1721eff09598fb01f3c7cda070c1b6a0f12d63c83236edf79a440abcc/blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83", size = 23161 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bb/2a/10164ed1f31196a2f7f3799368a821765c62851ead0e630ab52b8e14b4d0/blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01", size = 9456 },
+]
+
+[[package]]
+name = "boto3"
+version = "1.35.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "botocore" },
+    { name = "jmespath" },
+    { name = "s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fd/a8/e825d84cdcf9136cedc89c1f317f80023179685f83469a6ad04b3b0709f4/boto3-1.35.0.tar.gz", hash = "sha256:bdc242e3ea81decc6ea551b04b2c122f088c29269d8e093b55862946aa0fcfc6", size = 108644 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0e/da/d7d3a9ad530b6c05548bfabe6e163687a5039fbdfecbf07f7b5532fd077b/boto3-1.35.0-py3-none-any.whl", hash = "sha256:ada32dab854c46a877cf967b8a55ab1a7d356c3c87f1c8bd556d446ff03dfd95", size = 139142 },
+]
+
+[[package]]
+name = "botocore"
+version = "1.35.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "jmespath" },
+    { name = "python-dateutil" },
+    { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d0/2f/592f563d9eadc4ffe846c1f5e0c8747c9eb514a309c19b8362d1e455dd05/botocore-1.35.3.tar.gz", hash = "sha256:ff0c3189c0aa588c3aeda3f3e5e37925d64deaac6748310124307f978933e768", size = 12680057 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9e/5e/9b7d41e3256872159d62e5026519c448acbb94873b1bd950946e82e8e327/botocore-1.35.3-py3-none-any.whl", hash = "sha256:3ff54075e125304a8978e5d3f27ab96485f2deedb561d458b0567a0a921dc243", size = 12468636 },
+]
+
+[[package]]
+name = "build"
+version = "1.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorama", marker = "os_name == 'nt'" },
+    { name = "packaging" },
+    { name = "pyproject-hooks" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ce/9e/2d725d2f7729c6e79ca62aeb926492abbc06e25910dd30139d60a68bcb19/build-1.2.1.tar.gz", hash = "sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d", size = 44781 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl", hash = "sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4", size = 21911 },
+]
+
+[[package]]
+name = "cachetools"
+version = "5.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c3/38/a0f315319737ecf45b4319a8cd1f3a908e29d9277b46942263292115eee7/cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a", size = 27661 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a4/07/14f8ad37f2d12a5ce41206c21820d8cb6561b728e51fad4530dff0552a67/cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292", size = 9524 },
+]
+
+[[package]]
+name = "certifi"
+version = "2024.7.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c2/02/a95f2b11e207f68bc64d7aae9666fed2e2b3f307748d5123dffb72a1bbea/certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b", size = 164065 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1c/d5/c84e1a17bf61d4df64ca866a1c9a913874b4e9bdc131ec689a0ad013fb36/certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90", size = 162960 },
+]
+
+[[package]]
+name = "cffi"
+version = "1.17.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pycparser" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1e/bf/82c351342972702867359cfeba5693927efe0a8dd568165490144f554b18/cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76", size = 516073 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/53/cc/9298fb6235522e00e47d78d6aa7f395332ef4e5f6fe124f9a03aa60600f7/cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720", size = 181912 },
+    { url = "https://files.pythonhosted.org/packages/e7/79/dc5334fbe60635d0846c56597a8d2af078a543ff22bc48d36551a0de62c2/cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9", size = 178297 },
+    { url = "https://files.pythonhosted.org/packages/39/d7/ef1b6b16b51ccbabaced90ff0d821c6c23567fc4b2e4a445aea25d3ceb92/cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb", size = 444909 },
+    { url = "https://files.pythonhosted.org/packages/29/b8/6e3c61885537d985c78ef7dd779b68109ba256263d74a2f615c40f44548d/cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424", size = 468854 },
+    { url = "https://files.pythonhosted.org/packages/0b/49/adad1228e19b931e523c2731e6984717d5f9e33a2f9971794ab42815b29b/cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d", size = 476890 },
+    { url = "https://files.pythonhosted.org/packages/76/54/c00f075c3e7fd14d9011713bcdb5b4f105ad044c5ad948db7b1a0a7e4e78/cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8", size = 459374 },
+    { url = "https://files.pythonhosted.org/packages/f3/b9/f163bb3fa4fbc636ee1f2a6a4598c096cdef279823ddfaa5734e556dd206/cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6", size = 466891 },
+    { url = "https://files.pythonhosted.org/packages/31/52/72bbc95f6d06ff2e88a6fa13786be4043e542cb24748e1351aba864cb0a7/cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91", size = 477658 },
+    { url = "https://files.pythonhosted.org/packages/67/20/d694811457eeae0c7663fa1a7ca201ce495533b646c1180d4ac25684c69c/cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8", size = 453890 },
+    { url = "https://files.pythonhosted.org/packages/dc/79/40cbf5739eb4f694833db5a27ce7f63e30a9b25b4a836c4f25fb7272aacc/cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb", size = 478254 },
+    { url = "https://files.pythonhosted.org/packages/e9/eb/2c384c385cca5cae67ca10ac4ef685277680b8c552b99aedecf4ea23ff7e/cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9", size = 171285 },
+    { url = "https://files.pythonhosted.org/packages/ca/42/74cb1e0f1b79cb64672f3cb46245b506239c1297a20c0d9c3aeb3929cb0c/cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0", size = 180842 },
+    { url = "https://files.pythonhosted.org/packages/1a/1f/7862231350cc959a3138889d2c8d33da7042b22e923457dfd4cd487d772a/cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc", size = 182826 },
+    { url = "https://files.pythonhosted.org/packages/8b/8c/26119bf8b79e05a1c39812064e1ee7981e1f8a5372205ba5698ea4dd958d/cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59", size = 178494 },
+    { url = "https://files.pythonhosted.org/packages/61/94/4882c47d3ad396d91f0eda6ef16d45be3d752a332663b7361933039ed66a/cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb", size = 454459 },
+    { url = "https://files.pythonhosted.org/packages/0f/7c/a6beb119ad515058c5ee1829742d96b25b2b9204ff920746f6e13bf574eb/cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195", size = 478502 },
+    { url = "https://files.pythonhosted.org/packages/61/8a/2575cd01a90e1eca96a30aec4b1ac101a6fae06c49d490ac2704fa9bc8ba/cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e", size = 485381 },
+    { url = "https://files.pythonhosted.org/packages/cd/66/85899f5a9f152db49646e0c77427173e1b77a1046de0191ab3b0b9a5e6e3/cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828", size = 470907 },
+    { url = "https://files.pythonhosted.org/packages/00/13/150924609bf377140abe6e934ce0a57f3fc48f1fd956ec1f578ce97a4624/cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150", size = 479074 },
+    { url = "https://files.pythonhosted.org/packages/17/fd/7d73d7110155c036303b0a6462c56250e9bc2f4119d7591d27417329b4d1/cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a", size = 484225 },
+    { url = "https://files.pythonhosted.org/packages/fc/83/8353e5c9b01bb46332dac3dfb18e6c597a04ceb085c19c814c2f78a8c0d0/cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885", size = 488388 },
+    { url = "https://files.pythonhosted.org/packages/73/0c/f9d5ca9a095b1fc88ef77d1f8b85d11151c374144e4606da33874e17b65b/cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492", size = 172096 },
+    { url = "https://files.pythonhosted.org/packages/72/21/8c5d285fe20a6e31d29325f1287bb0e55f7d93630a5a44cafdafb5922495/cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2", size = 181478 },
+    { url = "https://files.pythonhosted.org/packages/17/8f/581f2f3c3464d5f7cf87c2f7a5ba9acc6976253e02d73804240964243ec2/cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118", size = 182638 },
+    { url = "https://files.pythonhosted.org/packages/8d/1c/c9afa66684b7039f48018eb11b229b659dfb32b7a16b88251bac106dd1ff/cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7", size = 178453 },
+    { url = "https://files.pythonhosted.org/packages/cc/b6/1a134d479d3a5a1ff2fabbee551d1d3f1dd70f453e081b5f70d604aae4c0/cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377", size = 454441 },
+    { url = "https://files.pythonhosted.org/packages/b1/b4/e1569475d63aad8042b0935dbf62ae2a54d1e9142424e2b0e924d2d4a529/cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb", size = 478543 },
+    { url = "https://files.pythonhosted.org/packages/d2/40/a9ad03fbd64309dec5bb70bc803a9a6772602de0ee164d7b9a6ca5a89249/cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555", size = 485463 },
+    { url = "https://files.pythonhosted.org/packages/a6/1a/f10be60e006dd9242a24bcc2b1cd55c34c578380100f742d8c610f7a5d26/cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204", size = 470854 },
+    { url = "https://files.pythonhosted.org/packages/cc/b3/c035ed21aa3d39432bd749fe331ee90e4bc83ea2dbed1f71c4bc26c41084/cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f", size = 479096 },
+    { url = "https://files.pythonhosted.org/packages/00/cb/6f7edde01131de9382c89430b8e253b8c8754d66b63a62059663ceafeab2/cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0", size = 484013 },
+    { url = "https://files.pythonhosted.org/packages/b9/83/8e4e8c211ea940210d293e951bf06b1bfb90f2eeee590e9778e99b4a8676/cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4", size = 488119 },
+    { url = "https://files.pythonhosted.org/packages/5e/52/3f7cfbc4f444cb4f73ff17b28690d12436dde665f67d68f1e1687908ab6c/cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a", size = 172122 },
+    { url = "https://files.pythonhosted.org/packages/94/19/cf5baa07ee0f0e55eab7382459fbddaba0fdb0ba45973dd92556ae0d02db/cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7", size = 181504 },
+]
+
+[[package]]
+name = "chardet"
+version = "5.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385 },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.3.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
+    { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
+    { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
+    { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
+    { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
+    { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
+    { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 },
+    { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 },
+    { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 },
+    { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 },
+    { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
+    { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
+    { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
+    { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
+    { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
+    { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
+    { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
+    { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
+    { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
+    { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
+    { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
+    { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 },
+    { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 },
+    { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 },
+    { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 },
+    { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
+    { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
+    { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
+    { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
+    { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
+    { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
+]
+
+[[package]]
+name = "chroma-hnswlib"
+version = "0.7.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/73/09/10d57569e399ce9cbc5eee2134996581c957f63a9addfa6ca657daf006b8/chroma_hnswlib-0.7.6.tar.gz", hash = "sha256:4dce282543039681160259d29fcde6151cc9106c6461e0485f57cdccd83059b7", size = 32256 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f5/af/d15fdfed2a204c0f9467ad35084fbac894c755820b203e62f5dcba2d41f1/chroma_hnswlib-0.7.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81181d54a2b1e4727369486a631f977ffc53c5533d26e3d366dda243fb0998ca", size = 196911 },
+    { url = "https://files.pythonhosted.org/packages/0d/19/aa6f2139f1ff7ad23a690ebf2a511b2594ab359915d7979f76f3213e46c4/chroma_hnswlib-0.7.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4b4ab4e11f1083dd0a11ee4f0e0b183ca9f0f2ed63ededba1935b13ce2b3606f", size = 185000 },
+    { url = "https://files.pythonhosted.org/packages/79/b1/1b269c750e985ec7d40b9bbe7d66d0a890e420525187786718e7f6b07913/chroma_hnswlib-0.7.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53db45cd9173d95b4b0bdccb4dbff4c54a42b51420599c32267f3abbeb795170", size = 2377289 },
+    { url = "https://files.pythonhosted.org/packages/c7/2d/d5663e134436e5933bc63516a20b5edc08b4c1b1588b9680908a5f1afd04/chroma_hnswlib-0.7.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c093f07a010b499c00a15bc9376036ee4800d335360570b14f7fe92badcdcf9", size = 2411755 },
+    { url = "https://files.pythonhosted.org/packages/3e/79/1bce519cf186112d6d5ce2985392a89528c6e1e9332d680bf752694a4cdf/chroma_hnswlib-0.7.6-cp311-cp311-win_amd64.whl", hash = "sha256:0540b0ac96e47d0aa39e88ea4714358ae05d64bbe6bf33c52f316c664190a6a3", size = 151888 },
+    { url = "https://files.pythonhosted.org/packages/93/ac/782b8d72de1c57b64fdf5cb94711540db99a92768d93d973174c62d45eb8/chroma_hnswlib-0.7.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e87e9b616c281bfbe748d01705817c71211613c3b063021f7ed5e47173556cb7", size = 197804 },
+    { url = "https://files.pythonhosted.org/packages/32/4e/fd9ce0764228e9a98f6ff46af05e92804090b5557035968c5b4198bc7af9/chroma_hnswlib-0.7.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec5ca25bc7b66d2ecbf14502b5729cde25f70945d22f2aaf523c2d747ea68912", size = 185421 },
+    { url = "https://files.pythonhosted.org/packages/d9/3d/b59a8dedebd82545d873235ef2d06f95be244dfece7ee4a1a6044f080b18/chroma_hnswlib-0.7.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305ae491de9d5f3c51e8bd52d84fdf2545a4a2bc7af49765cda286b7bb30b1d4", size = 2389672 },
+    { url = "https://files.pythonhosted.org/packages/74/1e/80a033ea4466338824974a34f418e7b034a7748bf906f56466f5caa434b0/chroma_hnswlib-0.7.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:822ede968d25a2c88823ca078a58f92c9b5c4142e38c7c8b4c48178894a0a3c5", size = 2436986 },
+]
+
+[[package]]
+name = "chromadb"
+version = "0.5.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "bcrypt" },
+    { name = "build" },
+    { name = "chroma-hnswlib" },
+    { name = "fastapi" },
+    { name = "grpcio" },
+    { name = "httpx" },
+    { name = "importlib-resources" },
+    { name = "kubernetes" },
+    { name = "mmh3" },
+    { name = "numpy" },
+    { name = "onnxruntime" },
+    { name = "opentelemetry-api" },
+    { name = "opentelemetry-exporter-otlp-proto-grpc" },
+    { name = "opentelemetry-instrumentation-fastapi" },
+    { name = "opentelemetry-sdk" },
+    { name = "orjson" },
+    { name = "overrides" },
+    { name = "posthog" },
+    { name = "pydantic" },
+    { name = "pypika" },
+    { name = "pyyaml" },
+    { name = "tenacity" },
+    { name = "tokenizers" },
+    { name = "tqdm" },
+    { name = "typer" },
+    { name = "typing-extensions" },
+    { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f9/31/7659067b51ac8b2ec355a100a77fb4d6d823aeb3ff111b6de87dfd18ace1/chromadb-0.5.5.tar.gz", hash = "sha256:84f4bfee320fb4912cbeb4d738f01690891e9894f0ba81f39ee02867102a1c4d", size = 31282293 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/80/4c/ee62b19a8daeed51e3c88c84b7da6047a74b786e598be3592b67a286d419/chromadb-0.5.5-py3-none-any.whl", hash = "sha256:2a5a4b84cb0fc32b380e193be68cdbadf3d9f77dbbf141649be9886e42910ddd", size = 584312 },
+]
+
+[[package]]
+name = "click"
+version = "8.1.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorama", marker = "platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 },
+]
+
+[[package]]
+name = "colbert-ai"
+version = "0.2.21"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "bitarray" },
+    { name = "datasets" },
+    { name = "flask" },
+    { name = "git-python" },
+    { name = "ninja" },
+    { name = "python-dotenv" },
+    { name = "scipy" },
+    { name = "tqdm" },
+    { name = "transformers" },
+    { name = "ujson" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bc/dc/7edb06e3bb01326610ecfdfc8e396c6867ba7de6e58cda2356a604419899/colbert_ai-0.2.21.tar.gz", hash = "sha256:a8d6fdb4e2272f2b08ed37f8e5096072160d8415d1e40585751898b77e625bab", size = 87978 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8d/9c/5d847be0f05e5266880fb1c183e642a6c34cd6a101c1d6219dfa74887543/colbert_ai-0.2.21-py3-none-any.whl", hash = "sha256:8c17e7be44e7f3989f2067f1176af4f65f4612d62850586657e8afb8314cb2a6", size = 116142 },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
+]
+
+[[package]]
+name = "colorclass"
+version = "2.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/1a/31ff00a33569a3b59d65bbdc445c73e12f92ad28195b7ace299f68b9af70/colorclass-2.2.2.tar.gz", hash = "sha256:6d4fe287766166a98ca7bc6f6312daf04a0481b1eda43e7173484051c0ab4366", size = 16709 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/30/b6/daf3e2976932da4ed3579cff7a30a53d22ea9323ee4f0d8e43be60454897/colorclass-2.2.2-py2.py3-none-any.whl", hash = "sha256:6f10c273a0ef7a1150b1120b6095cbdd68e5cf36dfd5d0fc957a2500bbf99a55", size = 18995 },
+]
+
+[[package]]
+name = "coloredlogs"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "humanfriendly" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018 },
+]
+
+[[package]]
+name = "compressed-rtf"
+version = "1.0.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/ac/abb196bb0b42a239d605fe97c314c3312374749013a07da4e6e0408f223c/compressed_rtf-1.0.6.tar.gz", hash = "sha256:c1c827f1d124d24608981a56e8b8691eb1f2a69a78ccad6440e7d92fde1781dd", size = 5800 }
+
+[[package]]
+name = "cryptography"
+version = "43.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/69/ec/9fb9dcf4f91f0e5e76de597256c43eedefd8423aa59be95c70c4c3db426a/cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e", size = 686873 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d3/46/dcd2eb6840b9452e7fbc52720f3dc54a85eb41e68414733379e8f98e3275/cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74", size = 6239718 },
+    { url = "https://files.pythonhosted.org/packages/e8/23/b0713319edff1d8633775b354f8b34a476e4dd5f4cd4b91e488baec3361a/cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895", size = 3808466 },
+    { url = "https://files.pythonhosted.org/packages/77/9d/0b98c73cebfd41e4fb0439fe9ce08022e8d059f51caa7afc8934fc1edcd9/cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22", size = 3998060 },
+    { url = "https://files.pythonhosted.org/packages/ae/71/e073795d0d1624847f323481f7d84855f699172a632aa37646464b0e1712/cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47", size = 3792596 },
+    { url = "https://files.pythonhosted.org/packages/83/25/439a8ddd8058e7f898b7d27c36f94b66c8c8a2d60e1855d725845f4be0bc/cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf", size = 4008355 },
+    { url = "https://files.pythonhosted.org/packages/c7/a2/1607f1295eb2c30fcf2c07d7fd0c3772d21dcdb827de2b2730b02df0af51/cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55", size = 3899133 },
+    { url = "https://files.pythonhosted.org/packages/5e/64/f41f42ddc9c583737c9df0093affb92c61de7d5b0d299bf644524afe31c1/cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431", size = 4096946 },
+    { url = "https://files.pythonhosted.org/packages/cd/cd/d165adcf3e707d6a049d44ade6ca89973549bed0ab3686fa49efdeefea53/cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc", size = 2616826 },
+    { url = "https://files.pythonhosted.org/packages/f9/b7/38924229e84c41b0e88d7a5eed8a29d05a44364f85fbb9ddb3984b746fd2/cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778", size = 3078700 },
+    { url = "https://files.pythonhosted.org/packages/66/d7/397515233e6a861f921bd0365b162b38e0cc513fcf4f1bdd9cc7bc5a3384/cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66", size = 6242814 },
+    { url = "https://files.pythonhosted.org/packages/58/aa/99b2c00a4f54c60d210d6d1759c720ecf28305aa32d6fb1bb1853f415be6/cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5", size = 3809467 },
+    { url = "https://files.pythonhosted.org/packages/76/eb/ab783b47b3b9b55371b4361c7ec695144bde1a3343ff2b7a8c1d8fe617bb/cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e", size = 3998617 },
+    { url = "https://files.pythonhosted.org/packages/a3/62/62770f34290ebb1b6542bd3f13b3b102875b90aed4804e296f8d2a5ac6d7/cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5", size = 3794003 },
+    { url = "https://files.pythonhosted.org/packages/0f/6c/b42660b3075ff543065b2c1c5a3d9bedaadcff8ebce2ee981be2babc2934/cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f", size = 4008774 },
+    { url = "https://files.pythonhosted.org/packages/f7/74/028cea86db9315ba3f991e307adabf9f0aa15067011137c38b2fb2aa16eb/cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0", size = 3900098 },
+    { url = "https://files.pythonhosted.org/packages/bd/f6/e4387edb55563e2546028ba4c634522fe727693d3cdd9ec0ecacedc75411/cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b", size = 4096867 },
+    { url = "https://files.pythonhosted.org/packages/ce/61/55560405e75432bdd9f6cf72fa516cab623b83a3f6d230791bc8fc4afeee/cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf", size = 2616481 },
+    { url = "https://files.pythonhosted.org/packages/e6/3d/696e7a0f04555c58a2813d47aaa78cb5ba863c1f453c74a4f45ae772b054/cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709", size = 3081462 },
+]
+
+[[package]]
+name = "ctranslate2"
+version = "4.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+    { name = "pyyaml" },
+    { name = "setuptools" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9c/51/e54ea77b4fb549ef627f537d6282604688ac583894b5e1ada6c3c2ba4761/ctranslate2-4.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a49dc5d339e2f4ed016553db0d0e6cbd369742697c87c6cc0cc15a47c7c72d00", size = 14661629 },
+    { url = "https://files.pythonhosted.org/packages/40/c0/b4a5ccb767c2e9a536e22b4fd823dd83d85d4c705db9950eb60dadc6945c/ctranslate2-4.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:def98f6f8900470b2cec9408e5b0402af75f40f771391ebacd2b60666b8d75b9", size = 1287132 },
+    { url = "https://files.pythonhosted.org/packages/41/d7/3319f6bc0bad3adbf50deeb12d13c14bb943693348f53ca06fb2d1ad15f9/ctranslate2-4.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c02fcd5a7be93bf42a8adf81a9ac4f394e23bd639192907b2e11feae589971", size = 16089754 },
+    { url = "https://files.pythonhosted.org/packages/6e/00/46f8aa5d219ca0ff8edcb41255ba56f7e3746b31bc923123ad6d3b24227d/ctranslate2-4.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a06043910a7dee91ea03634be2cff2e1338a9f87bb51e062c03bae69e2c826b6", size = 192505863 },
+    { url = "https://files.pythonhosted.org/packages/ba/f1/f8080272e1969ec39347f0bfb0fca16da766f1c40b5729104c50d856b01c/ctranslate2-4.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f49834b63848f17dfdc1b2b8c632c31932ad69e130ce0f7b1e2505aa3923e6c", size = 174907166 },
+    { url = "https://files.pythonhosted.org/packages/dc/a0/e75f10f76b4af815614f4a8f4b26969a8dde56857dd3ab4d69436e40b4d5/ctranslate2-4.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fcf649d976070ddd33cdda00a7a60fde6f1fbe27d65d2c6141dd95153f965f01", size = 14668179 },
+    { url = "https://files.pythonhosted.org/packages/69/c4/18b386794b631f2a21afb5c8b79bc1ee7ab087eb7b854e686ae87ee00f88/ctranslate2-4.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f63f779f1d4518acdc694b1938887d4f28613ac2dfe507ccc2c0d56dd8c95b40", size = 1288547 },
+    { url = "https://files.pythonhosted.org/packages/af/93/a012f82fd3a31304e1cdbb269de2b67592cb29c216eb0dbf87577cf63f69/ctranslate2-4.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68301fbc5fb7daa609eb12ca6c2ed8aa29852c20f962532317762d1889e751d9", size = 16169466 },
+    { url = "https://files.pythonhosted.org/packages/4c/96/3065479817cd1854c51a27d46e771846c12d72b5bc6097d89af24a70b5e2/ctranslate2-4.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45c5b352783bd3806f0c9f5dcbfa49d89c0dde71cb7d1b1c527c525e85af3ded", size = 192678220 },
+    { url = "https://files.pythonhosted.org/packages/be/f4/e7105f5efc72165e964cd3f726ecd94de32ced068f4fb6c0c37ebaec5985/ctranslate2-4.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08626f115d5a39c56a666680735d6eebfc4d8a215288896d4d8afc14cfcdcffe", size = 174910451 },
+]
+
+[[package]]
+name = "dataclasses-json"
+version = "0.6.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "marshmallow" },
+    { name = "typing-inspect" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 },
+]
+
+[[package]]
+name = "datasets"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "aiohttp" },
+    { name = "dill" },
+    { name = "filelock" },
+    { name = "fsspec", extra = ["http"] },
+    { name = "huggingface-hub" },
+    { name = "multiprocess" },
+    { name = "numpy" },
+    { name = "packaging" },
+    { name = "pandas" },
+    { name = "pyarrow" },
+    { name = "pyyaml" },
+    { name = "requests" },
+    { name = "tqdm" },
+    { name = "xxhash" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c8/93/38c0d3c74b5932c4bb7596888c3d3b20adfd563e56552f437f8f57e6377b/datasets-3.0.0.tar.gz", hash = "sha256:592317eb137f0fc5aac068ff283ba13c3c66d10c9c034d44bc8aa584126cf3e2", size = 1876827 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a5/52/45dab187f03d48c765b94db0464f5c10431756e47ae4cc6a8029a7d57a36/datasets-3.0.0-py3-none-any.whl", hash = "sha256:c23fefb6c953dcb1cd5f6deb6c502729c733ef98791e0c3f2d80c7ca2d9a01dd", size = 474265 },
+]
+
+[[package]]
+name = "deepdiff"
+version = "7.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "ordered-set" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/70/10/6f4b0bd0627d542f63a24f38e29d77095dc63d5f45bc1a7b4a6ca8750fa9/deepdiff-7.0.1.tar.gz", hash = "sha256:260c16f052d4badbf60351b4f77e8390bee03a0b516246f6839bc813fb429ddf", size = 421718 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/18/e6/d27d37dc55dbf40cdbd665aa52844b065ac760c9a02a02265f97ea7a4256/deepdiff-7.0.1-py3-none-any.whl", hash = "sha256:447760081918216aa4fd4ca78a4b6a848b81307b2ea94c810255334b759e1dc3", size = 80825 },
+]
+
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604 },
+]
+
+[[package]]
+name = "deprecated"
+version = "1.2.14"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/92/14/1e41f504a246fc224d2ac264c227975427a85caf37c3979979edb9b1b232/Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3", size = 2974416 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/20/8d/778b7d51b981a96554f29136cd59ca7880bf58094338085bcf2a979a0e6a/Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", size = 9561 },
+]
+
+[[package]]
+name = "dill"
+version = "0.3.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/17/4d/ac7ffa80c69ea1df30a8aa11b3578692a5118e7cd1aa157e3ef73b092d15/dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca", size = 184847 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c9/7a/cef76fd8438a42f96db64ddaa85280485a9c395e7df3db8158cfec1eee34/dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7", size = 116252 },
+]
+
+[[package]]
+name = "distro"
+version = "1.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 },
+]
+
+[[package]]
+name = "dnspython"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/7d/c871f55054e403fdfd6b8f65fd6d1c4e147ed100d3e9f9ba1fe695403939/dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc", size = 332727 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/87/a1/8c5287991ddb8d3e4662f71356d9656d91ab3a36618c3dd11b280df0d255/dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50", size = 307696 },
+]
+
+[[package]]
+name = "docker"
+version = "7.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pywin32", marker = "sys_platform == 'win32'" },
+    { name = "requests" },
+    { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774 },
+]
+
+[[package]]
+name = "docx2txt"
+version = "0.8"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/7d/60ee3f2b16d9bfdfa72e8599470a2c1a5b759cb113c6fe1006be28359327/docx2txt-0.8.tar.gz", hash = "sha256:2c06d98d7cfe2d3947e5760a57d924e3ff07745b379c8737723922e7009236e5", size = 2814 }
+
+[[package]]
+name = "duckduckgo-search"
+version = "6.2.13"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "primp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/2e/fe93c096b2b2bfcbd0a4f3a1de47d9a1c4b0dd4c7573d9f6c2b5c80ce6dc/duckduckgo_search-6.2.13.tar.gz", hash = "sha256:f89a9782f0f47d18c01a761c83453d0aef7a4335d1b6466fc47709652d5ca791", size = 33075 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e2/07/c0a6d0b1192790364ee9cf655c72c47ad13d68971570f19401f220cac50d/duckduckgo_search-6.2.13-py3-none-any.whl", hash = "sha256:a6618fb2744fa1d081b1bf2e47ef8051de993276a15c20f4e879a150726472de", size = 27468 },
+]
+
+[[package]]
+name = "easygui"
+version = "0.98.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cc/ad/e35f7a30272d322be09dc98592d2f55d27cc933a7fde8baccbbeb2bd9409/easygui-0.98.3.tar.gz", hash = "sha256:d653ff79ee1f42f63b5a090f2f98ce02335d86ad8963b3ce2661805cafe99a04", size = 85583 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8e/a7/b276ff776533b423710a285c8168b52551cb2ab0855443131fdc7fd8c16f/easygui-0.98.3-py2.py3-none-any.whl", hash = "sha256:33498710c68b5376b459cd3fc48d1d1f33822139eb3ed01defbc0528326da3ba", size = 92655 },
+]
+
+[[package]]
+name = "ebcdic"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0d/2f/633031205333bee5f9f93761af8268746aa75f38754823aabb8570eb245b/ebcdic-1.1.1-py2.py3-none-any.whl", hash = "sha256:33b4cb729bc2d0bf46cc1847b0e5946897cb8d3f53520c5b9aa5fa98d7e735f1", size = 128537 },
+]
+
+[[package]]
+name = "ecdsa"
+version = "0.19.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/d0/ec8ac1de7accdcf18cfe468653ef00afd2f609faf67c423efbd02491051b/ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8", size = 197791 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/00/e7/ed3243b30d1bec41675b6394a1daae46349dc2b855cb83be846a5a918238/ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a", size = 149266 },
+]
+
+[[package]]
+name = "einops"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/79/ca/9f5dcb8bead39959454c3912266bedc4c315839cee0e0ca9f4328f4588c1/einops-0.8.0.tar.gz", hash = "sha256:63486517fed345712a8385c100cb279108d9d47e6ae59099b07657e983deae85", size = 58861 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/44/5a/f0b9ad6c0a9017e62d4735daaeb11ba3b6c009d69a26141b258cd37b5588/einops-0.8.0-py3-none-any.whl", hash = "sha256:9572fb63046264a862693b0a87088af3bdc8c068fde03de63453cbbde245465f", size = 43223 },
+]
+
+[[package]]
+name = "email-validator"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "dnspython" },
+    { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
+]
+
+[[package]]
+name = "emoji"
+version = "2.12.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/13/ae307086e7d761fb7fdb2e3439bdd4628b10b7b372639e33fac4e52cfbc2/emoji-2.12.1.tar.gz", hash = "sha256:4aa0488817691aa58d83764b6c209f8a27c0b3ab3f89d1b8dceca1a62e4973eb", size = 442019 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e6/90/20ad30babfa8f2b5ab46281d8e17bdfdbb3ac294cda14d525b9c2d958846/emoji-2.12.1-py3-none-any.whl", hash = "sha256:a00d62173bdadc2510967a381810101624a2f0986145b8da0cffa42e29430235", size = 431357 },
+]
+
+[[package]]
+name = "environs"
+version = "9.5.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "marshmallow" },
+    { name = "python-dotenv" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d4/e3/c3c6c76f3dbe3e019e9a451b35bf9f44690026a5bb1232f7b77097b72ff5/environs-9.5.0.tar.gz", hash = "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9", size = 20795 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ca/5e/f0f217dc393372681bfe05c50f06a212e78d0a3fee907a74ab451ec1dcdb/environs-9.5.0-py2.py3-none-any.whl", hash = "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124", size = 12548 },
+]
+
+[[package]]
+name = "et-xmlfile"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3d/5d/0413a31d184a20c763ad741cc7852a659bf15094c24840c5bdd1754765cd/et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", size = 3218 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/96/c2/3dd434b0108730014f1b96fd286040dc3bcb70066346f7e01ec2ac95865f/et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada", size = 4688 },
+]
+
+[[package]]
+name = "extract-msg"
+version = "0.49.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "beautifulsoup4" },
+    { name = "compressed-rtf" },
+    { name = "ebcdic" },
+    { name = "olefile" },
+    { name = "red-black-tree-mod" },
+    { name = "rtfde" },
+    { name = "tzlocal" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8a/9b/0adbb58e3638eeec64a89e8571db2d6566c65504e943d4f1cef0b8732c77/extract_msg-0.49.0.tar.gz", hash = "sha256:cc700cdcc0cb6fcdbfa3b9e477201958cc28c584716d5c45fdd47261c1f2dfcc", size = 326634 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/28/f7/6c87de35c32e74978e223a647d752fab87b04a893f2fb11340be8c961a8c/extract_msg-0.49.0-py3-none-any.whl", hash = "sha256:6a1756164ef2d0c230bce1966d52155da8bd4bec9a6a1c3166cbdff8ffd9e0ba", size = 333646 },
+]
+
+[[package]]
+name = "fake-useragent"
+version = "1.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/24/a1/1f662631ab153975fa8dbf09296324ecbaf53370dce922054e8de6b57370/fake-useragent-1.5.1.tar.gz", hash = "sha256:6387269f5a2196b5ba7ed8935852f75486845a1c95c50e72460e6a8e762f5c49", size = 22631 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e4/99/60d8cf1b26938c2e0a57e232f7f15641dfcd6f8deda454d73e4145910ff6/fake_useragent-1.5.1-py3-none-any.whl", hash = "sha256:57415096557c8a4e23b62a375c21c55af5fd4ba30549227f562d2c4f5b60e3b3", size = 17190 },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.111.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "email-validator" },
+    { name = "fastapi-cli" },
+    { name = "httpx" },
+    { name = "jinja2" },
+    { name = "orjson" },
+    { name = "pydantic" },
+    { name = "python-multipart" },
+    { name = "starlette" },
+    { name = "typing-extensions" },
+    { name = "ujson" },
+    { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0e/1f/f4a99e92c583780787e04b05aa9d8a8db9ec76d091d81545948a006f5b44/fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7", size = 288414 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e6/33/de41e554e5a187d583906e10d53bfae5fd6c07e98cbf4fe5262bd37e739a/fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0", size = 91993 },
+]
+
+[[package]]
+name = "fastapi-cli"
+version = "0.0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "typer" },
+    { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c5/f8/1ad5ce32d029aeb9117e9a5a9b3e314a8477525d60c12a9b7730a3c186ec/fastapi_cli-0.0.5.tar.gz", hash = "sha256:d30e1239c6f46fcb95e606f02cdda59a1e2fa778a54b64686b3ff27f6211ff9f", size = 15571 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/24/ea/4b5011012ac925fe2f83b19d0e09cee9d324141ec7bf5e78bb2817f96513/fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46", size = 9489 },
+]
+
+[[package]]
+name = "faster-whisper"
+version = "1.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "av" },
+    { name = "ctranslate2" },
+    { name = "huggingface-hub" },
+    { name = "onnxruntime" },
+    { name = "tokenizers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1e/f2/77437ee937233d6e8259e3df511a4662cd7e833dabeaaddbfc929d2a3ed5/faster-whisper-1.0.3.tar.gz", hash = "sha256:1a145db86450b56aaa623c8df7d4ef86e8a1159900f60533e2890e98e8453a17", size = 1980019 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7f/00/4742b1cd3afd23d0ff9b7e72ec40b2c398988332a5578115728fd83415d1/faster_whisper-1.0.3-py3-none-any.whl", hash = "sha256:364d0e378ab232ed26f39656e5c98548b38045224e206b20f7d8c90e2745b9d3", size = 1974982 },
+]
+
+[[package]]
+name = "filelock"
+version = "3.15.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/08/dd/49e06f09b6645156550fb9aee9cc1e59aba7efbc972d665a1bd6ae0435d4/filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb", size = 18007 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159 },
+]
+
+[[package]]
+name = "filetype"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970 },
+]
+
+[[package]]
+name = "flask"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "blinker" },
+    { name = "click" },
+    { name = "itsdangerous" },
+    { name = "jinja2" },
+    { name = "werkzeug" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/41/e1/d104c83026f8d35dfd2c261df7d64738341067526406b40190bc063e829a/flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842", size = 676315 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/61/80/ffe1da13ad9300f87c93af113edd0638c75138c42a0994becfacac078c06/flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3", size = 101735 },
+]
+
+[[package]]
+name = "flask-cors"
+version = "5.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "flask" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4f/d0/d9e52b154e603b0faccc0b7c2ad36a764d8755ef4036acbf1582a67fb86b/flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef", size = 30954 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/56/07/1afa0514c876282bebc1c9aee83c6bb98fe6415cf57b88d9b06e7e29bf9c/Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc", size = 14463 },
+]
+
+[[package]]
+name = "flatbuffers"
+version = "24.3.25"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/74/2df95ef84b214d2bee0886d572775a6f38793f5ca6d7630c3239c91104ac/flatbuffers-24.3.25.tar.gz", hash = "sha256:de2ec5b203f21441716617f38443e0a8ebf3d25bf0d9c0bb0ce68fa00ad546a4", size = 22139 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/41/f0/7e988a019bc54b2dbd0ad4182ef2d53488bb02e58694cd79d61369e85900/flatbuffers-24.3.25-py2.py3-none-any.whl", hash = "sha256:8dbdec58f935f3765e4f7f3cf635ac3a77f83568138d6a2311f524ec96364812", size = 26784 },
+]
+
+[[package]]
+name = "fonttools"
+version = "4.53.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c6/cb/cd80a0da995adde8ade6044a8744aee0da5efea01301cadf770f7fbe7dcc/fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4", size = 3452797 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8b/6a/206391c869ab22d1374e2575cad7cab36b93b9e3d37f48f4696eed2c6e9e/fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1", size = 2762654 },
+    { url = "https://files.pythonhosted.org/packages/f5/7e/4060d88dbfaf446e1c9f0fe9cf13dba36ba47c4da85ce5c1df084ce47e7d/fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923", size = 2247865 },
+    { url = "https://files.pythonhosted.org/packages/e1/67/fff766817e17d67208f8a1e72de15066149485acb5e4ff0816b11fd5fca3/fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719", size = 4873046 },
+    { url = "https://files.pythonhosted.org/packages/a4/22/0a0ad59d9367997fd74a00ad2e88d10559122e09f105e94d34c155aecc0a/fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3", size = 4920859 },
+    { url = "https://files.pythonhosted.org/packages/0b/c4/b4e2f1699a5e2244373a6e8175f862f49f377b444adc6c7b1fe1f5b3d04d/fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb", size = 4885904 },
+    { url = "https://files.pythonhosted.org/packages/64/e7/b9a07c386adf8ad0348163fbcaab74daed6ef18ddb3f49b61b5c19900aeb/fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2", size = 5054708 },
+    { url = "https://files.pythonhosted.org/packages/e9/53/2a79462ae38d7943e63290209c04fef89677c67b29cb329cdc549c18d4d5/fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88", size = 2158885 },
+    { url = "https://files.pythonhosted.org/packages/c8/e1/059700c154bd7170d1c37061239836d2e51ff608f47075450f06dd3c292a/fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02", size = 2205133 },
+    { url = "https://files.pythonhosted.org/packages/87/63/8271f50f3e7bff8b78e03914c4c2893f2f21bd4db2975c60d11ecfbdd174/fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58", size = 2756146 },
+    { url = "https://files.pythonhosted.org/packages/dd/bd/cb8fd2dddd68089c112bf42a88afe188b8ace73f94406539857dcc9347a6/fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8", size = 2244990 },
+    { url = "https://files.pythonhosted.org/packages/ae/71/2b9761e25697bdaf3dfe8269541bd4324f3eb0e4cc13f71d7f90cd272394/fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60", size = 4787604 },
+    { url = "https://files.pythonhosted.org/packages/db/2b/5779cfd48625e013c2dfcf0c246474d5b1f5d061a5f1e476037bf9fff3a3/fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f", size = 4871141 },
+    { url = "https://files.pythonhosted.org/packages/b8/3d/ac3cec35a503bf789d03e9d155a220c9e574f4f1573f00a3bea55695d535/fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2", size = 4764714 },
+    { url = "https://files.pythonhosted.org/packages/ac/9f/27135ac0328e22cca1ba23ee6a1a1f971c13e9f0387adc5598d4635c501d/fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f", size = 5023568 },
+    { url = "https://files.pythonhosted.org/packages/04/40/44d6a94e52e91fe104f9ca95944466af34828992cbc66b666f541de137f1/fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670", size = 2147572 },
+    { url = "https://files.pythonhosted.org/packages/6d/9a/b695930e1b4e6929cc60e294489421632a05c105ac8c56ee63ef56a47872/fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab", size = 2193313 },
+    { url = "https://files.pythonhosted.org/packages/e4/b9/0394d67056d4ad36a3807b439571934b318f1df925593a95e9ec0516b1a7/fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d", size = 1090472 },
+]
+
+[[package]]
+name = "fpdf2"
+version = "2.7.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "defusedxml" },
+    { name = "fonttools" },
+    { name = "pillow" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9d/1f/2e23158904cc6783652e78757fa3f9d870729ff4dc2cf1caec51b8085f82/fpdf2-2.7.9.tar.gz", hash = "sha256:f364c0d816a5e364eeeda9761cf5c961bae8c946f080cf87fed7f38ab773b318", size = 231582 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a6/69/57b1a738e5a008f29b228af7f677b64ba8e5a3de831262955c94e036bf0b/fpdf2-2.7.9-py2.py3-none-any.whl", hash = "sha256:1f176aea4a1cc0fa1da5c799fce8f8bcfe2e936b9d2298a75514c9efc7528bfa", size = 206381 },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/3d/2102257e7acad73efc4a0c306ad3953f68c504c16982bbdfee3ad75d8085/frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b", size = 37820 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/01/bc/8d33f2d84b9368da83e69e42720cff01c5e199b5a868ba4486189a4d8fa9/frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0", size = 97060 },
+    { url = "https://files.pythonhosted.org/packages/af/b2/904500d6a162b98a70e510e743e7ea992241b4f9add2c8063bf666ca21df/frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49", size = 55347 },
+    { url = "https://files.pythonhosted.org/packages/5b/9c/f12b69997d3891ddc0d7895999a00b0c6a67f66f79498c0e30f27876435d/frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced", size = 53374 },
+    { url = "https://files.pythonhosted.org/packages/ac/6e/e0322317b7c600ba21dec224498c0c5959b2bce3865277a7c0badae340a9/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0", size = 273288 },
+    { url = "https://files.pythonhosted.org/packages/a7/76/180ee1b021568dad5b35b7678616c24519af130ed3fa1e0f1ed4014e0f93/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106", size = 284737 },
+    { url = "https://files.pythonhosted.org/packages/05/08/40159d706a6ed983c8aca51922a93fc69f3c27909e82c537dd4054032674/frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068", size = 280267 },
+    { url = "https://files.pythonhosted.org/packages/e0/18/9f09f84934c2b2aa37d539a322267939770362d5495f37783440ca9c1b74/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2", size = 258778 },
+    { url = "https://files.pythonhosted.org/packages/b3/c9/0bc5ee7e1f5cc7358ab67da0b7dfe60fbd05c254cea5c6108e7d1ae28c63/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19", size = 272276 },
+    { url = "https://files.pythonhosted.org/packages/12/5d/147556b73a53ad4df6da8bbb50715a66ac75c491fdedac3eca8b0b915345/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82", size = 272424 },
+    { url = "https://files.pythonhosted.org/packages/83/61/2087bbf24070b66090c0af922685f1d0596c24bb3f3b5223625bdeaf03ca/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec", size = 260881 },
+    { url = "https://files.pythonhosted.org/packages/a8/be/a235bc937dd803258a370fe21b5aa2dd3e7bfe0287a186a4bec30c6cccd6/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a", size = 282327 },
+    { url = "https://files.pythonhosted.org/packages/5d/e7/b2469e71f082948066b9382c7b908c22552cc705b960363c390d2e23f587/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74", size = 281502 },
+    { url = "https://files.pythonhosted.org/packages/db/1b/6a5b970e55dffc1a7d0bb54f57b184b2a2a2ad0b7bca16a97ca26d73c5b5/frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2", size = 272292 },
+    { url = "https://files.pythonhosted.org/packages/1a/05/ebad68130e6b6eb9b287dacad08ea357c33849c74550c015b355b75cc714/frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17", size = 44446 },
+    { url = "https://files.pythonhosted.org/packages/b3/21/c5aaffac47fd305d69df46cfbf118768cdf049a92ee6b0b5cb029d449dcf/frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825", size = 50459 },
+    { url = "https://files.pythonhosted.org/packages/b4/db/4cf37556a735bcdb2582f2c3fa286aefde2322f92d3141e087b8aeb27177/frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae", size = 93937 },
+    { url = "https://files.pythonhosted.org/packages/46/03/69eb64642ca8c05f30aa5931d6c55e50b43d0cd13256fdd01510a1f85221/frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb", size = 53656 },
+    { url = "https://files.pythonhosted.org/packages/3f/ab/c543c13824a615955f57e082c8a5ee122d2d5368e80084f2834e6f4feced/frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b", size = 51868 },
+    { url = "https://files.pythonhosted.org/packages/a9/b8/438cfd92be2a124da8259b13409224d9b19ef8f5a5b2507174fc7e7ea18f/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86", size = 280652 },
+    { url = "https://files.pythonhosted.org/packages/54/72/716a955521b97a25d48315c6c3653f981041ce7a17ff79f701298195bca3/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480", size = 286739 },
+    { url = "https://files.pythonhosted.org/packages/65/d8/934c08103637567084568e4d5b4219c1016c60b4d29353b1a5b3587827d6/frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09", size = 289447 },
+    { url = "https://files.pythonhosted.org/packages/70/bb/d3b98d83ec6ef88f9bd63d77104a305d68a146fd63a683569ea44c3085f6/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a", size = 265466 },
+    { url = "https://files.pythonhosted.org/packages/0b/f2/b8158a0f06faefec33f4dff6345a575c18095a44e52d4f10c678c137d0e0/frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd", size = 281530 },
+    { url = "https://files.pythonhosted.org/packages/ea/a2/20882c251e61be653764038ece62029bfb34bd5b842724fff32a5b7a2894/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6", size = 281295 },
+    { url = "https://files.pythonhosted.org/packages/4c/f9/8894c05dc927af2a09663bdf31914d4fb5501653f240a5bbaf1e88cab1d3/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1", size = 268054 },
+    { url = "https://files.pythonhosted.org/packages/37/ff/a613e58452b60166507d731812f3be253eb1229808e59980f0405d1eafbf/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b", size = 286904 },
+    { url = "https://files.pythonhosted.org/packages/cc/6e/0091d785187f4c2020d5245796d04213f2261ad097e0c1cf35c44317d517/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e", size = 290754 },
+    { url = "https://files.pythonhosted.org/packages/a5/c2/e42ad54bae8bcffee22d1e12a8ee6c7717f7d5b5019261a8c861854f4776/frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8", size = 282602 },
+    { url = "https://files.pythonhosted.org/packages/b6/61/56bad8cb94f0357c4bc134acc30822e90e203b5cb8ff82179947de90c17f/frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89", size = 44063 },
+    { url = "https://files.pythonhosted.org/packages/3e/dc/96647994a013bc72f3d453abab18340b7f5e222b7b7291e3697ca1fcfbd5/frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5", size = 50452 },
+    { url = "https://files.pythonhosted.org/packages/83/10/466fe96dae1bff622021ee687f68e5524d6392b0a2f80d05001cd3a451ba/frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7", size = 11552 },
+]
+
+[[package]]
+name = "fsspec"
+version = "2024.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/90/b6/eba5024a9889fcfff396db543a34bef0ab9d002278f163129f9f01005960/fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49", size = 284584 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5e/44/73bea497ac69bafde2ee4269292fa3b41f1198f4bb7bbaaabde30ad29d4a/fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e", size = 177561 },
+]
+
+[package.optional-dependencies]
+http = [
+    { name = "aiohttp" },
+]
+
+[[package]]
+name = "git-python"
+version = "1.0.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "gitpython" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/3c/f0726fd577517ff8f09e6fe49f153ec9c595f1964c02c209c757d473780a/git-python-1.0.3.zip", hash = "sha256:a7f51d07c7a0b0a15cb4dfa78601196dd20624211153d07c092b811edb6e86fb", size = 4286 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8a/de/0cc6353a45cdb1e137cffac5383097b300cc578e2e1133eeb847e23a1394/git_python-1.0.3-py2.py3-none-any.whl", hash = "sha256:8820ce93786cd11a76d44c7153708588e8056213e4c512406ea3732871aa9ad6", size = 1888 },
+]
+
+[[package]]
+name = "gitdb"
+version = "4.0.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "smmap" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/19/0d/bbb5b5ee188dec84647a4664f3e11b06ade2bde568dbd489d9d64adef8ed/gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b", size = 394469 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/fd/5b/8f0c4a5bb9fd491c277c21eff7ccae71b47d43c4446c9d0c6cff2fe8c2c4/gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4", size = 62721 },
+]
+
+[[package]]
+name = "gitpython"
+version = "3.1.43"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "gitdb" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b6/a1/106fd9fa2dd989b6fb36e5893961f82992cf676381707253e0bf93eb1662/GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c", size = 214149 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e9/bd/cc3a402a6439c15c3d4294333e13042b915bbeab54edc457c723931fed3f/GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff", size = 207337 },
+]
+
+[[package]]
+name = "google-ai-generativelanguage"
+version = "0.6.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "google-api-core", extra = ["grpc"] },
+    { name = "google-auth" },
+    { name = "proto-plus" },
+    { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/28/38/3d717e70a0020cde7bef8ec998ef3c605f208cc77ba93d22450e09f4d4ee/google-ai-generativelanguage-0.6.6.tar.gz", hash = "sha256:1739f035caeeeca5c28f887405eec8690f3372daf79fecf26454a97a4f1733a8", size = 758303 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/81/64/bac34c331e8103a0c32df8298823520787e6ff32ea736785c46b1322d62e/google_ai_generativelanguage-0.6.6-py3-none-any.whl", hash = "sha256:59297737931f073d55ce1268dcc6d95111ee62850349d2b6cde942b16a4fca5c", size = 718256 },
+]
+
+[[package]]
+name = "google-api-core"
+version = "2.19.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "google-auth" },
+    { name = "googleapis-common-protos" },
+    { name = "proto-plus" },
+    { name = "protobuf" },
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c2/41/42a127bf163d9bf1f21540a3bf41c69b231b88707d8d753680b8878201a6/google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd", size = 148925 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/44/99/daa3541e8ecd7d8b7907b714ba92126097a976b5b3dbabdb5febdcf08554/google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125", size = 139384 },
+]
+
+[package.optional-dependencies]
+grpc = [
+    { name = "grpcio" },
+    { name = "grpcio-status" },
+]
+
+[[package]]
+name = "google-api-python-client"
+version = "2.142.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "google-api-core" },
+    { name = "google-auth" },
+    { name = "google-auth-httplib2" },
+    { name = "httplib2" },
+    { name = "uritemplate" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fe/d2/1dc1b95e9fef7bec1df1e04941d9556b6e384691d2ba520777c68429230f/google_api_python_client-2.142.0.tar.gz", hash = "sha256:a1101ac9e24356557ca22f07ff48b7f61fa5d4b4e7feeef3bda16e5dcb86350e", size = 11680160 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f0/41/957e29b392728ba94d1df652e2f3ce59022a6d7bb0164575c016ad204a52/google_api_python_client-2.142.0-py2.py3-none-any.whl", hash = "sha256:266799082bb8301f423ec204dffbffb470b502abbf29efd1f83e644d36eb5a8f", size = 12186205 },
+]
+
+[[package]]
+name = "google-auth"
+version = "2.34.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "cachetools" },
+    { name = "pyasn1-modules" },
+    { name = "rsa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/ae/634dafb151366d91eb848a25846a780dbce4326906ef005d199723fbbca0/google_auth-2.34.0.tar.gz", hash = "sha256:8eb87396435c19b20d32abd2f984e31c191a15284af72eb922f10e5bde9c04cc", size = 257875 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bb/fb/9af9e3f2996677bdda72734482934fe85a3abde174e5f0783ac2f817ba98/google_auth-2.34.0-py2.py3-none-any.whl", hash = "sha256:72fd4733b80b6d777dcde515628a9eb4a577339437012874ea286bca7261ee65", size = 200870 },
+]
+
+[[package]]
+name = "google-auth-httplib2"
+version = "0.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "google-auth" },
+    { name = "httplib2" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/56/be/217a598a818567b28e859ff087f347475c807a5649296fb5a817c58dacef/google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05", size = 10842 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/be/8a/fe34d2f3f9470a27b01c9e76226965863f153d5fbe276f83608562e49c04/google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d", size = 9253 },
+]
+
+[[package]]
+name = "google-generativeai"
+version = "0.7.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "google-ai-generativelanguage" },
+    { name = "google-api-core" },
+    { name = "google-api-python-client" },
+    { name = "google-auth" },
+    { name = "protobuf" },
+    { name = "pydantic" },
+    { name = "tqdm" },
+    { name = "typing-extensions" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f7/92/766c19a6ccdc3e5272ecb831e131672e290d1ca4ec5cd6a4040a78454707/google_generativeai-0.7.2-py3-none-any.whl", hash = "sha256:3117d1ebc92ee77710d4bc25ab4763492fddce9b6332eb25d124cf5d8b78b339", size = 164212 },
+]
+
+[[package]]
+name = "googleapis-common-protos"
+version = "1.63.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0b/1a/41723ae380fa9c561cbe7b61c4eef9091d5fe95486465ccfc84845877331/googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87", size = 112890 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/02/48/87422ff1bddcae677fb6f58c97f5cfc613304a5e8ce2c3662760199c0a84/googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945", size = 220001 },
+]
+
+[[package]]
+name = "greenlet"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/17/14/3bddb1298b9a6786539ac609ba4b7c9c0842e12aa73aaa4d8d73ec8f8185/greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", size = 182013 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/6e/20/68a278a6f93fa36e21cfc3d7599399a8a831225644eb3b6b18755cd3d6fc/greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", size = 271666 },
+    { url = "https://files.pythonhosted.org/packages/21/b4/90e06e07c78513ab03855768200bdb35c8e764e805b3f14fb488e56f82dc/greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", size = 657689 },
+    { url = "https://files.pythonhosted.org/packages/f6/a2/0ed21078039072f9dc738bbf3af12b103a84106b1385ac4723841f846ce7/greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", size = 673009 },
+    { url = "https://files.pythonhosted.org/packages/42/11/42ad6b1104c357826bbee7d7b9e4f24dbd9fde94899a03efb004aab62963/greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33", size = 667432 },
+    { url = "https://files.pythonhosted.org/packages/bb/6b/384dee7e0121cbd1757bdc1824a5ee28e43d8d4e3f99aa59521f629442fe/greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", size = 667442 },
+    { url = "https://files.pythonhosted.org/packages/c6/1f/12d5a6cc26e8b483c2e7975f9c22e088ac735c0d8dcb8a8f72d31a4e5f04/greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", size = 620032 },
+    { url = "https://files.pythonhosted.org/packages/c7/ec/85b647e59e0f137c7792a809156f413e38379cf7f3f2e1353c37f4be4026/greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", size = 1154218 },
+    { url = "https://files.pythonhosted.org/packages/94/ed/1e5f4bca691a81700e5a88e86d6f0e538acb10188cd2cc17140e523255ef/greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", size = 1180754 },
+    { url = "https://files.pythonhosted.org/packages/47/79/26d54d7d700ef65b689fc2665a40846d13e834da0486674a8d4f0f371a47/greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", size = 292822 },
+    { url = "https://files.pythonhosted.org/packages/a2/2f/461615adc53ba81e99471303b15ac6b2a6daa8d2a0f7f77fd15605e16d5b/greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", size = 273085 },
+    { url = "https://files.pythonhosted.org/packages/e9/55/2c3cfa3cdbb940cf7321fbcf544f0e9c74898eed43bf678abf416812d132/greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", size = 660514 },
+    { url = "https://files.pythonhosted.org/packages/38/77/efb21ab402651896c74f24a172eb4d7479f9f53898bd5e56b9e20bb24ffd/greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", size = 674295 },
+    { url = "https://files.pythonhosted.org/packages/74/3a/92f188ace0190f0066dca3636cf1b09481d0854c46e92ec5e29c7cefe5b1/greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", size = 669395 },
+    { url = "https://files.pythonhosted.org/packages/63/0f/847ed02cdfce10f0e6e3425cd054296bddb11a17ef1b34681fa01a055187/greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", size = 670455 },
+    { url = "https://files.pythonhosted.org/packages/bd/37/56b0da468a85e7704f3b2bc045015301bdf4be2184a44868c71f6dca6fe2/greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", size = 625692 },
+    { url = "https://files.pythonhosted.org/packages/7c/68/b5f4084c0a252d7e9c0d95fc1cfc845d08622037adb74e05be3a49831186/greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", size = 1152597 },
+    { url = "https://files.pythonhosted.org/packages/a4/fa/31e22345518adcd69d1d6ab5087a12c178aa7f3c51103f6d5d702199d243/greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", size = 1181043 },
+    { url = "https://files.pythonhosted.org/packages/53/80/3d94d5999b4179d91bcc93745d1b0815b073d61be79dd546b840d17adb18/greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", size = 293635 },
+]
+
+[[package]]
+name = "grpcio"
+version = "1.65.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6c/d8/1d8f1640649808db79b689d65b03556077d5504baad5ea64b167a5adedad/grpcio-1.65.5.tar.gz", hash = "sha256:ec6f219fb5d677a522b0deaf43cea6697b16f338cb68d009e30930c4aa0d2209", size = 12260860 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4a/01/8b8ccc747e0a7572631afe0ad86dafb5e252e045e1bf35d299b8baa80078/grpcio-1.65.5-cp311-cp311-linux_armv7l.whl", hash = "sha256:3207ae60d07e5282c134b6e02f9271a2cb523c6d7a346c6315211fe2bf8d61ed", size = 4886796 },
+    { url = "https://files.pythonhosted.org/packages/a5/8a/773ec9ea43b18b7ba0ed131dcb8ed13958b3a87762d966e3e6275961b968/grpcio-1.65.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a2f80510f99f82d4eb825849c486df703f50652cea21c189eacc2b84f2bde764", size = 10443520 },
+    { url = "https://files.pythonhosted.org/packages/2d/5a/2fdd707bf1415ac6b18141893f38bfd0d1603903dbea0ed08cbf595e2ad6/grpcio-1.65.5-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a80e9a5e3f93c54f5eb82a3825ea1fc4965b2fa0026db2abfecb139a5c4ecdf1", size = 5398157 },
+    { url = "https://files.pythonhosted.org/packages/e5/32/e65418aa0e330e019988bf0fac3cb3fa4b93cf2c6668b11067116a3b3a6e/grpcio-1.65.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b2944390a496567de9e70418f3742b477d85d8ca065afa90432edc91b4bb8ad", size = 5983855 },
+    { url = "https://files.pythonhosted.org/packages/99/6a/d9021f91eacf30e6410f4d1809517a950f0e8b9ccd9f1a0afa05b0d1c07c/grpcio-1.65.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3655139d7be213c32c79ef6fb2367cae28e56ef68e39b1961c43214b457f257", size = 5660388 },
+    { url = "https://files.pythonhosted.org/packages/6b/61/f1281d7a3b2e42b3f924773b81e340340fc81fde51af4f9702be97af44af/grpcio-1.65.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05f02d68fc720e085f061b704ee653b181e6d5abfe315daef085719728d3d1fd", size = 6294373 },
+    { url = "https://files.pythonhosted.org/packages/ac/6e/9158f50864a26da29343269d4b8e3c79a934b081f05b62ee296205f6ba85/grpcio-1.65.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1c4caafe71aef4dabf53274bbf4affd6df651e9f80beedd6b8e08ff438ed3260", size = 5911981 },
+    { url = "https://files.pythonhosted.org/packages/ac/90/85e229a7d1ce432d42566f3340a16055c30066debbd31a58c53b971df7d6/grpcio-1.65.5-cp311-cp311-win32.whl", hash = "sha256:84c901cdec16a092099f251ef3360d15e29ef59772150fa261d94573612539b5", size = 3437955 },
+    { url = "https://files.pythonhosted.org/packages/f0/24/d87cfae0e8cc73532221892b414bf0ffc9fe8b84ac6bea5b6be5045963ae/grpcio-1.65.5-cp311-cp311-win_amd64.whl", hash = "sha256:11f8b16121768c1cb99d7dcb84e01510e60e6a206bf9123e134118802486f035", size = 4148265 },
+    { url = "https://files.pythonhosted.org/packages/75/00/079c2c8ca6b92f595937aacd256f8b0e94bb750a4d313b0249a8603aaa67/grpcio-1.65.5-cp312-cp312-linux_armv7l.whl", hash = "sha256:ee6ed64a27588a2c94e8fa84fe8f3b5c89427d4d69c37690903d428ec61ca7e4", size = 4825951 },
+    { url = "https://files.pythonhosted.org/packages/6b/8c/35a8d0f7135dbeb87f522ec743cd06423dba8eaec6c891f9466b0f46284c/grpcio-1.65.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:76991b7a6fb98630a3328839755181ce7c1aa2b1842aa085fd4198f0e5198960", size = 10408025 },
+    { url = "https://files.pythonhosted.org/packages/b8/e4/76fbbc753e8925e7779045e63896cf177f39b2b9419ee64910de4b055376/grpcio-1.65.5-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:89c00a18801b1ed9cc441e29b521c354725d4af38c127981f2c950c796a09b6e", size = 5339884 },
+    { url = "https://files.pythonhosted.org/packages/27/0d/b7be8fffa0c3d721c8ab3553745aa880849c60b2a2a7da0da625f4c4fbaf/grpcio-1.65.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:078038e150a897e5e402ed3d57f1d31ebf604cbed80f595bd281b5da40762a92", size = 5925035 },
+    { url = "https://files.pythonhosted.org/packages/44/3f/0151cd08156be773884c7a0f0e22daecdb71fe009a5decf3f1abe1e35159/grpcio-1.65.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97962720489ef31b5ad8a916e22bc31bba3664e063fb9f6702dce056d4aa61b", size = 5605652 },
+    { url = "https://files.pythonhosted.org/packages/87/a2/b2896199638a94a55d87aaaabe77a04eb59a4a73f420791ae3822ec7a714/grpcio-1.65.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b8270b15b99781461b244f5c81d5c2bc9696ab9189fb5ff86c841417fb3b39fe", size = 6238617 },
+    { url = "https://files.pythonhosted.org/packages/40/ce/340d07e224ac802011f8fcf1e03e73f45927ed7aa8be86ed4ea62ec99f94/grpcio-1.65.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e5c4c15ac3fe1eb68e46bc51e66ad29be887479f231f8237cf8416058bf0cc1", size = 5855677 },
+    { url = "https://files.pythonhosted.org/packages/8c/22/9c8084ccd67d27f4119f3ca6fd74408a4fceaf73536eca9811594baf5b86/grpcio-1.65.5-cp312-cp312-win32.whl", hash = "sha256:f5b5970341359341d0e4c789da7568264b2a89cd976c05ea476036852b5950cd", size = 3420261 },
+    { url = "https://files.pythonhosted.org/packages/3e/72/bd4c82ac27dd84ab39e33ab445be1157fe9b1d659dda6355079b479053dd/grpcio-1.65.5-cp312-cp312-win_amd64.whl", hash = "sha256:238a625f391a1b9f5f069bdc5930f4fd71b74426bea52196fc7b83f51fa97d34", size = 4134287 },
+]
+
+[[package]]
+name = "grpcio-status"
+version = "1.62.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "googleapis-common-protos" },
+    { name = "grpcio" },
+    { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7c/d7/013ef01c5a1c2fd0932c27c904934162f69f41ca0f28396d3ffe4d386123/grpcio-status-1.62.3.tar.gz", hash = "sha256:289bdd7b2459794a12cf95dc0cb727bd4a1742c37bd823f760236c937e53a485", size = 13063 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/90/40/972271de05f9315c0d69f9f7ebbcadd83bc85322f538637d11bb8c67803d/grpcio_status-1.62.3-py3-none-any.whl", hash = "sha256:f9049b762ba8de6b1086789d8315846e094edac2c50beaf462338b301a8fd4b8", size = 14448 },
+]
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "certifi" },
+    { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/17/b0/5e8b8674f8d203335a62fdfcfa0d11ebe09e23613c3391033cbba35f7926/httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61", size = 83234 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/78/d4/e5d7e4f2174f8a4d63c8897d79eb8fe2503f7ecc03282fee1fa2719c2704/httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5", size = 77926 },
+]
+
+[[package]]
+name = "httplib2"
+version = "0.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pyparsing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/ad/2371116b22d616c194aa25ec410c9c6c37f23599dcd590502b74db197584/httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81", size = 351116 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a8/6c/d2fbdaaa5959339d53ba38e94c123e4e84b8fbc4b84beb0e70d7c1608486/httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc", size = 96854 },
+]
+
+[[package]]
+name = "httptools"
+version = "0.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/67/1d/d77686502fced061b3ead1c35a2d70f6b281b5f723c4eff7a2277c04e4a2/httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a", size = 191228 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f5/d1/53283b96ed823d5e4d89ee9aa0f29df5a1bdf67f148e061549a595d534e4/httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1", size = 145855 },
+    { url = "https://files.pythonhosted.org/packages/80/dd/cebc9d4b1d4b70e9f3d40d1db0829a28d57ca139d0b04197713816a11996/httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0", size = 75604 },
+    { url = "https://files.pythonhosted.org/packages/76/7a/45c5a9a2e9d21f7381866eb7b6ead5a84d8fe7e54e35208eeb18320a29b4/httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc", size = 324784 },
+    { url = "https://files.pythonhosted.org/packages/59/23/047a89e66045232fb82c50ae57699e40f70e073ae5ccd53f54e532fbd2a2/httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2", size = 318547 },
+    { url = "https://files.pythonhosted.org/packages/82/f5/50708abc7965d7d93c0ee14a148ccc6d078a508f47fe9357c79d5360f252/httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837", size = 330211 },
+    { url = "https://files.pythonhosted.org/packages/e3/1e/9823ca7aab323c0e0e9dd82ce835a6e93b69f69aedffbc94d31e327f4283/httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d", size = 322174 },
+    { url = "https://files.pythonhosted.org/packages/14/e4/20d28dfe7f5b5603b6b04c33bb88662ad749de51f0c539a561f235f42666/httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3", size = 55434 },
+    { url = "https://files.pythonhosted.org/packages/60/13/b62e086b650752adf9094b7e62dab97f4cb7701005664544494b7956a51e/httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0", size = 146354 },
+    { url = "https://files.pythonhosted.org/packages/f8/5d/9ad32b79b6c24524087e78aa3f0a2dfcf58c11c90e090e4593b35def8a86/httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2", size = 75785 },
+    { url = "https://files.pythonhosted.org/packages/d0/a4/b503851c40f20bcbd453db24ed35d961f62abdae0dccc8f672cd5d350d87/httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90", size = 345396 },
+    { url = "https://files.pythonhosted.org/packages/a2/9a/aa406864f3108e06f7320425a528ff8267124dead1fd72a3e9da2067f893/httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503", size = 344741 },
+    { url = "https://files.pythonhosted.org/packages/cf/3a/3fd8dfb987c4247651baf2ac6f28e8e9f889d484ca1a41a9ad0f04dfe300/httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84", size = 345096 },
+    { url = "https://files.pythonhosted.org/packages/80/01/379f6466d8e2edb861c1f44ccac255ed1f8a0d4c5c666a1ceb34caad7555/httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb", size = 343535 },
+    { url = "https://files.pythonhosted.org/packages/d3/97/60860e9ee87a7d4712b98f7e1411730520053b9d69e9e42b0b9751809c17/httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949", size = 55660 },
+]
+
+[[package]]
+name = "httpx"
+version = "0.27.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+    { name = "certifi" },
+    { name = "httpcore" },
+    { name = "idna" },
+    { name = "sniffio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5", size = 126413 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/41/7b/ddacf6dcebb42466abd03f368782142baa82e08fc0c1f8eaa05b4bae87d5/httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5", size = 75590 },
+]
+
+[[package]]
+name = "huggingface-hub"
+version = "0.24.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "filelock" },
+    { name = "fsspec" },
+    { name = "packaging" },
+    { name = "pyyaml" },
+    { name = "requests" },
+    { name = "tqdm" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/65/24/b98fce967b7d63700e5805b915012ba25bb538a81fcf11e97f3cc3f4f012/huggingface_hub-0.24.6.tar.gz", hash = "sha256:cc2579e761d070713eaa9c323e3debe39d5b464ae3a7261c39a9195b27bb8000", size = 349200 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b9/8f/d6718641c14d98a5848c6a24d2376028d292074ffade0702940a4b1dde76/huggingface_hub-0.24.6-py3-none-any.whl", hash = "sha256:a990f3232aa985fe749bc9474060cbad75e8b2f115f6665a9fda5b9c97818970", size = 417509 },
+]
+
+[[package]]
+name = "humanfriendly"
+version = "10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pyreadline3", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794 },
+]
+
+[[package]]
+name = "idna"
+version = "3.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/ed/f86a79a07470cb07819390452f178b3bef1d375f2ec021ecfc709fc7cf07/idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", size = 189575 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e5/3e/741d8c82801c347547f8a2a06aa57dbb1992be9e948df2ea0eda2c8b79e8/idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0", size = 66836 },
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "8.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "zipp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/20/ff/bd28f70283b9cca0cbf0c2a6082acbecd822d1962ae7b2a904861b9965f8/importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812", size = 52667 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/dc/ef/38766b2edb096260d9b1b6ad35adaa0bce3b0567abb452b21eb074af88c4/importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f", size = 24769 },
+]
+
+[[package]]
+name = "importlib-resources"
+version = "6.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0e/6a/3ac38d1458685a04fafa299dce821713a4f65e5ec30466bec07113f2f891/importlib_resources-6.4.4.tar.gz", hash = "sha256:20600c8b7361938dc0bb2d5ec0297802e575df486f5a544fa414da65e13721f7", size = 42721 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/db/2a/728c8ae66011600fac5731a7db030d23c42f1321fd9547654f0c3b2b32d7/importlib_resources-6.4.4-py3-none-any.whl", hash = "sha256:dda242603d1c9cd836c3368b1174ed74cb4049ecd209e7a1a0104620c18c5c11", size = 35608 },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
+]
+
+[[package]]
+name = "itsdangerous"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234 },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 },
+]
+
+[[package]]
+name = "jiter"
+version = "0.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/1a/aa64be757afc614484b370a4d9fc1747dc9237b37ce464f7f9d9ca2a3d38/jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a", size = 158300 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/94/5f/3ac960ed598726aae46edea916e6df4df7ff6fe084bc60774b95cf3154e6/jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553", size = 284131 },
+    { url = "https://files.pythonhosted.org/packages/03/eb/2308fa5f5c14c97c4c7720fef9465f1fa0771826cddb4eec9866bdd88846/jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3", size = 299310 },
+    { url = "https://files.pythonhosted.org/packages/3c/f6/dba34ca10b44715fa5302b8e8d2113f72eb00a9297ddf3fa0ae4fd22d1d1/jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6", size = 332282 },
+    { url = "https://files.pythonhosted.org/packages/69/f7/64e0a7439790ec47f7681adb3871c9d9c45fff771102490bbee5e92c00b7/jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4", size = 342370 },
+    { url = "https://files.pythonhosted.org/packages/55/31/1efbfff2ae8e4d919144c53db19b828049ad0622a670be3bbea94a86282c/jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9", size = 363591 },
+    { url = "https://files.pythonhosted.org/packages/30/c3/7ab2ca2276426a7398c6dfb651e38dbc81954c79a3bfbc36c514d8599499/jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614", size = 378551 },
+    { url = "https://files.pythonhosted.org/packages/47/e7/5d88031cd743c62199b125181a591b1671df3ff2f6e102df85c58d8f7d31/jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e", size = 319152 },
+    { url = "https://files.pythonhosted.org/packages/4c/2d/09ea58e1adca9f0359f3d41ef44a1a18e59518d7c43a21f4ece9e72e28c0/jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06", size = 357377 },
+    { url = "https://files.pythonhosted.org/packages/7d/2f/83ff1058cb56fc3ff73e0d3c6440703ddc9cdb7f759b00cfbde8228fc435/jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403", size = 511091 },
+    { url = "https://files.pythonhosted.org/packages/ae/c9/4f85f97c9894382ab457382337aea0012711baaa17f2ed55c0ff25f3668a/jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646", size = 492948 },
+    { url = "https://files.pythonhosted.org/packages/4d/f2/2e987e0eb465e064c5f52c2f29c8d955452e3b316746e326269263bfb1b7/jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb", size = 195183 },
+    { url = "https://files.pythonhosted.org/packages/ab/59/05d1c3203c349b37c4dd28b02b9b4e5915a7bcbd9319173b4548a67d2e93/jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae", size = 191032 },
+    { url = "https://files.pythonhosted.org/packages/aa/bd/c3950e2c478161e131bed8cb67c36aed418190e2a961a1c981e69954e54b/jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a", size = 283511 },
+    { url = "https://files.pythonhosted.org/packages/80/1c/8ce58d8c37a589eeaaa5d07d131fd31043886f5e77ab50c00a66d869a361/jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df", size = 296974 },
+    { url = "https://files.pythonhosted.org/packages/4d/b8/6faeff9eed8952bed93a77ea1cffae7b946795b88eafd1a60e87a67b09e0/jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248", size = 331897 },
+    { url = "https://files.pythonhosted.org/packages/4f/54/1d9a2209b46d39ce6f0cef3ad87c462f9c50312ab84585e6bd5541292b35/jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544", size = 342962 },
+    { url = "https://files.pythonhosted.org/packages/2a/de/90360be7fc54b2b4c2dfe79eb4ed1f659fce9c96682e6a0be4bbe71371f7/jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba", size = 363844 },
+    { url = "https://files.pythonhosted.org/packages/ba/ad/ef32b173191b7a53ea8a6757b80723cba321f8469834825e8c71c96bde17/jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f", size = 378709 },
+    { url = "https://files.pythonhosted.org/packages/07/de/353ce53743c0defbbbd652e89c106a97dbbac4eb42c95920b74b5056b93a/jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e", size = 319038 },
+    { url = "https://files.pythonhosted.org/packages/3f/92/42d47310bf9530b9dece9e2d7c6d51cf419af5586ededaf5e66622d160e2/jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a", size = 357763 },
+    { url = "https://files.pythonhosted.org/packages/bd/8c/2bb76a9a84474d48fdd133d3445db8a4413da4e87c23879d917e000a9d87/jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e", size = 511031 },
+    { url = "https://files.pythonhosted.org/packages/33/4f/9f23d79c0795e0a8e56e7988e8785c2dcda27e0ed37977256d50c77c6a19/jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338", size = 493042 },
+    { url = "https://files.pythonhosted.org/packages/df/67/8a4f975aa834b8aecdb6b131422390173928fd47f42f269dcc32034ab432/jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4", size = 195405 },
+    { url = "https://files.pythonhosted.org/packages/15/81/296b1e25c43db67848728cdab34ac3eb5c5cbb4955ceb3f51ae60d4a5e3d/jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5", size = 189720 },
+]
+
+[[package]]
+name = "jmespath"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/2a/e867e8531cf3e36b41201936b7fa7ba7b5702dbef42922193f05c8976cd6/jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe", size = 25843 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/31/b4/b9b800c45527aadd64d5b442f9b932b00648617eb5d63d2c7a6587b7cafc/jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", size = 20256 },
+]
+
+[[package]]
+name = "joblib"
+version = "1.4.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 },
+]
+
+[[package]]
+name = "jsonpatch"
+version = "1.33"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "jsonpointer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898 },
+]
+
+[[package]]
+name = "jsonpath-python"
+version = "1.0.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/49/e582e50b0c54c1b47e714241c4a4767bf28758bf90212248aea8e1ce8516/jsonpath-python-1.0.6.tar.gz", hash = "sha256:dd5be4a72d8a2995c3f583cf82bf3cd1a9544cfdabf2d22595b67aff07349666", size = 18121 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/16/8a/d63959f4eff03893a00e6e63592e3a9f15b9266ed8e0275ab77f8c7dbc94/jsonpath_python-1.0.6-py3-none-any.whl", hash = "sha256:1e3b78df579f5efc23565293612decee04214609208a2335884b3ee3f786b575", size = 7552 },
+]
+
+[[package]]
+name = "jsonpointer"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 },
+]
+
+[[package]]
+name = "kubernetes"
+version = "30.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "certifi" },
+    { name = "google-auth" },
+    { name = "oauthlib" },
+    { name = "python-dateutil" },
+    { name = "pyyaml" },
+    { name = "requests" },
+    { name = "requests-oauthlib" },
+    { name = "six" },
+    { name = "urllib3" },
+    { name = "websocket-client" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/82/3c/9f29f6cab7f35df8e54f019e5719465fa97b877be2454e99f989270b4f34/kubernetes-30.1.0.tar.gz", hash = "sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc", size = 887810 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/62/a1/2027ddede72d33be2effc087580aeba07e733a7360780ae87226f1f91bd8/kubernetes-30.1.0-py2.py3-none-any.whl", hash = "sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d", size = 1706042 },
+]
+
+[[package]]
+name = "langchain"
+version = "0.2.15"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "aiohttp" },
+    { name = "langchain-core" },
+    { name = "langchain-text-splitters" },
+    { name = "langsmith" },
+    { name = "numpy" },
+    { name = "pydantic" },
+    { name = "pyyaml" },
+    { name = "requests" },
+    { name = "sqlalchemy" },
+    { name = "tenacity" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/f6/9e1044e0b805f5f704435f9b378c26f02dfad3cd361f455e0a1129f8d7ad/langchain-0.2.15.tar.gz", hash = "sha256:f613ce7594be34f9bac687134a56f6e8274951907b798dbd037aefc95df78953", size = 414499 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2a/5f/fec41e34c31265e4dc197ebe24d138c73dfe6a15832fe5db9a83c70e570c/langchain-0.2.15-py3-none-any.whl", hash = "sha256:9e6231441870aaa8523be24a5785ccccfdde759a7e27dd082b6ec80f68e49dec", size = 1001055 },
+]
+
+[[package]]
+name = "langchain-chroma"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "chromadb" },
+    { name = "fastapi" },
+    { name = "langchain-core" },
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/62/06/3ad3efe140eb17fe0c8e1fba03aca0ec6b3fa0451ce7350675c1fefb2a95/langchain_chroma-0.1.2.tar.gz", hash = "sha256:745a53b93e7ae058f9666a48e15ff211122656032ed0e8ffb7291b402f5bf23b", size = 9395 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/10/05/34b30ff33af5ea7e6e5b6d1bf8ea3a0f2c1462c6b7f750f21dd0179fdf1e/langchain_chroma-0.1.2-py3-none-any.whl", hash = "sha256:0948f2975091dfef685a7981c140b8fd8a3b0f0602abba61abbcac7959beee4c", size = 9324 },
+]
+
+[[package]]
+name = "langchain-community"
+version = "0.2.12"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "aiohttp" },
+    { name = "dataclasses-json" },
+    { name = "langchain" },
+    { name = "langchain-core" },
+    { name = "langsmith" },
+    { name = "numpy" },
+    { name = "pyyaml" },
+    { name = "requests" },
+    { name = "sqlalchemy" },
+    { name = "tenacity" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/aa/29/10a27e2a5285decb9cefd4ddd9bf814fb68d6e848cc099a7b3b6b60619aa/langchain_community-0.2.12.tar.gz", hash = "sha256:d671cfc6a4f3b65f49a2e59ab420d0164f109d0a56fc4b4996518205c63b8c7e", size = 1566700 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/83/3a/3becea565ec4232d6d7f6d8774696e2d64e94b388fa3c45bfa264a3787c2/langchain_community-0.2.12-py3-none-any.whl", hash = "sha256:50e74473dd2309bdef561760afbbf0c5ea17ed91fc4dfa0d52279dd16d6d34e0", size = 2311611 },
+]
+
+[[package]]
+name = "langchain-core"
+version = "0.2.38"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "jsonpatch" },
+    { name = "langsmith" },
+    { name = "packaging" },
+    { name = "pydantic" },
+    { name = "pyyaml" },
+    { name = "tenacity" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a0/1c/c1016073a338206358b4a0dba936c109819fb4b070ec2c91456688c1a6dd/langchain_core-0.2.38.tar.gz", hash = "sha256:eb69dbedd344f2ee1f15bcea6c71a05884b867588fadc42d04632e727c1238f3", size = 316363 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1c/e4/501fbe904530dad6ed80f03b188d7602081560dd5cc0bcf0b3c51778c314/langchain_core-0.2.38-py3-none-any.whl", hash = "sha256:8a5729bc7e68b4af089af20eff44fe4e7ca21d0e0c87ec21cef7621981fd1a4a", size = 396442 },
+]
+
+[[package]]
+name = "langchain-text-splitters"
+version = "0.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "langchain-core" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/3d/b6cad97223a88465114da8623e9f3da7a74821e76afb3bf1f34f419d93d9/langchain_text_splitters-0.2.2.tar.gz", hash = "sha256:a1e45de10919fa6fb080ef0525deab56557e9552083600455cb9fa4238076140", size = 20188 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/06/76/9e0ca1b8881f64bf927f2205bf6c43a085c04646a71d911b3c05d76e90bb/langchain_text_splitters-0.2.2-py3-none-any.whl", hash = "sha256:1c80d4b11b55e2995f02d2a326c0323ee1eeff24507329bb22924e420c782dff", size = 25239 },
+]
+
+[[package]]
+name = "langdetect"
+version = "1.0.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0e/72/a3add0e4eec4eb9e2569554f7c70f4a3c27712f40e3284d483e88094cc0e/langdetect-1.0.9.tar.gz", hash = "sha256:cbc1fef89f8d062739774bd51eda3da3274006b3661d199c2655f6b3f6d605a0", size = 981474 }
+
+[[package]]
+name = "langfuse"
+version = "2.44.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+    { name = "backoff" },
+    { name = "httpx" },
+    { name = "idna" },
+    { name = "packaging" },
+    { name = "pydantic" },
+    { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a4/85/5a21a1d3be81e71f11f6d50affbb929eea3d5bbfab649bf8811bba83776f/langfuse-2.44.0.tar.gz", hash = "sha256:dfa5378ff7022ae9fe5b8b842c0365347c98f9ef2b772dcee6a93a45442de28c", size = 106834 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1f/54/03550a879a90ae4242542335b0883bb2d297c75f32cb58604064879a8f3b/langfuse-2.44.0-py3-none-any.whl", hash = "sha256:adb73400a6ad6d597cc95c31381c82f81face3d5fb69391181f224a26f7e8562", size = 195931 },
+]
+
+[[package]]
+name = "langsmith"
+version = "0.1.101"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "httpx" },
+    { name = "orjson" },
+    { name = "pydantic" },
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2c/7d/2adac2d02aa9405f0df823d80ffd10d136bf74c5ae7d62e7bc8e0fc06baa/langsmith-0.1.101.tar.gz", hash = "sha256:caf4d95f314bb6cd3c4e0632eed821fd5cd5d0f18cb824772fce6d7a9113895b", size = 138302 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b5/af/b82590f2830559a0c2438e96d110bfeb002b2687c08a57bf117d9ea6b714/langsmith-0.1.101-py3-none-any.whl", hash = "sha256:572e2c90709cda1ad837ac86cedda7295f69933f2124c658a92a35fb890477cc", size = 148892 },
+]
+
+[[package]]
+name = "lark"
+version = "1.1.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2c/e1/804b6196b3fbdd0f8ba785fc62837b034782a891d6f663eea2f30ca23cfa/lark-1.1.9.tar.gz", hash = "sha256:15fa5236490824c2c4aba0e22d2d6d823575dcaf4cdd1848e34b6ad836240fba", size = 255451 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e7/9c/eef7c591e6dc952f3636cfe0df712c0f9916cedf317810a3bb53ccb65cdd/lark-1.1.9-py3-none-any.whl", hash = "sha256:a0dd3a87289f8ccbb325901e4222e723e7d745dbfc1803eaf5f3d2ace19cf2db", size = 111693 },
+]
+
+[[package]]
+name = "lxml"
+version = "5.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056 },
+    { url = "https://files.pythonhosted.org/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238 },
+    { url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 },
+    { url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 },
+    { url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 },
+    { url = "https://files.pythonhosted.org/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", size = 4866657 },
+    { url = "https://files.pythonhosted.org/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", size = 4967017 },
+    { url = "https://files.pythonhosted.org/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", size = 4810730 },
+    { url = "https://files.pythonhosted.org/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", size = 5455154 },
+    { url = "https://files.pythonhosted.org/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", size = 4969416 },
+    { url = "https://files.pythonhosted.org/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", size = 5013672 },
+    { url = "https://files.pythonhosted.org/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", size = 4878644 },
+    { url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 },
+    { url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 },
+    { url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 },
+    { url = "https://files.pythonhosted.org/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226 },
+    { url = "https://files.pythonhosted.org/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971 },
+    { url = "https://files.pythonhosted.org/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753 },
+    { url = "https://files.pythonhosted.org/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955 },
+    { url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 },
+    { url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 },
+    { url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 },
+    { url = "https://files.pythonhosted.org/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", size = 4813963 },
+    { url = "https://files.pythonhosted.org/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", size = 4923353 },
+    { url = "https://files.pythonhosted.org/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", size = 4740541 },
+    { url = "https://files.pythonhosted.org/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", size = 5346504 },
+    { url = "https://files.pythonhosted.org/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", size = 4898077 },
+    { url = "https://files.pythonhosted.org/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", size = 4946543 },
+    { url = "https://files.pythonhosted.org/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", size = 4816841 },
+    { url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 },
+    { url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 },
+    { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 },
+    { url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 },
+    { url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 },
+    { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 },
+    { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 },
+    { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 },
+    { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 },
+    { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 },
+    { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 },
+    { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 },
+    { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 },
+    { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 },
+    { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 },
+    { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 },
+    { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 },
+    { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 },
+    { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 },
+    { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 },
+    { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 },
+    { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 },
+]
+
+[[package]]
+name = "mako"
+version = "1.3.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/67/03/fb5ba97ff65ce64f6d35b582aacffc26b693a98053fa831ab43a437cbddb/Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc", size = 392738 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/03/62/70f5a0c2dd208f9f3f2f9afd103aec42ee4d9ad2401d78342f75e9b8da36/Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a", size = 78565 },
+]
+
+[[package]]
+name = "markdown"
+version = "3.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/28/3af612670f82f4c056911fbbbb42760255801b3068c48de792d354ff4472/markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2", size = 357086 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3f/08/83871f3c50fc983b88547c196d11cf8c3340e37c32d2e9d6152abe2c61f7/Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803", size = 106349 },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
+]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 },
+    { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 },
+    { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 },
+    { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 },
+    { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 },
+    { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 },
+    { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 },
+    { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 },
+    { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 },
+    { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 },
+    { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 },
+    { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 },
+    { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 },
+    { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 },
+    { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 },
+    { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 },
+    { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 },
+    { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 },
+    { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 },
+    { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 },
+]
+
+[[package]]
+name = "marshmallow"
+version = "3.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "packaging" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/70/40/faa10dc4500bca85f41ca9d8cefab282dd23d0fcc7a9b5fab40691e72e76/marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e", size = 176836 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3c/78/c1de55eb3311f2c200a8b91724414b8d6f5ae78891c15d9d936ea43c3dba/marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9", size = 49334 },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
+]
+
+[[package]]
+name = "milvus-lite"
+version = "2.4.10"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "tqdm" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7c/0b/262997e63e2eaaf5d9f93d73c9d3e75499c6ec058bd1307864be7efb6704/milvus_lite-2.4.10-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fc4246d3ed7d1910847afce0c9ba18212e93a6e9b8406048436940578dfad5cb", size = 22281832 },
+    { url = "https://files.pythonhosted.org/packages/17/20/9054ace78c61d64a6c24b8e3d6c8a73b23b447028e43c4d1e6c878e8294a/milvus_lite-2.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:74a8e07c5e3b057df17fbb46913388e84df1dc403a200f4e423799a58184c800", size = 19855994 },
+    { url = "https://files.pythonhosted.org/packages/34/8e/7858d12d89bf9e84302c6ffd5faf776d7b2de4372a07461f726ce6f9929e/milvus_lite-2.4.10-py3-none-manylinux2014_aarch64.whl", hash = "sha256:240c7386b747bad696ecb5bd1f58d491e86b9d4b92dccee3315ed7256256eddc", size = 39614866 },
+    { url = "https://files.pythonhosted.org/packages/84/65/639cb552c892ba5fef73301f878b2e7cabb59c918e0c49c9cf3026d49447/milvus_lite-2.4.10-py3-none-manylinux2014_x86_64.whl", hash = "sha256:211d2e334a043f9282bdd9755f76b9b2d93b23bffa7af240919ffce6a8dfe325", size = 49377774 },
+]
+
+[[package]]
+name = "mmh3"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/63/96/aa247e82878b123468f0079ce2ac77e948315bab91ce45d2934a62e0af95/mmh3-4.1.0.tar.gz", hash = "sha256:a1cf25348b9acd229dda464a094d6170f47d2850a1fcb762a3b6172d2ce6ca4a", size = 26357 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2e/d6/86beea107e7e9700df9522466346c23a2f54faa81337c86fd17002aa95a6/mmh3-4.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3280a463855b0eae64b681cd5b9ddd9464b73f81151e87bb7c91a811d25619e6", size = 39427 },
+    { url = "https://files.pythonhosted.org/packages/1c/08/65fa5489044e2afc304e8540c6c607d5d7b136ddc5cd8315c13de0adc34c/mmh3-4.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:97ac57c6c3301769e757d444fa7c973ceb002cb66534b39cbab5e38de61cd896", size = 29281 },
+    { url = "https://files.pythonhosted.org/packages/b3/aa/98511d3ea3f6ba958136d913be3be3c1009be935a20ecc7b2763f0a605b6/mmh3-4.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a7b6502cdb4dbd880244818ab363c8770a48cdccecf6d729ade0241b736b5ec0", size = 30130 },
+    { url = "https://files.pythonhosted.org/packages/3c/b7/1a93f81643435b0e57f1046c4ffe46f0214693eaede0d9b0a1a236776e70/mmh3-4.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ba2da04671a9621580ddabf72f06f0e72c1c9c3b7b608849b58b11080d8f14", size = 69072 },
+    { url = "https://files.pythonhosted.org/packages/45/9e/2ff70246aefd9cf146bc6a420c28ed475a0d1a325f31ee203be02f9215d4/mmh3-4.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a5fef4c4ecc782e6e43fbeab09cff1bac82c998a1773d3a5ee6a3605cde343e", size = 72470 },
+    { url = "https://files.pythonhosted.org/packages/dc/cb/57bc1fdbdbe6837aebfca982494e23e2498ee2a89585c9054713b22e4167/mmh3-4.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5135358a7e00991f73b88cdc8eda5203bf9de22120d10a834c5761dbeb07dd13", size = 71251 },
+    { url = "https://files.pythonhosted.org/packages/4d/c2/46d7d2721b69fbdfd30231309e6395f62ff6744e5c00dd8113b9faa06fba/mmh3-4.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cff9ae76a54f7c6fe0167c9c4028c12c1f6de52d68a31d11b6790bb2ae685560", size = 66035 },
+    { url = "https://files.pythonhosted.org/packages/6f/a4/7ba4bcc838818bcf018e26d118d5ddb605c23c4fad040dc4d811f1cfcb04/mmh3-4.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f02576a4d106d7830ca90278868bf0983554dd69183b7bbe09f2fcd51cf54f", size = 67844 },
+    { url = "https://files.pythonhosted.org/packages/71/ed/8e80d1038e7bb15eaf739711d1fc36f2341acb6b1b95fa77003f2799c91e/mmh3-4.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:073d57425a23721730d3ff5485e2da489dd3c90b04e86243dd7211f889898106", size = 76724 },
+    { url = "https://files.pythonhosted.org/packages/1c/22/a6a70ca81f0ce8fe2f3a68d89c1184c2d2d0fbe0ee305da50e972c5ff9fa/mmh3-4.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:71e32ddec7f573a1a0feb8d2cf2af474c50ec21e7a8263026e8d3b4b629805db", size = 75004 },
+    { url = "https://files.pythonhosted.org/packages/73/20/abe50b605760f1f5b6e0b436c650649e69ca478d0f41b154f300367c09e4/mmh3-4.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7cbb20b29d57e76a58b40fd8b13a9130db495a12d678d651b459bf61c0714cea", size = 82230 },
+    { url = "https://files.pythonhosted.org/packages/45/80/a1fc99d3ee50b573df0bfbb1ad518463af78d2ebca44bfca3b3f9473d651/mmh3-4.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a42ad267e131d7847076bb7e31050f6c4378cd38e8f1bf7a0edd32f30224d5c9", size = 78679 },
+    { url = "https://files.pythonhosted.org/packages/9e/51/6c9ee2ddf3b386f45ff83b6926a5e826635757d91dab04cbf16eee05f9a7/mmh3-4.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a013979fc9390abadc445ea2527426a0e7a4495c19b74589204f9b71bcaafeb", size = 77382 },
+    { url = "https://files.pythonhosted.org/packages/ee/fa/4b377f244c27fac5f0343cc4dc0d2eb0a08049afc8d5322d07be7461a768/mmh3-4.1.0-cp311-cp311-win32.whl", hash = "sha256:1d3b1cdad7c71b7b88966301789a478af142bddcb3a2bee563f7a7d40519a00f", size = 31232 },
+    { url = "https://files.pythonhosted.org/packages/d1/b0/500ef56c29b276d796bfdb47c16d34fa18a68945e4d730a6fa7d483583ed/mmh3-4.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0dc6dc32eb03727467da8e17deffe004fbb65e8b5ee2b502d36250d7a3f4e2ec", size = 31276 },
+    { url = "https://files.pythonhosted.org/packages/cc/84/94795e6e710c3861f8f355a12be9c9f4b8433a538c983e75bd4c00496a8a/mmh3-4.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:9ae3a5c1b32dda121c7dc26f9597ef7b01b4c56a98319a7fe86c35b8bc459ae6", size = 30142 },
+    { url = "https://files.pythonhosted.org/packages/18/45/b4d41e86b00eed8c500adbe0007129861710e181c7f49c507ef6beae9496/mmh3-4.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0033d60c7939168ef65ddc396611077a7268bde024f2c23bdc283a19123f9e9c", size = 39495 },
+    { url = "https://files.pythonhosted.org/packages/a6/d4/f041b8704cb8d1aad3717105daa582e29818b78a540622dfed84cd00d88f/mmh3-4.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d6af3e2287644b2b08b5924ed3a88c97b87b44ad08e79ca9f93d3470a54a41c5", size = 29334 },
+    { url = "https://files.pythonhosted.org/packages/cb/bb/8f75378e1a83b323f9ed06248333c383e7dac614c2f95e1419965cb91693/mmh3-4.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d82eb4defa245e02bb0b0dc4f1e7ee284f8d212633389c91f7fba99ba993f0a2", size = 30144 },
+    { url = "https://files.pythonhosted.org/packages/3e/50/5e36c1945bd83e780a37361fc1999fc4c5a59ecc10a373557fdf0e58eb1f/mmh3-4.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba245e94b8d54765e14c2d7b6214e832557e7856d5183bc522e17884cab2f45d", size = 69094 },
+    { url = "https://files.pythonhosted.org/packages/70/c7/6ae37e7519a938226469476b84bcea2650e2a2cc7a848e6a206ea98ecee3/mmh3-4.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb04e2feeabaad6231e89cd43b3d01a4403579aa792c9ab6fdeef45cc58d4ec0", size = 72611 },
+    { url = "https://files.pythonhosted.org/packages/5e/47/6613f69f57f1e5045e66b22fae9c2fb39ef754c455805d3917f6073e316e/mmh3-4.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e3b1a27def545ce11e36158ba5d5390cdbc300cfe456a942cc89d649cf7e3b2", size = 71462 },
+    { url = "https://files.pythonhosted.org/packages/e0/0a/e423db18ce7b479c4b96381a112b443f0985c611de420f95c58a9f934080/mmh3-4.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce0ab79ff736d7044e5e9b3bfe73958a55f79a4ae672e6213e92492ad5e734d5", size = 66165 },
+    { url = "https://files.pythonhosted.org/packages/4c/7b/bfeb68bee5bddc8baf7ef630b93edc0a533202d84eb076dbb6c77e7e5fd5/mmh3-4.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b02268be6e0a8eeb8a924d7db85f28e47344f35c438c1e149878bb1c47b1cd3", size = 68088 },
+    { url = "https://files.pythonhosted.org/packages/d4/a6/b82e30143997c05776887f5177f724e3b714aa7e7346fbe2ec70f52abcd0/mmh3-4.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:deb887f5fcdaf57cf646b1e062d56b06ef2f23421c80885fce18b37143cba828", size = 76241 },
+    { url = "https://files.pythonhosted.org/packages/6c/60/a3d5872cf7610fcb13e36c472476020c5cf217b23c092bad452eb7784407/mmh3-4.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99dd564e9e2b512eb117bd0cbf0f79a50c45d961c2a02402787d581cec5448d5", size = 74538 },
+    { url = "https://files.pythonhosted.org/packages/f6/d5/742173a94c78f4edab71c04097f6f9150c47f8fd034d592f5f34a9444719/mmh3-4.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:08373082dfaa38fe97aa78753d1efd21a1969e51079056ff552e687764eafdfe", size = 81793 },
+    { url = "https://files.pythonhosted.org/packages/d0/7a/a1db0efe7c67b761d83be3d50e35ef26628ef56b3b8bc776d07412ee8b16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:54b9c6a2ea571b714e4fe28d3e4e2db37abfd03c787a58074ea21ee9a8fd1740", size = 78217 },
+    { url = "https://files.pythonhosted.org/packages/b3/78/1ff8da7c859cd09704e2f500588d171eda9688fcf6f29e028ef261262a16/mmh3-4.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a7b1edf24c69e3513f879722b97ca85e52f9032f24a52284746877f6a7304086", size = 77052 },
+    { url = "https://files.pythonhosted.org/packages/ed/c7/cf16ace81fc9fbe54a75c914306252af26c6ea485366bb3b579bf6e3dbb8/mmh3-4.1.0-cp312-cp312-win32.whl", hash = "sha256:411da64b951f635e1e2284b71d81a5a83580cea24994b328f8910d40bed67276", size = 31277 },
+    { url = "https://files.pythonhosted.org/packages/d2/0b/b3b1637dca9414451edf287fd91e667e7231d5ffd7498137fe011951fc0a/mmh3-4.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:bebc3ecb6ba18292e3d40c8712482b4477abd6981c2ebf0e60869bd90f8ac3a9", size = 31318 },
+    { url = "https://files.pythonhosted.org/packages/dd/6c/c0f06040c58112ccbd0df989055ede98f7c1a1f392dc6a3fc63ec6c124ec/mmh3-4.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:168473dd608ade6a8d2ba069600b35199a9af837d96177d3088ca91f2b3798e3", size = 30147 },
+]
+
+[[package]]
+name = "monotonic"
+version = "1.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ea/ca/8e91948b782ddfbd194f323e7e7d9ba12e5877addf04fb2bf8fca38e86ac/monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7", size = 7615 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9a/67/7e8406a29b6c45be7af7740456f7f37025f0506ae2e05fb9009a53946860/monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c", size = 8154 },
+]
+
+[[package]]
+name = "mpmath"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198 },
+]
+
+[[package]]
+name = "msoffcrypto-tool"
+version = "5.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "cryptography" },
+    { name = "olefile" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d2/b7/0fd6573157e0ec60c0c470e732ab3322fba4d2834fd24e1088d670522a01/msoffcrypto_tool-5.4.2.tar.gz", hash = "sha256:44b545adba0407564a0cc3d6dde6ca36b7c0fdf352b85bca51618fa1d4817370", size = 41183 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/03/54/7f6d3d9acad083dae8c22d9ab483b657359a1bf56fee1d7af88794677707/msoffcrypto_tool-5.4.2-py3-none-any.whl", hash = "sha256:274fe2181702d1e5a107ec1b68a4c9fea997a44972ae1cc9ae0cb4f6a50fef0e", size = 48713 },
+]
+
+[[package]]
+name = "multidict"
+version = "6.0.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/79/722ca999a3a09a63b35aac12ec27dfa8e5bb3a38b0f857f7a1a209a88836/multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da", size = 59867 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5f/da/b10ea65b850b54f44a6479177c6987f456bc2d38f8dc73009b78afcf0ede/multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba", size = 50815 },
+    { url = "https://files.pythonhosted.org/packages/21/db/3403263f158b0bc7b0d4653766d71cb39498973f2042eead27b2e9758782/multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e", size = 30269 },
+    { url = "https://files.pythonhosted.org/packages/02/c1/b15ecceb6ffa5081ed2ed450aea58d65b0e0358001f2b426705f9f41f4c2/multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd", size = 30500 },
+    { url = "https://files.pythonhosted.org/packages/3f/e1/7fdd0f39565df3af87d6c2903fb66a7d529fbd0a8a066045d7a5b6ad1145/multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3", size = 130751 },
+    { url = "https://files.pythonhosted.org/packages/76/bc/9f593f9e38c6c09bbf0344b56ad67dd53c69167937c2edadee9719a5e17d/multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf", size = 138185 },
+    { url = "https://files.pythonhosted.org/packages/28/32/d7799a208701d537b92705f46c777ded812a6dc139c18d8ed599908f6b1c/multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29", size = 133585 },
+    { url = "https://files.pythonhosted.org/packages/52/ec/be54a3ad110f386d5bd7a9a42a4ff36b3cd723ebe597f41073a73ffa16b8/multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed", size = 128684 },
+    { url = "https://files.pythonhosted.org/packages/36/e1/a680eabeb71e25d4733276d917658dfa1cd3a99b1223625dbc247d266c98/multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733", size = 120994 },
+    { url = "https://files.pythonhosted.org/packages/ef/08/08f4f44a8a43ea4cee13aa9cdbbf4a639af8db49310a0637ca389c4cf817/multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f", size = 159689 },
+    { url = "https://files.pythonhosted.org/packages/aa/a9/46cdb4cb40bbd4b732169413f56b04a6553460b22bd914f9729c9ba63761/multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4", size = 150611 },
+    { url = "https://files.pythonhosted.org/packages/e9/32/35668bb3e6ab2f12f4e4f7f4000f72f714882a94f904d4c3633fbd036753/multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1", size = 164444 },
+    { url = "https://files.pythonhosted.org/packages/fa/10/f1388a91552af732d8ec48dab928abc209e732767e9e8f92d24c3544353c/multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc", size = 160158 },
+    { url = "https://files.pythonhosted.org/packages/14/c3/f602601f1819983e018156e728e57b3f19726cb424b543667faab82f6939/multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e", size = 156072 },
+    { url = "https://files.pythonhosted.org/packages/82/a6/0290af8487326108c0d03d14f8a0b8b1001d71e4494df5f96ab0c88c0b88/multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c", size = 25731 },
+    { url = "https://files.pythonhosted.org/packages/88/aa/ea217cb18325aa05cb3e3111c19715f1e97c50a4a900cbc20e54648de5f5/multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea", size = 28176 },
+    { url = "https://files.pythonhosted.org/packages/90/9c/7fda9c0defa09538c97b1f195394be82a1f53238536f70b32eb5399dfd4e/multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e", size = 49575 },
+    { url = "https://files.pythonhosted.org/packages/be/21/d6ca80dd1b9b2c5605ff7475699a8ff5dc6ea958cd71fb2ff234afc13d79/multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b", size = 29638 },
+    { url = "https://files.pythonhosted.org/packages/9c/18/9565f32c19d186168731e859692dfbc0e98f66a1dcf9e14d69c02a78b75a/multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5", size = 29874 },
+    { url = "https://files.pythonhosted.org/packages/4e/4e/3815190e73e6ef101b5681c174c541bf972a1b064e926e56eea78d06e858/multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450", size = 129914 },
+    { url = "https://files.pythonhosted.org/packages/0c/08/bb47f886457e2259aefc10044e45c8a1b62f0c27228557e17775869d0341/multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496", size = 134589 },
+    { url = "https://files.pythonhosted.org/packages/d5/2f/952f79b5f0795cf4e34852fc5cf4dfda6166f63c06c798361215b69c131d/multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a", size = 133259 },
+    { url = "https://files.pythonhosted.org/packages/24/1f/af976383b0b772dd351210af5b60ff9927e3abb2f4a103e93da19a957da0/multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226", size = 130779 },
+    { url = "https://files.pythonhosted.org/packages/fc/b1/b0a7744be00b0f5045c7ed4e4a6b8ee6bde4672b2c620474712299df5979/multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271", size = 120125 },
+    { url = "https://files.pythonhosted.org/packages/d0/bf/2a1d667acf11231cdf0b97a6cd9f30e7a5cf847037b5cf6da44884284bd0/multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb", size = 167095 },
+    { url = "https://files.pythonhosted.org/packages/5e/e8/ad6ee74b1a2050d3bc78f566dabcc14c8bf89cbe87eecec866c011479815/multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef", size = 155823 },
+    { url = "https://files.pythonhosted.org/packages/45/7c/06926bb91752c52abca3edbfefac1ea90d9d1bc00c84d0658c137589b920/multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24", size = 170233 },
+    { url = "https://files.pythonhosted.org/packages/3c/29/3dd36cf6b9c5abba8b97bba84eb499a168ba59c3faec8829327b3887d123/multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6", size = 169035 },
+    { url = "https://files.pythonhosted.org/packages/60/47/9a0f43470c70bbf6e148311f78ef5a3d4996b0226b6d295bdd50fdcfe387/multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda", size = 166229 },
+    { url = "https://files.pythonhosted.org/packages/1d/23/c1b7ae7a0b8a3e08225284ef3ecbcf014b292a3ee821bc4ed2185fd4ce7d/multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5", size = 25840 },
+    { url = "https://files.pythonhosted.org/packages/4a/68/66fceb758ad7a88993940dbdf3ac59911ba9dc46d7798bf6c8652f89f853/multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556", size = 27905 },
+    { url = "https://files.pythonhosted.org/packages/fa/a2/17e1e23c6be0a916219c5292f509360c345b5fa6beeb50d743203c27532c/multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7", size = 9729 },
+]
+
+[[package]]
+name = "multiprocess"
+version = "0.70.16"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "dill" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b5/ae/04f39c5d0d0def03247c2893d6f2b83c136bf3320a2154d7b8858f2ba72d/multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1", size = 1772603 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/50/15/b56e50e8debaf439f44befec5b2af11db85f6e0f344c3113ae0be0593a91/multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a", size = 143519 },
+    { url = "https://files.pythonhosted.org/packages/0a/7d/a988f258104dcd2ccf1ed40fdc97e26c4ac351eeaf81d76e266c52d84e2f/multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e", size = 146741 },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
+]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 },
+]
+
+[[package]]
+name = "networkx"
+version = "3.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/04/e6/b164f94c869d6b2c605b5128b7b0cfe912795a87fc90e78533920001f3ec/networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9", size = 2126579 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/38/e9/5f72929373e1a0e8d142a130f3f97e6ff920070f87f91c4e13e40e0fba5a/networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2", size = 1702396 },
+]
+
+[[package]]
+name = "ninja"
+version = "1.11.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/37/2c/d717d13a413d6f7579cdaa1e28e6e2c98de95461549b08d311c8a5bf4c51/ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c", size = 132392 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3d/6e/04ed11bb244039908f6f212cb5f3e97933e238655248e4ce307c1687ba1f/ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee", size = 270611 },
+    { url = "https://files.pythonhosted.org/packages/2c/52/0e5423311eb9939b6f9354059a6d88a6211eb4fa1c7a4ef303ecee1c1fe0/ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877", size = 324256 },
+    { url = "https://files.pythonhosted.org/packages/6d/92/8d7aebd4430ab5ff65df2bfee6d5745f95c004284db2d8ca76dcbfd9de47/ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b", size = 307194 },
+    { url = "https://files.pythonhosted.org/packages/01/c8/96424839fd127b4492229acf50763ed9940d864ca35d17d151934aef1f6f/ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a", size = 155643 },
+    { url = "https://files.pythonhosted.org/packages/6b/fa/5ca8e65a98cdb9a71d4f1e38cac7bd757bbb9555a5aef5a4d293aa890e5c/ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd", size = 179538 },
+    { url = "https://files.pythonhosted.org/packages/45/ef/60086f02cbc6882da00a02c81d645cefd8d2d65b01fade41b873d8dd85a2/ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef", size = 156217 },
+    { url = "https://files.pythonhosted.org/packages/1c/00/2fd13ac6aafdb566f00d6b541101fca54e58ae58bf96c00f9780df019607/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea", size = 372069 },
+    { url = "https://files.pythonhosted.org/packages/ad/5d/6e97c8a25167d4867694c7fb0b9bdbc9b096d6479c8e56c5bd41b49613f6/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249", size = 418859 },
+    { url = "https://files.pythonhosted.org/packages/43/78/34af88d753389a9412438d16142c77e587e0d69152faf0bbf99701063dd8/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205", size = 419782 },
+    { url = "https://files.pythonhosted.org/packages/3b/74/de0633f8bced3b188942fca64a950e8f2206c60c10c97af465b356ae9b25/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f", size = 415476 },
+    { url = "https://files.pythonhosted.org/packages/9a/f3/3e4a56ff77739d1582749b93497bdebf11e003fbc7a66363ef6c772ebd0a/ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885", size = 379229 },
+    { url = "https://files.pythonhosted.org/packages/c5/ee/53df34fcc9c0b1db62b2f2e2c848e28d9354e1c7f0dce029ee50b16ca157/ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082", size = 265049 },
+    { url = "https://files.pythonhosted.org/packages/b6/2f/a3bc50fa63fc4fe9348e15b53dc8c87febfd4e0c660fcf250c4b19a3aa3b/ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1", size = 312958 },
+    { url = "https://files.pythonhosted.org/packages/73/2a/f5b7b3b7ecd5cf4e31375580bf5c6a01a328ed1ebdfff90fab463e3f4bc7/ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a", size = 272686 },
+]
+
+[[package]]
+name = "nltk"
+version = "3.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "joblib" },
+    { name = "regex" },
+    { name = "tqdm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3c/87/db8be88ad32c2d042420b6fd9ffd4a149f9a0d7f0e86b3f543be2eeeedd2/nltk-3.9.1.tar.gz", hash = "sha256:87d127bd3de4bd89a4f81265e5fa59cb1b199b27440175370f7417d2bc7ae868", size = 2904691 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4d/66/7d9e26593edda06e8cb531874633f7c2372279c3b0f46235539fe546df8b/nltk-3.9.1-py3-none-any.whl", hash = "sha256:4fa26829c5b00715afe3061398a8989dc643b92ce7dd93fb4585a70930d168a1", size = 1505442 },
+]
+
+[[package]]
+name = "numpy"
+version = "1.26.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 },
+    { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 },
+    { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 },
+    { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 },
+    { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 },
+    { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 },
+    { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 },
+    { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 },
+    { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 },
+    { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 },
+    { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 },
+    { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 },
+    { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 },
+    { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 },
+    { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 },
+    { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 },
+]
+
+[[package]]
+name = "nvidia-cublas-cu12"
+version = "12.1.3.1"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/37/6d/121efd7382d5b0284239f4ab1fc1590d86d34ed4a4a2fdb13b30ca8e5740/nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728", size = 410594774 },
+    { url = "https://files.pythonhosted.org/packages/c5/ef/32a375b74bea706c93deea5613552f7c9104f961b21df423f5887eca713b/nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906", size = 439918445 },
+]
+
+[[package]]
+name = "nvidia-cuda-cupti-cu12"
+version = "12.1.105"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7e/00/6b218edd739ecfc60524e585ba8e6b00554dd908de2c9c66c1af3e44e18d/nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e", size = 14109015 },
+    { url = "https://files.pythonhosted.org/packages/d0/56/0021e32ea2848c24242f6b56790bd0ccc8bf99f973ca790569c6ca028107/nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4", size = 10154340 },
+]
+
+[[package]]
+name = "nvidia-cuda-nvrtc-cu12"
+version = "12.1.105"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b6/9f/c64c03f49d6fbc56196664d05dba14e3a561038a81a638eeb47f4d4cfd48/nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2", size = 23671734 },
+    { url = "https://files.pythonhosted.org/packages/ad/1d/f76987c4f454eb86e0b9a0e4f57c3bf1ac1d13ad13cd1a4da4eb0e0c0ce9/nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed", size = 19331863 },
+]
+
+[[package]]
+name = "nvidia-cuda-runtime-cu12"
+version = "12.1.105"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/eb/d5/c68b1d2cdfcc59e72e8a5949a37ddb22ae6cade80cd4a57a84d4c8b55472/nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40", size = 823596 },
+    { url = "https://files.pythonhosted.org/packages/9f/e2/7a2b4b5064af56ea8ea2d8b2776c0f2960d95c88716138806121ae52a9c9/nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344", size = 821226 },
+]
+
+[[package]]
+name = "nvidia-cudnn-cu12"
+version = "9.1.0.70"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741 },
+    { url = "https://files.pythonhosted.org/packages/3f/d0/f90ee6956a628f9f04bf467932c0a25e5a7e706a684b896593c06c82f460/nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a", size = 679925892 },
+]
+
+[[package]]
+name = "nvidia-cufft-cu12"
+version = "11.0.2.54"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/86/94/eb540db023ce1d162e7bea9f8f5aa781d57c65aed513c33ee9a5123ead4d/nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56", size = 121635161 },
+    { url = "https://files.pythonhosted.org/packages/f7/57/7927a3aa0e19927dfed30256d1c854caf991655d847a4e7c01fe87e3d4ac/nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253", size = 121344196 },
+]
+
+[[package]]
+name = "nvidia-curand-cu12"
+version = "10.3.2.106"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/44/31/4890b1c9abc496303412947fc7dcea3d14861720642b49e8ceed89636705/nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0", size = 56467784 },
+    { url = "https://files.pythonhosted.org/packages/5c/97/4c9c7c79efcdf5b70374241d48cf03b94ef6707fd18ea0c0f53684931d0b/nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a", size = 55995813 },
+]
+
+[[package]]
+name = "nvidia-cusolver-cu12"
+version = "11.4.5.107"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')" },
+    { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')" },
+    { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd", size = 124161928 },
+    { url = "https://files.pythonhosted.org/packages/b8/80/8fca0bf819122a631c3976b6fc517c1b10741b643b94046bd8dd451522c5/nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5", size = 121643081 },
+]
+
+[[package]]
+name = "nvidia-cusparse-cu12"
+version = "12.1.0.106"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and platform_system != 'Darwin') or (platform_system != 'Darwin' and platform_system != 'Linux')" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c", size = 195958278 },
+    { url = "https://files.pythonhosted.org/packages/0f/95/48fdbba24c93614d1ecd35bc6bdc6087bd17cbacc3abc4b05a9c2a1ca232/nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a", size = 195414588 },
+]
+
+[[package]]
+name = "nvidia-nccl-cu12"
+version = "2.20.5"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c1/bb/d09dda47c881f9ff504afd6f9ca4f502ded6d8fc2f572cacc5e39da91c28/nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1fc150d5c3250b170b29410ba682384b14581db722b2531b0d8d33c595f33d01", size = 176238458 },
+    { url = "https://files.pythonhosted.org/packages/4b/2a/0a131f572aa09f741c30ccd45a8e56316e8be8dfc7bc19bf0ab7cfef7b19/nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56", size = 176249402 },
+]
+
+[[package]]
+name = "nvidia-nvjitlink-cu12"
+version = "12.6.20"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/81/b3/e456a1b2d499bb84bdc6670bfbcf41ff3bac58bd2fae6880d62834641558/nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_aarch64.whl", hash = "sha256:84fb38465a5bc7c70cbc320cfd0963eb302ee25a5e939e9f512bbba55b6072fb", size = 19252608 },
+    { url = "https://files.pythonhosted.org/packages/59/65/7ff0569494fbaea45ad2814972cc88da843d53cc96eb8554fcd0908941d9/nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_x86_64.whl", hash = "sha256:562ab97ea2c23164823b2a89cb328d01d45cb99634b8c65fe7cd60d14562bd79", size = 19724950 },
+    { url = "https://files.pythonhosted.org/packages/cb/ef/8f96c82e1cfcf6d5b770f7b043c3cc24841fc247b37629a7cc643dbf72a1/nvidia_nvjitlink_cu12-12.6.20-py3-none-win_amd64.whl", hash = "sha256:ed3c43a17f37b0c922a919203d2d36cbef24d41cc3e6b625182f8b58203644f6", size = 162012830 },
+]
+
+[[package]]
+name = "nvidia-nvtx-cu12"
+version = "12.1.105"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/da/d3/8057f0587683ed2fcd4dbfbdfdfa807b9160b809976099d36b8f60d08f03/nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5", size = 99138 },
+    { url = "https://files.pythonhosted.org/packages/b8/d7/bd7cb2d95ac6ac6e8d05bfa96cdce69619f1ef2808e072919044c2d47a8c/nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82", size = 66307 },
+]
+
+[[package]]
+name = "oauthlib"
+version = "3.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6d/fa/fbf4001037904031639e6bfbfc02badfc7e12f137a8afa254df6c4c8a670/oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918", size = 177352 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 },
+]
+
+[[package]]
+name = "olefile"
+version = "0.47"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/69/1b/077b508e3e500e1629d366249c3ccb32f95e50258b231705c09e3c7a4366/olefile-0.47.zip", hash = "sha256:599383381a0bf3dfbd932ca0ca6515acd174ed48870cbf7fee123d698c192c1c", size = 112240 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/17/d3/b64c356a907242d719fc668b71befd73324e47ab46c8ebbbede252c154b2/olefile-0.47-py2.py3-none-any.whl", hash = "sha256:543c7da2a7adadf21214938bb79c83ea12b473a4b6ee4ad4bf854e7715e13d1f", size = 114565 },
+]
+
+[[package]]
+name = "oletools"
+version = "0.60.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorclass" },
+    { name = "easygui" },
+    { name = "msoffcrypto-tool", marker = "platform_python_implementation != 'PyPy' or (platform_system != 'Darwin' and platform_system != 'Windows')" },
+    { name = "olefile" },
+    { name = "pcodedmp" },
+    { name = "pyparsing" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5c/2f/037f40e44706d542b94a2312ccc33ee2701ebfc9a83b46b55263d49ce55a/oletools-0.60.2.zip", hash = "sha256:ad452099f4695ffd8855113f453348200d195ee9fa341a09e197d66ee7e0b2c3", size = 3433750 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ac/ff/05257b7183279b80ecec6333744de23f48f0faeeba46c93e6d13ce835515/oletools-0.60.2-py2.py3-none-any.whl", hash = "sha256:72ad8bd748fd0c4e7b5b4733af770d11543ebb2bf2697455f99f975fcd50cc96", size = 989449 },
+]
+
+[[package]]
+name = "onnxruntime"
+version = "1.19.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "coloredlogs" },
+    { name = "flatbuffers" },
+    { name = "numpy" },
+    { name = "packaging" },
+    { name = "protobuf" },
+    { name = "sympy" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/80/16/fc200316725d04731d8ffc5d2105887a1e400d760b0c7fd464744335cd29/onnxruntime-1.19.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:a2b53b3c287cd933e5eb597273926e899082d8c84ab96e1b34035764a1627e17", size = 16778356 },
+    { url = "https://files.pythonhosted.org/packages/cc/3c/ff2ecf2a842822bc5e9758747bdfd4163c53af470421f07afd6cba1ced7d/onnxruntime-1.19.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e94984663963e74fbb468bde9ec6f19dcf890b594b35e249c4dc8789d08993c5", size = 11492628 },
+    { url = "https://files.pythonhosted.org/packages/fa/ca/769da06e76b14a315a1effa5b01d906963379495cd82c00b5023be4c3e61/onnxruntime-1.19.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f379d1f050cfb55ce015d53727b78ee362febc065c38eed81512b22b757da73", size = 13172071 },
+    { url = "https://files.pythonhosted.org/packages/75/7c/5a7e3fd98f9af3c43d6073c38afff8c18d201a72d1eba77c93dd230b8501/onnxruntime-1.19.0-cp311-cp311-win32.whl", hash = "sha256:4ccb48faea02503275ae7e79e351434fc43c294c4cb5c4d8bcb7479061396614", size = 9589924 },
+    { url = "https://files.pythonhosted.org/packages/78/86/fd21288f9e4096d9c27bd0f221cb61719baa97d5e187549a9f0e84e386ae/onnxruntime-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:9cdc8d311289a84e77722de68bd22b8adfb94eea26f4be6f9e017350faac8b18", size = 11083172 },
+    { url = "https://files.pythonhosted.org/packages/d1/3c/7cd126254658f0371fadf8651957387d7f743b1b85545e3b783a7f717215/onnxruntime-1.19.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:1b59eaec1be9a8613c5fdeaafe67f73a062edce3ac03bbbdc9e2d98b58a30617", size = 16789643 },
+    { url = "https://files.pythonhosted.org/packages/bf/6e/aae5420a45cbbcacef4c65f70067c11bed7cbb8fda12e0728f37d29746e5/onnxruntime-1.19.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be4144d014a4b25184e63ce7a463a2e7796e2f3df931fccc6a6aefa6f1365dc5", size = 11483896 },
+    { url = "https://files.pythonhosted.org/packages/e6/0f/ad2ec6d490d9cb4ea82dd46382396827cb8ca9a469a56368fc7ef2fb52a4/onnxruntime-1.19.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10d7e7d4ca7021ce7f29a66dbc6071addf2de5839135339bd855c6d9c2bba371", size = 13177713 },
+    { url = "https://files.pythonhosted.org/packages/de/4e/059cae46e48d183ac9b1d0be7ece1c5878711f4a31a206a9dcb34a89e3f5/onnxruntime-1.19.0-cp312-cp312-win32.whl", hash = "sha256:87f2c58b577a1fb31dc5d92b647ecc588fd5f1ea0c3ad4526f5f80a113357c8d", size = 9591661 },
+    { url = "https://files.pythonhosted.org/packages/a0/ed/7ac157855cd2135ba894836ce4d027830b78d71832c9e658046e5b1b3d23/onnxruntime-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:8a1f50d49676d7b69566536ff039d9e4e95fc482a55673719f46528218ecbb94", size = 11084335 },
+]
+
+[[package]]
+name = "open-webui"
+version = "0.3.29"
+source = { editable = "." }
+dependencies = [
+    { name = "aiohttp" },
+    { name = "alembic" },
+    { name = "anthropic" },
+    { name = "apscheduler" },
+    { name = "argon2-cffi" },
+    { name = "authlib" },
+    { name = "bcrypt" },
+    { name = "black" },
+    { name = "boto3" },
+    { name = "chromadb" },
+    { name = "colbert-ai" },
+    { name = "docker" },
+    { name = "docx2txt" },
+    { name = "duckduckgo-search" },
+    { name = "einops" },
+    { name = "extract-msg" },
+    { name = "fake-useragent" },
+    { name = "fastapi" },
+    { name = "faster-whisper" },
+    { name = "flask" },
+    { name = "flask-cors" },
+    { name = "fpdf2" },
+    { name = "google-generativeai" },
+    { name = "langchain" },
+    { name = "langchain-chroma" },
+    { name = "langchain-community" },
+    { name = "langfuse" },
+    { name = "markdown" },
+    { name = "nltk" },
+    { name = "openai" },
+    { name = "opencv-python-headless" },
+    { name = "openpyxl" },
+    { name = "pandas" },
+    { name = "passlib", extra = ["bcrypt"] },
+    { name = "peewee" },
+    { name = "peewee-migrate" },
+    { name = "psutil" },
+    { name = "psycopg2-binary" },
+    { name = "pydantic" },
+    { name = "pydub" },
+    { name = "pyjwt", extra = ["crypto"] },
+    { name = "pymilvus" },
+    { name = "pymongo" },
+    { name = "pymysql" },
+    { name = "pypandoc" },
+    { name = "pypdf" },
+    { name = "pytest" },
+    { name = "pytest-docker" },
+    { name = "python-jose" },
+    { name = "python-multipart" },
+    { name = "python-pptx" },
+    { name = "python-socketio" },
+    { name = "pytube" },
+    { name = "pyxlsb" },
+    { name = "rank-bm25" },
+    { name = "rapidocr-onnxruntime" },
+    { name = "redis" },
+    { name = "requests" },
+    { name = "sentence-transformers" },
+    { name = "sqlalchemy" },
+    { name = "tiktoken" },
+    { name = "unstructured" },
+    { name = "uvicorn", extra = ["standard"] },
+    { name = "validators" },
+    { name = "xlrd" },
+    { name = "youtube-transcript-api" },
+]
+
+[package.metadata]
+requires-dist = [
+    { name = "aiohttp", specifier = "==3.10.5" },
+    { name = "alembic", specifier = "==1.13.2" },
+    { name = "anthropic" },
+    { name = "apscheduler", specifier = "==3.10.4" },
+    { name = "argon2-cffi", specifier = "==23.1.0" },
+    { name = "authlib", specifier = "==1.3.2" },
+    { name = "bcrypt", specifier = "==4.2.0" },
+    { name = "black", specifier = "==24.8.0" },
+    { name = "boto3", specifier = "==1.35.0" },
+    { name = "chromadb", specifier = "==0.5.5" },
+    { name = "colbert-ai", specifier = "==0.2.21" },
+    { name = "docker", specifier = "~=7.1.0" },
+    { name = "docx2txt", specifier = "==0.8" },
+    { name = "duckduckgo-search", specifier = "~=6.2.11" },
+    { name = "einops", specifier = "==0.8.0" },
+    { name = "extract-msg" },
+    { name = "fake-useragent", specifier = "==1.5.1" },
+    { name = "fastapi", specifier = "==0.111.0" },
+    { name = "faster-whisper", specifier = "==1.0.3" },
+    { name = "flask", specifier = "==3.0.3" },
+    { name = "flask-cors", specifier = "==5.0.0" },
+    { name = "fpdf2", specifier = "==2.7.9" },
+    { name = "google-generativeai", specifier = "==0.7.2" },
+    { name = "langchain", specifier = "==0.2.15" },
+    { name = "langchain-chroma", specifier = "==0.1.2" },
+    { name = "langchain-community", specifier = "==0.2.12" },
+    { name = "langfuse", specifier = "==2.44.0" },
+    { name = "markdown", specifier = "==3.7" },
+    { name = "nltk", specifier = "==3.9.1" },
+    { name = "openai" },
+    { name = "opencv-python-headless", specifier = "==4.10.0.84" },
+    { name = "openpyxl", specifier = "==3.1.5" },
+    { name = "pandas", specifier = "==2.2.2" },
+    { name = "passlib", extras = ["bcrypt"], specifier = "==1.7.4" },
+    { name = "peewee", specifier = "==3.17.6" },
+    { name = "peewee-migrate", specifier = "==1.12.2" },
+    { name = "psutil" },
+    { name = "psycopg2-binary", specifier = "==2.9.9" },
+    { name = "pydantic", specifier = "==2.8.2" },
+    { name = "pydub" },
+    { name = "pyjwt", extras = ["crypto"], specifier = "==2.9.0" },
+    { name = "pymilvus", specifier = "==2.4.6" },
+    { name = "pymongo" },
+    { name = "pymysql", specifier = "==1.1.1" },
+    { name = "pypandoc", specifier = "==1.13" },
+    { name = "pypdf", specifier = "==4.3.1" },
+    { name = "pytest", specifier = "~=8.2.2" },
+    { name = "pytest-docker", specifier = "~=3.1.1" },
+    { name = "python-jose", specifier = "==3.3.0" },
+    { name = "python-multipart", specifier = "==0.0.9" },
+    { name = "python-pptx", specifier = "==1.0.0" },
+    { name = "python-socketio", specifier = "==5.11.3" },
+    { name = "pytube", specifier = "==15.0.0" },
+    { name = "pyxlsb", specifier = "==1.0.10" },
+    { name = "rank-bm25", specifier = "==0.2.2" },
+    { name = "rapidocr-onnxruntime", specifier = "==1.3.24" },
+    { name = "redis" },
+    { name = "requests", specifier = "==2.32.3" },
+    { name = "sentence-transformers", specifier = "==3.0.1" },
+    { name = "sqlalchemy", specifier = "==2.0.32" },
+    { name = "tiktoken" },
+    { name = "unstructured", specifier = "==0.15.9" },
+    { name = "uvicorn", extras = ["standard"], specifier = "==0.30.6" },
+    { name = "validators", specifier = "==0.33.0" },
+    { name = "xlrd", specifier = "==2.0.1" },
+    { name = "youtube-transcript-api", specifier = "==0.6.2" },
+]
+
+[[package]]
+name = "openai"
+version = "1.42.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+    { name = "distro" },
+    { name = "httpx" },
+    { name = "jiter" },
+    { name = "pydantic" },
+    { name = "sniffio" },
+    { name = "tqdm" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8c/1f/310b0b5efb6178ad9f9ca4a80b2ead3cb7cbc16a1b843941bcf1c52dd884/openai-1.42.0.tar.gz", hash = "sha256:c9d31853b4e0bc2dc8bd08003b462a006035655a701471695d0bfdc08529cde3", size = 290549 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/cf/9e/d77569d06e365f093977d94f305a395b7ac5ccd746016a2e8dd34c4e20c1/openai-1.42.0-py3-none-any.whl", hash = "sha256:dc91e0307033a4f94931e5d03cc3b29b9717014ad5e73f9f2051b6cb5eda4d80", size = 362858 },
+]
+
+[[package]]
+name = "opencv-python"
+version = "4.10.0.84"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/b70a2d9ab205110d715906fc8ec83fbb00404aeb3a37a0654fdb68eb0c8c/opencv-python-4.10.0.84.tar.gz", hash = "sha256:72d234e4582e9658ffea8e9cae5b63d488ad06994ef12d81dc303b17472f3526", size = 95103981 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/66/82/564168a349148298aca281e342551404ef5521f33fba17b388ead0a84dc5/opencv_python-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:fc182f8f4cda51b45f01c64e4cbedfc2f00aff799debebc305d8d0210c43f251", size = 54835524 },
+    { url = "https://files.pythonhosted.org/packages/64/4a/016cda9ad7cf18c58ba074628a4eaae8aa55f3fd06a266398cef8831a5b9/opencv_python-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:71e575744f1d23f79741450254660442785f45a0797212852ee5199ef12eed98", size = 56475426 },
+    { url = "https://files.pythonhosted.org/packages/81/e4/7a987ebecfe5ceaf32db413b67ff18eb3092c598408862fff4d7cc3fd19b/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09a332b50488e2dda866a6c5573ee192fe3583239fb26ff2f7f9ceb0bc119ea6", size = 41746971 },
+    { url = "https://files.pythonhosted.org/packages/3f/a4/d2537f47fd7fcfba966bd806e3ec18e7ee1681056d4b0a9c8d983983e4d5/opencv_python-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ace140fc6d647fbe1c692bcb2abce768973491222c067c131d80957c595b71f", size = 62548253 },
+    { url = "https://files.pythonhosted.org/packages/1e/39/bbf57e7b9dab623e8773f6ff36385456b7ae7fa9357a5e53db732c347eac/opencv_python-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:2db02bb7e50b703f0a2d50c50ced72e95c574e1e5a0bb35a8a86d0b35c98c236", size = 28737688 },
+    { url = "https://files.pythonhosted.org/packages/ec/6c/fab8113424af5049f85717e8e527ca3773299a3c6b02506e66436e19874f/opencv_python-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:32dbbd94c26f611dc5cc6979e6b7aa1f55a64d6b463cc1dcd3c95505a63e48fe", size = 38842521 },
+]
+
+[[package]]
+name = "opencv-python-headless"
+version = "4.10.0.84"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/2f/7e/d20f68a5f1487adf19d74378d349932a386b1ece3be9be9915e5986db468/opencv-python-headless-4.10.0.84.tar.gz", hash = "sha256:f2017c6101d7c2ef8d7bc3b414c37ff7f54d64413a1847d89970b6b7069b4e1a", size = 95117755 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1c/9b/583c8d9259f6fc19413f83fd18dd8e6cbc8eefb0b4dc6da52dd151fe3272/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:a4f4bcb07d8f8a7704d9c8564c224c8b064c63f430e95b61ac0bffaa374d330e", size = 54835657 },
+    { url = "https://files.pythonhosted.org/packages/c0/7b/b4c67f5dad7a9a61c47f7a39e4050e8a4628bd64b3c3daaeb755d759f928/opencv_python_headless-4.10.0.84-cp37-abi3-macosx_12_0_x86_64.whl", hash = "sha256:5ae454ebac0eb0a0b932e3406370aaf4212e6a3fdb5038cc86c7aea15a6851da", size = 56475470 },
+    { url = "https://files.pythonhosted.org/packages/91/61/f838ce2046f3ec3591ea59ea3549085e399525d3b4558c4ed60b55ed88c0/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46071015ff9ab40fccd8a163da0ee14ce9846349f06c6c8c0f2870856ffa45db", size = 29329705 },
+    { url = "https://files.pythonhosted.org/packages/d1/09/248f86a404567303cdf120e4a301f389b68e3b18e5c0cc428de327da609c/opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:377d08a7e48a1405b5e84afcbe4798464ce7ee17081c1c23619c8b398ff18295", size = 49858781 },
+    { url = "https://files.pythonhosted.org/packages/30/c0/66f88d58500e990a9a0a5c06f98862edf1d0a3a430781218a8c193948438/opencv_python_headless-4.10.0.84-cp37-abi3-win32.whl", hash = "sha256:9092404b65458ed87ce932f613ffbb1106ed2c843577501e5768912360fc50ec", size = 28675298 },
+    { url = "https://files.pythonhosted.org/packages/26/d0/22f68eb23eea053a31655960f133c0be9726c6a881547e6e9e7e2a946c4f/opencv_python_headless-4.10.0.84-cp37-abi3-win_amd64.whl", hash = "sha256:afcf28bd1209dd58810d33defb622b325d3cbe49dcd7a43a902982c33e5fad05", size = 38754031 },
+]
+
+[[package]]
+name = "openpyxl"
+version = "3.1.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "et-xmlfile" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910 },
+]
+
+[[package]]
+name = "opentelemetry-api"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "deprecated" },
+    { name = "importlib-metadata" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/d4/e9a0ddef6eed086c96e8265d864a46da099611b7be153b0cfb63fd47e1b4/opentelemetry_api-1.26.0.tar.gz", hash = "sha256:2bd639e4bed5b18486fef0b5a520aaffde5a18fc225e808a1ac4df363f43a1ce", size = 60904 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e3/a7/6322d1d7a1fb926e8b99208c27730f21217da2f1e0e11dab48a78a0427a4/opentelemetry_api-1.26.0-py3-none-any.whl", hash = "sha256:7d7ea33adf2ceda2dd680b18b1677e4152000b37ca76e679da71ff103b943064", size = 61533 },
+]
+
+[[package]]
+name = "opentelemetry-exporter-otlp-proto-common"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "opentelemetry-proto" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/84/cd/ed9eaa1d80facb6609d02af6c393b02ce3797a15742361be4859db6fdc17/opentelemetry_exporter_otlp_proto_common-1.26.0.tar.gz", hash = "sha256:bdbe50e2e22a1c71acaa0c8ba6efaadd58882e5a5978737a44a4c4b10d304c92", size = 17815 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/25/2f/0f7e0a73fd901c9abc6ea680d7f19a803dac830c450f21e1123d3a3ec488/opentelemetry_exporter_otlp_proto_common-1.26.0-py3-none-any.whl", hash = "sha256:ee4d8f8891a1b9c372abf8d109409e5b81947cf66423fd998e56880057afbc71", size = 17837 },
+]
+
+[[package]]
+name = "opentelemetry-exporter-otlp-proto-grpc"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "deprecated" },
+    { name = "googleapis-common-protos" },
+    { name = "grpcio" },
+    { name = "opentelemetry-api" },
+    { name = "opentelemetry-exporter-otlp-proto-common" },
+    { name = "opentelemetry-proto" },
+    { name = "opentelemetry-sdk" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a0/23/cac89aca97ecb8f7498a875dc2ac89224b4f3345bcb8ffff643b59886196/opentelemetry_exporter_otlp_proto_grpc-1.26.0.tar.gz", hash = "sha256:a65b67a9a6b06ba1ec406114568e21afe88c1cdb29c464f2507d529eb906d8ae", size = 25239 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4d/0c/e4473692fec8076008c7926dfcef7223fc6d2785f04ad9d8402347a4eba9/opentelemetry_exporter_otlp_proto_grpc-1.26.0-py3-none-any.whl", hash = "sha256:e2be5eff72ebcb010675b818e8d7c2e7d61ec451755b8de67a140bc49b9b0280", size = 18228 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation"
+version = "0.47b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "opentelemetry-api" },
+    { name = "setuptools" },
+    { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ab/9d/de2726729dbe5d210683245315ed5a20bf90465d1cc5e7f9cb0bee6673a6/opentelemetry_instrumentation-0.47b0.tar.gz", hash = "sha256:96f9885e450c35e3f16a4f33145f2ebf620aea910c9fd74a392bbc0f807a350f", size = 24516 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1f/6a/be31a84ddd13e9018fcca6885e4710f227eb0fd06eda1896da67287faa2e/opentelemetry_instrumentation-0.47b0-py3-none-any.whl", hash = "sha256:88974ee52b1db08fc298334b51c19d47e53099c33740e48c4f084bd1afd052d5", size = 29218 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-asgi"
+version = "0.47b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "asgiref" },
+    { name = "opentelemetry-api" },
+    { name = "opentelemetry-instrumentation" },
+    { name = "opentelemetry-semantic-conventions" },
+    { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/59/a5/895c3810f27cdd3bdb02320df3489d2d33f158970d8447755deb7fc3fef7/opentelemetry_instrumentation_asgi-0.47b0.tar.gz", hash = "sha256:e78b7822c1bca0511e5e9610ec484b8994a81670375e570c76f06f69af7c506a", size = 23398 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ba/d9/c74cb6d69589cc97d856cb3f427dfcef37ec16f9564586290c9c075d9020/opentelemetry_instrumentation_asgi-0.47b0-py3-none-any.whl", hash = "sha256:b798dc4957b3edc9dfecb47a4c05809036a4b762234c5071212fda39ead80ade", size = 15946 },
+]
+
+[[package]]
+name = "opentelemetry-instrumentation-fastapi"
+version = "0.47b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "opentelemetry-api" },
+    { name = "opentelemetry-instrumentation" },
+    { name = "opentelemetry-instrumentation-asgi" },
+    { name = "opentelemetry-semantic-conventions" },
+    { name = "opentelemetry-util-http" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3f/8f/c68dbef4be5db9330b0e9f492277b0dcdc8870d86de0c749b537406c590a/opentelemetry_instrumentation_fastapi-0.47b0.tar.gz", hash = "sha256:0c7c10b5d971e99a420678ffd16c5b1ea4f0db3b31b62faf305fbb03b4ebee36", size = 17332 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a5/29/a97842d6dfa679bf0f3624ce1ea3458eb185befd536cafe580daa9ab68ae/opentelemetry_instrumentation_fastapi-0.47b0-py3-none-any.whl", hash = "sha256:5ac28dd401160b02e4f544a85a9e4f61a8cbe5b077ea0379d411615376a2bd21", size = 11715 },
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a9/06/9505ef04e527fa711ebffb47f3f56cac6015405953ff688fc349d170fb9c/opentelemetry_proto-1.26.0.tar.gz", hash = "sha256:c5c18796c0cab3751fc3b98dee53855835e90c0422924b484432ac852d93dc1e", size = 34749 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/15/f4/66a3892eea913cded9bac0fdd3fb1a412fa2da8eb50014ec87a52648444a/opentelemetry_proto-1.26.0-py3-none-any.whl", hash = "sha256:6c4d7b4d4d9c88543bcf8c28ae3f8f0448a753dc291c18c5390444c90b76a725", size = 52466 },
+]
+
+[[package]]
+name = "opentelemetry-sdk"
+version = "1.26.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "opentelemetry-api" },
+    { name = "opentelemetry-semantic-conventions" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d3/85/8ca0d5ebfe708287b091dffcd15553b74bbfe4532f8dd42662b78b2e0cab/opentelemetry_sdk-1.26.0.tar.gz", hash = "sha256:c90d2868f8805619535c05562d699e2f4fb1f00dbd55a86dcefca4da6fa02f85", size = 143139 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/92/f1/a9b550d0f9c049653dd2eab45cecf8fe4baa9795ed143d87834056ffabaf/opentelemetry_sdk-1.26.0-py3-none-any.whl", hash = "sha256:feb5056a84a88670c041ea0ded9921fca559efec03905dddeb3885525e0af897", size = 109475 },
+]
+
+[[package]]
+name = "opentelemetry-semantic-conventions"
+version = "0.47b0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "deprecated" },
+    { name = "opentelemetry-api" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/93/85/edef14d10ad00ddd9fffb20e4d3d938f4c5c1247e11a175066fe2b4a72f8/opentelemetry_semantic_conventions-0.47b0.tar.gz", hash = "sha256:a8d57999bbe3495ffd4d510de26a97dadc1dace53e0275001b2c1b2f67992a7e", size = 83994 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/00/c2/ca5cef8e4cd8eec5a95deed95ec3f6005e499fd9d17ca08731ced03a6921/opentelemetry_semantic_conventions-0.47b0-py3-none-any.whl", hash = "sha256:4ff9d595b85a59c1c1413f02bba320ce7ea6bf9e2ead2b0913c4395c7bbc1063", size = 138027 },
+]
+
+[[package]]
+name = "opentelemetry-util-http"
+version = "0.47b0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8f/b5/fb15aafe7391b6a36f5cd9bcb9f6c3efaeb87a0626e4d2dfef12f66ebf3e/opentelemetry_util_http-0.47b0.tar.gz", hash = "sha256:352a07664c18eef827eb8ddcbd64c64a7284a39dd1655e2f16f577eb046ccb32", size = 7863 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/10/7e/98749e14a4e3f4db8bc016e6b42aba40e4d934baeb8767b8658a99d0dfac/opentelemetry_util_http-0.47b0-py3-none-any.whl", hash = "sha256:3d3215e09c4a723b12da6d0233a31395aeb2bb33a64d7b15a1500690ba250f19", size = 6946 },
+]
+
+[[package]]
+name = "ordered-set"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4c/ca/bfac8bc689799bcca4157e0e0ced07e70ce125193fc2e166d2e685b7e2fe/ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8", size = 12826 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/33/55/af02708f230eb77084a299d7b08175cff006dea4f2721074b92cdb0296c0/ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", size = 7634 },
+]
+
+[[package]]
+name = "orjson"
+version = "3.10.7"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/03/821c8197d0515e46ea19439f5c5d5fd9a9889f76800613cfac947b5d7845/orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3", size = 5056450 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/89/c9/dd286c97c2f478d43839bd859ca4d9820e2177d4e07a64c516dc3e018062/orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2", size = 251312 },
+    { url = "https://files.pythonhosted.org/packages/b9/72/d90bd11e83a0e9623b3803b079478a93de8ec4316c98fa66110d594de5fa/orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09", size = 148125 },
+    { url = "https://files.pythonhosted.org/packages/9d/b6/ed61e87f327a4cbb2075ed0716e32ba68cb029aa654a68c3eb27803050d8/orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0", size = 147278 },
+    { url = "https://files.pythonhosted.org/packages/66/9f/e6a11b5d1ad11e9dc869d938707ef93ff5ed20b53d6cda8b5e2ac532a9d2/orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a", size = 152954 },
+    { url = "https://files.pythonhosted.org/packages/92/ee/702d5e8ccd42dc2b9d1043f22daa1ba75165616aa021dc19fb0c5a726ce8/orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e", size = 163953 },
+    { url = "https://files.pythonhosted.org/packages/d3/cb/55205f3f1ee6ba80c0a9a18ca07423003ca8de99192b18be30f1f31b4cdd/orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6", size = 141895 },
+    { url = "https://files.pythonhosted.org/packages/bb/ab/1185e472f15c00d37d09c395e478803ed0eae7a3a3d055a5f3885e1ea136/orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6", size = 170169 },
+    { url = "https://files.pythonhosted.org/packages/53/b9/10abe9089bdb08cd4218cc45eb7abfd787c82cf301cecbfe7f141542d7f4/orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0", size = 167808 },
+    { url = "https://files.pythonhosted.org/packages/8a/ad/26b40ccef119dcb0f4a39745ffd7d2d319152c1a52859b1ebbd114eca19c/orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f", size = 143010 },
+    { url = "https://files.pythonhosted.org/packages/e7/63/5f4101e4895b78ada568f4cf8f870dd594139ca2e75e654e373da78b03b0/orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5", size = 137307 },
+    { url = "https://files.pythonhosted.org/packages/14/7c/b4ecc2069210489696a36e42862ccccef7e49e1454a3422030ef52881b01/orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f", size = 251409 },
+    { url = "https://files.pythonhosted.org/packages/60/84/e495edb919ef0c98d054a9b6d05f2700fdeba3886edd58f1c4dfb25d514a/orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3", size = 147913 },
+    { url = "https://files.pythonhosted.org/packages/c5/27/e40bc7d79c4afb7e9264f22320c285d06d2c9574c9c682ba0f1be3012833/orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93", size = 147390 },
+    { url = "https://files.pythonhosted.org/packages/30/be/fd646fb1a461de4958a6eacf4ecf064b8d5479c023e0e71cc89b28fa91ac/orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313", size = 152973 },
+    { url = "https://files.pythonhosted.org/packages/b1/00/414f8d4bc5ec3447e27b5c26b4e996e4ef08594d599e79b3648f64da060c/orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864", size = 164039 },
+    { url = "https://files.pythonhosted.org/packages/a0/6b/34e6904ac99df811a06e42d8461d47b6e0c9b86e2fe7ee84934df6e35f0d/orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09", size = 142035 },
+    { url = "https://files.pythonhosted.org/packages/17/7e/254189d9b6df89660f65aec878d5eeaa5b1ae371bd2c458f85940445d36f/orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5", size = 169941 },
+    { url = "https://files.pythonhosted.org/packages/02/1a/d11805670c29d3a1b29fc4bd048dc90b094784779690592efe8c9f71249a/orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b", size = 167994 },
+    { url = "https://files.pythonhosted.org/packages/20/5f/03d89b007f9d6733dc11bc35d64812101c85d6c4e9c53af9fa7e7689cb11/orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb", size = 143130 },
+    { url = "https://files.pythonhosted.org/packages/c6/9d/9b9fb6c60b8a0e04031ba85414915e19ecea484ebb625402d968ea45b8d5/orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1", size = 137326 },
+    { url = "https://files.pythonhosted.org/packages/15/05/121af8a87513c56745d01ad7cf215c30d08356da9ad882ebe2ba890824cd/orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149", size = 251331 },
+    { url = "https://files.pythonhosted.org/packages/73/7f/8d6ccd64a6f8bdbfe6c9be7c58aeb8094aa52a01fbbb2cda42ff7e312bd7/orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe", size = 142012 },
+    { url = "https://files.pythonhosted.org/packages/04/65/f2a03fd1d4f0308f01d372e004c049f7eb9bc5676763a15f20f383fa9c01/orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c", size = 169920 },
+    { url = "https://files.pythonhosted.org/packages/e2/1c/3ef8d83d7c6a619ad3d69a4d5318591b4ce5862e6eda7c26bbe8208652ca/orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad", size = 167916 },
+    { url = "https://files.pythonhosted.org/packages/f2/0d/820a640e5a7dfbe525e789c70871ebb82aff73b0c7bf80082653f86b9431/orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2", size = 143089 },
+    { url = "https://files.pythonhosted.org/packages/1a/72/a424db9116c7cad2950a8f9e4aeb655a7b57de988eb015acd0fcd1b4609b/orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024", size = 137081 },
+]
+
+[[package]]
+name = "overrides"
+version = "7.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/36/86/b585f53236dec60aba864e050778b25045f857e17f6e5ea0ae95fe80edd2/overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a", size = 22812 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49", size = 17832 },
+]
+
+[[package]]
+name = "packaging"
+version = "23.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fb/2b/9b9c33ffed44ee921d0967086d653047286054117d584f1b1a7c22ceaf7b/packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", size = 146714 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7", size = 53011 },
+]
+
+[[package]]
+name = "pandas"
+version = "2.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+    { name = "python-dateutil" },
+    { name = "pytz" },
+    { name = "tzdata" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 },
+    { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 },
+    { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 },
+    { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 },
+    { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 },
+    { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 },
+    { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 },
+    { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 },
+    { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 },
+    { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 },
+    { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 },
+    { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 },
+    { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 },
+    { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 },
+]
+
+[[package]]
+name = "passlib"
+version = "1.7.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b6/06/9da9ee59a67fae7761aab3ccc84fa4f3f33f125b370f1ccdb915bf967c11/passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04", size = 689844 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3b/a4/ab6b7589382ca3df236e03faa71deac88cae040af60c071a78d254a62172/passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", size = 525554 },
+]
+
+[package.optional-dependencies]
+bcrypt = [
+    { name = "bcrypt" },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 },
+]
+
+[[package]]
+name = "pcodedmp"
+version = "1.2.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "oletools" },
+    { name = "win-unicode-console", marker = "platform_python_implementation != 'PyPy' and platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/20/6d461e29135f474408d0d7f95b2456a9ba245560768ee51b788af10f7429/pcodedmp-1.2.6.tar.gz", hash = "sha256:025f8c809a126f45a082ffa820893e6a8d990d9d7ddb68694b5a9f0a6dbcd955", size = 35549 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ba/72/b380fb5c89d89c3afafac8cf02a71a45f4f4a4f35531ca949a34683962d1/pcodedmp-1.2.6-py2.py3-none-any.whl", hash = "sha256:4441f7c0ab4cbda27bd4668db3b14f36261d86e5059ce06c0828602cbe1c4278", size = 30939 },
+]
+
+[[package]]
+name = "peewee"
+version = "3.17.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bd/be/e9c886b4601a19f4c34a1b75c5fe8b98a2115dd964251a76b24c977c369d/peewee-3.17.6.tar.gz", hash = "sha256:cea5592c6f4da1592b7cff8eaf655be6648a1f5857469e30037bf920c03fb8fb", size = 2954075 }
+
+[[package]]
+name = "peewee-migrate"
+version = "1.12.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "peewee" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/23/52/a2faf82c9872d2948935f9e9070ca1cb9e6bcf36d87fde01067ef2c88500/peewee_migrate-1.12.2.tar.gz", hash = "sha256:c8187c97b756909ea57e77cce06ae66395219e86764ef0b286a7bc72ff7405ad", size = 16406 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8c/32/de329eb77c16ebe2d52971d55954e4c34c7302ab285df8897b8d8dfd705e/peewee_migrate-1.12.2-py3-none-any.whl", hash = "sha256:2930bf83ef802cdb5fb123116c5eb87cbf3756cb27674f674923be6bb27dabee", size = 18580 },
+]
+
+[[package]]
+name = "pillow"
+version = "10.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 },
+    { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 },
+    { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 },
+    { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 },
+    { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 },
+    { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 },
+    { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 },
+    { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 },
+    { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 },
+    { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 },
+    { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 },
+    { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 },
+    { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 },
+    { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 },
+    { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 },
+    { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 },
+    { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 },
+    { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 },
+    { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 },
+    { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 },
+    { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 },
+    { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 },
+    { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 },
+    { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 },
+    { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 },
+    { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 },
+    { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 },
+    { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 },
+    { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 },
+    { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 },
+    { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 },
+    { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 },
+    { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/52/0763d1d976d5c262df53ddda8d8d4719eedf9594d046f117c25a27261a19/platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3", size = 20916 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/68/13/2aa1f0e1364feb2c9ef45302f387ac0bd81484e9c9a4c5688a322fbdfd08/platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", size = 18146 },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
+]
+
+[[package]]
+name = "posthog"
+version = "3.5.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "backoff" },
+    { name = "monotonic" },
+    { name = "python-dateutil" },
+    { name = "requests" },
+    { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/22/a6/a260008b95152d31ef19845ae3100411b481279ec98d22e8c87606abe78e/posthog-3.5.2.tar.gz", hash = "sha256:a383a80c1f47e0243f5ce359e81e06e2e7b37eb39d1d6f8d01c3e64ed29df2ee", size = 38380 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/53/62/2e7f75beedf9b5411f133a5af558cc7d76e20bbf6778a51ade15e6d3152b/posthog-3.5.2-py2.py3-none-any.whl", hash = "sha256:605b3d92369971cc99290b1fcc8534cbddac3726ef7972caa993454a5ecfb644", size = 41545 },
+]
+
+[[package]]
+name = "primp"
+version = "0.6.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/63/9c/10d2c7b734228021cf17d92b2872ed53535103d29a38a6ad4eee89a8ae1b/primp-0.6.3.tar.gz", hash = "sha256:17d30ebe26864defad5232dbbe1372e80483940012356e1f68846bb182282039", size = 78662 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/61/09/96e8327fd8c1224226d9a170daf1dcba7d3f6578edeb9f811803f2a61aba/primp-0.6.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bdbe6a7cdaaf5c9ed863432a941f4a75bd4c6ff626cbc8d32fc232793c70ba06", size = 2719331 },
+    { url = "https://files.pythonhosted.org/packages/b3/a1/b58ac752b0500208df1be3b762eeaff1a117ec3108e26b38821ae9ac31e0/primp-0.6.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:eeb53eb987bdcbcd85740633470255cab887d921df713ffa12a36a13366c9cdb", size = 2517811 },
+    { url = "https://files.pythonhosted.org/packages/f7/85/ca027d7ec6121346d5927205777ad74bf918f291a538d710b5f9e0957333/primp-0.6.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78da53d3c92a8e3f05bd3286ac76c291f1b6fe5e08ea63b7ba92b0f9141800bb", size = 2826729 },
+    { url = "https://files.pythonhosted.org/packages/3a/45/ff51ccc5dbc82afa6d2dda15bf07b21b6b44708d02934cd7562007d1f719/primp-0.6.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:86337b44deecdac752bd8112909987fc9fa9b894f30191c80a164dc8f895da53", size = 2739692 },
+    { url = "https://files.pythonhosted.org/packages/74/a1/a626fb8d2f6499d3e05971b2dcd33d770244722ee52f7d2c4ab636c1157f/primp-0.6.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d3cd9a22b97f3eae42b2a5fb99f00480daf4cd6d9b139e05b0ffb03f7cc037f3", size = 2900573 },
+    { url = "https://files.pythonhosted.org/packages/09/55/c96cb510c9f7881fa01bc7b269e446d32635ab9f0adbd36918c69b45a140/primp-0.6.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7732bec917e2d3c48a31cdb92e1250f4ad6203a1aa4f802bd9abd84f2286a1e0", size = 3058818 },
+    { url = "https://files.pythonhosted.org/packages/98/de/5c1dab24c1bff7933d0e4e8f4d0f46b66fc23531258d8433789c4468cac3/primp-0.6.3-cp38-abi3-win_amd64.whl", hash = "sha256:1e4113c34b86c676ae321af185f03a372caef3ee009f1682c2d62e30ec87348c", size = 2757631 },
+]
+
+[[package]]
+name = "proto-plus"
+version = "1.24.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "protobuf" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3e/fc/e9a65cd52c1330d8d23af6013651a0bc50b6d76bcbdf91fae7cd19c68f29/proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445", size = 55942 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7c/6f/db31f0711c0402aa477257205ce7d29e86a75cb52cd19f7afb585f75cda0/proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12", size = 50080 },
+]
+
+[[package]]
+name = "protobuf"
+version = "4.25.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/ab/cb61a4b87b2e7e6c312dce33602bd5884797fd054e0e53205f1c27cf0f66/protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d", size = 380283 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c8/43/27b48d9040763b78177d3083e16c70dba6e3c3ee2af64b659f6332c2b06e/protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4", size = 392409 },
+    { url = "https://files.pythonhosted.org/packages/0c/d4/589d673ada9c4c62d5f155218d7ff7ac796efb9c6af95b0bd29d438ae16e/protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d", size = 413398 },
+    { url = "https://files.pythonhosted.org/packages/34/ca/bf85ffe3dd16f1f2aaa6c006da8118800209af3da160ae4d4f47500eabd9/protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b", size = 394160 },
+    { url = "https://files.pythonhosted.org/packages/68/1d/e8961af9a8e534d66672318d6b70ea8e3391a6b13e16a29b039e4a99c214/protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835", size = 293700 },
+    { url = "https://files.pythonhosted.org/packages/ca/6c/cc7ab2fb3a4a7f07f211d8a7bbb76bba633eb09b148296dbd4281e217f95/protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040", size = 294612 },
+    { url = "https://files.pythonhosted.org/packages/b5/95/0ba7f66934a0a798006f06fc3d74816da2b7a2bcfd9b98c53d26f684c89e/protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978", size = 156464 },
+]
+
+[[package]]
+name = "psutil"
+version = "6.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/18/c7/8c6872f7372eb6a6b2e4708b88419fb46b857f7a2e1892966b851cc79fc9/psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2", size = 508067 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c5/66/78c9c3020f573c58101dc43a44f6855d01bbbd747e24da2f0c4491200ea3/psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35", size = 249766 },
+    { url = "https://files.pythonhosted.org/packages/e1/3f/2403aa9558bea4d3854b0e5e567bc3dd8e9fbc1fc4453c0aa9aafeb75467/psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1", size = 253024 },
+    { url = "https://files.pythonhosted.org/packages/0b/37/f8da2fbd29690b3557cca414c1949f92162981920699cd62095a984983bf/psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0", size = 250961 },
+    { url = "https://files.pythonhosted.org/packages/35/56/72f86175e81c656a01c4401cd3b1c923f891b31fbcebe98985894176d7c9/psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0", size = 287478 },
+    { url = "https://files.pythonhosted.org/packages/19/74/f59e7e0d392bc1070e9a70e2f9190d652487ac115bb16e2eff6b22ad1d24/psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd", size = 290455 },
+    { url = "https://files.pythonhosted.org/packages/cd/5f/60038e277ff0a9cc8f0c9ea3d0c5eb6ee1d2470ea3f9389d776432888e47/psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132", size = 292046 },
+    { url = "https://files.pythonhosted.org/packages/8b/20/2ff69ad9c35c3df1858ac4e094f20bd2374d33c8643cf41da8fd7cdcb78b/psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d", size = 253560 },
+    { url = "https://files.pythonhosted.org/packages/73/44/561092313ae925f3acfaace6f9ddc4f6a9c748704317bad9c8c8f8a36a79/psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3", size = 257399 },
+    { url = "https://files.pythonhosted.org/packages/7c/06/63872a64c312a24fb9b4af123ee7007a306617da63ff13bcc1432386ead7/psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0", size = 251988 },
+]
+
+[[package]]
+name = "psycopg2-binary"
+version = "2.9.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fc/07/e720e53bfab016ebcc34241695ccc06a9e3d91ba19b40ca81317afbdc440/psycopg2-binary-2.9.9.tar.gz", hash = "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", size = 384973 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a5/ac/702d300f3df169b9d0cbef0340d9f34a78bc18dc2dbafbcb39ff0f165cf8/psycopg2_binary-2.9.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", size = 2822581 },
+    { url = "https://files.pythonhosted.org/packages/7a/1f/a6cf0cdf944253f7c45d90fbc876cc8bed5cc9942349306245715c0d88d6/psycopg2_binary-2.9.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", size = 2552633 },
+    { url = "https://files.pythonhosted.org/packages/81/0b/3adf561107c865928455891156d1dde5325253f7f4316fe56cd2c3f73570/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", size = 2851075 },
+    { url = "https://files.pythonhosted.org/packages/f7/98/c2fedcbf0a9607519a010dcf88571138b2251062dbde3610cdba5ba1eee1/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", size = 3080509 },
+    { url = "https://files.pythonhosted.org/packages/c2/05/81e8bc7fca95574c9323e487d9ce1b58a4cfcc17f89b8fe843af46361211/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", size = 3264303 },
+    { url = "https://files.pythonhosted.org/packages/ce/85/62825cabc6aad53104b7b6d12eb2ad74737d268630032d07b74d4444cb72/psycopg2_binary-2.9.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", size = 3019515 },
+    { url = "https://files.pythonhosted.org/packages/e9/b0/9ca2b8e01a0912c9a14234fd5df7a241a1e44778c5797bf4b8eaa8dc3d3a/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", size = 2355892 },
+    { url = "https://files.pythonhosted.org/packages/73/17/ba28bb0022db5e2015a82d2df1c4b0d419c37fa07a588b3aff3adc4939f6/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", size = 2534903 },
+    { url = "https://files.pythonhosted.org/packages/3b/92/b463556409cdc12791cd8b1dae0072bf8efe817ef68b7ea3d9cf7d0e5656/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", size = 2486597 },
+    { url = "https://files.pythonhosted.org/packages/92/57/96576e07132d7f7a1ac1df939575e6fdd8951aea337ee152b586bb51a971/psycopg2_binary-2.9.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", size = 2454908 },
+    { url = "https://files.pythonhosted.org/packages/7c/ae/cedd56e1f4a2b0e37213283caf3733a875c4c76f3372241e19c0d2a87355/psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", size = 1024240 },
+    { url = "https://files.pythonhosted.org/packages/25/1f/7ae31759142999a8d06b3e250c1346c4abcdcada8fa884376775dc1de686/psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", size = 1163655 },
+    { url = "https://files.pythonhosted.org/packages/a7/d0/5f2db14e7b53552276ab613399a83f83f85b173a862d3f20580bc7231139/psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", size = 2823784 },
+    { url = "https://files.pythonhosted.org/packages/18/ca/da384fd47233e300e3e485c90e7aab5d7def896d1281239f75901faf87d4/psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", size = 2553308 },
+    { url = "https://files.pythonhosted.org/packages/50/66/fa53d2d3d92f6e1ef469d92afc6a4fe3f6e8a9a04b687aa28fb1f1d954ee/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", size = 2851283 },
+    { url = "https://files.pythonhosted.org/packages/04/37/2429360ac5547378202db14eec0dde76edbe1f6627df5a43c7e164922859/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", size = 3081839 },
+    { url = "https://files.pythonhosted.org/packages/62/2a/c0530b59d7e0d09824bc2102ecdcec0456b8ca4d47c0caa82e86fce3ed4c/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", size = 3264488 },
+    { url = "https://files.pythonhosted.org/packages/19/57/9f172b900795ea37246c78b5f52e00f4779984370855b3e161600156906d/psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", size = 3020700 },
+    { url = "https://files.pythonhosted.org/packages/94/68/1176fc14ea76861b7b8360be5176e87fb20d5091b137c76570eb4e237324/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", size = 2355968 },
+    { url = "https://files.pythonhosted.org/packages/70/bb/aec2646a705a09079d008ce88073401cd61fc9b04f92af3eb282caa3a2ec/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", size = 2536101 },
+    { url = "https://files.pythonhosted.org/packages/14/33/12818c157e333cb9d9e6753d1b2463b6f60dbc1fade115f8e4dc5c52cac4/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", size = 2487064 },
+    { url = "https://files.pythonhosted.org/packages/56/a2/7851c68fe8768f3c9c246198b6356ee3e4a8a7f6820cc798443faada3400/psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", size = 2456257 },
+    { url = "https://files.pythonhosted.org/packages/6f/ee/3ba07c6dc7c3294e717e94720da1597aedc82a10b1b180203ce183d4631a/psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", size = 1024709 },
+    { url = "https://files.pythonhosted.org/packages/7b/08/9c66c269b0d417a0af9fb969535f0371b8c538633535a7a6a5ca3f9231e2/psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", size = 1163864 },
+]
+
+[[package]]
+name = "pyarrow"
+version = "17.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/27/4e/ea6d43f324169f8aec0e57569443a38bab4b398d09769ca64f7b4d467de3/pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28", size = 1112479 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f9/46/ce89f87c2936f5bb9d879473b9663ce7a4b1f4359acc2f0eb39865eaa1af/pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977", size = 29028748 },
+    { url = "https://files.pythonhosted.org/packages/8d/8e/ce2e9b2146de422f6638333c01903140e9ada244a2a477918a368306c64c/pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3", size = 27190965 },
+    { url = "https://files.pythonhosted.org/packages/3b/c8/5675719570eb1acd809481c6d64e2136ffb340bc387f4ca62dce79516cea/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15", size = 39269081 },
+    { url = "https://files.pythonhosted.org/packages/5e/78/3931194f16ab681ebb87ad252e7b8d2c8b23dad49706cadc865dff4a1dd3/pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597", size = 39864921 },
+    { url = "https://files.pythonhosted.org/packages/d8/81/69b6606093363f55a2a574c018901c40952d4e902e670656d18213c71ad7/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420", size = 38740798 },
+    { url = "https://files.pythonhosted.org/packages/4c/21/9ca93b84b92ef927814cb7ba37f0774a484c849d58f0b692b16af8eebcfb/pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4", size = 39871877 },
+    { url = "https://files.pythonhosted.org/packages/30/d1/63a7c248432c71c7d3ee803e706590a0b81ce1a8d2b2ae49677774b813bb/pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03", size = 25151089 },
+    { url = "https://files.pythonhosted.org/packages/d4/62/ce6ac1275a432b4a27c55fe96c58147f111d8ba1ad800a112d31859fae2f/pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22", size = 29019418 },
+    { url = "https://files.pythonhosted.org/packages/8e/0a/dbd0c134e7a0c30bea439675cc120012337202e5fac7163ba839aa3691d2/pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053", size = 27152197 },
+    { url = "https://files.pythonhosted.org/packages/cb/05/3f4a16498349db79090767620d6dc23c1ec0c658a668d61d76b87706c65d/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a", size = 39263026 },
+    { url = "https://files.pythonhosted.org/packages/c2/0c/ea2107236740be8fa0e0d4a293a095c9f43546a2465bb7df34eee9126b09/pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc", size = 39880798 },
+    { url = "https://files.pythonhosted.org/packages/f6/b0/b9164a8bc495083c10c281cc65064553ec87b7537d6f742a89d5953a2a3e/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a", size = 38715172 },
+    { url = "https://files.pythonhosted.org/packages/f1/c4/9625418a1413005e486c006e56675334929fad864347c5ae7c1b2e7fe639/pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b", size = 39874508 },
+    { url = "https://files.pythonhosted.org/packages/ae/49/baafe2a964f663413be3bd1cf5c45ed98c5e42e804e2328e18f4570027c1/pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7", size = 25099235 },
+]
+
+[[package]]
+name = "pyasn1"
+version = "0.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4a/a3/d2157f333900747f20984553aca98008b6dc843eb62f3a36030140ccec0d/pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", size = 148088 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/23/7e/5f50d07d5e70a2addbccd90ac2950f81d1edd0783630651d9268d7f1db49/pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473", size = 85313 },
+]
+
+[[package]]
+name = "pyasn1-modules"
+version = "0.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pyasn1" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f7/00/e7bd1dec10667e3f2be602686537969a7ac92b0a7c5165be2e5875dc3971/pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6", size = 307859 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/13/68/8906226b15ef38e71dc926c321d2fe99de8048e9098b5dfd38343011c886/pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b", size = 181220 },
+]
+
+[[package]]
+name = "pyclipper"
+version = "1.3.0.post5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/3d/e5b5ff36b24f3fc9b962a68ce4f6932ab698b8ba860261f402be37b85d17/pyclipper-1.3.0.post5.tar.gz", hash = "sha256:c0239f928e0bf78a3efc2f2f615a10bfcdb9f33012d46d64c8d1225b4bde7096", size = 164719 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/21/47/9c6a9d2523735d7a5ec2991e6a05370b96e19db26c5628fedd1143dc6e4f/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b7a983ae019932bfa0a1971a2dc8c856704add5f3d567bed8fac02dbc0e7f0bf", size = 279155 },
+    { url = "https://files.pythonhosted.org/packages/05/f0/3e4ca96c1adb32f254ba0ba3a5a4cf4bd6794c285177f10357f3574d11d5/pyclipper-1.3.0.post5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8760075c395b924f894aa16ee06e8c040c6f9b63e0903e49de3cc8d82d9e637", size = 146859 },
+    { url = "https://files.pythonhosted.org/packages/ed/79/64cfb4bf0338c3dcd4ef4b819f0fb48a65bc9a9b5b2644cf21a665d08ae8/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ea61ca5899d3346c614951342c506f119601ed0a1f4889a9cc236558afec6b", size = 952640 },
+    { url = "https://files.pythonhosted.org/packages/43/64/9c8e0c7d96d32c63e38f92da92e4e38685e30773644d9dcb73d2325beb47/pyclipper-1.3.0.post5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46499b361ae067662b22578401d83d57716f3cc0071d592feb07d504b439fea7", size = 971470 },
+    { url = "https://files.pythonhosted.org/packages/da/ff/29f1fa1473d6c8abaa2f41f60dfeb23e92819ee67f7d9387715e53e2a414/pyclipper-1.3.0.post5-cp311-cp311-win32.whl", hash = "sha256:d5c77e39ab05a6cf277c819639968b21e6959e996ea1a074afc24236541708ff", size = 99360 },
+    { url = "https://files.pythonhosted.org/packages/f3/ec/56da9f2d5d846f144530d5313a05078afb7cfc26ec179be5af35f057d064/pyclipper-1.3.0.post5-cp311-cp311-win_amd64.whl", hash = "sha256:0f78a1c18ff4f9276f78d9353d6ed4309c3886a9d0172437e48328aef499165e", size = 108311 },
+    { url = "https://files.pythonhosted.org/packages/87/f0/2a9dbd3359bd834b24691ba65b1011c1a7a7cafd92691165506ece1eeb3b/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5237282f906049c307e6c90333c7d56f6b8712bf087ef97b141830c40b09ca0a", size = 278102 },
+    { url = "https://files.pythonhosted.org/packages/28/30/1b532eff31728e0233684eada4153773af11a81992bb9791160ed27760af/pyclipper-1.3.0.post5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aca8635573646b65c054399433fb3493637f1445db942de8a52fca9ef493ba3d", size = 145946 },
+    { url = "https://files.pythonhosted.org/packages/b0/95/e0d4c036c1c936b6f7e6265d15f56b5b3ceb4a4d7dcb491cdd2604882f93/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1158a2b13d59bdfab33d1d928f7b72c8c7fb8a76e7d2283839cb45d7c0ff2140", size = 947205 },
+    { url = "https://files.pythonhosted.org/packages/22/f3/c5b39f3515d7af0c96b67f6eb13b62d0cd471f348ebafa106d6fcb8d9d33/pyclipper-1.3.0.post5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a041f1a7982b17cf92fd3be349ec41ff1901792149c166bf283f469567b52d6", size = 966618 },
+    { url = "https://files.pythonhosted.org/packages/0a/67/1fe463403bbd2ea7ca79f328a118b12fff495d0e83c98bdf5afd187ccccc/pyclipper-1.3.0.post5-cp312-cp312-win32.whl", hash = "sha256:bf3a2ccd6e4e078250b0a31a12c519b0be6d1bc160acfceee62407dbd68558f6", size = 98762 },
+    { url = "https://files.pythonhosted.org/packages/a1/f0/760e614b84dd4d8f03dd5432dda100d699e23074c263b38b6c117adc8395/pyclipper-1.3.0.post5-cp312-cp312-win_amd64.whl", hash = "sha256:2ce6e0a6ab32182c26537965cf521822cd11a28a7ffcef48635a94c6ca8559ef", size = 108190 },
+]
+
+[[package]]
+name = "pycparser"
+version = "2.22"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.8.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "annotated-types" },
+    { name = "pydantic-core" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8c/99/d0a5dca411e0a017762258013ba9905cd6e7baa9a3fd1fe8b6529472902e/pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", size = 739834 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1f/fa/b7f815b8c9ad021c07f88875b601222ef5e70619391ade4a49234d12d278/pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8", size = 423875 },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.20.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/12/e3/0d5ad91211dba310f7ded335f4dad871172b9cc9ce204f5a56d76ccd6247/pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", size = 388371 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/61/db/f6a724db226d990a329910727cfac43539ff6969edc217286dd05cda3ef6/pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", size = 1834507 },
+    { url = "https://files.pythonhosted.org/packages/9b/83/6f2bfe75209d557ae1c3550c1252684fc1827b8b12fbed84c3b4439e135d/pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", size = 1773527 },
+    { url = "https://files.pythonhosted.org/packages/93/ef/513ea76d7ca81f2354bb9c8d7839fc1157673e652613f7e1aff17d8ce05d/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", size = 1787879 },
+    { url = "https://files.pythonhosted.org/packages/31/0a/ac294caecf235f0cc651de6232f1642bb793af448d1cfc541b0dc1fd72b8/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", size = 1774694 },
+    { url = "https://files.pythonhosted.org/packages/46/a4/08f12b5512f095963550a7cb49ae010e3f8f3f22b45e508c2cb4d7744fce/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", size = 1976369 },
+    { url = "https://files.pythonhosted.org/packages/15/59/b2495be4410462aedb399071c71884042a2c6443319cbf62d00b4a7ed7a5/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", size = 2691250 },
+    { url = "https://files.pythonhosted.org/packages/3c/ae/fc99ce1ba791c9e9d1dee04ce80eef1dae5b25b27e3fc8e19f4e3f1348bf/pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", size = 2061462 },
+    { url = "https://files.pythonhosted.org/packages/44/bb/eb07cbe47cfd638603ce3cb8c220f1a054b821e666509e535f27ba07ca5f/pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", size = 1893923 },
+    { url = "https://files.pythonhosted.org/packages/ce/ef/5a52400553b8faa0e7f11fd7a2ba11e8d2feb50b540f9e7973c49b97eac0/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27", size = 1966779 },
+    { url = "https://files.pythonhosted.org/packages/4c/5b/fb37fe341344d9651f5c5f579639cd97d50a457dc53901aa8f7e9f28beb9/pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", size = 2109044 },
+    { url = "https://files.pythonhosted.org/packages/70/1a/6f7278802dbc66716661618807ab0dfa4fc32b09d1235923bbbe8b3a5757/pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", size = 1708265 },
+    { url = "https://files.pythonhosted.org/packages/35/7f/58758c42c61b0bdd585158586fecea295523d49933cb33664ea888162daf/pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", size = 1901750 },
+    { url = "https://files.pythonhosted.org/packages/6f/47/ef0d60ae23c41aced42921728650460dc831a0adf604bfa66b76028cb4d0/pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", size = 1839225 },
+    { url = "https://files.pythonhosted.org/packages/6a/23/430f2878c9cd977a61bb39f71751d9310ec55cee36b3d5bf1752c6341fd0/pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", size = 1768604 },
+    { url = "https://files.pythonhosted.org/packages/9e/2b/ec4e7225dee79e0dc80ccc3c35ab33cc2c4bbb8a1a7ecf060e5e453651ec/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", size = 1789767 },
+    { url = "https://files.pythonhosted.org/packages/64/b0/38b24a1fa6d2f96af3148362e10737ec073768cd44d3ec21dca3be40a519/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", size = 1772061 },
+    { url = "https://files.pythonhosted.org/packages/5e/da/bb73274c42cb60decfa61e9eb0c9029da78b3b9af0a9de0309dbc8ff87b6/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", size = 1974573 },
+    { url = "https://files.pythonhosted.org/packages/c8/65/41693110fb3552556180460daffdb8bbeefb87fc026fd9aa4b849374015c/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", size = 2625596 },
+    { url = "https://files.pythonhosted.org/packages/09/b3/a5a54b47cccd1ab661ed5775235c5e06924753c2d4817737c5667bfa19a8/pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", size = 2099064 },
+    { url = "https://files.pythonhosted.org/packages/52/fa/443a7a6ea54beaba45ff3a59f3d3e6e3004b7460bcfb0be77bcf98719d3b/pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", size = 1900345 },
+    { url = "https://files.pythonhosted.org/packages/8e/e6/9aca9ffae60f9cdf0183069de3e271889b628d0fb175913fcb3db5618fb1/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", size = 1968252 },
+    { url = "https://files.pythonhosted.org/packages/46/5e/6c716810ea20a6419188992973a73c2fb4eb99cd382368d0637ddb6d3c99/pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", size = 2119191 },
+    { url = "https://files.pythonhosted.org/packages/06/fc/6123b00a9240fbb9ae0babad7a005d51103d9a5d39c957a986f5cdd0c271/pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", size = 1717788 },
+    { url = "https://files.pythonhosted.org/packages/d5/36/e61ad5a46607a469e2786f398cd671ebafcd9fb17f09a2359985c7228df5/pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", size = 1898188 },
+    { url = "https://files.pythonhosted.org/packages/49/75/40b0e98b658fdba02a693b3bacb4c875a28bba87796c7b13975976597d8c/pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", size = 1838688 },
+    { url = "https://files.pythonhosted.org/packages/75/02/d8ba2d4a266591a6a623c68b331b96523d4b62ab82a951794e3ed8907390/pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", size = 1768409 },
+    { url = "https://files.pythonhosted.org/packages/91/ae/25ecd9bc4ce4993e99a1a3c9ab111c082630c914260e129572fafed4ecc2/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", size = 1789317 },
+    { url = "https://files.pythonhosted.org/packages/7a/80/72057580681cdbe55699c367963d9c661b569a1d39338b4f6239faf36cdc/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", size = 1771949 },
+    { url = "https://files.pythonhosted.org/packages/a2/be/d9bbabc55b05019013180f141fcaf3b14dbe15ca7da550e95b60c321009a/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", size = 1974392 },
+    { url = "https://files.pythonhosted.org/packages/79/2d/7bcd938c6afb0f40293283f5f09988b61fb0a4f1d180abe7c23a2f665f8e/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", size = 2625565 },
+    { url = "https://files.pythonhosted.org/packages/ac/88/ca758e979457096008a4b16a064509028e3e092a1e85a5ed6c18ced8da88/pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", size = 2098784 },
+    { url = "https://files.pythonhosted.org/packages/eb/de/2fad6d63c3c42e472e985acb12ec45b7f56e42e6f4cd6dfbc5e87ee8678c/pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", size = 1900198 },
+    { url = "https://files.pythonhosted.org/packages/fe/50/077c7f35b6488dc369a6d22993af3a37901e198630f38ac43391ca730f5b/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", size = 1968005 },
+    { url = "https://files.pythonhosted.org/packages/5d/1f/f378631574ead46d636b9a04a80ff878b9365d4b361b1905ef1667d4182a/pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", size = 2118920 },
+    { url = "https://files.pythonhosted.org/packages/7a/ea/e4943f17df7a3031d709481fe4363d4624ae875a6409aec34c28c9e6cf59/pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", size = 1717397 },
+    { url = "https://files.pythonhosted.org/packages/13/63/b95781763e8d84207025071c0cec16d921c0163c7a9033ae4b9a0e020dc7/pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", size = 1898013 },
+]
+
+[[package]]
+name = "pydub"
+version = "0.25.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327 },
+]
+
+[[package]]
+name = "pygments"
+version = "2.18.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
+]
+
+[[package]]
+name = "pyjwt"
+version = "2.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 },
+]
+
+[package.optional-dependencies]
+crypto = [
+    { name = "cryptography" },
+]
+
+[[package]]
+name = "pymilvus"
+version = "2.4.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "environs" },
+    { name = "grpcio" },
+    { name = "milvus-lite", marker = "sys_platform != 'win32'" },
+    { name = "pandas" },
+    { name = "protobuf" },
+    { name = "setuptools" },
+    { name = "ujson" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/40/20/203f77e8c35cac3e462307ba216c00525fd895618921d918fe814d426155/pymilvus-2.4.6.tar.gz", hash = "sha256:6ac3eb91c92cc01bbe444fe83f895f02d7b2546d96ac67998630bf31ac074d66", size = 1214006 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1b/41/af144715fc246ee2232e609bf93ab7cfd4b417e14087606fd6d93cce7336/pymilvus-2.4.6-py3-none-any.whl", hash = "sha256:b4c43472edc313b845d313be50610e19054e6954b2c5c3b515565c596c2d3d97", size = 197837 },
+]
+
+[[package]]
+name = "pymongo"
+version = "4.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "dnspython" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/05/2c/ad0896cb94668c3cad1eb702ab60ae17036b051f54cfe547f11a0322f1d3/pymongo-4.8.0.tar.gz", hash = "sha256:454f2295875744dc70f1881e4b2eb99cdad008a33574bc8aaf120530f66c0cde", size = 1506091 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0a/3d/bba2845c76dddcd8c34d5014da80346851df048eefa826acb13265affba2/pymongo-4.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b50040d9767197b77ed420ada29b3bf18a638f9552d80f2da817b7c4a4c9c68", size = 645578 },
+    { url = "https://files.pythonhosted.org/packages/c2/ca/d177c3ad846bad631b548b27c261821d25a08d608dca134aedb1b00b98fe/pymongo-4.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:417369ce39af2b7c2a9c7152c1ed2393edfd1cbaf2a356ba31eb8bcbd5c98dd7", size = 645731 },
+    { url = "https://files.pythonhosted.org/packages/be/1a/3d9b9fb3f9de9da46919fef900fe88090f5865a09ae9e0e19496a603a819/pymongo-4.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf821bd3befb993a6db17229a2c60c1550e957de02a6ff4dd0af9476637b2e4d", size = 1399930 },
+    { url = "https://files.pythonhosted.org/packages/57/64/281c9c8efb98ab6c6fcf44bf7cc33e17bcb163cb9c9260c9d78d2318d013/pymongo-4.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9365166aa801c63dff1a3cb96e650be270da06e3464ab106727223123405510f", size = 1451584 },
+    { url = "https://files.pythonhosted.org/packages/37/ed/5258d22a91ea6e0b9d72e0aa7674f5a9951fea0c036d1063f29bc45a35d2/pymongo-4.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc8b8582f4209c2459b04b049ac03c72c618e011d3caa5391ff86d1bda0cc486", size = 1423899 },
+    { url = "https://files.pythonhosted.org/packages/f3/7f/6d231046d9caf43395f9406dbef885f122edbee172ec6a3a6ea330e07848/pymongo-4.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e5019f75f6827bb5354b6fef8dfc9d6c7446894a27346e03134d290eb9e758", size = 1397112 },
+    { url = "https://files.pythonhosted.org/packages/af/81/4074148396415ac19074a1a144e1cd6b2ff000f5ef253ed24a4e3e9ff340/pymongo-4.8.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b5802151fc2b51cd45492c80ed22b441d20090fb76d1fd53cd7760b340ff554", size = 1357689 },
+    { url = "https://files.pythonhosted.org/packages/bc/26/799fe943573b2d86970698a0667d8d8636790e86242d979f4b3d870d269f/pymongo-4.8.0-cp311-cp311-win32.whl", hash = "sha256:4bf58e6825b93da63e499d1a58de7de563c31e575908d4e24876234ccb910eba", size = 611133 },
+    { url = "https://files.pythonhosted.org/packages/51/28/577224211f43e2079126bfec53080efba46e59218f47808098f125139558/pymongo-4.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b747c0e257b9d3e6495a018309b9e0c93b7f0d65271d1d62e572747f4ffafc88", size = 630990 },
+    { url = "https://files.pythonhosted.org/packages/9e/8d/b082d026f96215a76553032620549f931679da7f941018e2c358fd549faa/pymongo-4.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e6a720a3d22b54183352dc65f08cd1547204d263e0651b213a0a2e577e838526", size = 699090 },
+    { url = "https://files.pythonhosted.org/packages/eb/da/fa51bb7d8d5c8b4672b72c05a9357b5f9300f48128574c746fa4825f607a/pymongo-4.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31e4d21201bdf15064cf47ce7b74722d3e1aea2597c6785882244a3bb58c7eab", size = 698800 },
+    { url = "https://files.pythonhosted.org/packages/7b/dc/78f0c931d38bece6ae1dc49035961c82f3eb42952c745391ebdd3a910222/pymongo-4.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b804bb4f2d9dc389cc9e827d579fa327272cdb0629a99bfe5b83cb3e269ebf", size = 1655527 },
+    { url = "https://files.pythonhosted.org/packages/74/36/92f0eeeb5111c332072e37efb1d5a668c5e4b75be53cbd06a77f6b4192d2/pymongo-4.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fbdb87fe5075c8beb17a5c16348a1ea3c8b282a5cb72d173330be2fecf22f5", size = 1718203 },
+    { url = "https://files.pythonhosted.org/packages/98/40/757579f837dadaddf167cd36ae85a7ab29c035bc0ae8d90bdc8a5fbdfc33/pymongo-4.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd39455b7ee70aabee46f7399b32ab38b86b236c069ae559e22be6b46b2bbfc4", size = 1685776 },
+    { url = "https://files.pythonhosted.org/packages/24/bb/13d23966ad01511610a471eae480bcb6a94b832c40f2bdbc706f7a757b76/pymongo-4.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940d456774b17814bac5ea7fc28188c7a1338d4a233efbb6ba01de957bded2e8", size = 1650569 },
+    { url = "https://files.pythonhosted.org/packages/b5/80/1f405ce80cb6a3867709147e24a2f69e342ff71fb1b9ba663d0237f0c5ed/pymongo-4.8.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:236bbd7d0aef62e64caf4b24ca200f8c8670d1a6f5ea828c39eccdae423bc2b2", size = 1601592 },
+    { url = "https://files.pythonhosted.org/packages/30/19/cd66230b6407c6b8cf45c1ae073659a88af5699c792c46fd4eaf317bd11e/pymongo-4.8.0-cp312-cp312-win32.whl", hash = "sha256:47ec8c3f0a7b2212dbc9be08d3bf17bc89abd211901093e3ef3f2adea7de7a69", size = 656042 },
+    { url = "https://files.pythonhosted.org/packages/99/1c/f5108dc39450077556844abfd92b768c57775f85270fc0b1dc834ad18113/pymongo-4.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84bc7707492f06fbc37a9f215374d2977d21b72e10a67f1b31893ec5a140ad8", size = 680400 },
+]
+
+[[package]]
+name = "pymysql"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/ce59b5e5ed4ce8512f879ff1fa5ab699d211ae2495f1adaa5fbba2a1eada/pymysql-1.1.1.tar.gz", hash = "sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0", size = 47678 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0c/94/e4181a1f6286f545507528c78016e00065ea913276888db2262507693ce5/PyMySQL-1.1.1-py3-none-any.whl", hash = "sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c", size = 44972 },
+]
+
+[[package]]
+name = "pypandoc"
+version = "1.13"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/95/ac/40008af3ae4af9a5cc796803fb4d2ef82385632113f2c74f1ca1e2b5e3ed/pypandoc-1.13.tar.gz", hash = "sha256:31652073c7960c2b03570bd1e94f602ca9bc3e70099df5ead4cea98ff5151c1e", size = 32657 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/fc/09/91ab02feebc195a39ce0a39edcafbe866e69ff700a59790e605b3d5f69b1/pypandoc-1.13-py3-none-any.whl", hash = "sha256:4c7d71bf2f1ed122aac287113b5c4d537a33bbc3c1df5aed11a7d4a7ac074681", size = 21236 },
+]
+
+[[package]]
+name = "pyparsing"
+version = "3.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/3a/31fd28064d016a2182584d579e033ec95b809d8e220e74c4af6f0f2e8842/pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad", size = 889571 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9d/ea/6d76df31432a0e6fdf81681a895f009a4bb47b3c39036db3e1b528191d52/pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742", size = 103245 },
+]
+
+[[package]]
+name = "pypdf"
+version = "4.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f0/65/2ed7c9e1d31d860f096061b3dd2d665f501e09faaa0409a3f0d719d2a16d/pypdf-4.3.1.tar.gz", hash = "sha256:b2f37fe9a3030aa97ca86067a56ba3f9d3565f9a791b305c7355d8392c30d91b", size = 293266 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3c/60/eccdd92dd4af3e4bea6d6a342f7588c618a15b9bec4b968af581e498bcc4/pypdf-4.3.1-py3-none-any.whl", hash = "sha256:64b31da97eda0771ef22edb1bfecd5deee4b72c3d1736b7df2689805076d6418", size = 295825 },
+]
+
+[[package]]
+name = "pypika"
+version = "0.48.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/2c/94ed7b91db81d61d7096ac8f2d325ec562fc75e35f3baea8749c85b28784/PyPika-0.48.9.tar.gz", hash = "sha256:838836a61747e7c8380cd1b7ff638694b7a7335345d0f559b04b2cd832ad5378", size = 67259 }
+
+[[package]]
+name = "pyproject-hooks"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/c7/07/6f63dda440d4abb191b91dc383b472dae3dd9f37e4c1e4a5c3db150531c6/pyproject_hooks-1.1.0.tar.gz", hash = "sha256:4b37730834edbd6bd37f26ece6b44802fb1c1ee2ece0e54ddff8bfc06db86965", size = 7838 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl", hash = "sha256:7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2", size = 9184 },
+]
+
+[[package]]
+name = "pyreadline3"
+version = "3.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/86/3d61a61f36a0067874a00cb4dceb9028d34b6060e47828f7fc86fb9f7ee9/pyreadline3-3.4.1.tar.gz", hash = "sha256:6f3d1f7b8a31ba32b73917cefc1f28cc660562f39aea8646d30bd6eff21f7bae", size = 86465 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/56/fc/a3c13ded7b3057680c8ae95a9b6cc83e63657c38e0005c400a5d018a33a7/pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb", size = 95203 },
+]
+
+[[package]]
+name = "pytest"
+version = "8.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorama", marker = "sys_platform == 'win32'" },
+    { name = "iniconfig" },
+    { name = "packaging" },
+    { name = "pluggy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a6/58/e993ca5357553c966b9e73cb3475d9c935fe9488746e13ebdf9b80fae508/pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977", size = 1427980 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4e/e7/81ebdd666d3bff6670d27349b5053605d83d55548e6bd5711f3b0ae7dd23/pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343", size = 339873 },
+]
+
+[[package]]
+name = "pytest-docker"
+version = "3.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "attrs" },
+    { name = "pytest" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e7/a6/543f2fb157ad228fcc04a8974aa16c989058834d8e539608814cf722f1b3/pytest-docker-3.1.1.tar.gz", hash = "sha256:2371524804a752aaa766c79b9eee8e634534afddb82597f3b573da7c5d6ffb5f", size = 12918 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/1a/a4/69defc13bf77ee5aeb3e7b7c45393d6c7312e9c4d8b55d280a094ff76ff3/pytest_docker-3.1.1-py3-none-any.whl", hash = "sha256:fd0d48d6feac41f62acbc758319215ec9bb805c2309622afb07c27fa5c5ae362", size = 8243 },
+]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "six" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
+]
+
+[[package]]
+name = "python-engineio"
+version = "4.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "simple-websocket" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/01/94faf505820f1fb94133a456dad87a76df589f6999de563229a342e412fa/python_engineio-4.9.1.tar.gz", hash = "sha256:7631cf5563086076611e494c643b3fa93dd3a854634b5488be0bba0ef9b99709", size = 89549 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ad/38/4642c75241686c9cf05d23c50b9ffbd760507292f12fdfb04adc2ab5d34a/python_engineio-4.9.1-py3-none-any.whl", hash = "sha256:f995e702b21f6b9ebde4e2000cd2ad0112ba0e5116ec8d22fe3515e76ba9dddd", size = 57686 },
+]
+
+[[package]]
+name = "python-iso639"
+version = "2024.4.27"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ff/d9/fbab4ccc1a712b989b28c1c964c94096aa4b44770245a49439532e787416/python_iso639-2024.4.27.tar.gz", hash = "sha256:97e63b5603e085c6a56a12a95740010e75d9134e0aab767e0978b53fd8824f13", size = 279163 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/01/08/5e649cf18dec750d498c53c6c8eb1d9790752ebd50fa7f7e69cc0c277cfe/python_iso639-2024.4.27-py3-none-any.whl", hash = "sha256:27526a84cebc4c4d53fea9d1ebbc7209c8d279bebaa343e6765a1fc8780565ab", size = 274742 },
+]
+
+[[package]]
+name = "python-jose"
+version = "3.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "ecdsa" },
+    { name = "pyasn1" },
+    { name = "rsa" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e4/19/b2c86504116dc5f0635d29f802da858404d77d930a25633d2e86a64a35b3/python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a", size = 129068 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/bd/2d/e94b2f7bab6773c70efc70a61d66e312e1febccd9e0db6b9e0adf58cbad1/python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a", size = 33530 },
+]
+
+[[package]]
+name = "python-magic"
+version = "0.4.27"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840 },
+]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5c/0f/9c55ac6c84c0336e22a26fa84ca6c51d58d7ac3a2d78b0dfa8748826c883/python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026", size = 31516 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3d/47/444768600d9e0ebc82f8e347775d24aef8f6348cf00e9fa0e81910814e6d/python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215", size = 22299 },
+]
+
+[[package]]
+name = "python-oxmsg"
+version = "0.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "olefile" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4f/d4/4ec721fd433453fe05344f41f17458775d111e9f6c668ce1a0fccec0fecd/python_oxmsg-0.0.1.tar.gz", hash = "sha256:b65c1f93d688b85a9410afa824192a1ddc39da359b04a0bd2cbd3874e84d4994", size = 34541 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d4/c8/fb23e1e7723ba9200b75bc121f22f67498ae098a202f1646acc4f6a54f5c/python_oxmsg-0.0.1-py3-none-any.whl", hash = "sha256:8ea7d5dda1bc161a413213da9e18ed152927c1fda2feaf5d1f02192d8ad45eea", size = 31426 },
+]
+
+[[package]]
+name = "python-pptx"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "lxml" },
+    { name = "pillow" },
+    { name = "typing-extensions" },
+    { name = "xlsxwriter" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5d/b5/b5f64158c9230429bbe9b87a372ccda6ce9b0a64fa48c43a377be284e144/python_pptx-1.0.0.tar.gz", hash = "sha256:5c0f9fbf564fccf825c03c8bb75af9245fbdfad75e2857d115f47fd94b65eae8", size = 10109490 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9b/06/62d0069a8b6ece6dded497593538a97339121e3096eff77ba6f3734a84a7/python_pptx-1.0.0-py3-none-any.whl", hash = "sha256:e099cbcb370e97ae1cca2186ac757774a2c7bf1b5cb7e6ac3cb77061466713c5", size = 472287 },
+]
+
+[[package]]
+name = "python-socketio"
+version = "5.11.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "bidict" },
+    { name = "python-engineio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1e/74/b1e8787cea757e1f533a7878e94f929679ef7e07a2aaf44de6b71065b1f2/python_socketio-5.11.3.tar.gz", hash = "sha256:194af8cdbb7b0768c2e807ba76c7abc288eb5bb85559b7cddee51a6bc7a65737", size = 117702 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e9/59/5ee858d5736594d75385b9a8c0f65af6eca5da2b359ed3fb6a7486526399/python_socketio-5.11.3-py3-none-any.whl", hash = "sha256:2a923a831ff70664b7c502df093c423eb6aa93c1ce68b8319e840227a26d8b69", size = 76180 },
+]
+
+[[package]]
+name = "pytube"
+version = "15.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/e7/16fec46c8d255c4bbc4b185d89c91dc92cdb802836570d8004d0db169c91/pytube-15.0.0.tar.gz", hash = "sha256:076052efe76f390dfa24b1194ff821d4e86c17d41cb5562f3a276a8bcbfc9d1d", size = 67229 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/51/64/bcf8632ed2b7a36bbf84a0544885ffa1d0b4bcf25cc0903dba66ec5fdad9/pytube-15.0.0-py3-none-any.whl", hash = "sha256:07b9904749e213485780d7eb606e5e5b8e4341aa4dccf699160876da00e12d78", size = 57594 },
+]
+
+[[package]]
+name = "pytz"
+version = "2024.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 },
+]
+
+[[package]]
+name = "pywin32"
+version = "306"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689 },
+    { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547 },
+    { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324 },
+    { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705 },
+    { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429 },
+    { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145 },
+]
+
+[[package]]
+name = "pyxlsb"
+version = "1.0.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3f/13/eebaeb7a40b062d1c6f7f91d09e73d30a69e33e4baa7cbe4b7658548b1cd/pyxlsb-1.0.10.tar.gz", hash = "sha256:8062d1ea8626d3f1980e8b1cfe91a4483747449242ecb61013bc2df85435f685", size = 22424 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/7e/92/345823838ae367c59b63e03aef9c331f485370f9df6d049256a61a28f06d/pyxlsb-1.0.10-py2.py3-none-any.whl", hash = "sha256:87c122a9a622e35ca5e741d2e541201d28af00fb46bec492cfa9586890b120b4", size = 23849 },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
+    { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
+    { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
+    { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
+    { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
+    { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
+    { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
+    { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
+    { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
+    { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
+    { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
+    { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
+    { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
+    { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
+    { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
+    { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
+    { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
+    { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
+    { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
+    { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
+    { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
+    { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
+    { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
+    { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
+    { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
+    { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
+    { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
+]
+
+[[package]]
+name = "rank-bm25"
+version = "0.2.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fc/0a/f9579384aa017d8b4c15613f86954b92a95a93d641cc849182467cf0bb3b/rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d", size = 8347 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2a/21/f691fb2613100a62b3fa91e9988c991e9ca5b89ea31c0d3152a3210344f9/rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae", size = 8584 },
+]
+
+[[package]]
+name = "rapidfuzz"
+version = "3.9.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e8/b0/e0756b5efe826c1bdf6442777cc924b41258685dcf372ee77399cc10408e/rapidfuzz-3.9.6.tar.gz", hash = "sha256:5cf2a7d621e4515fee84722e93563bf77ff2cbe832a77a48b81f88f9e23b9e8d", size = 1596107 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5e/55/5ba0016fe8fba98d8ff55832dd7d79f2d6b93fe27be7863ccf3f79366d76/rapidfuzz-3.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:52e4675f642fbc85632f691b67115a243cd4d2a47bdcc4a3d9a79e784518ff97", size = 2055435 },
+    { url = "https://files.pythonhosted.org/packages/55/23/1d0c51c01fbff028ff5746a388edd610a591e76153fca72f6f7e68b5fc14/rapidfuzz-3.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1f93a2f13038700bd245b927c46a2017db3dcd4d4ff94687d74b5123689b873b", size = 1510617 },
+    { url = "https://files.pythonhosted.org/packages/28/d6/8dd267f4377d8bf1698d891a28c24d417499724355f6a3541c0b5ab1c35d/rapidfuzz-3.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b70500bca460264b8141d8040caee22e9cf0418c5388104ff0c73fb69ee28f", size = 1559930 },
+    { url = "https://files.pythonhosted.org/packages/6a/3e/254fd9e2ce895480bc43a5a11a35d4825b11918d694c959cc0e214d842a9/rapidfuzz-3.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1e037fb89f714a220f68f902fc6300ab7a33349f3ce8ffae668c3b3a40b0b06", size = 5964374 },
+    { url = "https://files.pythonhosted.org/packages/d2/cd/3e555024d9168dbd732e919fb0f7d05c3f6809d6ed86042383efeb6f98a0/rapidfuzz-3.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6792f66d59b86ccfad5e247f2912e255c85c575789acdbad8e7f561412ffed8a", size = 1825493 },
+    { url = "https://files.pythonhosted.org/packages/32/5f/c47c511e2b174e80ea1091722359b4db7900e4987a4bcacffae6f27237c0/rapidfuzz-3.9.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68d9cffe710b67f1969cf996983608cee4490521d96ea91d16bd7ea5dc80ea98", size = 1830053 },
+    { url = "https://files.pythonhosted.org/packages/e9/85/88f1fd986714887ad4448c7b321c85b9aa5842df9deb606bcdfdfc35fae6/rapidfuzz-3.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63daaeeea76da17fa0bbe7fb05cba8ed8064bb1a0edf8360636557f8b6511961", size = 3384091 },
+    { url = "https://files.pythonhosted.org/packages/cc/3f/9a941793dbc419a9e9aad742d8057c8440f191ab3758d1ce2c12e9f27ccd/rapidfuzz-3.9.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d214e063bffa13e3b771520b74f674b22d309b5720d4df9918ff3e0c0f037720", size = 2458230 },
+    { url = "https://files.pythonhosted.org/packages/9d/40/40b75226e0b45ba0212b8ce19996b8970fd3de4748341f6bdd0302b54856/rapidfuzz-3.9.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ed443a2062460f44c0346cb9d269b586496b808c2419bbd6057f54061c9b9c75", size = 7239493 },
+    { url = "https://files.pythonhosted.org/packages/df/62/47401ac22299f70a8231f7e7421b3f804cd716f6a24521ef172dec7afe01/rapidfuzz-3.9.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5b0c9b227ee0076fb2d58301c505bb837a290ae99ee628beacdb719f0626d749", size = 2837398 },
+    { url = "https://files.pythonhosted.org/packages/59/f2/a3db1b31dc80d906528e03d213a9f9bd8c44547cb1cbf9a3c59649058c2e/rapidfuzz-3.9.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:82c9722b7dfaa71e8b61f8c89fed0482567fb69178e139fe4151fc71ed7df782", size = 3386617 },
+    { url = "https://files.pythonhosted.org/packages/5f/41/2d285edb31f718c81e3433a56142d5e606ba20d0997fcf57774042f79850/rapidfuzz-3.9.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c18897c95c0a288347e29537b63608a8f63a5c3cb6da258ac46fcf89155e723e", size = 4392081 },
+    { url = "https://files.pythonhosted.org/packages/37/7b/d355309d5aa606dd91d91501a64c87db32e3d75b775950a19df8ec0288f0/rapidfuzz-3.9.6-cp311-cp311-win32.whl", hash = "sha256:3e910cf08944da381159587709daaad9e59d8ff7bca1f788d15928f3c3d49c2a", size = 1854906 },
+    { url = "https://files.pythonhosted.org/packages/aa/bb/cdd512d40f8ea67692deee6b0da4f7235c6a0f9e126fdded32b62c5d91fe/rapidfuzz-3.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:59c4a61fab676d37329fc3a671618a461bfeef53a4d0b8b12e3bc24a14e166f8", size = 1654104 },
+    { url = "https://files.pythonhosted.org/packages/53/92/5014563b1e8f901f983f96606c6982ff4ed286a8ae4335b67012a4a50cf9/rapidfuzz-3.9.6-cp311-cp311-win_arm64.whl", hash = "sha256:8b4afea244102332973377fddbe54ce844d0916e1c67a5123432291717f32ffa", size = 855134 },
+    { url = "https://files.pythonhosted.org/packages/df/f4/e8175a4ad862ede4caa8dd287d187d12db2f4eb426b00b030b1cb7f4d2dd/rapidfuzz-3.9.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:70591b28b218fff351b88cdd7f2359a01a71f9f7f5a2e465ce3715ed4b3c422b", size = 2053429 },
+    { url = "https://files.pythonhosted.org/packages/22/a5/8c14e41bcdea3be343764de6ad464ff87300352f8e2d01064bf0f1809e9d/rapidfuzz-3.9.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee2d8355c7343c631a03e57540ea06e8717c19ecf5ff64ea07e0498f7f161457", size = 1506224 },
+    { url = "https://files.pythonhosted.org/packages/75/92/51d74bdf539475d8c71df76da73d4609231254ac2499beb24efa78667b14/rapidfuzz-3.9.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:708fb675de0f47b9635d1cc6fbbf80d52cb710d0a1abbfae5c84c46e3abbddc3", size = 1542825 },
+    { url = "https://files.pythonhosted.org/packages/b9/7a/29bf00754308ba43eb6f95988445b86953b29bc780164f8961d0c3d67b89/rapidfuzz-3.9.6-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d66c247c2d3bb7a9b60567c395a15a929d0ebcc5f4ceedb55bfa202c38c6e0c", size = 5858827 },
+    { url = "https://files.pythonhosted.org/packages/0a/a3/1b5a2bb95e5b532fe571b6b0c8c4cf35893e748eac5cac2ae7d3c0f41d47/rapidfuzz-3.9.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15146301b32e6e3d2b7e8146db1a26747919d8b13690c7f83a4cb5dc111b3a08", size = 1794607 },
+    { url = "https://files.pythonhosted.org/packages/0e/42/7ee9c15087d6b7146e73e4650bfb8ddbbe2161a9edbe2b44be9ffb68a2f1/rapidfuzz-3.9.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7a03da59b6c7c97e657dd5cd4bcaab5fe4a2affd8193958d6f4d938bee36679", size = 1818934 },
+    { url = "https://files.pythonhosted.org/packages/15/30/0a4bc8b641e2374475c03d6c6ec6568303ac2070698c067c690daefd9b8f/rapidfuzz-3.9.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d2c2fe19e392dbc22695b6c3b2510527e2b774647e79936bbde49db7742d6f1", size = 3381282 },
+    { url = "https://files.pythonhosted.org/packages/e1/45/6c9bbba66a5ada5679c0705375068337c2573a940443868cbcba5c909c07/rapidfuzz-3.9.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:91aaee4c94cb45930684f583ffc4e7c01a52b46610971cede33586cf8a04a12e", size = 2425764 },
+    { url = "https://files.pythonhosted.org/packages/74/b3/b02d002e643ec5f97278b6f04c2293b9b678249220f253b7e649a4e0df3a/rapidfuzz-3.9.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3f5702828c10768f9281180a7ff8597da1e5002803e1304e9519dd0f06d79a85", size = 7176966 },
+    { url = "https://files.pythonhosted.org/packages/3f/0d/52cd49cafb91fc0cc4e75e3beab7b16fffe62a6c66d5fb5d336b2deacda5/rapidfuzz-3.9.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ccd1763b608fb4629a0b08f00b3c099d6395e67c14e619f6341b2c8429c2f310", size = 2800253 },
+    { url = "https://files.pythonhosted.org/packages/4f/ee/82004bf9274566711b134bb2628bf878046a9212c798bd10f0138ad6cca9/rapidfuzz-3.9.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc7a0d4b2cb166bc46d02c8c9f7551cde8e2f3c9789df3827309433ee9771163", size = 3345106 },
+    { url = "https://files.pythonhosted.org/packages/d8/78/7008dcd6701cc84eb74fad6c743f03c33c7f41516d15a313fcd759b2d614/rapidfuzz-3.9.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7496f53d40560a58964207b52586783633f371683834a8f719d6d965d223a2eb", size = 4360019 },
+    { url = "https://files.pythonhosted.org/packages/07/25/5de7013daac2eb7b18bc0123fa5d83ddbe5508673b14b9ca7e32f2be3cbe/rapidfuzz-3.9.6-cp312-cp312-win32.whl", hash = "sha256:5eb1a9272ca71bc72be5415c2fa8448a6302ea4578e181bb7da9db855b367df0", size = 1842659 },
+    { url = "https://files.pythonhosted.org/packages/7b/81/bfd28ed4a5638b594985988921d19fd716f119dde93703d8545c1bcb8e7e/rapidfuzz-3.9.6-cp312-cp312-win_amd64.whl", hash = "sha256:0d21fc3c0ca507a1180152a6dbd129ebaef48facde3f943db5c1055b6e6be56a", size = 1648213 },
+    { url = "https://files.pythonhosted.org/packages/8a/81/249ed13ce5cee9c9aa9c69656e2d78ed240534d478ebaada04930513820a/rapidfuzz-3.9.6-cp312-cp312-win_arm64.whl", hash = "sha256:43bb27a57c29dc5fa754496ba6a1a508480d21ae99ac0d19597646c16407e9f3", size = 849878 },
+    { url = "https://files.pythonhosted.org/packages/d1/de/83b660bb054a3bd85f033a2521ba99ae65e49b95f3b18745cfcf68a94ae7/rapidfuzz-3.9.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:83a5ac6547a9d6eedaa212975cb8f2ce2aa07e6e30833b40e54a52b9f9999aa4", size = 2026087 },
+    { url = "https://files.pythonhosted.org/packages/6f/f3/7b7f8ddb80562722c6cb86a3396ad6945c0003f36addbca610782fe7e113/rapidfuzz-3.9.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:10f06139142ecde67078ebc9a745965446132b998f9feebffd71acdf218acfcc", size = 1499413 },
+    { url = "https://files.pythonhosted.org/packages/bd/e9/a37db9a3674b210d6ef21b15c66eb1585125d553ed5b73f6481dadf932c3/rapidfuzz-3.9.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74720c3f24597f76c7c3e2c4abdff55f1664f4766ff5b28aeaa689f8ffba5fab", size = 1537241 },
+    { url = "https://files.pythonhosted.org/packages/75/67/e111c7a11eafd70677d6688c36d93b54a43783119b16ee5591589a110ff9/rapidfuzz-3.9.6-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce2bce52b5c150878e558a0418c2b637fb3dbb6eb38e4eb27d24aa839920483e", size = 5877164 },
+    { url = "https://files.pythonhosted.org/packages/80/58/ce20051fbbac74bc369d4ab3f450683ebf70e456474d6172ba9b6f6164f0/rapidfuzz-3.9.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1611199f178793ca9a060c99b284e11f6d7d124998191f1cace9a0245334d219", size = 1766888 },
+    { url = "https://files.pythonhosted.org/packages/8f/b4/afde433a48fb18f5d7ddf144ffb2b0b674f3f176a2aca89f2ea06fe54d4f/rapidfuzz-3.9.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0308b2ad161daf502908a6e21a57c78ded0258eba9a8f5e2545e2dafca312507", size = 1819868 },
+    { url = "https://files.pythonhosted.org/packages/53/47/089d2d8a27c4e90dd4dfb532d31bc873537fcc479874e9909a9018156855/rapidfuzz-3.9.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eda91832201b86e3b70835f91522587725bec329ec68f2f7faf5124091e5ca7", size = 3361094 },
+    { url = "https://files.pythonhosted.org/packages/95/a5/f5e1fc00485f45ed17803f91f7877fa07b0fb4006ff42cfebd7355ad1bdc/rapidfuzz-3.9.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ece873c093aedd87fc07c2a7e333d52e458dc177016afa1edaf157e82b6914d8", size = 2421352 },
+    { url = "https://files.pythonhosted.org/packages/57/fa/0d6573980251fb63147a35be8f4fae962d79ebbe4b675fdaf0cf16726aa0/rapidfuzz-3.9.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d97d3c9d209d5c30172baea5966f2129e8a198fec4a1aeb2f92abb6e82a2edb1", size = 7190683 },
+    { url = "https://files.pythonhosted.org/packages/71/50/2f866735f788d565a2ed6d113a54a410b4a412b46cb399e3ae466e72730f/rapidfuzz-3.9.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6c4550d0db4931f5ebe9f0678916d1b06f06f5a99ba0b8a48b9457fd8959a7d4", size = 2791255 },
+    { url = "https://files.pythonhosted.org/packages/a2/90/7c36906695b216bf489cd234d95e5e00f06f976054ce3c441085a5ae650f/rapidfuzz-3.9.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b6b8dd4af6324fc325d9483bec75ecf9be33e590928c9202d408e4eafff6a0a6", size = 3341004 },
+    { url = "https://files.pythonhosted.org/packages/16/2b/15f6df84ef774457fd77a80436154e8fc6c903bfc997490a275fca84adda/rapidfuzz-3.9.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16122ae448bc89e2bea9d81ce6cb0f751e4e07da39bd1e70b95cae2493857853", size = 4355422 },
+    { url = "https://files.pythonhosted.org/packages/7f/3b/e593d8f0b7bb2c1cb00514552056a4b61000d2d8f4fd24e016fdd2e1afe1/rapidfuzz-3.9.6-cp313-cp313-win32.whl", hash = "sha256:71cc168c305a4445109cd0d4925406f6e66bcb48fde99a1835387c58af4ecfe9", size = 1840420 },
+    { url = "https://files.pythonhosted.org/packages/fe/09/d25939dfe7eea5e4a474dfebd60073acd792621d42a9ffe7534627c8ad26/rapidfuzz-3.9.6-cp313-cp313-win_amd64.whl", hash = "sha256:59ee78f2ecd53fef8454909cda7400fe2cfcd820f62b8a5d4dfe930102268054", size = 1645331 },
+    { url = "https://files.pythonhosted.org/packages/d8/26/6eda9e43dd5b0833feb4cf9ea0ad4e46b07c4811d8b8815a8517b379c640/rapidfuzz-3.9.6-cp313-cp313-win_arm64.whl", hash = "sha256:58b4ce83f223605c358ae37e7a2d19a41b96aa65b1fede99cc664c9053af89ac", size = 848759 },
+]
+
+[[package]]
+name = "rapidocr-onnxruntime"
+version = "1.3.24"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+    { name = "onnxruntime" },
+    { name = "opencv-python" },
+    { name = "pillow" },
+    { name = "pyclipper" },
+    { name = "pyyaml" },
+    { name = "shapely" },
+    { name = "six" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/54/08/6b3c1762c9800faa4346d6141b88af9f94f3680c9d83774e700b1b75c256/rapidocr_onnxruntime-1.3.24-py3-none-any.whl", hash = "sha256:4282ff0b8db05ad2a53afc8d0ef2e7d879c53022fc61a4f8e84c58d737822cd2", size = 14910742 },
+]
+
+[[package]]
+name = "red-black-tree-mod"
+version = "1.20"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/34/12/944f61bc67a1e918953741c0b3b75a28f96d8060d08fd3614233309ced3b/red-black-tree-mod-1.20.tar.gz", hash = "sha256:2448e6fc9cbf1be204c753f352c6ee49aa8156dbf1faa57dfc26bd7705077e0a", size = 28589 }
+
+[[package]]
+name = "redis"
+version = "5.0.8"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "async-timeout", marker = "python_full_version < '3.11.3'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/10/defc227d65ea9c2ff5244645870859865cba34da7373477c8376629746ec/redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870", size = 4595651 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c5/d1/19a9c76811757684a0f74adc25765c8a901d67f9f6472ac9d57c844a23c8/redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4", size = 255608 },
+]
+
+[[package]]
+name = "regex"
+version = "2024.7.24"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3f/51/64256d0dc72816a4fe3779449627c69ec8fee5a5625fd60ba048f53b3478/regex-2024.7.24.tar.gz", hash = "sha256:9cfd009eed1a46b27c14039ad5bbc5e71b6367c5b2e6d5f5da0ea91600817506", size = 393485 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/cb/ec/261f8434a47685d61e59a4ef3d9ce7902af521219f3ebd2194c7adb171a6/regex-2024.7.24-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:382281306e3adaaa7b8b9ebbb3ffb43358a7bbf585fa93821300a418bb975281", size = 470810 },
+    { url = "https://files.pythonhosted.org/packages/f0/47/f33b1cac88841f95fff862476a9e875d9a10dae6912a675c6f13c128e5d9/regex-2024.7.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fdd1384619f406ad9037fe6b6eaa3de2749e2e12084abc80169e8e075377d3b", size = 282126 },
+    { url = "https://files.pythonhosted.org/packages/fc/1b/256ca4e2d5041c0aa2f1dc222f04412b796346ab9ce2aa5147405a9457b4/regex-2024.7.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3d974d24edb231446f708c455fd08f94c41c1ff4f04bcf06e5f36df5ef50b95a", size = 278920 },
+    { url = "https://files.pythonhosted.org/packages/91/03/4603ec057c0bafd2f6f50b0bdda4b12a0ff81022decf1de007b485c356a6/regex-2024.7.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec4419a3fe6cf8a4795752596dfe0adb4aea40d3683a132bae9c30b81e8d73", size = 785420 },
+    { url = "https://files.pythonhosted.org/packages/75/f8/13b111fab93e6273e26de2926345e5ecf6ddad1e44c4d419d7b0924f9c52/regex-2024.7.24-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb563dd3aea54c797adf513eeec819c4213d7dbfc311874eb4fd28d10f2ff0f2", size = 828164 },
+    { url = "https://files.pythonhosted.org/packages/4a/80/bc3b9d31bd47ff578758af929af0ac1d6169b247e26fa6e87764007f3d93/regex-2024.7.24-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:45104baae8b9f67569f0f1dca5e1f1ed77a54ae1cd8b0b07aba89272710db61e", size = 812621 },
+    { url = "https://files.pythonhosted.org/packages/8b/77/92d4a14530900d46dddc57b728eea65d723cc9fcfd07b96c2c141dabba84/regex-2024.7.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994448ee01864501912abf2bad9203bffc34158e80fe8bfb5b031f4f8e16da51", size = 786609 },
+    { url = "https://files.pythonhosted.org/packages/35/58/06695fd8afad4c8ed0a53ec5e222156398b9fe5afd58887ab94ea68e4d16/regex-2024.7.24-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fac296f99283ac232d8125be932c5cd7644084a30748fda013028c815ba3364", size = 775290 },
+    { url = "https://files.pythonhosted.org/packages/1b/0f/50b97ee1fc6965744b9e943b5c0f3740792ab54792df73d984510964ef29/regex-2024.7.24-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7e37e809b9303ec3a179085415cb5f418ecf65ec98cdfe34f6a078b46ef823ee", size = 772849 },
+    { url = "https://files.pythonhosted.org/packages/8f/64/565ff6cf241586ab7ae76bb4138c4d29bc1d1780973b457c2db30b21809a/regex-2024.7.24-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:01b689e887f612610c869421241e075c02f2e3d1ae93a037cb14f88ab6a8934c", size = 778428 },
+    { url = "https://files.pythonhosted.org/packages/e5/fe/4ceabf4382e44e1e096ac46fd5e3bca490738b24157116a48270fd542e88/regex-2024.7.24-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f6442f0f0ff81775eaa5b05af8a0ffa1dda36e9cf6ec1e0d3d245e8564b684ce", size = 849436 },
+    { url = "https://files.pythonhosted.org/packages/68/23/1868e40d6b594843fd1a3498ffe75d58674edfc90d95e18dd87865b93bf2/regex-2024.7.24-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:871e3ab2838fbcb4e0865a6e01233975df3a15e6fce93b6f99d75cacbd9862d1", size = 849484 },
+    { url = "https://files.pythonhosted.org/packages/f3/52/bff76de2f6e2bc05edce3abeb7e98e6309aa022fc06071100a0216fbeb50/regex-2024.7.24-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c918b7a1e26b4ab40409820ddccc5d49871a82329640f5005f73572d5eaa9b5e", size = 776712 },
+    { url = "https://files.pythonhosted.org/packages/f2/72/70ade7b0b5fe5c6df38fdfa2a5a8273e3ea6a10b772aa671b7e889e78bae/regex-2024.7.24-cp311-cp311-win32.whl", hash = "sha256:2dfbb8baf8ba2c2b9aa2807f44ed272f0913eeeba002478c4577b8d29cde215c", size = 257716 },
+    { url = "https://files.pythonhosted.org/packages/04/4d/80e04f4e27ab0cbc9096e2d10696da6d9c26a39b60db52670fd57614fea5/regex-2024.7.24-cp311-cp311-win_amd64.whl", hash = "sha256:538d30cd96ed7d1416d3956f94d54e426a8daf7c14527f6e0d6d425fcb4cca52", size = 269662 },
+    { url = "https://files.pythonhosted.org/packages/0f/26/f505782f386ac0399a9237571833f187414882ab6902e2e71a1ecb506835/regex-2024.7.24-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4ebef608553aff8deb845c7f4f1d0740ff76fa672c011cc0bacb2a00fbde86", size = 471748 },
+    { url = "https://files.pythonhosted.org/packages/bb/1d/ea9a21beeb433dbfca31ab82867d69cb67ff8674af9fab6ebd55fa9d3387/regex-2024.7.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:74007a5b25b7a678459f06559504f1eec2f0f17bca218c9d56f6a0a12bfffdad", size = 282841 },
+    { url = "https://files.pythonhosted.org/packages/9b/f2/c6182095baf0a10169c34e87133a8e73b2e816a80035669b1278e927685e/regex-2024.7.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7df9ea48641da022c2a3c9c641650cd09f0cd15e8908bf931ad538f5ca7919c9", size = 279114 },
+    { url = "https://files.pythonhosted.org/packages/72/58/b5161bf890b6ca575a25685f19a4a3e3b6f4a072238814f8658123177d84/regex-2024.7.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a1141a1dcc32904c47f6846b040275c6e5de0bf73f17d7a409035d55b76f289", size = 789749 },
+    { url = "https://files.pythonhosted.org/packages/09/fb/5381b19b62f3a3494266be462f6a015a869cf4bfd8e14d6e7db67e2c8069/regex-2024.7.24-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80c811cfcb5c331237d9bad3bea2c391114588cf4131707e84d9493064d267f9", size = 831666 },
+    { url = "https://files.pythonhosted.org/packages/3d/6d/2a21c85f970f9be79357d12cf4b97f4fc6bf3bf6b843c39dabbc4e5f1181/regex-2024.7.24-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7214477bf9bd195894cf24005b1e7b496f46833337b5dedb7b2a6e33f66d962c", size = 817544 },
+    { url = "https://files.pythonhosted.org/packages/f9/ae/5f23e64f6cf170614237c654f3501a912dfb8549143d4b91d1cd13dba319/regex-2024.7.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d55588cba7553f0b6ec33130bc3e114b355570b45785cebdc9daed8c637dd440", size = 790854 },
+    { url = "https://files.pythonhosted.org/packages/29/0a/d04baad1bbc49cdfb4aef90c4fc875a60aaf96d35a1616f1dfe8149716bc/regex-2024.7.24-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558a57cfc32adcf19d3f791f62b5ff564922942e389e3cfdb538a23d65a6b610", size = 779242 },
+    { url = "https://files.pythonhosted.org/packages/3a/27/b242a962f650c3213da4596d70e24c7c1c46e3aa0f79f2a81164291085f8/regex-2024.7.24-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a512eed9dfd4117110b1881ba9a59b31433caed0c4101b361f768e7bcbaf93c5", size = 776932 },
+    { url = "https://files.pythonhosted.org/packages/9c/ae/de659bdfff80ad2c0b577a43dd89dbc43870a4fc4bbf604e452196758e83/regex-2024.7.24-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:86b17ba823ea76256b1885652e3a141a99a5c4422f4a869189db328321b73799", size = 784521 },
+    { url = "https://files.pythonhosted.org/packages/d4/ac/eb6a796da0bdefbf09644a7868309423b18d344cf49963a9d36c13502d46/regex-2024.7.24-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5eefee9bfe23f6df09ffb6dfb23809f4d74a78acef004aa904dc7c88b9944b05", size = 854548 },
+    { url = "https://files.pythonhosted.org/packages/56/77/fde8d825dec69e70256e0925af6c81eea9acf0a634d3d80f619d8dcd6888/regex-2024.7.24-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:731fcd76bbdbf225e2eb85b7c38da9633ad3073822f5ab32379381e8c3c12e94", size = 853345 },
+    { url = "https://files.pythonhosted.org/packages/ff/04/2b79ad0bb9bc05ab4386caa2c19aa047a66afcbdfc2640618ffc729841e4/regex-2024.7.24-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eaef80eac3b4cfbdd6de53c6e108b4c534c21ae055d1dbea2de6b3b8ff3def38", size = 781414 },
+    { url = "https://files.pythonhosted.org/packages/bf/71/d0af58199283ada7d25b20e416f5b155f50aad99b0e791c0966ff5a1cd00/regex-2024.7.24-cp312-cp312-win32.whl", hash = "sha256:185e029368d6f89f36e526764cf12bf8d6f0e3a2a7737da625a76f594bdfcbfc", size = 258125 },
+    { url = "https://files.pythonhosted.org/packages/95/b3/10e875c45c60b010b66fc109b899c6fc4f05d485fe1d54abff98ce791124/regex-2024.7.24-cp312-cp312-win_amd64.whl", hash = "sha256:2f1baff13cc2521bea83ab2528e7a80cbe0ebb2c6f0bfad15be7da3aed443908", size = 269162 },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.3"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "certifi" },
+    { name = "charset-normalizer" },
+    { name = "idna" },
+    { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
+]
+
+[[package]]
+name = "requests-oauthlib"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "oauthlib" },
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 },
+]
+
+[[package]]
+name = "requests-toolbelt"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 },
+]
+
+[[package]]
+name = "rich"
+version = "13.7.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "markdown-it-py" },
+    { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b3/01/c954e134dc440ab5f96952fe52b4fdc64225530320a910473c1fe270d9aa/rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432", size = 221248 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/87/67/a37f6214d0e9fe57f6ae54b2956d550ca8365857f42a1ce0392bb21d9410/rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222", size = 240681 },
+]
+
+[[package]]
+name = "rsa"
+version = "4.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pyasn1" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21", size = 29711 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 },
+]
+
+[[package]]
+name = "rtfde"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "lark" },
+    { name = "oletools" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c9/32/1ad82739351117c0711767b828e8f2567a5ffb783741a87120d955564a19/RTFDE-0.1.2-py3-none-any.whl", hash = "sha256:f6d1450c99b04e930da130e8b419aa33b1f953623e1b94ad5c0f67f0362eb737", size = 36142 },
+]
+
+[[package]]
+name = "s3transfer"
+version = "0.10.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "botocore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/cb/67/94c6730ee4c34505b14d94040e2f31edf144c230b6b49e971b4f25ff8fab/s3transfer-0.10.2.tar.gz", hash = "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6", size = 144095 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/3c/4a/b221409913760d26cf4498b7b1741d510c82d3ad38381984a3ddc135ec66/s3transfer-0.10.2-py3-none-any.whl", hash = "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69", size = 82716 },
+]
+
+[[package]]
+name = "safetensors"
+version = "0.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/41/5b/0e63bf736e171463481c5ea3406650dc25aa044083062d321820e7a1ef9f/safetensors-0.4.4.tar.gz", hash = "sha256:5fe3e9b705250d0172ed4e100a811543108653fb2b66b9e702a088ad03772a07", size = 69522 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/0f/1b/27cea7a581019d0d674284048ff76e3a6e048bc3ae3c31cb0bfc93641180/safetensors-0.4.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bbaa31f2cb49013818bde319232ccd72da62ee40f7d2aa532083eda5664e85ff", size = 392373 },
+    { url = "https://files.pythonhosted.org/packages/36/46/93c39c96188a88ca15d12759bb51f52ce7365f6fd19ef09580bc096e8860/safetensors-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fdcb80f4e9fbb33b58e9bf95e7dbbedff505d1bcd1c05f7c7ce883632710006", size = 381488 },
+    { url = "https://files.pythonhosted.org/packages/37/a2/93cab60b8e2c8ea6343a04cdd2c09c860c9640eaaffbf8b771a0e8f98e7d/safetensors-0.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c14c20be247b8a1aeaf3ab4476265e3ca83096bb8e09bb1a7aa806088def4f", size = 441025 },
+    { url = "https://files.pythonhosted.org/packages/19/37/2a5220dce5eff841328bfc3071f4a7063f3eb12341893b2688669fc67115/safetensors-0.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:949aaa1118660f992dbf0968487b3e3cfdad67f948658ab08c6b5762e90cc8b6", size = 439791 },
+    { url = "https://files.pythonhosted.org/packages/f8/93/1d894ff44df26baf4c2471a5874388361390d3cb1cc4811cff40fc01373e/safetensors-0.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c11a4ab7debc456326a2bac67f35ee0ac792bcf812c7562a4a28559a5c795e27", size = 477752 },
+    { url = "https://files.pythonhosted.org/packages/a5/17/b697f517c7ffb8d62d1ef17c6224c00edbb96b931e565d887476a51ac803/safetensors-0.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cea44bba5c5601b297bc8307e4075535b95163402e4906b2e9b82788a2a6df", size = 496019 },
+    { url = "https://files.pythonhosted.org/packages/af/b9/c33f69f4dad9c65209efb76c2be6968af5219e31ccfd344a0025d972252f/safetensors-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9d752c97f6bbe327352f76e5b86442d776abc789249fc5e72eacb49e6916482", size = 435416 },
+    { url = "https://files.pythonhosted.org/packages/71/59/f6480a68df2f4fb5aefae45a800d9bc043c0549210075275fef190a896ce/safetensors-0.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03f2bb92e61b055ef6cc22883ad1ae898010a95730fa988c60a23800eb742c2c", size = 456771 },
+    { url = "https://files.pythonhosted.org/packages/09/01/2a7507cdf7318fb68596e6537ef81e83cfc171c483b4a786b9c947368e19/safetensors-0.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf3f91a9328a941acc44eceffd4e1f5f89b030985b2966637e582157173b98", size = 619456 },
+    { url = "https://files.pythonhosted.org/packages/80/b3/4bb5b1fb025cb8c81fe8a76371334860a9c276fade616f83fd53feef2740/safetensors-0.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:20d218ec2b6899d29d6895419a58b6e44cc5ff8f0cc29fac8d236a8978ab702e", size = 605125 },
+    { url = "https://files.pythonhosted.org/packages/09/93/0d6d54b84eff8361dc257fa306ae0ef1899025a2d9657efe8384ac8b7267/safetensors-0.4.4-cp311-none-win32.whl", hash = "sha256:8079486118919f600c603536e2490ca37b3dbd3280e3ad6eaacfe6264605ac8a", size = 272273 },
+    { url = "https://files.pythonhosted.org/packages/21/4f/5ee44681c7ea827f9d3c104ca429865b41c05a4163eff7f0599152c2e682/safetensors-0.4.4-cp311-none-win_amd64.whl", hash = "sha256:2f8c2eb0615e2e64ee27d478c7c13f51e5329d7972d9e15528d3e4cfc4a08f0d", size = 285982 },
+    { url = "https://files.pythonhosted.org/packages/e2/41/a491dbe3fc1c195ce648939a87d3b4b3800eaade2f05278a6dc02b575c51/safetensors-0.4.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:baec5675944b4a47749c93c01c73d826ef7d42d36ba8d0dba36336fa80c76426", size = 391372 },
+    { url = "https://files.pythonhosted.org/packages/3a/a1/d99aa8d10fa8d82276ee2aaa87afd0a6b96e69c128eaa9f93524b52c5276/safetensors-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f15117b96866401825f3e94543145028a2947d19974429246ce59403f49e77c6", size = 381800 },
+    { url = "https://files.pythonhosted.org/packages/c8/1c/4fa05b79afdd4688a357a42433565b5b09137af6b4f6cd0c9e371466e2f1/safetensors-0.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a13a9caea485df164c51be4eb0c87f97f790b7c3213d635eba2314d959fe929", size = 440817 },
+    { url = "https://files.pythonhosted.org/packages/65/c0/152b059debd3cee4f44b7df972e915a38f776379ea99ce4a3cbea3f78dbd/safetensors-0.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b54bc4ca5f9b9bba8cd4fb91c24b2446a86b5ae7f8975cf3b7a277353c3127c", size = 439483 },
+    { url = "https://files.pythonhosted.org/packages/9c/93/20c05daeecf6fa93b9403c3660df1d983d7ddd5cdb3e3710ff41b72754dd/safetensors-0.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08332c22e03b651c8eb7bf5fc2de90044f3672f43403b3d9ac7e7e0f4f76495e", size = 476631 },
+    { url = "https://files.pythonhosted.org/packages/84/2f/bfe3e54b7dbcaef3f10b8f3c71146790ab18b0bd79ad9ca2bc2c950b68df/safetensors-0.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb62841e839ee992c37bb75e75891c7f4904e772db3691c59daaca5b4ab960e1", size = 493575 },
+    { url = "https://files.pythonhosted.org/packages/1b/0b/2a1b405131f26b95acdb3ed6c8e3a8c84de72d364fd26202d43e68ec4bad/safetensors-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5b927acc5f2f59547270b0309a46d983edc44be64e1ca27a7fcb0474d6cd67", size = 434891 },
+    { url = "https://files.pythonhosted.org/packages/31/ce/cad390a08128ebcb74be79a1e03c496a4773059b2541c6a97a52fd1705fb/safetensors-0.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a69c71b1ae98a8021a09a0b43363b0143b0ce74e7c0e83cacba691b62655fb8", size = 457631 },
+    { url = "https://files.pythonhosted.org/packages/9f/83/d9d6e6a45d624c27155f4336af8e7b2bcde346137f6460dcd5e1bcdc2e3f/safetensors-0.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23654ad162c02a5636f0cd520a0310902c4421aab1d91a0b667722a4937cc445", size = 619367 },
+    { url = "https://files.pythonhosted.org/packages/9f/20/b37e1ae87cb83a1c2fe5cf0710bab12d6f186474cbbdda4fda2d7d57d225/safetensors-0.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0677c109d949cf53756859160b955b2e75b0eefe952189c184d7be30ecf7e858", size = 605302 },
+    { url = "https://files.pythonhosted.org/packages/99/5a/9237f1d0adba5eec3711d7c1911b3111631a86779d692fe8ad2cd709d6a4/safetensors-0.4.4-cp312-none-win32.whl", hash = "sha256:a51d0ddd4deb8871c6de15a772ef40b3dbd26a3c0451bb9e66bc76fc5a784e5b", size = 273434 },
+    { url = "https://files.pythonhosted.org/packages/b9/dd/b11f3a33fe7b6c94fde08b3de094b93d3438d67922ef90bcb5002e306e0b/safetensors-0.4.4-cp312-none-win_amd64.whl", hash = "sha256:2d065059e75a798bc1933c293b68d04d79b586bb7f8c921e0ca1e82759d0dbb1", size = 286347 },
+    { url = "https://files.pythonhosted.org/packages/b3/d6/7a4db869a295b57066e1399eb467c38df86439d3766c850ca8eb75b5e3a3/safetensors-0.4.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9d625692578dd40a112df30c02a1adf068027566abd8e6a74893bb13d441c150", size = 391373 },
+    { url = "https://files.pythonhosted.org/packages/1e/97/de856ad42ef65822ff982e7af7fc889cd717240672b45c647af7ea05c631/safetensors-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7cabcf39c81e5b988d0adefdaea2eb9b4fd9bd62d5ed6559988c62f36bfa9a89", size = 382523 },
+    { url = "https://files.pythonhosted.org/packages/07/d2/d9316af4c15b4ca0362cb4498abe47be6e04f7119f3ccf697e38ee04d33b/safetensors-0.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8359bef65f49d51476e9811d59c015f0ddae618ee0e44144f5595278c9f8268c", size = 441039 },
+    { url = "https://files.pythonhosted.org/packages/e8/ac/478e910c891feadb693316b31447f14929b7047a612df9b628589b89be3c/safetensors-0.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a32c662e7df9226fd850f054a3ead0e4213a96a70b5ce37b2d26ba27004e013", size = 439516 },
+    { url = "https://files.pythonhosted.org/packages/81/43/f9929e854c4fcca98459f03de003d9619dd5f7d10d74e03df7af9907b119/safetensors-0.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c329a4dcc395364a1c0d2d1574d725fe81a840783dda64c31c5a60fc7d41472c", size = 477242 },
+    { url = "https://files.pythonhosted.org/packages/0a/4d/b754f59fe395ea5bd8531c090c557e161fffed1753eeb3d87c0f8eaa62c4/safetensors-0.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:239ee093b1db877c9f8fe2d71331a97f3b9c7c0d3ab9f09c4851004a11f44b65", size = 494615 },
+    { url = "https://files.pythonhosted.org/packages/54/7d/b26801dab2ecb499eb1ebdb46be65600b49bb062fe12b298150695a6e23c/safetensors-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd574145d930cf9405a64f9923600879a5ce51d9f315443a5f706374841327b6", size = 434933 },
+    { url = "https://files.pythonhosted.org/packages/e2/40/0f6627ad98e21e620a6835f02729f6b701804d3c452f8773648cbd0b9c2c/safetensors-0.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6784eed29f9e036acb0b7769d9e78a0dc2c72c2d8ba7903005350d817e287a4", size = 457646 },
+    { url = "https://files.pythonhosted.org/packages/30/1e/7f7819d1be7c36fbedcb7099a461b79e0ed19631b3ca5595e0f81501bb2c/safetensors-0.4.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:65a4a6072436bf0a4825b1c295d248cc17e5f4651e60ee62427a5bcaa8622a7a", size = 619204 },
+    { url = "https://files.pythonhosted.org/packages/b1/58/e91e8c9888303919ce56f038fcad4147431fd95630890799bf8c928d1d34/safetensors-0.4.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:df81e3407630de060ae8313da49509c3caa33b1a9415562284eaf3d0c7705f9f", size = 605400 },
+]
+
+[[package]]
+name = "scikit-learn"
+version = "1.5.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "joblib" },
+    { name = "numpy" },
+    { name = "scipy" },
+    { name = "threadpoolctl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/92/72/2961b9874a9ddf2b0f95f329d4e67f67c3301c1d88ba5e239ff25661bb85/scikit_learn-1.5.1.tar.gz", hash = "sha256:0ea5d40c0e3951df445721927448755d3fe1d80833b0b7308ebff5d2a45e6414", size = 6958368 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/03/86/ab9f95e338c5ef5b4e79463ee91e55aae553213835e59bf038bc0cc21bf8/scikit_learn-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:154297ee43c0b83af12464adeab378dee2d0a700ccd03979e2b821e7dd7cc1c2", size = 12087598 },
+    { url = "https://files.pythonhosted.org/packages/7d/d7/fb80c63062b60b1fa5dcb2d4dd3a4e83bd8c68cdc83cf6ff8c016228f184/scikit_learn-1.5.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b5e865e9bd59396220de49cb4a57b17016256637c61b4c5cc81aaf16bc123bbe", size = 10979067 },
+    { url = "https://files.pythonhosted.org/packages/c1/f8/fd3fa610cac686952d8c78b8b44cf5263c6c03885bd8e5d5819c684b44e8/scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:909144d50f367a513cee6090873ae582dba019cb3fca063b38054fa42704c3a4", size = 12485469 },
+    { url = "https://files.pythonhosted.org/packages/32/63/ed228892adad313aab0d0f9261241e7bf1efe36730a2788ad424bcad00ca/scikit_learn-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689b6f74b2c880276e365fe84fe4f1befd6a774f016339c65655eaff12e10cbf", size = 13335048 },
+    { url = "https://files.pythonhosted.org/packages/5d/55/0403bf2031250ac982c8053397889fbc5a3a2b3798b913dae4f51c3af6a4/scikit_learn-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a07f90846313a7639af6a019d849ff72baadfa4c74c778821ae0fad07b7275b", size = 10988436 },
+    { url = "https://files.pythonhosted.org/packages/b1/8d/cf392a56e24627093a467642c8b9263052372131359b570df29aaf4811ab/scikit_learn-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5944ce1faada31c55fb2ba20a5346b88e36811aab504ccafb9f0339e9f780395", size = 12102404 },
+    { url = "https://files.pythonhosted.org/packages/d5/2c/734fc9269bdb6768905ac41b82d75264b26925b1e462f4ebf45fe4f17646/scikit_learn-1.5.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:0828673c5b520e879f2af6a9e99eee0eefea69a2188be1ca68a6121b809055c1", size = 11037398 },
+    { url = "https://files.pythonhosted.org/packages/d3/a9/15774b178bcd1cde1c470adbdb554e1504dce7c302e02ff736c90d65e014/scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508907e5f81390e16d754e8815f7497e52139162fd69c4fdbd2dfa5d6cc88915", size = 12089887 },
+    { url = "https://files.pythonhosted.org/packages/8a/5d/047cde25131eef3a38d03317fa7d25d6f60ce6e8ccfd24ac88b3e309fc00/scikit_learn-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97625f217c5c0c5d0505fa2af28ae424bd37949bb2f16ace3ff5f2f81fb4498b", size = 13079093 },
+    { url = "https://files.pythonhosted.org/packages/cb/be/dec2a8d31d133034a8ec51ae68ac564ec9bde1c78a64551f1438c3690b9e/scikit_learn-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:da3f404e9e284d2b0a157e1b56b6566a34eb2798205cba35a211df3296ab7a74", size = 10945350 },
+]
+
+[[package]]
+name = "scipy"
+version = "1.14.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b2/ab/070ccfabe870d9f105b04aee1e2860520460ef7ca0213172abfe871463b9/scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675", size = 39076999 },
+    { url = "https://files.pythonhosted.org/packages/a7/c5/02ac82f9bb8f70818099df7e86c3ad28dae64e1347b421d8e3adf26acab6/scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2", size = 29894570 },
+    { url = "https://files.pythonhosted.org/packages/ed/05/7f03e680cc5249c4f96c9e4e845acde08eb1aee5bc216eff8a089baa4ddb/scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617", size = 23103567 },
+    { url = "https://files.pythonhosted.org/packages/5e/fc/9f1413bef53171f379d786aabc104d4abeea48ee84c553a3e3d8c9f96a9c/scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8", size = 25499102 },
+    { url = "https://files.pythonhosted.org/packages/c2/4b/b44bee3c2ddc316b0159b3d87a3d467ef8d7edfd525e6f7364a62cd87d90/scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37", size = 35586346 },
+    { url = "https://files.pythonhosted.org/packages/93/6b/701776d4bd6bdd9b629c387b5140f006185bd8ddea16788a44434376b98f/scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2", size = 41165244 },
+    { url = "https://files.pythonhosted.org/packages/06/57/e6aa6f55729a8f245d8a6984f2855696c5992113a5dc789065020f8be753/scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2", size = 42817917 },
+    { url = "https://files.pythonhosted.org/packages/ea/c2/5ecadc5fcccefaece775feadcd795060adf5c3b29a883bff0e678cfe89af/scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94", size = 44781033 },
+    { url = "https://files.pythonhosted.org/packages/c0/04/2bdacc8ac6387b15db6faa40295f8bd25eccf33f1f13e68a72dc3c60a99e/scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", size = 39128781 },
+    { url = "https://files.pythonhosted.org/packages/c8/53/35b4d41f5fd42f5781dbd0dd6c05d35ba8aa75c84ecddc7d44756cd8da2e/scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", size = 29939542 },
+    { url = "https://files.pythonhosted.org/packages/66/67/6ef192e0e4d77b20cc33a01e743b00bc9e68fb83b88e06e636d2619a8767/scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", size = 23148375 },
+    { url = "https://files.pythonhosted.org/packages/f6/32/3a6dedd51d68eb7b8e7dc7947d5d841bcb699f1bf4463639554986f4d782/scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc", size = 25578573 },
+    { url = "https://files.pythonhosted.org/packages/f0/5a/efa92a58dc3a2898705f1dc9dbaf390ca7d4fba26d6ab8cfffb0c72f656f/scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310", size = 35319299 },
+    { url = "https://files.pythonhosted.org/packages/8e/ee/8a26858ca517e9c64f84b4c7734b89bda8e63bec85c3d2f432d225bb1886/scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", size = 40849331 },
+    { url = "https://files.pythonhosted.org/packages/a5/cd/06f72bc9187840f1c99e1a8750aad4216fc7dfdd7df46e6280add14b4822/scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", size = 42544049 },
+    { url = "https://files.pythonhosted.org/packages/aa/7d/43ab67228ef98c6b5dd42ab386eae2d7877036970a0d7e3dd3eb47a0d530/scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", size = 44521212 },
+    { url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 },
+    { url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 },
+    { url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 },
+    { url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 },
+    { url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 },
+    { url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 },
+    { url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 },
+    { url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 },
+]
+
+[[package]]
+name = "sentence-transformers"
+version = "3.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "huggingface-hub" },
+    { name = "numpy" },
+    { name = "pillow" },
+    { name = "scikit-learn" },
+    { name = "scipy" },
+    { name = "torch" },
+    { name = "tqdm" },
+    { name = "transformers" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/41/fb/2368f84127920d86330b533792e66b26264e92b729b5c1998aaa33d2e22f/sentence_transformers-3.0.1.tar.gz", hash = "sha256:8a3d2c537cc4d1014ccc20ac92be3d6135420a3bc60ae29a3a8a9b4bb35fbff6", size = 177258 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/58/4b/922436953394e1bfda05e4bf1fe0e80f609770f256c59a9df7a9254f3e0d/sentence_transformers-3.0.1-py3-none-any.whl", hash = "sha256:01050cc4053c49b9f5b78f6980b5a72db3fd3a0abb9169b1792ac83875505ee6", size = 227071 },
+]
+
+[[package]]
+name = "setuptools"
+version = "73.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8d/37/f4d4ce9bc15e61edba3179f9b0f763fc6d439474d28511b11f0d95bab7a2/setuptools-73.0.1.tar.gz", hash = "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193", size = 2526506 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/07/6a/0270e295bf30c37567736b7fca10167640898214ff911273af37ddb95770/setuptools-73.0.1-py3-none-any.whl", hash = "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e", size = 2346588 },
+]
+
+[[package]]
+name = "shapely"
+version = "2.0.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "numpy" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4a/89/0d20bac88016be35ff7d3c0c2ae64b477908f1b1dfa540c5d69ac7af07fe/shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6", size = 282361 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/37/15/269d8e1f7f658a37e61f7028683c546f520e4e7cedba1e32c77ff9d3a3c7/shapely-2.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5aeb0f51a9db176da9a30cb2f4329b6fbd1e26d359012bb0ac3d3c7781667a9e", size = 1449578 },
+    { url = "https://files.pythonhosted.org/packages/37/63/e182e43081fffa0a2d970c480f2ef91647a6ab94098f61748c23c2a485f2/shapely-2.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a7a78b0d51257a367ee115f4d41ca4d46edbd0dd280f697a8092dd3989867b2", size = 1296792 },
+    { url = "https://files.pythonhosted.org/packages/6e/5a/d019f69449329dcd517355444fdb9ddd58bec5e080b8bdba007e8e4c546d/shapely-2.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32c23d2f43d54029f986479f7c1f6e09c6b3a19353a3833c2ffb226fb63a855", size = 2443997 },
+    { url = "https://files.pythonhosted.org/packages/25/aa/53f145e5a610a49af9ac49f2f1be1ec8659ebd5c393d66ac94e57c83b00e/shapely-2.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dc9fb0eb56498912025f5eb352b5126f04801ed0e8bdbd867d21bdbfd7cbd0", size = 2528334 },
+    { url = "https://files.pythonhosted.org/packages/64/64/0c7b0a22b416d36f6296b92bb4219d82b53d0a7c47e16fd0a4c85f2f117c/shapely-2.0.6-cp311-cp311-win32.whl", hash = "sha256:d93b7e0e71c9f095e09454bf18dad5ea716fb6ced5df3cb044564a00723f339d", size = 1294669 },
+    { url = "https://files.pythonhosted.org/packages/b1/5a/6a67d929c467a1973b6bb9f0b00159cc343b02bf9a8d26db1abd2f87aa23/shapely-2.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:c02eb6bf4cfb9fe6568502e85bb2647921ee49171bcd2d4116c7b3109724ef9b", size = 1442032 },
+    { url = "https://files.pythonhosted.org/packages/46/77/efd9f9d4b6a762f976f8b082f54c9be16f63050389500fb52e4f6cc07c1a/shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0", size = 1450326 },
+    { url = "https://files.pythonhosted.org/packages/68/53/5efa6e7a4036a94fe6276cf7bbb298afded51ca3396b03981ad680c8cc7d/shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3", size = 1298480 },
+    { url = "https://files.pythonhosted.org/packages/88/a2/1be1db4fc262e536465a52d4f19d85834724fedf2299a1b9836bc82fe8fa/shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8", size = 2439311 },
+    { url = "https://files.pythonhosted.org/packages/d5/7d/9a57e187cbf2fbbbdfd4044a4f9ce141c8d221f9963750d3b001f0ec080d/shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726", size = 2524835 },
+    { url = "https://files.pythonhosted.org/packages/6d/0a/f407509ab56825f39bf8cfce1fb410238da96cf096809c3e404e5bc71ea1/shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f", size = 1295613 },
+    { url = "https://files.pythonhosted.org/packages/7b/b3/857afd9dfbfc554f10d683ac412eac6fa260d1f4cd2967ecb655c57e831a/shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48", size = 1442539 },
+    { url = "https://files.pythonhosted.org/packages/34/e8/d164ef5b0eab86088cde06dee8415519ffd5bb0dd1bd9d021e640e64237c/shapely-2.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:42805ef90783ce689a4dde2b6b2f261e2c52609226a0438d882e3ced40bb3013", size = 1445344 },
+    { url = "https://files.pythonhosted.org/packages/ce/e2/9fba7ac142f7831757a10852bfa465683724eadbc93d2d46f74a16f9af04/shapely-2.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d2cb146191a47bd0cee8ff5f90b47547b82b6345c0d02dd8b25b88b68af62d7", size = 1296182 },
+    { url = "https://files.pythonhosted.org/packages/cf/dc/790d4bda27d196cd56ec66975eaae3351c65614cafd0e16ddde39ec9fb92/shapely-2.0.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3fdef0a1794a8fe70dc1f514440aa34426cc0ae98d9a1027fb299d45741c381", size = 2423426 },
+    { url = "https://files.pythonhosted.org/packages/af/b0/f8169f77eac7392d41e231911e0095eb1148b4d40c50ea9e34d999c89a7e/shapely-2.0.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c665a0301c645615a107ff7f52adafa2153beab51daf34587170d85e8ba6805", size = 2513249 },
+    { url = "https://files.pythonhosted.org/packages/f6/1d/a8c0e9ab49ff2f8e4dedd71b0122eafb22a18ad7e9d256025e1f10c84704/shapely-2.0.6-cp313-cp313-win32.whl", hash = "sha256:0334bd51828f68cd54b87d80b3e7cee93f249d82ae55a0faf3ea21c9be7b323a", size = 1294848 },
+    { url = "https://files.pythonhosted.org/packages/23/38/2bc32dd1e7e67a471d4c60971e66df0bdace88656c47a9a728ace0091075/shapely-2.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:d37d070da9e0e0f0a530a621e17c0b8c3c9d04105655132a87cfff8bd77cc4c2", size = 1441371 },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 },
+]
+
+[[package]]
+name = "simple-websocket"
+version = "1.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "wsproto" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d3/82/3cf87d317911864a2f2a8daf1779fc7f82d5d55e6a8aaa0315f8209047a7/simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8", size = 13071 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/6d/ea/288a8ac1d9551354488ff60c0ac6a76acc3b6b60f0460ac1944c75e240da/simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc", size = 13712 },
+]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 },
+]
+
+[[package]]
+name = "smmap"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/88/04/b5bf6d21dc4041000ccba7eb17dd3055feb237e7ffc2c20d3fae3af62baa/smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", size = 22291 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a7/a5/10f97f73544edcdef54409f1d839f6049a0d79df68adbc1ceb24d1aaca42/smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da", size = 24282 },
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
+]
+
+[[package]]
+name = "soupsieve"
+version = "2.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d7/ce/fbaeed4f9fb8b2daa961f90591662df6a86c1abf25c548329a86920aedfb/soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", size = 101569 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d1/c2/fe97d779f3ef3b15f05c94a2f1e3d21732574ed441687474db9d342a7315/soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9", size = 36186 },
+]
+
+[[package]]
+name = "sqlalchemy"
+version = "2.0.32"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/af/6f/967e987683908af816aa3072c1a6997ac9933cf38d66b0474fb03f253323/SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8", size = 9546691 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/fc/a9/e3bd92004095ed6796ea4ac5fdd9606b1e53117ef5b90ae79ac3fc6e225e/SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f", size = 2088752 },
+    { url = "https://files.pythonhosted.org/packages/a9/34/b97f4458eefbdead7ee5ce69cbf3591574c5ba44162dbe52c4386818623f/SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5", size = 2079150 },
+    { url = "https://files.pythonhosted.org/packages/6b/b5/95ff12f5d4eb7813dd5a59ccc8e3c68d4683fedf59801b40704593c3b757/SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d", size = 3197551 },
+    { url = "https://files.pythonhosted.org/packages/ca/af/379f8695ab751acf61868b0098c8d66e2b2ad8b11d9939d5144c82d05bc5/SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0", size = 3197551 },
+    { url = "https://files.pythonhosted.org/packages/ff/0c/5feaea51f23b5f008f16f9dbf7eec18ee5b9b8eb2875d6e367f52daf633e/SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2", size = 3134583 },
+    { url = "https://files.pythonhosted.org/packages/cc/83/4eca3604f9049a2b92a9ffb818ea1cc8186f722e539a6feee58f931bad34/SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961", size = 3154911 },
+    { url = "https://files.pythonhosted.org/packages/3d/56/485ad322f148a8b70060e03b5f130e714f95d839b5e50315e5c5efd1fc05/SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28", size = 2059047 },
+    { url = "https://files.pythonhosted.org/packages/bb/8c/4548ae42b4ab7f3fe9f1aeb4b1f28ea795485ca44840cb0f3f57aa8ecfcc/SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924", size = 2084480 },
+    { url = "https://files.pythonhosted.org/packages/06/95/88beb07aa61c611829c9ce950f349adcf00065c1bb313090c20d80a520ca/SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92", size = 2087267 },
+    { url = "https://files.pythonhosted.org/packages/11/93/0b28f9d261af927eef3df472e5bbf144fb33e062de770b2c312bb516702b/SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9", size = 2077732 },
+    { url = "https://files.pythonhosted.org/packages/84/50/1ce1dec4b1cce8f1163c2c58bb1588ac5076c3dbc4bb1d3eab70e798fdd4/SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8", size = 3227230 },
+    { url = "https://files.pythonhosted.org/packages/9d/b8/aa822988d390cf06afa3c69d86a3a38bba79b51385207cd7cd99d0be17bb/SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec", size = 3238118 },
+    { url = "https://files.pythonhosted.org/packages/c3/d7/7a65172ed2713acf0262a65392dfcf05ca2b7a67c988ebad425eba9b3843/SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c", size = 3173610 },
+    { url = "https://files.pythonhosted.org/packages/a9/0f/8da0613e3f0b095ef423802943ed4b98242370736034ed5043a43c46c3d4/SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb", size = 3200224 },
+    { url = "https://files.pythonhosted.org/packages/50/ef/973e0bbf2be5c12e34dca92139ca100f51ba078e36c3c06fd1dc8480c209/SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d", size = 2057626 },
+    { url = "https://files.pythonhosted.org/packages/db/5f/440c324aae82a2ce892ac0fe1d114b9dc9f04e934e8f0762574876a168b5/SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb", size = 2083167 },
+    { url = "https://files.pythonhosted.org/packages/99/1b/045185a9f6481d926a451aafaa0d07c98f19ac7abe730dff9630c9ead4fa/SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202", size = 1878765 },
+]
+
+[[package]]
+name = "starlette"
+version = "0.37.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/b5/6bceb93ff20bd7ca36e6f7c540581abb18f53130fabb30ba526e26fd819b/starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823", size = 2843736 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/fd/18/31fa32ed6c68ba66220204ef0be798c349d0a20c1901f9d4a794e08c76d8/starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee", size = 71908 },
+]
+
+[[package]]
+name = "sympy"
+version = "1.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "mpmath" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/94/15/4a041424c7187f41cce678f5a02189b244e9aac61a18b45cd415a3a470f3/sympy-1.13.2.tar.gz", hash = "sha256:401449d84d07be9d0c7a46a64bd54fe097667d5e7181bfe67ec777be9e01cb13", size = 7532926 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c1/f9/6845bf8fca0eaf847da21c5d5bc6cd92797364662824a11d3f836423a1a5/sympy-1.13.2-py3-none-any.whl", hash = "sha256:c51d75517712f1aed280d4ce58506a4a88d635d6b5dd48b39102a7ae1f3fcfe9", size = 6189289 },
+]
+
+[[package]]
+name = "tabulate"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 },
+]
+
+[[package]]
+name = "tenacity"
+version = "8.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a3/4d/6a19536c50b849338fcbe9290d562b52cbdcf30d8963d3588a68a4107df1/tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78", size = 47309 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/d2/3f/8ba87d9e287b9d385a02a7114ddcef61b26f86411e121c9003eb509a1773/tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687", size = 28165 },
+]
+
+[[package]]
+name = "threadpoolctl"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 },
+]
+
+[[package]]
+name = "tiktoken"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "regex" },
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c4/4a/abaec53e93e3ef37224a4dd9e2fc6bb871e7a538c2b6b9d2a6397271daf4/tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6", size = 33437 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/22/eb/57492b2568eea1d546da5cc1ae7559d924275280db80ba07e6f9b89a914b/tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f", size = 961468 },
+    { url = "https://files.pythonhosted.org/packages/30/ef/e07dbfcb2f85c84abaa1b035a9279575a8da0236305491dc22ae099327f7/tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f", size = 907005 },
+    { url = "https://files.pythonhosted.org/packages/ea/9b/f36db825b1e9904c3a2646439cb9923fc1e09208e2e071c6d9dd64ead131/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b", size = 1049183 },
+    { url = "https://files.pythonhosted.org/packages/61/b4/b80d1fe33015e782074e96bbbf4108ccd283b8deea86fb43c15d18b7c351/tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992", size = 1080830 },
+    { url = "https://files.pythonhosted.org/packages/2a/40/c66ff3a21af6d62a7e0ff428d12002c4e0389f776d3ff96dcaa0bb354eee/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1", size = 1092967 },
+    { url = "https://files.pythonhosted.org/packages/2e/80/f4c9e255ff236e6a69ce44b927629cefc1b63d3a00e2d1c9ed540c9492d2/tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89", size = 1142682 },
+    { url = "https://files.pythonhosted.org/packages/b1/10/c04b4ff592a5f46b28ebf4c2353f735c02ae7f0ce1b165d00748ced6467e/tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb", size = 799009 },
+    { url = "https://files.pythonhosted.org/packages/1d/46/4cdda4186ce900608f522da34acf442363346688c71b938a90a52d7b84cc/tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908", size = 960446 },
+    { url = "https://files.pythonhosted.org/packages/b6/30/09ced367d280072d7a3e21f34263dfbbf6378661e7a0f6414e7c18971083/tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410", size = 906652 },
+    { url = "https://files.pythonhosted.org/packages/e6/7b/c949e4954441a879a67626963dff69096e3c774758b9f2bb0853f7b4e1e7/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704", size = 1047904 },
+    { url = "https://files.pythonhosted.org/packages/50/81/1842a22f15586072280364c2ab1e40835adaf64e42fe80e52aff921ee021/tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350", size = 1079836 },
+    { url = "https://files.pythonhosted.org/packages/6d/87/51a133a3d5307cf7ae3754249b0faaa91d3414b85c3d36f80b54d6817aa6/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4", size = 1092472 },
+    { url = "https://files.pythonhosted.org/packages/a5/1f/c93517dc6d3b2c9e988b8e24f87a8b2d4a4ab28920a3a3f3ea338397ae0c/tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97", size = 1141881 },
+    { url = "https://files.pythonhosted.org/packages/bf/4b/48ca098cb580c099b5058bf62c4cb5e90ca6130fa43ef4df27088536245b/tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f", size = 799281 },
+]
+
+[[package]]
+name = "tokenizers"
+version = "0.19.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "huggingface-hub" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/48/04/2071c150f374aab6d5e92aaec38d0f3c368d227dd9e0469a1f0966ac68d1/tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3", size = 321039 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/c8/d6/6e1d728d765eb4102767f071bf7f6439ab10d7f4a975c9217db65715207a/tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059", size = 2533448 },
+    { url = "https://files.pythonhosted.org/packages/90/79/d17a0f491d10817cd30f1121a07aa09c8e97a81114b116e473baf1577f09/tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14", size = 2440254 },
+    { url = "https://files.pythonhosted.org/packages/c7/28/2d11c3ff94f9d42eceb2ea549a06e3f166fe391c5a025e5d96fac898a3ac/tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594", size = 3684971 },
+    { url = "https://files.pythonhosted.org/packages/36/c6/537f22b57e6003904d35d07962dbde2f2e9bdd791d0241da976a4c7f8194/tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc", size = 3568894 },
+    { url = "https://files.pythonhosted.org/packages/af/ef/3c1deed14ec59b2c8e7e2fa27b2a53f7d101181277a43b89ab17d891ef2e/tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2", size = 3426873 },
+    { url = "https://files.pythonhosted.org/packages/06/db/c0320c4798ac6bd12d2ef895bec9d10d216a3b4d6fff10e9d68883ea7edc/tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe", size = 3965050 },
+    { url = "https://files.pythonhosted.org/packages/4c/8a/a166888d6cb14db55f5eb7ce0b1d4777d145aa27cbf4f945712cf6c29935/tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d", size = 4047855 },
+    { url = "https://files.pythonhosted.org/packages/a7/03/fb50fc03f86016b227a967c8d474f90230c885c0d18f78acdfda7a96ce56/tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa", size = 3608228 },
+    { url = "https://files.pythonhosted.org/packages/5b/cd/0385e1026e1e03732fd398e964792a3a8433918b166748c82507e014d748/tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6", size = 9633115 },
+    { url = "https://files.pythonhosted.org/packages/25/50/8f8ad0bbdaf09d04b15e6502d1fa1c653754ed7e016e4ae009726aa1a4e4/tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b", size = 9949062 },
+    { url = "https://files.pythonhosted.org/packages/db/11/31be66710f1d14526f3588a441efadeb184e1e68458067007b20ead03c59/tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256", size = 2041039 },
+    { url = "https://files.pythonhosted.org/packages/65/8e/6d7d72b28f22c422cff8beae10ac3c2e4376b9be721ef8167b7eecd1da62/tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66", size = 2220386 },
+    { url = "https://files.pythonhosted.org/packages/63/90/2890cd096898dcdb596ee172cde40c0f54a9cf43b0736aa260a5501252af/tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153", size = 2530580 },
+    { url = "https://files.pythonhosted.org/packages/74/d1/f4e1e950adb36675dfd8f9d0f4be644f3f3aaf22a5677a4f5c81282b662e/tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a", size = 2436682 },
+    { url = "https://files.pythonhosted.org/packages/ed/30/89b321a16c58d233e301ec15072c0d3ed5014825e72da98604cd3ab2fba1/tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95", size = 3693494 },
+    { url = "https://files.pythonhosted.org/packages/05/40/fa899f32de483500fbc78befd378fd7afba4270f17db707d1a78c0a4ddc3/tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266", size = 3566541 },
+    { url = "https://files.pythonhosted.org/packages/67/14/e7da32ae5fb4971830f1ef335932fae3fa57e76b537e852f146c850aefdf/tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52", size = 3430792 },
+    { url = "https://files.pythonhosted.org/packages/f2/4b/aae61bdb6ab584d2612170801703982ee0e35f8b6adacbeefe5a3b277621/tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f", size = 3962812 },
+    { url = "https://files.pythonhosted.org/packages/0a/b6/f7b7ef89c4da7b20256e6eab23d3835f05d1ca8f451d31c16cbfe3cd9eb6/tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840", size = 4024688 },
+    { url = "https://files.pythonhosted.org/packages/80/54/12047a69f5b382d7ee72044dc89151a2dd0d13b2c9bdcc22654883704d31/tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3", size = 3610961 },
+    { url = "https://files.pythonhosted.org/packages/52/b7/1e8a913d18ac28feeda42d4d2d51781874398fb59cd1c1e2653a4b5742ed/tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea", size = 9631367 },
+    { url = "https://files.pythonhosted.org/packages/ac/3d/2284f6d99f8f21d09352b88b8cfefa24ab88468d962aeb0aa15c20d76b32/tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c", size = 9950121 },
+    { url = "https://files.pythonhosted.org/packages/2a/94/ec3369dbc9b7200c14c8c7a1a04c78b7a7398d0c001e1b7d1ffe30eb93a0/tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57", size = 2044069 },
+    { url = "https://files.pythonhosted.org/packages/0c/97/80bff6937e0c67d30c0facacd4f0bcf4254e581aa4995c73cef8c8640e56/tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a", size = 2214527 },
+]
+
+[[package]]
+name = "torch"
+version = "2.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "filelock" },
+    { name = "fsspec" },
+    { name = "jinja2" },
+    { name = "networkx" },
+    { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "sympy" },
+    { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and platform_system == 'Linux'" },
+    { name = "typing-extensions" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/80/83/9b7681e41e59adb6c2b042f7e8eb716515665a6eed3dda4215c6b3385b90/torch-2.4.0-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:e743adadd8c8152bb8373543964551a7cb7cc20ba898dc8f9c0cdbe47c283de0", size = 797262052 },
+    { url = "https://files.pythonhosted.org/packages/84/fa/2b510a02809ddd70aed821bc2328c4effd206503df38a1328c9f1f957813/torch-2.4.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:7334325c0292cbd5c2eac085f449bf57d3690932eac37027e193ba775703c9e6", size = 89850473 },
+    { url = "https://files.pythonhosted.org/packages/18/cf/f69dff972a748e08e1bf602ef94ea5c6d4dd2f41cea22c8ad67a607d8b41/torch-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:97730014da4c57ffacb3c09298c6ce05400606e890bd7a05008d13dd086e46b1", size = 197860580 },
+    { url = "https://files.pythonhosted.org/packages/b7/d0/5e8f96d83889e77b478b90e7d8d24a5fc14c5c9350c6b93d071f45f39096/torch-2.4.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:f169b4ea6dc93b3a33319611fcc47dc1406e4dd539844dcbd2dec4c1b96e166d", size = 62144370 },
+    { url = "https://files.pythonhosted.org/packages/bf/55/b6c74df4695f94a9c3505021bc2bd662e271d028d055b3b2529f3442a3bd/torch-2.4.0-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:997084a0f9784d2a89095a6dc67c7925e21bf25dea0b3d069b41195016ccfcbb", size = 797168571 },
+    { url = "https://files.pythonhosted.org/packages/9a/5d/327fb72044c22d68a826643abf2e220db3d7f6005a41a6b167af1ffbc708/torch-2.4.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:bc3988e8b36d1e8b998d143255d9408d8c75da4ab6dd0dcfd23b623dfb0f0f57", size = 89746726 },
+    { url = "https://files.pythonhosted.org/packages/dc/95/a14dd84ce65e5ce176176393a80b2f74864ee134a31f590140456a4c0959/torch-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:3374128bbf7e62cdaed6c237bfd39809fbcfaa576bee91e904706840c3f2195c", size = 197807123 },
+    { url = "https://files.pythonhosted.org/packages/c7/87/489ebb234e75760e06fa4789fa6d4e13c125beefa1483ce35c9e43dcd395/torch-2.4.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:91aaf00bfe1ffa44dc5b52809d9a95129fca10212eca3ac26420eb11727c6288", size = 62123112 },
+]
+
+[[package]]
+name = "tqdm"
+version = "4.66.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "colorama", marker = "platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/58/83/6ba9844a41128c62e810fddddd72473201f3eacde02046066142a2d96cc5/tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad", size = 169504 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/48/5d/acf5905c36149bbaec41ccf7f2b68814647347b72075ac0b1fe3022fdc73/tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", size = 78351 },
+]
+
+[[package]]
+name = "transformers"
+version = "4.44.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "filelock" },
+    { name = "huggingface-hub" },
+    { name = "numpy" },
+    { name = "packaging" },
+    { name = "pyyaml" },
+    { name = "regex" },
+    { name = "requests" },
+    { name = "safetensors" },
+    { name = "tokenizers" },
+    { name = "tqdm" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/46/62e914365ab463addb0357a88f8d2614aae02f1a2b2b5c24c7ee005ff157/transformers-4.44.1.tar.gz", hash = "sha256:3b9a1a07ca65c665c7bf6109b7da76182184d10bb58d9ab14e6892e7b9e073a2", size = 8110315 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/47/ab/c42556ba7c5aed687256466d472abb9a1b9cbff5730aa42a884d892e061a/transformers-4.44.1-py3-none-any.whl", hash = "sha256:bd2642da18b4e6d29b135c17650cd7ca8e874f2d092d2eddd3ed6b71a93a155c", size = 9465379 },
+]
+
+[[package]]
+name = "triton"
+version = "3.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "filelock", marker = "(python_full_version < '3.13' and platform_machine != 'aarch64' and platform_system != 'Darwin') or (python_full_version < '3.13' and platform_system != 'Darwin' and platform_system != 'Linux')" },
+]
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/33/3e/a2f59384587eff6aeb7d37b6780de7fedd2214935e27520430ca9f5b7975/triton-3.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ce8520437c602fb633f1324cc3871c47bee3b67acf9756c1a66309b60e3216c", size = 209438883 },
+    { url = "https://files.pythonhosted.org/packages/fe/7b/7757205dee3628f75e7991021d15cd1bd0c9b044ca9affe99b50879fc0e1/triton-3.0.0-1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e509deb77f1c067d8640725ef00c5cbfcb2052a1a3cb6a6d343841f92624eb", size = 209464695 },
+]
+
+[[package]]
+name = "typer"
+version = "0.12.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "rich" },
+    { name = "shellingham" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d4/f7/f174a1cae84848ae8b27170a96187b91937b743f0580ff968078fe16930a/typer-0.12.4.tar.gz", hash = "sha256:c9c1613ed6a166162705b3347b8d10b661ccc5d95692654d0fb628118f2c34e6", size = 97945 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ae/cc/15083dcde1252a663398b1b2a173637a3ec65adadfb95137dc95df1e6adc/typer-0.12.4-py3-none-any.whl", hash = "sha256:819aa03699f438397e876aa12b0d63766864ecba1b579092cc9fe35d886e34b6", size = 47402 },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
+]
+
+[[package]]
+name = "typing-inspect"
+version = "0.9.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "mypy-extensions" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 },
+]
+
+[[package]]
+name = "tzdata"
+version = "2024.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 },
+]
+
+[[package]]
+name = "tzlocal"
+version = "5.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "tzdata", marker = "platform_system == 'Windows'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/04/d3/c19d65ae67636fe63953b20c2e4a8ced4497ea232c43ff8d01db16de8dc0/tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e", size = 30201 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/97/3f/c4c51c55ff8487f2e6d0e618dba917e3c3ee2caae6cf0fbb59c9b1876f2e/tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8", size = 17859 },
+]
+
+[[package]]
+name = "ujson"
+version = "5.10.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f0/00/3110fd566786bfa542adb7932d62035e0c0ef662a8ff6544b6643b3d6fd7/ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1", size = 7154885 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/23/ec/3c551ecfe048bcb3948725251fb0214b5844a12aa60bee08d78315bb1c39/ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00", size = 55353 },
+    { url = "https://files.pythonhosted.org/packages/8d/9f/4731ef0671a0653e9f5ba18db7c4596d8ecbf80c7922dd5fe4150f1aea76/ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126", size = 51813 },
+    { url = "https://files.pythonhosted.org/packages/1f/2b/44d6b9c1688330bf011f9abfdb08911a9dc74f76926dde74e718d87600da/ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8", size = 51988 },
+    { url = "https://files.pythonhosted.org/packages/29/45/f5f5667427c1ec3383478092a414063ddd0dfbebbcc533538fe37068a0a3/ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b", size = 53561 },
+    { url = "https://files.pythonhosted.org/packages/26/21/a0c265cda4dd225ec1be595f844661732c13560ad06378760036fc622587/ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9", size = 58497 },
+    { url = "https://files.pythonhosted.org/packages/28/36/8fde862094fd2342ccc427a6a8584fed294055fdee341661c78660f7aef3/ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f", size = 997877 },
+    { url = "https://files.pythonhosted.org/packages/90/37/9208e40d53baa6da9b6a1c719e0670c3f474c8fc7cc2f1e939ec21c1bc93/ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4", size = 1140632 },
+    { url = "https://files.pythonhosted.org/packages/89/d5/2626c87c59802863d44d19e35ad16b7e658e4ac190b0dead17ff25460b4c/ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1", size = 1043513 },
+    { url = "https://files.pythonhosted.org/packages/2f/ee/03662ce9b3f16855770f0d70f10f0978ba6210805aa310c4eebe66d36476/ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f", size = 38616 },
+    { url = "https://files.pythonhosted.org/packages/3e/20/952dbed5895835ea0b82e81a7be4ebb83f93b079d4d1ead93fcddb3075af/ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720", size = 42071 },
+    { url = "https://files.pythonhosted.org/packages/e8/a6/fd3f8bbd80842267e2d06c3583279555e8354c5986c952385199d57a5b6c/ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5", size = 55642 },
+    { url = "https://files.pythonhosted.org/packages/a8/47/dd03fd2b5ae727e16d5d18919b383959c6d269c7b948a380fdd879518640/ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e", size = 51807 },
+    { url = "https://files.pythonhosted.org/packages/25/23/079a4cc6fd7e2655a473ed9e776ddbb7144e27f04e8fc484a0fb45fe6f71/ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043", size = 51972 },
+    { url = "https://files.pythonhosted.org/packages/04/81/668707e5f2177791869b624be4c06fb2473bf97ee33296b18d1cf3092af7/ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1", size = 53686 },
+    { url = "https://files.pythonhosted.org/packages/bd/50/056d518a386d80aaf4505ccf3cee1c40d312a46901ed494d5711dd939bc3/ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3", size = 58591 },
+    { url = "https://files.pythonhosted.org/packages/fc/d6/aeaf3e2d6fb1f4cfb6bf25f454d60490ed8146ddc0600fae44bfe7eb5a72/ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21", size = 997853 },
+    { url = "https://files.pythonhosted.org/packages/f8/d5/1f2a5d2699f447f7d990334ca96e90065ea7f99b142ce96e85f26d7e78e2/ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2", size = 1140689 },
+    { url = "https://files.pythonhosted.org/packages/f2/2c/6990f4ccb41ed93744aaaa3786394bca0875503f97690622f3cafc0adfde/ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e", size = 1043576 },
+    { url = "https://files.pythonhosted.org/packages/14/f5/a2368463dbb09fbdbf6a696062d0c0f62e4ae6fa65f38f829611da2e8fdd/ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e", size = 38764 },
+    { url = "https://files.pythonhosted.org/packages/59/2d/691f741ffd72b6c84438a93749ac57bf1a3f217ac4b0ea4fd0e96119e118/ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc", size = 42211 },
+    { url = "https://files.pythonhosted.org/packages/0d/69/b3e3f924bb0e8820bb46671979770c5be6a7d51c77a66324cdb09f1acddb/ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287", size = 55646 },
+    { url = "https://files.pythonhosted.org/packages/32/8a/9b748eb543c6cabc54ebeaa1f28035b1bd09c0800235b08e85990734c41e/ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e", size = 51806 },
+    { url = "https://files.pythonhosted.org/packages/39/50/4b53ea234413b710a18b305f465b328e306ba9592e13a791a6a6b378869b/ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557", size = 51975 },
+    { url = "https://files.pythonhosted.org/packages/b4/9d/8061934f960cdb6dd55f0b3ceeff207fcc48c64f58b43403777ad5623d9e/ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988", size = 53693 },
+    { url = "https://files.pythonhosted.org/packages/f5/be/7bfa84b28519ddbb67efc8410765ca7da55e6b93aba84d97764cd5794dbc/ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816", size = 58594 },
+    { url = "https://files.pythonhosted.org/packages/48/eb/85d465abafb2c69d9699cfa5520e6e96561db787d36c677370e066c7e2e7/ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20", size = 997853 },
+    { url = "https://files.pythonhosted.org/packages/9f/76/2a63409fc05d34dd7d929357b7a45e3a2c96f22b4225cd74becd2ba6c4cb/ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0", size = 1140694 },
+    { url = "https://files.pythonhosted.org/packages/45/ed/582c4daba0f3e1688d923b5cb914ada1f9defa702df38a1916c899f7c4d1/ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f", size = 1043580 },
+    { url = "https://files.pythonhosted.org/packages/d7/0c/9837fece153051e19c7bade9f88f9b409e026b9525927824cdf16293b43b/ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165", size = 38766 },
+    { url = "https://files.pythonhosted.org/packages/d7/72/6cb6728e2738c05bbe9bd522d6fc79f86b9a28402f38663e85a28fddd4a0/ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539", size = 42212 },
+]
+
+[[package]]
+name = "unstructured"
+version = "0.15.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "backoff" },
+    { name = "beautifulsoup4" },
+    { name = "chardet" },
+    { name = "dataclasses-json" },
+    { name = "emoji" },
+    { name = "filetype" },
+    { name = "langdetect" },
+    { name = "lxml" },
+    { name = "nltk" },
+    { name = "numpy" },
+    { name = "psutil" },
+    { name = "python-iso639" },
+    { name = "python-magic" },
+    { name = "python-oxmsg" },
+    { name = "rapidfuzz" },
+    { name = "requests" },
+    { name = "tabulate" },
+    { name = "tqdm" },
+    { name = "typing-extensions" },
+    { name = "unstructured-client" },
+    { name = "wrapt" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/db/be587e728e2edf684a6c2ead46d05e02951f78b2949c571fed78266941eb/unstructured-0.15.9.tar.gz", hash = "sha256:de26d0e38bac4aa3ae2950f175d0c53a5ccae5c45806b67f55a4af8dea4c407a", size = 1858477 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/58/7b/93126eed91753d65d0c07e9f4c80bd715b6b6003f139483024ae00749aa2/unstructured-0.15.9-py3-none-any.whl", hash = "sha256:ddbb043461cfb9efa1d48a18e62e3b43ff4e0cec25fbf0f28bf345589c1af4d2", size = 2120717 },
+]
+
+[[package]]
+name = "unstructured-client"
+version = "0.25.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "certifi" },
+    { name = "charset-normalizer" },
+    { name = "dataclasses-json" },
+    { name = "deepdiff" },
+    { name = "httpx" },
+    { name = "idna" },
+    { name = "jsonpath-python" },
+    { name = "marshmallow" },
+    { name = "mypy-extensions" },
+    { name = "nest-asyncio" },
+    { name = "packaging" },
+    { name = "pypdf" },
+    { name = "python-dateutil" },
+    { name = "requests" },
+    { name = "requests-toolbelt" },
+    { name = "six" },
+    { name = "typing-extensions" },
+    { name = "typing-inspect" },
+    { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/83/6a37f3737acb1593f6e9f2f206af8986de964c7df05493e543e9f444976b/unstructured-client-0.25.5.tar.gz", hash = "sha256:adb97ea56ce65f8b277d5b05f093e9d13a3320ac8dea7265ffa71f5e13ed5f84", size = 38191 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/58/c8/4dadb117b2f43fc2fc3f44d8718e7cff897ad8d4e8c86fc3f27457fbc236/unstructured_client-0.25.5-py3-none-any.whl", hash = "sha256:23537fee984e43d06a75f986a73e420a9659cc92010afb8324fbf67c85962eaf", size = 43888 },
+]
+
+[[package]]
+name = "uritemplate"
+version = "4.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d2/5a/4742fdba39cd02a56226815abfa72fe0aa81c33bed16ed045647d6000eba/uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", size = 273898 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/81/c0/7461b49cd25aeece13766f02ee576d1db528f1c37ce69aee300e075b485b/uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e", size = 10356 },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/6d/fa469ae21497ddc8bc93e5877702dca7cb8f911e337aca7452b5724f1bb6/urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168", size = 292266 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ca/1c/89ffc63a9605b583d5df2be791a27bc1a42b7c32bab68d3c8f2f73a98cd4/urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472", size = 121444 },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.30.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "click" },
+    { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5a/01/5e637e7aa9dd031be5376b9fb749ec20b86f5a5b6a49b87fabd374d5fa9f/uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788", size = 42825 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f5/8e/cdc7d6263db313030e4c257dd5ba3909ebc4e4fb53ad62d5f09b1a2f5458/uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5", size = 62835 },
+]
+
+[package.optional-dependencies]
+standard = [
+    { name = "colorama", marker = "sys_platform == 'win32'" },
+    { name = "httptools" },
+    { name = "python-dotenv" },
+    { name = "pyyaml" },
+    { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+    { name = "watchfiles" },
+    { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/bc/f1/dc9577455e011ad43d9379e836ee73f40b4f99c02946849a44f7ae64835e/uvloop-0.20.0.tar.gz", hash = "sha256:4603ca714a754fc8d9b197e325db25b2ea045385e8a3ad05d3463de725fdf469", size = 2329938 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/64/bf/45828beccf685b7ed9638d9b77ef382b470c6ca3b5bff78067e02ffd5663/uvloop-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e50289c101495e0d1bb0bfcb4a60adde56e32f4449a67216a1ab2750aa84f037", size = 1320593 },
+    { url = "https://files.pythonhosted.org/packages/27/c0/3c24e50bee7802a2add96ca9f0d5eb0ebab07e0a5615539d38aeb89499b9/uvloop-0.20.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e237f9c1e8a00e7d9ddaa288e535dc337a39bcbf679f290aee9d26df9e72bce9", size = 736676 },
+    { url = "https://files.pythonhosted.org/packages/83/ce/ffa3c72954eae36825acfafd2b6a9221d79abd2670c0d25e04d6ef4a2007/uvloop-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:746242cd703dc2b37f9d8b9f173749c15e9a918ddb021575a0205ec29a38d31e", size = 3494573 },
+    { url = "https://files.pythonhosted.org/packages/46/6d/4caab3a36199ba52b98d519feccfcf48921d7a6649daf14a93c7e77497e9/uvloop-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82edbfd3df39fb3d108fc079ebc461330f7c2e33dbd002d146bf7c445ba6e756", size = 3489932 },
+    { url = "https://files.pythonhosted.org/packages/e4/4f/49c51595bd794945c88613df88922c38076eae2d7653f4624aa6f4980b07/uvloop-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80dc1b139516be2077b3e57ce1cb65bfed09149e1d175e0478e7a987863b68f0", size = 4185596 },
+    { url = "https://files.pythonhosted.org/packages/b8/94/7e256731260d313f5049717d1c4582d52a3b132424c95e16954a50ab95d3/uvloop-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4f44af67bf39af25db4c1ac27e82e9665717f9c26af2369c404be865c8818dcf", size = 4185746 },
+    { url = "https://files.pythonhosted.org/packages/2d/64/31cbd379d6e260ac8de3f672f904e924f09715c3f192b09f26cc8e9f574c/uvloop-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4b75f2950ddb6feed85336412b9a0c310a2edbcf4cf931aa5cfe29034829676d", size = 1324302 },
+    { url = "https://files.pythonhosted.org/packages/1e/6b/9207e7177ff30f78299401f2e1163ea41130d4fd29bcdc6d12572c06b728/uvloop-0.20.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:77fbc69c287596880ecec2d4c7a62346bef08b6209749bf6ce8c22bbaca0239e", size = 738105 },
+    { url = "https://files.pythonhosted.org/packages/c1/ba/b64b10f577519d875992dc07e2365899a1a4c0d28327059ce1e1bdfb6854/uvloop-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6462c95f48e2d8d4c993a2950cd3d31ab061864d1c226bbf0ee2f1a8f36674b9", size = 4090658 },
+    { url = "https://files.pythonhosted.org/packages/0a/f8/5ceea6876154d926604f10c1dd896adf9bce6d55a55911364337b8a5ed8d/uvloop-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649c33034979273fa71aa25d0fe120ad1777c551d8c4cd2c0c9851d88fcb13ab", size = 4173357 },
+    { url = "https://files.pythonhosted.org/packages/18/b2/117ab6bfb18274753fbc319607bf06e216bd7eea8be81d5bac22c912d6a7/uvloop-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a609780e942d43a275a617c0839d85f95c334bad29c4c0918252085113285b5", size = 4029868 },
+    { url = "https://files.pythonhosted.org/packages/6f/52/deb4be09060637ef4752adaa0b75bf770c20c823e8108705792f99cd4a6f/uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00", size = 4115980 },
+]
+
+[[package]]
+name = "validators"
+version = "0.33.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/5d/af/5ad4fed95276e3eb7628d858c88cd205799bcad847e46223760a3129cbb1/validators-0.33.0.tar.gz", hash = "sha256:535867e9617f0100e676a1257ba1e206b9bfd847ddc171e4d44811f07ff0bfbf", size = 70741 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/04/22/91b4bd36df27e651daedd93d03d5d3bb6029fdb0b55494e45ee46c36c570/validators-0.33.0-py3-none-any.whl", hash = "sha256:134b586a98894f8139865953899fc2daeb3d0c35569552c5518f089ae43ed075", size = 43298 },
+]
+
+[[package]]
+name = "watchfiles"
+version = "0.23.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9e/1a/b06613ef620d7f5ca712a3d4928ec1c07182159a64277fcdf7738edb0b32/watchfiles-0.23.0.tar.gz", hash = "sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b", size = 37384 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/14/5f/787386438d895145099e1415d1fbd3ff047a4f5e329134fd30677fe83f1f/watchfiles-0.23.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab", size = 374801 },
+    { url = "https://files.pythonhosted.org/packages/76/6f/3075cd9c69fdce2544fb13cb9e3c8ad51424cb2c552b019514799a14966e/watchfiles-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3", size = 368210 },
+    { url = "https://files.pythonhosted.org/packages/ab/6b/cd4faa27088a8b612ffdfa25e3d413e676a6173b8b02a33e7fec152d75ca/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4", size = 441356 },
+    { url = "https://files.pythonhosted.org/packages/39/ba/d361135dac6cd0fb4449f4f058c053eb9b42f70ff4d9a13767808e18851c/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf", size = 437615 },
+    { url = "https://files.pythonhosted.org/packages/34/2c/c279de01628f467d16b444bdcedf9c4ce3bc5242cb23f9bfb8fbff8522ee/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a", size = 456227 },
+    { url = "https://files.pythonhosted.org/packages/a4/9f/a3c9f1fbcd1099554e4f707e14473ff23f0e05013d553755b98c2d86716d/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8", size = 472219 },
+    { url = "https://files.pythonhosted.org/packages/22/ee/06a0a6cbde8ac6fff57c33da9e428f42dd0989e60a6ad72ca6534f650a47/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788", size = 479948 },
+    { url = "https://files.pythonhosted.org/packages/b9/f0/76ad5227da9461b1190de2f9dd21fece09660a9a44607de9c728f3d3e93f/watchfiles-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1", size = 427559 },
+    { url = "https://files.pythonhosted.org/packages/e1/15/daf4361e0a6e6b27f516aaaacbb16baa8d1a266657b2314862fc73f2deaf/watchfiles-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220", size = 616447 },
+    { url = "https://files.pythonhosted.org/packages/b3/e4/2647ca9aaa072e139a4cc6c83c8a15d2f8fa6740913903ab998917a5ed97/watchfiles-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320", size = 598031 },
+    { url = "https://files.pythonhosted.org/packages/3d/02/f223537cd0e3c22df45629710b27b7f89fdf4114be2f3399b83faedf1446/watchfiles-0.23.0-cp311-none-win32.whl", hash = "sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b", size = 264354 },
+    { url = "https://files.pythonhosted.org/packages/03/31/c1b5ea92100d9774f5a8a89115a43ef1c4fb169b643b6cc930e0cd2c5728/watchfiles-0.23.0-cp311-none-win_amd64.whl", hash = "sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e", size = 275821 },
+    { url = "https://files.pythonhosted.org/packages/23/9c/810ede8d4dff7e65393b50cbb1a3ef10b6cdb1312a97d8106712175355c8/watchfiles-0.23.0-cp311-none-win_arm64.whl", hash = "sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304", size = 266906 },
+    { url = "https://files.pythonhosted.org/packages/61/52/85cdf326a53f1ae3fbe5dcab13f5729ca91ec2d61140e095a2a4cdf6a9ca/watchfiles-0.23.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5", size = 373314 },
+    { url = "https://files.pythonhosted.org/packages/20/5e/a97417a6544615b21c7960a45aeea13e3b42779e0ed3ebdd2d76ad62ab50/watchfiles-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8", size = 368915 },
+    { url = "https://files.pythonhosted.org/packages/bc/82/537945ed624af6248c9820a99cbfd5902bb5e6a71a01a5b3de0c00f1872e/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f", size = 441495 },
+    { url = "https://files.pythonhosted.org/packages/28/24/060b064f28083866d916052fcced5c3547c5081a8e27b0702434666aa9a0/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77", size = 437357 },
+    { url = "https://files.pythonhosted.org/packages/b6/00/ac760f3fa8d8975dbeaef9af99b21077e7c38898ac5051c8601649d86d99/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1", size = 456584 },
+    { url = "https://files.pythonhosted.org/packages/f7/52/2f7bbedc5f524d2ba0e9d792dab01ef4418d0f5045a9f5f4e5aca142a30d/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b", size = 471863 },
+    { url = "https://files.pythonhosted.org/packages/b1/64/a80f51cb55c967629930682bf120d5ca9d1c65077c38328be635ed0d567c/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f", size = 478307 },
+    { url = "https://files.pythonhosted.org/packages/03/f1/fdacfdbffb0635a7d0140ecca6ef7b5bce6566a085f76a65eb796ee54ddd/watchfiles-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0", size = 427117 },
+    { url = "https://files.pythonhosted.org/packages/d1/23/89b2bef692c350de8a4c2bde501fdf6087889a55f52a3201f0c53b616087/watchfiles-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c", size = 616352 },
+    { url = "https://files.pythonhosted.org/packages/2c/35/a683945181a527083a1146620997b5d6ffe06d716c4497d388bfea813f0c/watchfiles-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581", size = 597165 },
+    { url = "https://files.pythonhosted.org/packages/9e/9b/ec2eabc996e5332fc89c633fbe762e08a58a7df6b5e595dd458c5f7778a4/watchfiles-0.23.0-cp312-none-win32.whl", hash = "sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75", size = 264293 },
+    { url = "https://files.pythonhosted.org/packages/e0/3a/62add8d90070f4b17f8bbfd66c9eaa9e08af3bc4020c07a9400d1b959aaf/watchfiles-0.23.0-cp312-none-win_amd64.whl", hash = "sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb", size = 275514 },
+    { url = "https://files.pythonhosted.org/packages/e8/9a/2792d4c24105104bfaf959bffefb09e02d14050913a83242ce4eb1e3f2ff/watchfiles-0.23.0-cp312-none-win_arm64.whl", hash = "sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8", size = 266607 },
+    { url = "https://files.pythonhosted.org/packages/f6/5b/1a1d9bca4eae8cf191e74b62cd970f4a010f56f897c11dd2e6caef3ce7e3/watchfiles-0.23.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9", size = 372999 },
+    { url = "https://files.pythonhosted.org/packages/98/e1/76ad010c0a2bb6efbb80383c0bba56db065238f12b0da6e6026b4e69f6aa/watchfiles-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e", size = 368511 },
+    { url = "https://files.pythonhosted.org/packages/a1/13/d2d59d545b84fd3cf4f08b69da358209b4276c2c932d060d94a421015074/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217", size = 441063 },
+    { url = "https://files.pythonhosted.org/packages/4b/d1/dab28bed3bc9172d44100e5fae8107bd01ef85fc6bddb80d223d0d9f709f/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1", size = 436805 },
+    { url = "https://files.pythonhosted.org/packages/06/9c/46e0d17853b62b5d4bf8095e7b9bb0b0ad4babb6c6133138929473f161f3/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1", size = 456411 },
+    { url = "https://files.pythonhosted.org/packages/2c/ff/e891b230bcf3a648352a00b920d4a1142a938f0b97c9e8e27c2eaaeda221/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0", size = 471563 },
+    { url = "https://files.pythonhosted.org/packages/0b/07/f5b54afa8b7c33386c5778d92e681562939900f4ee1c6de9bffc49e7221f/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d", size = 478385 },
+    { url = "https://files.pythonhosted.org/packages/a3/b6/243c1dd351ac9b8258a3ea99c33d04ecdc9766e6c7f13a43452883e92a7a/watchfiles-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0", size = 427485 },
+    { url = "https://files.pythonhosted.org/packages/28/8a/6d00aa4aa9a9938de645c1d411e3af82e74db8d25a0c05427b7a88b4d8d3/watchfiles-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef", size = 615839 },
+    { url = "https://files.pythonhosted.org/packages/5a/d9/120d212d2952342e2c9673096f5c17cd48e90a7c9ff203ab1ad2f974befe/watchfiles-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1", size = 596603 },
+    { url = "https://files.pythonhosted.org/packages/3b/25/ec3676b140a93ac256d058a6f82810cf5e0e42fd444b948c62bc56f57f52/watchfiles-0.23.0-cp313-none-win32.whl", hash = "sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b", size = 263898 },
+    { url = "https://files.pythonhosted.org/packages/1a/c6/bf3b8cbe6944499fbe0d400175560a200cdecadccbacc8ace74486565d74/watchfiles-0.23.0-cp313-none-win_amd64.whl", hash = "sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff", size = 275220 },
+]
+
+[[package]]
+name = "websocket-client"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e6/30/fba0d96b4b5fbf5948ed3f4681f7da2f9f64512e1d303f94b4cc174c24a5/websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da", size = 54648 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 },
+]
+
+[[package]]
+name = "websockets"
+version = "13.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0f/b0/e53bdd53d86447d211694f3cf66f163d077c5d68e6bcaa726bf64e88ae3a/websockets-13.0.tar.gz", hash = "sha256:b7bf950234a482b7461afdb2ec99eee3548ec4d53f418c7990bb79c620476602", size = 147622 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/12/29/9fdf8a7f1ced2bac55d36e0b879991498c9858f1e524763434025948d254/websockets-13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:06df8306c241c235075d2ae77367038e701e53bc8c1bb4f6644f4f53aa6dedd0", size = 150915 },
+    { url = "https://files.pythonhosted.org/packages/b9/27/723276e7fcb41a3e0859e347014e3e24637982a29222132746b98095ec02/websockets-13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85a1f92a02f0b8c1bf02699731a70a8a74402bb3f82bee36e7768b19a8ed9709", size = 148575 },
+    { url = "https://files.pythonhosted.org/packages/04/54/39b1f809e34f78ebb1dcb9cf57465db9705bbf59f30bd1b3b381272dff2b/websockets-13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9ed02c604349068d46d87ef4c2012c112c791f2bec08671903a6bb2bd9c06784", size = 148825 },
+    { url = "https://files.pythonhosted.org/packages/fe/df/0a8a90162c32ceb9f28415291c1d689310b503288d29169302964105a351/websockets-13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b89849171b590107f6724a7b0790736daead40926ddf47eadf998b4ff51d6414", size = 158482 },
+    { url = "https://files.pythonhosted.org/packages/20/05/227dbb1861cd1e2eb04ac79b136da841dbf6f196e4dc0bd1e67edb4ee69d/websockets-13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:939a16849d71203628157a5e4a495da63967c744e1e32018e9b9e2689aca64d4", size = 157478 },
+    { url = "https://files.pythonhosted.org/packages/fe/dd/3384d3eb26022703895d6ed65aec2d3af6976c3d9aed06200a322e7192cb/websockets-13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad818cdac37c0ad4c58e51cb4964eae4f18b43c4a83cb37170b0d90c31bd80cf", size = 157855 },
+    { url = "https://files.pythonhosted.org/packages/93/ad/0320a24cd8309e1a257d43d762a732162f2956b769c1ad950b70d4d4d15a/websockets-13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cbfe82a07596a044de78bb7a62519e71690c5812c26c5f1d4b877e64e4f46309", size = 158160 },
+    { url = "https://files.pythonhosted.org/packages/d0/33/acc24e576228301d1dc23ce9d3f7d20f51dfe6c16d1b241e6ba4b2904d3e/websockets-13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e07e76c49f39c5b45cbd7362b94f001ae209a3ea4905ae9a09cfd53b3c76373d", size = 157598 },
+    { url = "https://files.pythonhosted.org/packages/83/47/01645a0ea041e32a9d8946a324845beb8daba2e2f00ee4fd2d04d3ceb598/websockets-13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:372f46a0096cfda23c88f7e42349a33f8375e10912f712e6b496d3a9a557290f", size = 157548 },
+    { url = "https://files.pythonhosted.org/packages/73/89/ea73bc41934eb3ea3f0c04fa7b16455ec5925b8b72aa5e016bd22df5feb5/websockets-13.0-cp311-cp311-win32.whl", hash = "sha256:376a43a4fd96725f13450d3d2e98f4f36c3525c562ab53d9a98dd2950dca9a8a", size = 151756 },
+    { url = "https://files.pythonhosted.org/packages/9b/b1/81f655476532b31c39814d55a1dc1e97ecedc5a1b4f9517ee665aec398f6/websockets-13.0-cp311-cp311-win_amd64.whl", hash = "sha256:2be1382a4daa61e2f3e2be3b3c86932a8db9d1f85297feb6e9df22f391f94452", size = 152200 },
+    { url = "https://files.pythonhosted.org/packages/ad/0a/baeea2931827e73ebe3d958fad9df74ec66d08341d0cf701ced0381adc91/websockets-13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b5407c34776b9b77bd89a5f95eb0a34aaf91889e3f911c63f13035220eb50107", size = 150928 },
+    { url = "https://files.pythonhosted.org/packages/6d/f7/306e2940829db34c5866e869eb5b1a08dd04d1c6d25c71327a028d124871/websockets-13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4782ec789f059f888c1e8fdf94383d0e64b531cffebbf26dd55afd53ab487ca4", size = 148585 },
+    { url = "https://files.pythonhosted.org/packages/2b/3c/183a4f79e0ce6be8733f824e0a48db3771a373a7206aef900bc1ae4c176e/websockets-13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c8feb8e19ef65c9994e652c5b0324abd657bedd0abeb946fb4f5163012c1e730", size = 148821 },
+    { url = "https://files.pythonhosted.org/packages/03/32/37e1c9dd9aa1e7fa6fb3147d6992d61a20ba63ffee2adc88a392e1ae7376/websockets-13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f3d2e20c442b58dbac593cb1e02bc02d149a86056cc4126d977ad902472e3b", size = 158746 },
+    { url = "https://files.pythonhosted.org/packages/6c/da/0cace6358289c7de1ee02ed0d572dfe92e5cb97270bda60f04a4e49ac5c5/websockets-13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e39d393e0ab5b8bd01717cc26f2922026050188947ff54fe6a49dc489f7750b7", size = 157699 },
+    { url = "https://files.pythonhosted.org/packages/c7/ab/b763b0e8598c4251ec6e17d18f46cbced157772b991200fb0d32550844c5/websockets-13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f661a4205741bdc88ac9c2b2ec003c72cee97e4acd156eb733662ff004ba429", size = 158124 },
+    { url = "https://files.pythonhosted.org/packages/d0/2d/40b8c3ba08792c2ecdb81613671a4b9bd33b83c50519b235e8eeb0ae21a0/websockets-13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:384129ad0490e06bab2b98c1da9b488acb35bb11e2464c728376c6f55f0d45f3", size = 158415 },
+    { url = "https://files.pythonhosted.org/packages/4c/5e/9a42db20f6c38d247a900bfb8633953df93d8873a99ed9432645a4d5e185/websockets-13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df5c0eff91f61b8205a6c9f7b255ff390cdb77b61c7b41f79ca10afcbb22b6cb", size = 157795 },
+    { url = "https://files.pythonhosted.org/packages/87/52/7fb5f052eefaa5d2b42da06b314c2af0467fadbd7f360716a1a4d4f7ab67/websockets-13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02cc9bb1a887dac0e08bf657c5d00aa3fac0d03215d35a599130c2034ae6663a", size = 157791 },
+    { url = "https://files.pythonhosted.org/packages/9c/8b/4b7064d1a40fcb85f64bc051d8bdc8a9e388572eb5bec5cb85ffb2c43e01/websockets-13.0-cp312-cp312-win32.whl", hash = "sha256:d9726d2c9bd6aed8cb994d89b3910ca0079406edce3670886ec828a73e7bdd53", size = 151765 },
+    { url = "https://files.pythonhosted.org/packages/8b/a3/297207726b292e85b9a8ce24ef6ab16a056c457100e915a67b6928a58fa9/websockets-13.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0839f35322f7b038d8adcf679e2698c3a483688cc92e3bd15ee4fb06669e9a", size = 152202 },
+    { url = "https://files.pythonhosted.org/packages/03/b6/778678e1ff104df3a869dacb0bc845df34d74f2ff7451f99babccd212203/websockets-13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:da7e501e59857e8e3e9d10586139dc196b80445a591451ca9998aafba1af5278", size = 150936 },
+    { url = "https://files.pythonhosted.org/packages/fa/25/28609b2555f11e4913a4021147b7a7c5117b5c41da5d26a604a91bae85b9/websockets-13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a00e1e587c655749afb5b135d8d3edcfe84ec6db864201e40a882e64168610b3", size = 148590 },
+    { url = "https://files.pythonhosted.org/packages/cb/1f/e06fb15fde90683fd98e6ca44fb54fe579161ce553d54fdbb578014ae1a7/websockets-13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a7fbf2a8fe7556a8f4e68cb3e736884af7bf93653e79f6219f17ebb75e97d8f0", size = 148826 },
+    { url = "https://files.pythonhosted.org/packages/22/00/9892eee346f44cd814c18888bc1a05880e3f8091e4eb999e6b34634cd278/websockets-13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ea9c9c7443a97ea4d84d3e4d42d0e8c4235834edae652993abcd2aff94affd7", size = 158717 },
+    { url = "https://files.pythonhosted.org/packages/dc/ad/2bdc3a5dd60b639e0f8e76ee4a57fda27abaf05f604708c61c6fd7f8ad88/websockets-13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c2221b539b360203f3f9ad168e527bf16d903e385068ae842c186efb13d0ea", size = 157660 },
+    { url = "https://files.pythonhosted.org/packages/0c/14/5585de16939608b77a37f8b88e1bd1d430d95ec19d3a8c26ec42a91f2815/websockets-13.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:358d37c5c431dd050ffb06b4b075505aae3f4f795d7fff9794e5ed96ce99b998", size = 158104 },
+    { url = "https://files.pythonhosted.org/packages/7b/1e/6cd9063fd34fe7f649ed9a56d3c91e80dea95cf3ab3344203ee774d51a56/websockets-13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:038e7a0f1bfafc7bf52915ab3506b7a03d1e06381e9f60440c856e8918138151", size = 158463 },
+    { url = "https://files.pythonhosted.org/packages/d9/4d/c3282f8e54103f3d38b5e56851d00911dafd0c37c8d03a9ecc7a25f2a9da/websockets-13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fd038bc9e2c134847f1e0ce3191797fad110756e690c2fdd9702ed34e7a43abb", size = 157850 },
+    { url = "https://files.pythonhosted.org/packages/a1/08/af4f67b74cc6891ee1c34a77b47a3cb77081b824c3df92c1196980df9a4f/websockets-13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93b8c2008f372379fb6e5d2b3f7c9ec32f7b80316543fd3a5ace6610c5cde1b0", size = 157843 },
+    { url = "https://files.pythonhosted.org/packages/b4/b7/2c991e51d48b1b98847d0a0b608508a3b687f215a2390f99cf0ee7dd2777/websockets-13.0-cp313-cp313-win32.whl", hash = "sha256:851fd0afb3bc0b73f7c5b5858975d42769a5fdde5314f4ef2c106aec63100687", size = 151763 },
+    { url = "https://files.pythonhosted.org/packages/bc/0f/f06ed6485cf9cdea7d89c2f6e9d19f1be963ba5d26fb79760bfd17dd4aa5/websockets-13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7d14901fdcf212804970c30ab9ee8f3f0212e620c7ea93079d6534863444fb4e", size = 152197 },
+    { url = "https://files.pythonhosted.org/packages/b2/89/c0be9f09eea478659e9d936210ff03e6a2a3a8d4b8dfac6b1143ff646ded/websockets-13.0-py3-none-any.whl", hash = "sha256:dbbac01e80aee253d44c4f098ab3cc17c822518519e869b284cfbb8cd16cc9de", size = 142957 },
+]
+
+[[package]]
+name = "werkzeug"
+version = "3.0.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/e2/6dbcaab07560909ff8f654d3a2e5a60552d937c909455211b1b36d7101dc/werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306", size = 803966 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/4b/84/997bbf7c2bf2dc3f09565c6d0b4959fefe5355c18c4096cfd26d83e0785b/werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", size = 227554 },
+]
+
+[[package]]
+name = "win-unicode-console"
+version = "0.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/89/8d/7aad74930380c8972ab282304a2ff45f3d4927108bb6693cabcc9fc6a099/win_unicode_console-0.5.zip", hash = "sha256:d4142d4d56d46f449d6f00536a73625a871cba040f0bc1a2e305a04578f07d1e", size = 31420 }
+
+[[package]]
+name = "wrapt"
+version = "1.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/95/4c/063a912e20bcef7124e0df97282a8af3ff3e4b603ce84c481d6d7346be0a/wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d", size = 53972 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/fd/03/c188ac517f402775b90d6f312955a5e53b866c964b32119f2ed76315697e/wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09", size = 37313 },
+    { url = "https://files.pythonhosted.org/packages/0f/16/ea627d7817394db04518f62934a5de59874b587b792300991b3c347ff5e0/wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d", size = 38164 },
+    { url = "https://files.pythonhosted.org/packages/7f/a7/f1212ba098f3de0fd244e2de0f8791ad2539c03bef6c05a9fcb03e45b089/wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389", size = 80890 },
+    { url = "https://files.pythonhosted.org/packages/b7/96/bb5e08b3d6db003c9ab219c487714c13a237ee7dcc572a555eaf1ce7dc82/wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060", size = 73118 },
+    { url = "https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1", size = 80746 },
+    { url = "https://files.pythonhosted.org/packages/11/fb/18ec40265ab81c0e82a934de04596b6ce972c27ba2592c8b53d5585e6bcd/wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3", size = 85668 },
+    { url = "https://files.pythonhosted.org/packages/0f/ef/0ecb1fa23145560431b970418dce575cfaec555ab08617d82eb92afc7ccf/wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956", size = 78556 },
+    { url = "https://files.pythonhosted.org/packages/25/62/cd284b2b747f175b5a96cbd8092b32e7369edab0644c45784871528eb852/wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d", size = 85712 },
+    { url = "https://files.pythonhosted.org/packages/e5/a7/47b7ff74fbadf81b696872d5ba504966591a3468f1bc86bca2f407baef68/wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362", size = 35327 },
+    { url = "https://files.pythonhosted.org/packages/cf/c3/0084351951d9579ae83a3d9e38c140371e4c6b038136909235079f2e6e78/wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89", size = 37523 },
+    { url = "https://files.pythonhosted.org/packages/92/17/224132494c1e23521868cdd57cd1e903f3b6a7ba6996b7b8f077ff8ac7fe/wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b", size = 37614 },
+    { url = "https://files.pythonhosted.org/packages/6a/d7/cfcd73e8f4858079ac59d9db1ec5a1349bc486ae8e9ba55698cc1f4a1dff/wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36", size = 38316 },
+    { url = "https://files.pythonhosted.org/packages/7e/79/5ff0a5c54bda5aec75b36453d06be4f83d5cd4932cc84b7cb2b52cee23e2/wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73", size = 86322 },
+    { url = "https://files.pythonhosted.org/packages/c4/81/e799bf5d419f422d8712108837c1d9bf6ebe3cb2a81ad94413449543a923/wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809", size = 79055 },
+    { url = "https://files.pythonhosted.org/packages/62/62/30ca2405de6a20448ee557ab2cd61ab9c5900be7cbd18a2639db595f0b98/wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b", size = 87291 },
+    { url = "https://files.pythonhosted.org/packages/49/4e/5d2f6d7b57fc9956bf06e944eb00463551f7d52fc73ca35cfc4c2cdb7aed/wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81", size = 90374 },
+    { url = "https://files.pythonhosted.org/packages/a6/9b/c2c21b44ff5b9bf14a83252a8b973fb84923764ff63db3e6dfc3895cf2e0/wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9", size = 83896 },
+    { url = "https://files.pythonhosted.org/packages/14/26/93a9fa02c6f257df54d7570dfe8011995138118d11939a4ecd82cb849613/wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c", size = 91738 },
+    { url = "https://files.pythonhosted.org/packages/a2/5b/4660897233eb2c8c4de3dc7cefed114c61bacb3c28327e64150dc44ee2f6/wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc", size = 35568 },
+    { url = "https://files.pythonhosted.org/packages/5c/cc/8297f9658506b224aa4bd71906447dea6bb0ba629861a758c28f67428b91/wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8", size = 37653 },
+    { url = "https://files.pythonhosted.org/packages/ff/21/abdedb4cdf6ff41ebf01a74087740a709e2edb146490e4d9beea054b0b7a/wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1", size = 23362 },
+]
+
+[[package]]
+name = "wsproto"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/4a/44d3c295350d776427904d73c189e10aeae66d7f555bb2feee16d1e4ba5a/wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065", size = 53425 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/78/58/e860788190eba3bcce367f74d29c4675466ce8dddfba85f7827588416f01/wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736", size = 24226 },
+]
+
+[[package]]
+name = "xlrd"
+version = "2.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/b3/19a2540d21dea5f908304375bd43f5ed7a4c28a370dc9122c565423e6b44/xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88", size = 100259 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a6/0c/c2a72d51fe56e08a08acc85d13013558a2d793028ae7385448a6ccdfae64/xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", size = 96531 },
+]
+
+[[package]]
+name = "xlsxwriter"
+version = "3.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a6/c3/b36fa44a0610a0f65d2e65ba6a262cbe2554b819f1449731971f7c16ea3c/XlsxWriter-3.2.0.tar.gz", hash = "sha256:9977d0c661a72866a61f9f7a809e25ebbb0fb7036baa3b9fe74afcfca6b3cb8c", size = 198732 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a7/ea/53d1fe468e63e092cf16e2c18d16f50c29851242f9dd12d6a66e0d7f0d02/XlsxWriter-3.2.0-py3-none-any.whl", hash = "sha256:ecfd5405b3e0e228219bcaf24c2ca0915e012ca9464a14048021d21a995d490e", size = 159925 },
+]
+
+[[package]]
+name = "xxhash"
+version = "3.5.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1", size = 31969 },
+    { url = "https://files.pythonhosted.org/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8", size = 30800 },
+    { url = "https://files.pythonhosted.org/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166", size = 221566 },
+    { url = "https://files.pythonhosted.org/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7", size = 201214 },
+    { url = "https://files.pythonhosted.org/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623", size = 429433 },
+    { url = "https://files.pythonhosted.org/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a", size = 194822 },
+    { url = "https://files.pythonhosted.org/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88", size = 208538 },
+    { url = "https://files.pythonhosted.org/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c", size = 216953 },
+    { url = "https://files.pythonhosted.org/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2", size = 203594 },
+    { url = "https://files.pythonhosted.org/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084", size = 210971 },
+    { url = "https://files.pythonhosted.org/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d", size = 415050 },
+    { url = "https://files.pythonhosted.org/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839", size = 192216 },
+    { url = "https://files.pythonhosted.org/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da", size = 30120 },
+    { url = "https://files.pythonhosted.org/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58", size = 30003 },
+    { url = "https://files.pythonhosted.org/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3", size = 26777 },
+    { url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969 },
+    { url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787 },
+    { url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959 },
+    { url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006 },
+    { url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326 },
+    { url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380 },
+    { url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934 },
+    { url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301 },
+    { url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351 },
+    { url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294 },
+    { url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674 },
+    { url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022 },
+    { url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170 },
+    { url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040 },
+    { url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796 },
+    { url = "https://files.pythonhosted.org/packages/c9/b8/e4b3ad92d249be5c83fa72916c9091b0965cb0faeff05d9a0a3870ae6bff/xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6", size = 31795 },
+    { url = "https://files.pythonhosted.org/packages/fc/d8/b3627a0aebfbfa4c12a41e22af3742cf08c8ea84f5cc3367b5de2d039cce/xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5", size = 30792 },
+    { url = "https://files.pythonhosted.org/packages/c3/cc/762312960691da989c7cd0545cb120ba2a4148741c6ba458aa723c00a3f8/xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc", size = 220950 },
+    { url = "https://files.pythonhosted.org/packages/fe/e9/cc266f1042c3c13750e86a535496b58beb12bf8c50a915c336136f6168dc/xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3", size = 199980 },
+    { url = "https://files.pythonhosted.org/packages/bf/85/a836cd0dc5cc20376de26b346858d0ac9656f8f730998ca4324921a010b9/xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c", size = 428324 },
+    { url = "https://files.pythonhosted.org/packages/b4/0e/15c243775342ce840b9ba34aceace06a1148fa1630cd8ca269e3223987f5/xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb", size = 194370 },
+    { url = "https://files.pythonhosted.org/packages/87/a1/b028bb02636dfdc190da01951d0703b3d904301ed0ef6094d948983bef0e/xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f", size = 207911 },
+    { url = "https://files.pythonhosted.org/packages/80/d5/73c73b03fc0ac73dacf069fdf6036c9abad82de0a47549e9912c955ab449/xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7", size = 216352 },
+    { url = "https://files.pythonhosted.org/packages/b6/2a/5043dba5ddbe35b4fe6ea0a111280ad9c3d4ba477dd0f2d1fe1129bda9d0/xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326", size = 203410 },
+    { url = "https://files.pythonhosted.org/packages/a2/b2/9a8ded888b7b190aed75b484eb5c853ddd48aa2896e7b59bbfbce442f0a1/xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf", size = 210322 },
+    { url = "https://files.pythonhosted.org/packages/98/62/440083fafbc917bf3e4b67c2ade621920dd905517e85631c10aac955c1d2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7", size = 414725 },
+    { url = "https://files.pythonhosted.org/packages/75/db/009206f7076ad60a517e016bb0058381d96a007ce3f79fa91d3010f49cc2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c", size = 192070 },
+    { url = "https://files.pythonhosted.org/packages/1f/6d/c61e0668943a034abc3a569cdc5aeae37d686d9da7e39cf2ed621d533e36/xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637", size = 30172 },
+    { url = "https://files.pythonhosted.org/packages/96/14/8416dce965f35e3d24722cdf79361ae154fa23e2ab730e5323aa98d7919e/xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43", size = 30041 },
+    { url = "https://files.pythonhosted.org/packages/27/ee/518b72faa2073f5aa8e3262408d284892cb79cf2754ba0c3a5870645ef73/xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b", size = 26801 },
+]
+
+[[package]]
+name = "yarl"
+version = "1.9.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "idna" },
+    { name = "multidict" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e0/ad/bedcdccbcbf91363fd425a948994f3340924145c2bc8ccb296f4a1e52c28/yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf", size = 141869 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/12/65/4c7f3676209a569405c9f0f492df2bc3a387c253f5d906e36944fdd12277/yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099", size = 132836 },
+    { url = "https://files.pythonhosted.org/packages/3b/c5/81e3dbf5271ab1510860d2ae7a704ef43f93f7cb9326bf7ebb1949a7260b/yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c", size = 83215 },
+    { url = "https://files.pythonhosted.org/packages/20/3d/7dabf580dfc0b588e48830486b488858122b10a61f33325e0d7cf1d6180b/yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0", size = 81237 },
+    { url = "https://files.pythonhosted.org/packages/38/45/7c669999f5d350f4f8f74369b94e0f6705918eee18e38610bfe44af93d4f/yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525", size = 324181 },
+    { url = "https://files.pythonhosted.org/packages/50/49/aa04effe2876cced8867bf9d89b620acf02b733c62adfe22a8218c35d70b/yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8", size = 339412 },
+    { url = "https://files.pythonhosted.org/packages/7d/95/4310771fb9c71599d8466f43347ac18fafd501621e65b93f4f4f16899b1d/yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9", size = 337973 },
+    { url = "https://files.pythonhosted.org/packages/9f/ea/94ad7d8299df89844e666e4aa8a0e9b88e02416cd6a7dd97969e9eae5212/yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42", size = 328126 },
+    { url = "https://files.pythonhosted.org/packages/6d/be/9d4885e2725f5860833547c9e4934b6e0f44a355b24ffc37957264761e3e/yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe", size = 316677 },
+    { url = "https://files.pythonhosted.org/packages/4a/70/5c744d67cad3d093e233cb02f37f2830cb89abfcbb7ad5b5af00ff21d14d/yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce", size = 324243 },
+    { url = "https://files.pythonhosted.org/packages/c2/80/8b38d8fed958ac37afb8b81a54bf4f767b107e2c2004dab165edb58fc51b/yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9", size = 318099 },
+    { url = "https://files.pythonhosted.org/packages/59/50/715bbc7bda65291f9295e757f67854206f4d8be9746d39187724919ac14d/yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572", size = 334924 },
+    { url = "https://files.pythonhosted.org/packages/a8/af/ca9962488027576d7162878a1864cbb1275d298af986ce96bdfd4807d7b2/yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958", size = 335060 },
+    { url = "https://files.pythonhosted.org/packages/28/c7/249a3a903d500ca7369eb542e2847a14f12f249638dcc10371db50cd17ff/yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98", size = 326689 },
+    { url = "https://files.pythonhosted.org/packages/ec/0c/f02dd0b875a7a460f95dc7cf18983ed43c693283d6ab92e0ad71b9e0de8f/yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31", size = 70407 },
+    { url = "https://files.pythonhosted.org/packages/27/41/945ae9a80590e4fb0be166863c6e63d75e4b35789fa3a61ff1dbdcdc220f/yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1", size = 76719 },
+    { url = "https://files.pythonhosted.org/packages/7b/cd/a921122610dedfed94e494af18e85aae23e93274c00ca464cfc591c8f4fb/yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81", size = 129561 },
+    { url = "https://files.pythonhosted.org/packages/7c/a0/887c93020c788f249c24eaab288c46e5fed4d2846080eaf28ed3afc36e8d/yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142", size = 81595 },
+    { url = "https://files.pythonhosted.org/packages/54/99/ed3c92c38f421ba6e36caf6aa91c34118771d252dce800118fa2f44d7962/yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074", size = 79400 },
+    { url = "https://files.pythonhosted.org/packages/ea/45/65801be625ef939acc8b714cf86d4a198c0646e80dc8970359d080c47204/yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129", size = 317397 },
+    { url = "https://files.pythonhosted.org/packages/06/91/9696601a8ba674c8f0c15035cc9e94ca31f541330364adcfd5a399f598bf/yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2", size = 327246 },
+    { url = "https://files.pythonhosted.org/packages/da/3e/bf25177b3618889bf067aacf01ef54e910cd569d14e2f84f5e7bec23bb82/yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78", size = 327321 },
+    { url = "https://files.pythonhosted.org/packages/28/1c/bdb3411467b805737dd2720b85fd082e49f59bf0cc12dc1dfcc80ab3d274/yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4", size = 322424 },
+    { url = "https://files.pythonhosted.org/packages/41/e9/53bc89f039df2824a524a2aa03ee0bfb8f0585b08949e7521f5eab607085/yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0", size = 310868 },
+    { url = "https://files.pythonhosted.org/packages/79/cd/a78c3b0304a4a970b5ae3993f4f5f649443bc8bfa5622f244aed44c810ed/yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51", size = 323452 },
+    { url = "https://files.pythonhosted.org/packages/2e/5e/1c78eb05ae0efae08498fd7ab939435a29f12c7f161732e7fe327e5b8ca1/yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff", size = 313554 },
+    { url = "https://files.pythonhosted.org/packages/04/e0/0029563a8434472697aebb269fdd2ffc8a19e3840add1d5fa169ec7c56e3/yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7", size = 331029 },
+    { url = "https://files.pythonhosted.org/packages/de/1b/7e6b1ad42ccc0ed059066a7ae2b6fd4bce67795d109a99ccce52e9824e96/yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc", size = 333839 },
+    { url = "https://files.pythonhosted.org/packages/85/8a/c364d6e2eeb4e128a5ee9a346fc3a09aa76739c0c4e2a7305989b54f174b/yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10", size = 328251 },
+    { url = "https://files.pythonhosted.org/packages/ec/9d/0da94b33b9fb89041e10f95a14a55b0fef36c60b6a1d5ff85a0c2ecb1a97/yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7", size = 70195 },
+    { url = "https://files.pythonhosted.org/packages/c5/f4/2fdc5a11503bc61818243653d836061c9ce0370e2dd9ac5917258a007675/yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984", size = 76397 },
+    { url = "https://files.pythonhosted.org/packages/4d/05/4d79198ae568a92159de0f89e710a8d19e3fa267b719a236582eee921f4a/yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad", size = 31638 },
+]
+
+[[package]]
+name = "youtube-transcript-api"
+version = "0.6.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "requests" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/a6/e9/82d16b9639bb9fedade810f87ecb18f705591160b5768a79001ac5b99a82/youtube_transcript_api-0.6.2.tar.gz", hash = "sha256:cad223d7620633cec44f657646bffc8bbc5598bd8e70b1ad2fa8277dec305eb7", size = 24565 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/52/42/5f57d37d56bdb09722f226ed81cc1bec63942da745aa27266b16b0e16a5d/youtube_transcript_api-0.6.2-py3-none-any.whl", hash = "sha256:019dbf265c6a68a0591c513fff25ed5a116ce6525832aefdfb34d4df5567121c", size = 24207 },
+]
+
+[[package]]
+name = "zipp"
+version = "3.20.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/0e/af/9f2de5bd32549a1b705af7a7c054af3878816a1267cb389c03cc4f342a51/zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31", size = 23244 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/da/cc/b9958af9f9c86b51f846d8487440af495ecf19b16e426fce1ed0b0796175/zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d", size = 9432 },
+]
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6af4bba559bece5432370df7712b7ea69a0aa66
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,30 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+// /** @type {import('vite').Plugin} */
+// const viteServerConfig = {
+// 	name: 'log-request-middleware',
+// 	configureServer(server) {
+// 		server.middlewares.use((req, res, next) => {
+// 			res.setHeader('Access-Control-Allow-Origin', '*');
+// 			res.setHeader('Access-Control-Allow-Methods', 'GET');
+// 			res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
+// 			res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
+// 			next();
+// 		});
+// 	}
+// };
+
+export default defineConfig({
+	plugins: [sveltekit()],
+	define: {
+		APP_VERSION: JSON.stringify(process.env.npm_package_version),
+		APP_BUILD_HASH: JSON.stringify(process.env.APP_BUILD_HASH || 'dev-build')
+	},
+	build: {
+		sourcemap: true
+	},
+	worker: {
+		format: 'es'
+	}
+});