mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-15 13:55:19 +00:00
Merge branch 'dev' into my-clean-fix
This commit is contained in:
commit
ec9e3ef2c3
135 changed files with 3633 additions and 915 deletions
|
|
@ -7,6 +7,15 @@ OPENAI_API_KEY=''
|
||||||
|
|
||||||
# AUTOMATIC1111_BASE_URL="http://localhost:7860"
|
# AUTOMATIC1111_BASE_URL="http://localhost:7860"
|
||||||
|
|
||||||
|
# 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 with CORS locally, you can set something like
|
||||||
|
# CORS_ALLOW_ORIGIN='http://localhost:5173;http://localhost:8080'
|
||||||
|
CORS_ALLOW_ORIGIN='*'
|
||||||
|
|
||||||
|
# For production you should set this to match the proxy configuration (127.0.0.1)
|
||||||
|
FORWARDED_ALLOW_IPS='*'
|
||||||
|
|
||||||
# DO NOT TRACK
|
# DO NOT TRACK
|
||||||
SCARF_NO_ANALYTICS=true
|
SCARF_NO_ANALYTICS=true
|
||||||
DO_NOT_TRACK=true
|
DO_NOT_TRACK=true
|
||||||
|
|
|
||||||
50
.gitattributes
vendored
50
.gitattributes
vendored
|
|
@ -1 +1,49 @@
|
||||||
*.sh text eol=lf
|
# TypeScript
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.tsx text eol=lf
|
||||||
|
|
||||||
|
# JavaScript
|
||||||
|
*.js text eol=lf
|
||||||
|
*.jsx text eol=lf
|
||||||
|
*.mjs text eol=lf
|
||||||
|
*.cjs text eol=lf
|
||||||
|
|
||||||
|
# Svelte
|
||||||
|
*.svelte text eol=lf
|
||||||
|
|
||||||
|
# HTML/CSS
|
||||||
|
*.html text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.scss text eol=lf
|
||||||
|
*.less text eol=lf
|
||||||
|
|
||||||
|
# Config files and JSON
|
||||||
|
*.json text eol=lf
|
||||||
|
*.jsonc text eol=lf
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.toml text eol=lf
|
||||||
|
|
||||||
|
# Shell scripts
|
||||||
|
*.sh text eol=lf
|
||||||
|
|
||||||
|
# Markdown & docs
|
||||||
|
*.md text eol=lf
|
||||||
|
*.mdx text eol=lf
|
||||||
|
*.txt text eol=lf
|
||||||
|
|
||||||
|
# Git-related
|
||||||
|
.gitattributes text eol=lf
|
||||||
|
.gitignore text eol=lf
|
||||||
|
|
||||||
|
# Prettier and other dotfiles
|
||||||
|
.prettierrc text eol=lf
|
||||||
|
.prettierignore text eol=lf
|
||||||
|
.eslintrc text eol=lf
|
||||||
|
.eslintignore text eol=lf
|
||||||
|
.stylelintrc text eol=lf
|
||||||
|
.editorconfig text eol=lf
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.env text eol=lf
|
||||||
|
*.lock text eol=lf
|
||||||
191
.github/workflows/docker-build.yaml
vendored
191
.github/workflows/docker-build.yaml
vendored
|
|
@ -14,16 +14,18 @@ env:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-main-image:
|
build-main-image:
|
||||||
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
include:
|
||||||
- linux/amd64
|
- platform: linux/amd64
|
||||||
- linux/arm64
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
# GitHub Packages requires the entire repository name to be in lowercase
|
||||||
|
|
@ -111,16 +113,18 @@ jobs:
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-cuda-image:
|
build-cuda-image:
|
||||||
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
include:
|
||||||
- linux/amd64
|
- platform: linux/amd64
|
||||||
- linux/arm64
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
# GitHub Packages requires the entire repository name to be in lowercase
|
||||||
|
|
@ -210,17 +214,122 @@ jobs:
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
build-ollama-image:
|
build-cuda126-image:
|
||||||
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
include:
|
||||||
- linux/amd64
|
- platform: linux/amd64
|
||||||
- linux/arm64
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
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 (cuda126 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=cuda126
|
||||||
|
flavor: |
|
||||||
|
latest=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
suffix=-cuda126,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-cuda126-${{ matrix.platform }}-
|
||||||
|
latest=false
|
||||||
|
|
||||||
|
- name: Build Docker image (cuda126)
|
||||||
|
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
|
||||||
|
USE_CUDA_VER=cu126
|
||||||
|
|
||||||
|
- 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-cuda126-${{ env.PLATFORM_PAIR }}
|
||||||
|
path: /tmp/digests/*
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
build-ollama-image:
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- platform: linux/amd64
|
||||||
|
runner: ubuntu-latest
|
||||||
|
- platform: linux/arm64
|
||||||
|
runner: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# GitHub Packages requires the entire repository name to be in lowercase
|
# GitHub Packages requires the entire repository name to be in lowercase
|
||||||
|
|
@ -420,6 +529,62 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
||||||
|
|
||||||
|
merge-cuda126-images:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build-cuda126-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-cuda126-*
|
||||||
|
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=cuda126
|
||||||
|
flavor: |
|
||||||
|
latest=${{ github.ref == 'refs/heads/main' }}
|
||||||
|
suffix=-cuda126,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:
|
merge-ollama-images:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build-ollama-image]
|
needs: [build-ollama-image]
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,6 @@
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
"pluginSearchDirs": ["."],
|
"pluginSearchDirs": ["."],
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }],
|
||||||
|
"endOfLine": "lf"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ Want to learn more about Open WebUI's features? Check out our [Open WebUI docume
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="https://warp.dev/open-webui" target="_blank">
|
<a href="https://warp.dev/open-webui" target="_blank">
|
||||||
<img src="https://docs.openwebui.com/sponsors/logos/warp.png" alt="n8n" style="width: 8rem; height: 8rem; border-radius: .75rem;" />
|
<img src="https://docs.openwebui.com/sponsors/logos/warp.png" alt="Warp" style="width: 8rem; height: 8rem; border-radius: .75rem;" />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -181,6 +181,8 @@ After installation, you can access Open WebUI at [http://localhost:3000](http://
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
Look at the [Local Development Guide](https://docs.openwebui.com/getting-started/advanced-topics/development) for instructions on setting up a local development environment.
|
||||||
|
|
||||||
### Troubleshooting
|
### 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).
|
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).
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
PORT="${PORT:-8080}"
|
PORT="${PORT:-8080}"
|
||||||
uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --forwarded-allow-ips '*' --reload
|
uvicorn open_webui.main:app --port $PORT --host 0.0.0.0 --reload
|
||||||
|
|
@ -901,9 +901,7 @@ TOOL_SERVER_CONNECTIONS = PersistentConfig(
|
||||||
####################################
|
####################################
|
||||||
|
|
||||||
|
|
||||||
WEBUI_URL = PersistentConfig(
|
WEBUI_URL = PersistentConfig("WEBUI_URL", "webui.url", os.environ.get("WEBUI_URL", ""))
|
||||||
"WEBUI_URL", "webui.url", os.environ.get("WEBUI_URL", "http://localhost:3000")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
ENABLE_SIGNUP = PersistentConfig(
|
ENABLE_SIGNUP = PersistentConfig(
|
||||||
|
|
@ -1247,12 +1245,6 @@ if THREAD_POOL_SIZE is not None and isinstance(THREAD_POOL_SIZE, str):
|
||||||
THREAD_POOL_SIZE = None
|
THREAD_POOL_SIZE = None
|
||||||
|
|
||||||
|
|
||||||
def validate_cors_origins(origins):
|
|
||||||
for origin in origins:
|
|
||||||
if origin != "*":
|
|
||||||
validate_cors_origin(origin)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_cors_origin(origin):
|
def validate_cors_origin(origin):
|
||||||
parsed_url = urlparse(origin)
|
parsed_url = urlparse(origin)
|
||||||
|
|
||||||
|
|
@ -1272,16 +1264,17 @@ def validate_cors_origin(origin):
|
||||||
# To test CORS_ALLOW_ORIGIN locally, you can set something like
|
# To test CORS_ALLOW_ORIGIN locally, you can set something like
|
||||||
# CORS_ALLOW_ORIGIN=http://localhost:5173;http://localhost:8080
|
# CORS_ALLOW_ORIGIN=http://localhost:5173;http://localhost:8080
|
||||||
# in your .env file depending on your frontend port, 5173 in this case.
|
# in your .env file depending on your frontend port, 5173 in this case.
|
||||||
CORS_ALLOW_ORIGIN = os.environ.get(
|
CORS_ALLOW_ORIGIN = os.environ.get("CORS_ALLOW_ORIGIN", "*").split(";")
|
||||||
"CORS_ALLOW_ORIGIN", "*;http://localhost:5173;http://localhost:8080"
|
|
||||||
).split(";")
|
|
||||||
|
|
||||||
if "*" in CORS_ALLOW_ORIGIN:
|
if CORS_ALLOW_ORIGIN == ["*"]:
|
||||||
log.warning(
|
log.warning(
|
||||||
"\n\nWARNING: CORS_ALLOW_ORIGIN IS SET TO '*' - NOT RECOMMENDED FOR PRODUCTION DEPLOYMENTS.\n"
|
"\n\nWARNING: CORS_ALLOW_ORIGIN IS SET TO '*' - NOT RECOMMENDED FOR PRODUCTION DEPLOYMENTS.\n"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
validate_cors_origins(CORS_ALLOW_ORIGIN)
|
# You have to pick between a single wildcard or a list of origins.
|
||||||
|
# Doing both will result in CORS errors in the browser.
|
||||||
|
for origin in CORS_ALLOW_ORIGIN:
|
||||||
|
validate_cors_origin(origin)
|
||||||
|
|
||||||
|
|
||||||
class BannerModel(BaseModel):
|
class BannerModel(BaseModel):
|
||||||
|
|
@ -1413,6 +1406,35 @@ Strictly return in JSON format:
|
||||||
{{MESSAGES:END:6}}
|
{{MESSAGES:END:6}}
|
||||||
</chat_history>"""
|
</chat_history>"""
|
||||||
|
|
||||||
|
|
||||||
|
FOLLOW_UP_GENERATION_PROMPT_TEMPLATE = PersistentConfig(
|
||||||
|
"FOLLOW_UP_GENERATION_PROMPT_TEMPLATE",
|
||||||
|
"task.follow_up.prompt_template",
|
||||||
|
os.environ.get("FOLLOW_UP_GENERATION_PROMPT_TEMPLATE", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
DEFAULT_FOLLOW_UP_GENERATION_PROMPT_TEMPLATE = """### Task:
|
||||||
|
Suggest 3-5 relevant follow-up questions or prompts that the user might naturally ask next in this conversation as a **user**, based on the chat history, to help continue or deepen the discussion.
|
||||||
|
### Guidelines:
|
||||||
|
- Write all follow-up questions from the user’s point of view, directed to the assistant.
|
||||||
|
- Make questions concise, clear, and directly related to the discussed topic(s).
|
||||||
|
- Only suggest follow-ups that make sense given the chat content and do not repeat what was already covered.
|
||||||
|
- If the conversation is very short or not specific, suggest more general (but relevant) follow-ups the user might ask.
|
||||||
|
- Use the conversation's primary language; default to English if multilingual.
|
||||||
|
- Response must be a JSON array of strings, no extra text or formatting.
|
||||||
|
### Output:
|
||||||
|
JSON format: { "follow_ups": ["Question 1?", "Question 2?", "Question 3?"] }
|
||||||
|
### Chat History:
|
||||||
|
<chat_history>
|
||||||
|
{{MESSAGES:END:6}}
|
||||||
|
</chat_history>"""
|
||||||
|
|
||||||
|
ENABLE_FOLLOW_UP_GENERATION = PersistentConfig(
|
||||||
|
"ENABLE_FOLLOW_UP_GENERATION",
|
||||||
|
"task.follow_up.enable",
|
||||||
|
os.environ.get("ENABLE_FOLLOW_UP_GENERATION", "True").lower() == "true",
|
||||||
|
)
|
||||||
|
|
||||||
ENABLE_TAGS_GENERATION = PersistentConfig(
|
ENABLE_TAGS_GENERATION = PersistentConfig(
|
||||||
"ENABLE_TAGS_GENERATION",
|
"ENABLE_TAGS_GENERATION",
|
||||||
"task.tags.enable",
|
"task.tags.enable",
|
||||||
|
|
@ -1945,6 +1967,40 @@ DOCLING_DO_PICTURE_DESCRIPTION = PersistentConfig(
|
||||||
os.getenv("DOCLING_DO_PICTURE_DESCRIPTION", "False").lower() == "true",
|
os.getenv("DOCLING_DO_PICTURE_DESCRIPTION", "False").lower() == "true",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_MODE = PersistentConfig(
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_MODE",
|
||||||
|
"rag.docling_picture_description_mode",
|
||||||
|
os.getenv("DOCLING_PICTURE_DESCRIPTION_MODE", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
docling_picture_description_local = os.getenv("DOCLING_PICTURE_DESCRIPTION_LOCAL", "")
|
||||||
|
try:
|
||||||
|
docling_picture_description_local = json.loads(docling_picture_description_local)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
docling_picture_description_local = {}
|
||||||
|
|
||||||
|
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_LOCAL = PersistentConfig(
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_LOCAL",
|
||||||
|
"rag.docling_picture_description_local",
|
||||||
|
docling_picture_description_local,
|
||||||
|
)
|
||||||
|
|
||||||
|
doclign_picture_description_api = os.getenv("DOCLING_PICTURE_DESCRIPTION_API", "")
|
||||||
|
try:
|
||||||
|
doclign_picture_description_api = json.loads(doclign_picture_description_api)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
doclign_picture_description_api = {}
|
||||||
|
|
||||||
|
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_API = PersistentConfig(
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_API",
|
||||||
|
"rag.docling_picture_description_api",
|
||||||
|
doclign_picture_description_api,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
DOCUMENT_INTELLIGENCE_ENDPOINT = PersistentConfig(
|
DOCUMENT_INTELLIGENCE_ENDPOINT = PersistentConfig(
|
||||||
"DOCUMENT_INTELLIGENCE_ENDPOINT",
|
"DOCUMENT_INTELLIGENCE_ENDPOINT",
|
||||||
"rag.document_intelligence_endpoint",
|
"rag.document_intelligence_endpoint",
|
||||||
|
|
@ -2444,6 +2500,18 @@ PERPLEXITY_API_KEY = PersistentConfig(
|
||||||
os.getenv("PERPLEXITY_API_KEY", ""),
|
os.getenv("PERPLEXITY_API_KEY", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PERPLEXITY_MODEL = PersistentConfig(
|
||||||
|
"PERPLEXITY_MODEL",
|
||||||
|
"rag.web.search.perplexity_model",
|
||||||
|
os.getenv("PERPLEXITY_MODEL", "sonar"),
|
||||||
|
)
|
||||||
|
|
||||||
|
PERPLEXITY_SEARCH_CONTEXT_USAGE = PersistentConfig(
|
||||||
|
"PERPLEXITY_SEARCH_CONTEXT_USAGE",
|
||||||
|
"rag.web.search.perplexity_search_context_usage",
|
||||||
|
os.getenv("PERPLEXITY_SEARCH_CONTEXT_USAGE", "medium"),
|
||||||
|
)
|
||||||
|
|
||||||
SOUGOU_API_SID = PersistentConfig(
|
SOUGOU_API_SID = PersistentConfig(
|
||||||
"SOUGOU_API_SID",
|
"SOUGOU_API_SID",
|
||||||
"rag.web.search.sougou_api_sid",
|
"rag.web.search.sougou_api_sid",
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ class TASKS(str, Enum):
|
||||||
|
|
||||||
DEFAULT = lambda task="": f"{task if task else 'generation'}"
|
DEFAULT = lambda task="": f"{task if task else 'generation'}"
|
||||||
TITLE_GENERATION = "title_generation"
|
TITLE_GENERATION = "title_generation"
|
||||||
|
FOLLOW_UP_GENERATION = "follow_up_generation"
|
||||||
TAGS_GENERATION = "tags_generation"
|
TAGS_GENERATION = "tags_generation"
|
||||||
EMOJI_GENERATION = "emoji_generation"
|
EMOJI_GENERATION = "emoji_generation"
|
||||||
QUERY_GENERATION = "query_generation"
|
QUERY_GENERATION = "query_generation"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
from uuid import uuid4
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import markdown
|
import markdown
|
||||||
|
|
@ -130,6 +131,7 @@ else:
|
||||||
PACKAGE_DATA = {"version": "0.0.0"}
|
PACKAGE_DATA = {"version": "0.0.0"}
|
||||||
|
|
||||||
VERSION = PACKAGE_DATA["version"]
|
VERSION = PACKAGE_DATA["version"]
|
||||||
|
INSTANCE_ID = os.environ.get("INSTANCE_ID", str(uuid4()))
|
||||||
|
|
||||||
|
|
||||||
# Function to parse each section
|
# Function to parse each section
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ from open_webui.socket.main import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from open_webui.models.users import UserModel
|
||||||
from open_webui.models.functions import Functions
|
from open_webui.models.functions import Functions
|
||||||
from open_webui.models.models import Models
|
from open_webui.models.models import Models
|
||||||
|
|
||||||
|
|
@ -227,12 +228,7 @@ async def generate_function_chat_completion(
|
||||||
"__task__": __task__,
|
"__task__": __task__,
|
||||||
"__task_body__": __task_body__,
|
"__task_body__": __task_body__,
|
||||||
"__files__": files,
|
"__files__": files,
|
||||||
"__user__": {
|
"__user__": user.model_dump() if isinstance(user, UserModel) else {},
|
||||||
"id": user.id,
|
|
||||||
"email": user.email,
|
|
||||||
"name": user.name,
|
|
||||||
"role": user.role,
|
|
||||||
},
|
|
||||||
"__metadata__": metadata,
|
"__metadata__": metadata,
|
||||||
"__request__": request,
|
"__request__": request,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import shutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from urllib.parse import urlencode, parse_qs, urlparse
|
from urllib.parse import urlencode, parse_qs, urlparse
|
||||||
|
|
@ -19,6 +21,7 @@ from aiocache import cached
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import anyio.to_thread
|
import anyio.to_thread
|
||||||
import requests
|
import requests
|
||||||
|
from redis import Redis
|
||||||
|
|
||||||
|
|
||||||
from fastapi import (
|
from fastapi import (
|
||||||
|
|
@ -37,7 +40,7 @@ from fastapi import (
|
||||||
from fastapi.openapi.docs import get_swagger_ui_html
|
from fastapi.openapi.docs import get_swagger_ui_html
|
||||||
|
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from fastapi.responses import JSONResponse, RedirectResponse
|
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from starlette_compress import CompressMiddleware
|
from starlette_compress import CompressMiddleware
|
||||||
|
|
@ -231,6 +234,9 @@ from open_webui.config import (
|
||||||
DOCLING_OCR_ENGINE,
|
DOCLING_OCR_ENGINE,
|
||||||
DOCLING_OCR_LANG,
|
DOCLING_OCR_LANG,
|
||||||
DOCLING_DO_PICTURE_DESCRIPTION,
|
DOCLING_DO_PICTURE_DESCRIPTION,
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_MODE,
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_LOCAL,
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_API,
|
||||||
DOCUMENT_INTELLIGENCE_ENDPOINT,
|
DOCUMENT_INTELLIGENCE_ENDPOINT,
|
||||||
DOCUMENT_INTELLIGENCE_KEY,
|
DOCUMENT_INTELLIGENCE_KEY,
|
||||||
MISTRAL_OCR_API_KEY,
|
MISTRAL_OCR_API_KEY,
|
||||||
|
|
@ -268,6 +274,8 @@ from open_webui.config import (
|
||||||
BRAVE_SEARCH_API_KEY,
|
BRAVE_SEARCH_API_KEY,
|
||||||
EXA_API_KEY,
|
EXA_API_KEY,
|
||||||
PERPLEXITY_API_KEY,
|
PERPLEXITY_API_KEY,
|
||||||
|
PERPLEXITY_MODEL,
|
||||||
|
PERPLEXITY_SEARCH_CONTEXT_USAGE,
|
||||||
SOUGOU_API_SID,
|
SOUGOU_API_SID,
|
||||||
SOUGOU_API_SK,
|
SOUGOU_API_SK,
|
||||||
KAGI_SEARCH_API_KEY,
|
KAGI_SEARCH_API_KEY,
|
||||||
|
|
@ -359,10 +367,12 @@ from open_webui.config import (
|
||||||
TASK_MODEL_EXTERNAL,
|
TASK_MODEL_EXTERNAL,
|
||||||
ENABLE_TAGS_GENERATION,
|
ENABLE_TAGS_GENERATION,
|
||||||
ENABLE_TITLE_GENERATION,
|
ENABLE_TITLE_GENERATION,
|
||||||
|
ENABLE_FOLLOW_UP_GENERATION,
|
||||||
ENABLE_SEARCH_QUERY_GENERATION,
|
ENABLE_SEARCH_QUERY_GENERATION,
|
||||||
ENABLE_RETRIEVAL_QUERY_GENERATION,
|
ENABLE_RETRIEVAL_QUERY_GENERATION,
|
||||||
ENABLE_AUTOCOMPLETE_GENERATION,
|
ENABLE_AUTOCOMPLETE_GENERATION,
|
||||||
TITLE_GENERATION_PROMPT_TEMPLATE,
|
TITLE_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||||
TAGS_GENERATION_PROMPT_TEMPLATE,
|
TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||||
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
||||||
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
|
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE,
|
||||||
|
|
@ -384,6 +394,7 @@ from open_webui.env import (
|
||||||
SAFE_MODE,
|
SAFE_MODE,
|
||||||
SRC_LOG_LEVELS,
|
SRC_LOG_LEVELS,
|
||||||
VERSION,
|
VERSION,
|
||||||
|
INSTANCE_ID,
|
||||||
WEBUI_BUILD_HASH,
|
WEBUI_BUILD_HASH,
|
||||||
WEBUI_SECRET_KEY,
|
WEBUI_SECRET_KEY,
|
||||||
WEBUI_SESSION_COOKIE_SAME_SITE,
|
WEBUI_SESSION_COOKIE_SAME_SITE,
|
||||||
|
|
@ -411,6 +422,7 @@ from open_webui.utils.chat import (
|
||||||
chat_completed as chat_completed_handler,
|
chat_completed as chat_completed_handler,
|
||||||
chat_action as chat_action_handler,
|
chat_action as chat_action_handler,
|
||||||
)
|
)
|
||||||
|
from open_webui.utils.embeddings import generate_embeddings
|
||||||
from open_webui.utils.middleware import process_chat_payload, process_chat_response
|
from open_webui.utils.middleware import process_chat_payload, process_chat_response
|
||||||
from open_webui.utils.access_control import has_access
|
from open_webui.utils.access_control import has_access
|
||||||
|
|
||||||
|
|
@ -424,8 +436,10 @@ from open_webui.utils.auth import (
|
||||||
from open_webui.utils.plugin import install_tool_and_function_dependencies
|
from open_webui.utils.plugin import install_tool_and_function_dependencies
|
||||||
from open_webui.utils.oauth import OAuthManager
|
from open_webui.utils.oauth import OAuthManager
|
||||||
from open_webui.utils.security_headers import SecurityHeadersMiddleware
|
from open_webui.utils.security_headers import SecurityHeadersMiddleware
|
||||||
|
from open_webui.utils.redis import get_redis_connection
|
||||||
|
|
||||||
from open_webui.tasks import (
|
from open_webui.tasks import (
|
||||||
|
redis_task_command_listener,
|
||||||
list_task_ids_by_chat_id,
|
list_task_ids_by_chat_id,
|
||||||
stop_task,
|
stop_task,
|
||||||
list_tasks,
|
list_tasks,
|
||||||
|
|
@ -477,7 +491,9 @@ https://github.com/open-webui/open-webui
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
|
app.state.instance_id = INSTANCE_ID
|
||||||
start_logger()
|
start_logger()
|
||||||
|
|
||||||
if RESET_CONFIG_ON_START:
|
if RESET_CONFIG_ON_START:
|
||||||
reset_config()
|
reset_config()
|
||||||
|
|
||||||
|
|
@ -489,6 +505,18 @@ async def lifespan(app: FastAPI):
|
||||||
log.info("Installing external dependencies of functions and tools...")
|
log.info("Installing external dependencies of functions and tools...")
|
||||||
install_tool_and_function_dependencies()
|
install_tool_and_function_dependencies()
|
||||||
|
|
||||||
|
app.state.redis = get_redis_connection(
|
||||||
|
redis_url=REDIS_URL,
|
||||||
|
redis_sentinels=get_sentinels_from_env(
|
||||||
|
REDIS_SENTINEL_HOSTS, REDIS_SENTINEL_PORT
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(app.state.redis, Redis):
|
||||||
|
app.state.redis_task_command_listener = asyncio.create_task(
|
||||||
|
redis_task_command_listener(app)
|
||||||
|
)
|
||||||
|
|
||||||
if THREAD_POOL_SIZE and THREAD_POOL_SIZE > 0:
|
if THREAD_POOL_SIZE and THREAD_POOL_SIZE > 0:
|
||||||
limiter = anyio.to_thread.current_default_thread_limiter()
|
limiter = anyio.to_thread.current_default_thread_limiter()
|
||||||
limiter.total_tokens = THREAD_POOL_SIZE
|
limiter.total_tokens = THREAD_POOL_SIZE
|
||||||
|
|
@ -497,6 +525,9 @@ async def lifespan(app: FastAPI):
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
if hasattr(app.state, "redis_task_command_listener"):
|
||||||
|
app.state.redis_task_command_listener.cancel()
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Open WebUI",
|
title="Open WebUI",
|
||||||
|
|
@ -508,10 +539,12 @@ app = FastAPI(
|
||||||
|
|
||||||
oauth_manager = OAuthManager(app)
|
oauth_manager = OAuthManager(app)
|
||||||
|
|
||||||
|
app.state.instance_id = None
|
||||||
app.state.config = AppConfig(
|
app.state.config = AppConfig(
|
||||||
redis_url=REDIS_URL,
|
redis_url=REDIS_URL,
|
||||||
redis_sentinels=get_sentinels_from_env(REDIS_SENTINEL_HOSTS, REDIS_SENTINEL_PORT),
|
redis_sentinels=get_sentinels_from_env(REDIS_SENTINEL_HOSTS, REDIS_SENTINEL_PORT),
|
||||||
)
|
)
|
||||||
|
app.state.redis = None
|
||||||
|
|
||||||
app.state.WEBUI_NAME = WEBUI_NAME
|
app.state.WEBUI_NAME = WEBUI_NAME
|
||||||
app.state.LICENSE_METADATA = None
|
app.state.LICENSE_METADATA = None
|
||||||
|
|
@ -696,6 +729,9 @@ app.state.config.DOCLING_SERVER_URL = DOCLING_SERVER_URL
|
||||||
app.state.config.DOCLING_OCR_ENGINE = DOCLING_OCR_ENGINE
|
app.state.config.DOCLING_OCR_ENGINE = DOCLING_OCR_ENGINE
|
||||||
app.state.config.DOCLING_OCR_LANG = DOCLING_OCR_LANG
|
app.state.config.DOCLING_OCR_LANG = DOCLING_OCR_LANG
|
||||||
app.state.config.DOCLING_DO_PICTURE_DESCRIPTION = DOCLING_DO_PICTURE_DESCRIPTION
|
app.state.config.DOCLING_DO_PICTURE_DESCRIPTION = DOCLING_DO_PICTURE_DESCRIPTION
|
||||||
|
app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE = DOCLING_PICTURE_DESCRIPTION_MODE
|
||||||
|
app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL = DOCLING_PICTURE_DESCRIPTION_LOCAL
|
||||||
|
app.state.config.DOCLING_PICTURE_DESCRIPTION_API = DOCLING_PICTURE_DESCRIPTION_API
|
||||||
app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = DOCUMENT_INTELLIGENCE_ENDPOINT
|
app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = DOCUMENT_INTELLIGENCE_ENDPOINT
|
||||||
app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY
|
app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY
|
||||||
app.state.config.MISTRAL_OCR_API_KEY = MISTRAL_OCR_API_KEY
|
app.state.config.MISTRAL_OCR_API_KEY = MISTRAL_OCR_API_KEY
|
||||||
|
|
@ -771,6 +807,8 @@ 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.BING_SEARCH_V7_SUBSCRIPTION_KEY = BING_SEARCH_V7_SUBSCRIPTION_KEY
|
||||||
app.state.config.EXA_API_KEY = EXA_API_KEY
|
app.state.config.EXA_API_KEY = EXA_API_KEY
|
||||||
app.state.config.PERPLEXITY_API_KEY = PERPLEXITY_API_KEY
|
app.state.config.PERPLEXITY_API_KEY = PERPLEXITY_API_KEY
|
||||||
|
app.state.config.PERPLEXITY_MODEL = PERPLEXITY_MODEL
|
||||||
|
app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE = PERPLEXITY_SEARCH_CONTEXT_USAGE
|
||||||
app.state.config.SOUGOU_API_SID = SOUGOU_API_SID
|
app.state.config.SOUGOU_API_SID = SOUGOU_API_SID
|
||||||
app.state.config.SOUGOU_API_SK = SOUGOU_API_SK
|
app.state.config.SOUGOU_API_SK = SOUGOU_API_SK
|
||||||
app.state.config.EXTERNAL_WEB_SEARCH_URL = EXTERNAL_WEB_SEARCH_URL
|
app.state.config.EXTERNAL_WEB_SEARCH_URL = EXTERNAL_WEB_SEARCH_URL
|
||||||
|
|
@ -959,6 +997,7 @@ app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION = ENABLE_RETRIEVAL_QUERY_GENE
|
||||||
app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = ENABLE_AUTOCOMPLETE_GENERATION
|
app.state.config.ENABLE_AUTOCOMPLETE_GENERATION = ENABLE_AUTOCOMPLETE_GENERATION
|
||||||
app.state.config.ENABLE_TAGS_GENERATION = ENABLE_TAGS_GENERATION
|
app.state.config.ENABLE_TAGS_GENERATION = ENABLE_TAGS_GENERATION
|
||||||
app.state.config.ENABLE_TITLE_GENERATION = ENABLE_TITLE_GENERATION
|
app.state.config.ENABLE_TITLE_GENERATION = ENABLE_TITLE_GENERATION
|
||||||
|
app.state.config.ENABLE_FOLLOW_UP_GENERATION = ENABLE_FOLLOW_UP_GENERATION
|
||||||
|
|
||||||
|
|
||||||
app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE
|
app.state.config.TITLE_GENERATION_PROMPT_TEMPLATE = TITLE_GENERATION_PROMPT_TEMPLATE
|
||||||
|
|
@ -966,6 +1005,9 @@ app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE = TAGS_GENERATION_PROMPT_TEMPLA
|
||||||
app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = (
|
app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = (
|
||||||
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE
|
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE
|
||||||
)
|
)
|
||||||
|
app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE = (
|
||||||
|
FOLLOW_UP_GENERATION_PROMPT_TEMPLATE
|
||||||
|
)
|
||||||
|
|
||||||
app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
|
app.state.config.TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE = (
|
||||||
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
|
TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE
|
||||||
|
|
@ -1197,6 +1239,37 @@ async def get_base_models(request: Request, user=Depends(get_admin_user)):
|
||||||
return {"data": models}
|
return {"data": models}
|
||||||
|
|
||||||
|
|
||||||
|
##################################
|
||||||
|
# Embeddings
|
||||||
|
##################################
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/embeddings")
|
||||||
|
async def embeddings(
|
||||||
|
request: Request, form_data: dict, user=Depends(get_verified_user)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
OpenAI-compatible embeddings endpoint.
|
||||||
|
|
||||||
|
This handler:
|
||||||
|
- Performs user/model checks and dispatches to the correct backend.
|
||||||
|
- Supports OpenAI, Ollama, arena models, pipelines, and any compatible provider.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): Request context.
|
||||||
|
form_data (dict): OpenAI-like payload (e.g., {"model": "...", "input": [...]})
|
||||||
|
user (UserModel): Authenticated user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: OpenAI-compatible embeddings response.
|
||||||
|
"""
|
||||||
|
# Make sure models are loaded in app state
|
||||||
|
if not request.app.state.MODELS:
|
||||||
|
await get_all_models(request, user=user)
|
||||||
|
# Use generic dispatcher in utils.embeddings
|
||||||
|
return await generate_embeddings(request, form_data, user)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/chat/completions")
|
@app.post("/api/chat/completions")
|
||||||
async def chat_completion(
|
async def chat_completion(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|
@ -1338,26 +1411,30 @@ async def chat_action(
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/tasks/stop/{task_id}")
|
@app.post("/api/tasks/stop/{task_id}")
|
||||||
async def stop_task_endpoint(task_id: str, user=Depends(get_verified_user)):
|
async def stop_task_endpoint(
|
||||||
|
request: Request, task_id: str, user=Depends(get_verified_user)
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
result = await stop_task(task_id)
|
result = await stop_task(request, task_id)
|
||||||
return result
|
return result
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/tasks")
|
@app.get("/api/tasks")
|
||||||
async def list_tasks_endpoint(user=Depends(get_verified_user)):
|
async def list_tasks_endpoint(request: Request, user=Depends(get_verified_user)):
|
||||||
return {"tasks": list_tasks()}
|
return {"tasks": list_tasks(request)}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/tasks/chat/{chat_id}")
|
@app.get("/api/tasks/chat/{chat_id}")
|
||||||
async def list_tasks_by_chat_id_endpoint(chat_id: str, user=Depends(get_verified_user)):
|
async def list_tasks_by_chat_id_endpoint(
|
||||||
|
request: Request, chat_id: str, user=Depends(get_verified_user)
|
||||||
|
):
|
||||||
chat = Chats.get_chat_by_id(chat_id)
|
chat = Chats.get_chat_by_id(chat_id)
|
||||||
if chat is None or chat.user_id != user.id:
|
if chat is None or chat.user_id != user.id:
|
||||||
return {"task_ids": []}
|
return {"task_ids": []}
|
||||||
|
|
||||||
task_ids = list_task_ids_by_chat_id(chat_id)
|
task_ids = list_task_ids_by_chat_id(request, chat_id)
|
||||||
|
|
||||||
print(f"Task IDs for chat {chat_id}: {task_ids}")
|
print(f"Task IDs for chat {chat_id}: {task_ids}")
|
||||||
return {"task_ids": task_ids}
|
return {"task_ids": task_ids}
|
||||||
|
|
@ -1628,7 +1705,20 @@ async def healthcheck_with_db():
|
||||||
|
|
||||||
|
|
||||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||||
app.mount("/cache", StaticFiles(directory=CACHE_DIR), name="cache")
|
|
||||||
|
|
||||||
|
@app.get("/cache/{path:path}")
|
||||||
|
async def serve_cache_file(
|
||||||
|
path: str,
|
||||||
|
user=Depends(get_verified_user),
|
||||||
|
):
|
||||||
|
file_path = os.path.abspath(os.path.join(CACHE_DIR, path))
|
||||||
|
# prevent path traversal
|
||||||
|
if not file_path.startswith(os.path.abspath(CACHE_DIR)):
|
||||||
|
raise HTTPException(status_code=404, detail="File not found")
|
||||||
|
if not os.path.isfile(file_path):
|
||||||
|
raise HTTPException(status_code=404, detail="File not found")
|
||||||
|
return FileResponse(file_path)
|
||||||
|
|
||||||
|
|
||||||
def swagger_ui_html(*args, **kwargs):
|
def swagger_ui_html(*args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ class UserRoleUpdateForm(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateForm(BaseModel):
|
class UserUpdateForm(BaseModel):
|
||||||
|
role: str
|
||||||
name: str
|
name: str
|
||||||
email: str
|
email: str
|
||||||
profile_image_url: str
|
profile_image_url: str
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import requests
|
||||||
import logging
|
import logging
|
||||||
import ftfy
|
import ftfy
|
||||||
import sys
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
from langchain_community.document_loaders import (
|
from langchain_community.document_loaders import (
|
||||||
AzureAIDocumentIntelligenceLoader,
|
AzureAIDocumentIntelligenceLoader,
|
||||||
|
|
@ -76,7 +77,6 @@ known_source_ext = [
|
||||||
"swift",
|
"swift",
|
||||||
"vue",
|
"vue",
|
||||||
"svelte",
|
"svelte",
|
||||||
"msg",
|
|
||||||
"ex",
|
"ex",
|
||||||
"exs",
|
"exs",
|
||||||
"erl",
|
"erl",
|
||||||
|
|
@ -147,17 +147,32 @@ class DoclingLoader:
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
params = {
|
params = {"image_export_mode": "placeholder", "table_mode": "accurate"}
|
||||||
"image_export_mode": "placeholder",
|
|
||||||
"table_mode": "accurate",
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.params:
|
if self.params:
|
||||||
if self.params.get("do_picture_classification"):
|
if self.params.get("do_picture_description"):
|
||||||
params["do_picture_classification"] = self.params.get(
|
params["do_picture_description"] = self.params.get(
|
||||||
"do_picture_classification"
|
"do_picture_description"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
picture_description_mode = self.params.get(
|
||||||
|
"picture_description_mode", ""
|
||||||
|
).lower()
|
||||||
|
|
||||||
|
if picture_description_mode == "local" and self.params.get(
|
||||||
|
"picture_description_local", {}
|
||||||
|
):
|
||||||
|
params["picture_description_local"] = self.params.get(
|
||||||
|
"picture_description_local", {}
|
||||||
|
)
|
||||||
|
|
||||||
|
elif picture_description_mode == "api" and self.params.get(
|
||||||
|
"picture_description_api", {}
|
||||||
|
):
|
||||||
|
params["picture_description_api"] = self.params.get(
|
||||||
|
"picture_description_api", {}
|
||||||
|
)
|
||||||
|
|
||||||
if self.params.get("ocr_engine") and self.params.get("ocr_lang"):
|
if self.params.get("ocr_engine") and self.params.get("ocr_lang"):
|
||||||
params["ocr_engine"] = self.params.get("ocr_engine")
|
params["ocr_engine"] = self.params.get("ocr_engine")
|
||||||
params["ocr_lang"] = [
|
params["ocr_lang"] = [
|
||||||
|
|
@ -285,17 +300,20 @@ class Loader:
|
||||||
if self._is_text_file(file_ext, file_content_type):
|
if self._is_text_file(file_ext, file_content_type):
|
||||||
loader = TextLoader(file_path, autodetect_encoding=True)
|
loader = TextLoader(file_path, autodetect_encoding=True)
|
||||||
else:
|
else:
|
||||||
|
# Build params for DoclingLoader
|
||||||
|
params = self.kwargs.get("DOCLING_PARAMS", {})
|
||||||
|
if not isinstance(params, dict):
|
||||||
|
try:
|
||||||
|
params = json.loads(params)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
log.error("Invalid DOCLING_PARAMS format, expected JSON object")
|
||||||
|
params = {}
|
||||||
|
|
||||||
loader = DoclingLoader(
|
loader = DoclingLoader(
|
||||||
url=self.kwargs.get("DOCLING_SERVER_URL"),
|
url=self.kwargs.get("DOCLING_SERVER_URL"),
|
||||||
file_path=file_path,
|
file_path=file_path,
|
||||||
mime_type=file_content_type,
|
mime_type=file_content_type,
|
||||||
params={
|
params=params,
|
||||||
"ocr_engine": self.kwargs.get("DOCLING_OCR_ENGINE"),
|
|
||||||
"ocr_lang": self.kwargs.get("DOCLING_OCR_LANG"),
|
|
||||||
"do_picture_classification": self.kwargs.get(
|
|
||||||
"DOCLING_DO_PICTURE_DESCRIPTION"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
elif (
|
elif (
|
||||||
self.engine == "document_intelligence"
|
self.engine == "document_intelligence"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,14 @@ class MistralLoader:
|
||||||
"""
|
"""
|
||||||
Enhanced Mistral OCR loader with both sync and async support.
|
Enhanced Mistral OCR loader with both sync and async support.
|
||||||
Loads documents by processing them through the Mistral OCR API.
|
Loads documents by processing them through the Mistral OCR API.
|
||||||
|
|
||||||
|
Performance Optimizations:
|
||||||
|
- Differentiated timeouts for different operations
|
||||||
|
- Intelligent retry logic with exponential backoff
|
||||||
|
- Memory-efficient file streaming for large files
|
||||||
|
- Connection pooling and keepalive optimization
|
||||||
|
- Semaphore-based concurrency control for batch processing
|
||||||
|
- Enhanced error handling with retryable error classification
|
||||||
"""
|
"""
|
||||||
|
|
||||||
BASE_API_URL = "https://api.mistral.ai/v1"
|
BASE_API_URL = "https://api.mistral.ai/v1"
|
||||||
|
|
@ -53,17 +61,40 @@ class MistralLoader:
|
||||||
self.max_retries = max_retries
|
self.max_retries = max_retries
|
||||||
self.debug = enable_debug_logging
|
self.debug = enable_debug_logging
|
||||||
|
|
||||||
# Pre-compute file info for performance
|
# PERFORMANCE OPTIMIZATION: Differentiated timeouts for different operations
|
||||||
|
# This prevents long-running OCR operations from affecting quick operations
|
||||||
|
# and improves user experience by failing fast on operations that should be quick
|
||||||
|
self.upload_timeout = min(
|
||||||
|
timeout, 120
|
||||||
|
) # Cap upload at 2 minutes - prevents hanging on large files
|
||||||
|
self.url_timeout = (
|
||||||
|
30 # URL requests should be fast - fail quickly if API is slow
|
||||||
|
)
|
||||||
|
self.ocr_timeout = (
|
||||||
|
timeout # OCR can take the full timeout - this is the heavy operation
|
||||||
|
)
|
||||||
|
self.cleanup_timeout = (
|
||||||
|
30 # Cleanup should be quick - don't hang on file deletion
|
||||||
|
)
|
||||||
|
|
||||||
|
# PERFORMANCE OPTIMIZATION: Pre-compute file info to avoid repeated filesystem calls
|
||||||
|
# This avoids multiple os.path.basename() and os.path.getsize() calls during processing
|
||||||
self.file_name = os.path.basename(file_path)
|
self.file_name = os.path.basename(file_path)
|
||||||
self.file_size = os.path.getsize(file_path)
|
self.file_size = os.path.getsize(file_path)
|
||||||
|
|
||||||
|
# ENHANCEMENT: Added User-Agent for better API tracking and debugging
|
||||||
self.headers = {
|
self.headers = {
|
||||||
"Authorization": f"Bearer {self.api_key}",
|
"Authorization": f"Bearer {self.api_key}",
|
||||||
"User-Agent": "OpenWebUI-MistralLoader/2.0",
|
"User-Agent": "OpenWebUI-MistralLoader/2.0", # Helps API provider track usage
|
||||||
}
|
}
|
||||||
|
|
||||||
def _debug_log(self, message: str, *args) -> None:
|
def _debug_log(self, message: str, *args) -> None:
|
||||||
"""Conditional debug logging for performance."""
|
"""
|
||||||
|
PERFORMANCE OPTIMIZATION: Conditional debug logging for performance.
|
||||||
|
|
||||||
|
Only processes debug messages when debug mode is enabled, avoiding
|
||||||
|
string formatting overhead in production environments.
|
||||||
|
"""
|
||||||
if self.debug:
|
if self.debug:
|
||||||
log.debug(message, *args)
|
log.debug(message, *args)
|
||||||
|
|
||||||
|
|
@ -115,53 +146,118 @@ class MistralLoader:
|
||||||
log.error(f"Unexpected error processing response: {e}")
|
log.error(f"Unexpected error processing response: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def _is_retryable_error(self, error: Exception) -> bool:
|
||||||
|
"""
|
||||||
|
ENHANCEMENT: Intelligent error classification for retry logic.
|
||||||
|
|
||||||
|
Determines if an error is retryable based on its type and status code.
|
||||||
|
This prevents wasting time retrying errors that will never succeed
|
||||||
|
(like authentication errors) while ensuring transient errors are retried.
|
||||||
|
|
||||||
|
Retryable errors:
|
||||||
|
- Network connection errors (temporary network issues)
|
||||||
|
- Timeouts (server might be temporarily overloaded)
|
||||||
|
- Server errors (5xx status codes - server-side issues)
|
||||||
|
- Rate limiting (429 status - temporary throttling)
|
||||||
|
|
||||||
|
Non-retryable errors:
|
||||||
|
- Authentication errors (401, 403 - won't fix with retry)
|
||||||
|
- Bad request errors (400 - malformed request)
|
||||||
|
- Not found errors (404 - resource doesn't exist)
|
||||||
|
"""
|
||||||
|
if isinstance(error, requests.exceptions.ConnectionError):
|
||||||
|
return True # Network issues are usually temporary
|
||||||
|
if isinstance(error, requests.exceptions.Timeout):
|
||||||
|
return True # Timeouts might resolve on retry
|
||||||
|
if isinstance(error, requests.exceptions.HTTPError):
|
||||||
|
# Only retry on server errors (5xx) or rate limits (429)
|
||||||
|
if hasattr(error, "response") and error.response is not None:
|
||||||
|
status_code = error.response.status_code
|
||||||
|
return status_code >= 500 or status_code == 429
|
||||||
|
return False
|
||||||
|
if isinstance(
|
||||||
|
error, (aiohttp.ClientConnectionError, aiohttp.ServerTimeoutError)
|
||||||
|
):
|
||||||
|
return True # Async network/timeout errors are retryable
|
||||||
|
if isinstance(error, aiohttp.ClientResponseError):
|
||||||
|
return error.status >= 500 or error.status == 429
|
||||||
|
return False # All other errors are non-retryable
|
||||||
|
|
||||||
def _retry_request_sync(self, request_func, *args, **kwargs):
|
def _retry_request_sync(self, request_func, *args, **kwargs):
|
||||||
"""Synchronous retry logic with exponential backoff."""
|
"""
|
||||||
|
ENHANCEMENT: Synchronous retry logic with intelligent error classification.
|
||||||
|
|
||||||
|
Uses exponential backoff with jitter to avoid thundering herd problems.
|
||||||
|
The wait time increases exponentially but is capped at 30 seconds to
|
||||||
|
prevent excessive delays. Only retries errors that are likely to succeed
|
||||||
|
on subsequent attempts.
|
||||||
|
"""
|
||||||
for attempt in range(self.max_retries):
|
for attempt in range(self.max_retries):
|
||||||
try:
|
try:
|
||||||
return request_func(*args, **kwargs)
|
return request_func(*args, **kwargs)
|
||||||
except (requests.exceptions.RequestException, Exception) as e:
|
except Exception as e:
|
||||||
if attempt == self.max_retries - 1:
|
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
wait_time = (2**attempt) + 0.5
|
# PERFORMANCE OPTIMIZATION: Exponential backoff with cap
|
||||||
|
# Prevents overwhelming the server while ensuring reasonable retry delays
|
||||||
|
wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds
|
||||||
log.warning(
|
log.warning(
|
||||||
f"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..."
|
f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. "
|
||||||
|
f"Retrying in {wait_time}s..."
|
||||||
)
|
)
|
||||||
time.sleep(wait_time)
|
time.sleep(wait_time)
|
||||||
|
|
||||||
async def _retry_request_async(self, request_func, *args, **kwargs):
|
async def _retry_request_async(self, request_func, *args, **kwargs):
|
||||||
"""Async retry logic with exponential backoff."""
|
"""
|
||||||
|
ENHANCEMENT: Async retry logic with intelligent error classification.
|
||||||
|
|
||||||
|
Async version of retry logic that doesn't block the event loop during
|
||||||
|
wait periods. Uses the same exponential backoff strategy as sync version.
|
||||||
|
"""
|
||||||
for attempt in range(self.max_retries):
|
for attempt in range(self.max_retries):
|
||||||
try:
|
try:
|
||||||
return await request_func(*args, **kwargs)
|
return await request_func(*args, **kwargs)
|
||||||
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
except Exception as e:
|
||||||
if attempt == self.max_retries - 1:
|
if attempt == self.max_retries - 1 or not self._is_retryable_error(e):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
wait_time = (2**attempt) + 0.5
|
# PERFORMANCE OPTIMIZATION: Non-blocking exponential backoff
|
||||||
|
wait_time = min((2**attempt) + 0.5, 30) # Cap at 30 seconds
|
||||||
log.warning(
|
log.warning(
|
||||||
f"Request failed (attempt {attempt + 1}/{self.max_retries}): {e}. Retrying in {wait_time}s..."
|
f"Retryable error (attempt {attempt + 1}/{self.max_retries}): {e}. "
|
||||||
|
f"Retrying in {wait_time}s..."
|
||||||
)
|
)
|
||||||
await asyncio.sleep(wait_time)
|
await asyncio.sleep(wait_time) # Non-blocking wait
|
||||||
|
|
||||||
def _upload_file(self) -> str:
|
def _upload_file(self) -> str:
|
||||||
"""Uploads the file to Mistral for OCR processing (sync version)."""
|
"""
|
||||||
|
PERFORMANCE OPTIMIZATION: Enhanced file upload with streaming consideration.
|
||||||
|
|
||||||
|
Uploads the file to Mistral for OCR processing (sync version).
|
||||||
|
Uses context manager for file handling to ensure proper resource cleanup.
|
||||||
|
Although streaming is not enabled for this endpoint, the file is opened
|
||||||
|
in a context manager to minimize memory usage duration.
|
||||||
|
"""
|
||||||
log.info("Uploading file to Mistral API")
|
log.info("Uploading file to Mistral API")
|
||||||
url = f"{self.BASE_API_URL}/files"
|
url = f"{self.BASE_API_URL}/files"
|
||||||
file_name = os.path.basename(self.file_path)
|
|
||||||
|
|
||||||
def upload_request():
|
def upload_request():
|
||||||
|
# MEMORY OPTIMIZATION: Use context manager to minimize file handle lifetime
|
||||||
|
# This ensures the file is closed immediately after reading, reducing memory usage
|
||||||
with open(self.file_path, "rb") as f:
|
with open(self.file_path, "rb") as f:
|
||||||
files = {"file": (file_name, f, "application/pdf")}
|
files = {"file": (self.file_name, f, "application/pdf")}
|
||||||
data = {"purpose": "ocr"}
|
data = {"purpose": "ocr"}
|
||||||
|
|
||||||
|
# NOTE: stream=False is required for this endpoint
|
||||||
|
# The Mistral API doesn't support chunked uploads for this endpoint
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url,
|
url,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
files=files,
|
files=files,
|
||||||
data=data,
|
data=data,
|
||||||
timeout=self.timeout,
|
timeout=self.upload_timeout, # Use specialized upload timeout
|
||||||
|
stream=False, # Keep as False for this endpoint
|
||||||
)
|
)
|
||||||
|
|
||||||
return self._handle_response(response)
|
return self._handle_response(response)
|
||||||
|
|
@ -209,7 +305,7 @@ class MistralLoader:
|
||||||
url,
|
url,
|
||||||
data=writer,
|
data=writer,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
timeout=aiohttp.ClientTimeout(total=self.upload_timeout),
|
||||||
) as response:
|
) as response:
|
||||||
return await self._handle_response_async(response)
|
return await self._handle_response_async(response)
|
||||||
|
|
||||||
|
|
@ -231,7 +327,7 @@ class MistralLoader:
|
||||||
|
|
||||||
def url_request():
|
def url_request():
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
url, headers=signed_url_headers, params=params, timeout=self.timeout
|
url, headers=signed_url_headers, params=params, timeout=self.url_timeout
|
||||||
)
|
)
|
||||||
return self._handle_response(response)
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
|
@ -261,7 +357,7 @@ class MistralLoader:
|
||||||
url,
|
url,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
params=params,
|
params=params,
|
||||||
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
timeout=aiohttp.ClientTimeout(total=self.url_timeout),
|
||||||
) as response:
|
) as response:
|
||||||
return await self._handle_response_async(response)
|
return await self._handle_response_async(response)
|
||||||
|
|
||||||
|
|
@ -294,7 +390,7 @@ class MistralLoader:
|
||||||
|
|
||||||
def ocr_request():
|
def ocr_request():
|
||||||
response = requests.post(
|
response = requests.post(
|
||||||
url, headers=ocr_headers, json=payload, timeout=self.timeout
|
url, headers=ocr_headers, json=payload, timeout=self.ocr_timeout
|
||||||
)
|
)
|
||||||
return self._handle_response(response)
|
return self._handle_response(response)
|
||||||
|
|
||||||
|
|
@ -336,7 +432,7 @@ class MistralLoader:
|
||||||
url,
|
url,
|
||||||
json=payload,
|
json=payload,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
timeout=aiohttp.ClientTimeout(total=self.ocr_timeout),
|
||||||
) as response:
|
) as response:
|
||||||
ocr_response = await self._handle_response_async(response)
|
ocr_response = await self._handle_response_async(response)
|
||||||
|
|
||||||
|
|
@ -353,7 +449,9 @@ class MistralLoader:
|
||||||
url = f"{self.BASE_API_URL}/files/{file_id}"
|
url = f"{self.BASE_API_URL}/files/{file_id}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.delete(url, headers=self.headers, timeout=30)
|
response = requests.delete(
|
||||||
|
url, headers=self.headers, timeout=self.cleanup_timeout
|
||||||
|
)
|
||||||
delete_response = self._handle_response(response)
|
delete_response = self._handle_response(response)
|
||||||
log.info(f"File deleted successfully: {delete_response}")
|
log.info(f"File deleted successfully: {delete_response}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -372,7 +470,7 @@ class MistralLoader:
|
||||||
url=f"{self.BASE_API_URL}/files/{file_id}",
|
url=f"{self.BASE_API_URL}/files/{file_id}",
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
timeout=aiohttp.ClientTimeout(
|
timeout=aiohttp.ClientTimeout(
|
||||||
total=30
|
total=self.cleanup_timeout
|
||||||
), # Shorter timeout for cleanup
|
), # Shorter timeout for cleanup
|
||||||
) as response:
|
) as response:
|
||||||
return await self._handle_response_async(response)
|
return await self._handle_response_async(response)
|
||||||
|
|
@ -388,29 +486,39 @@ class MistralLoader:
|
||||||
async def _get_session(self):
|
async def _get_session(self):
|
||||||
"""Context manager for HTTP session with optimized settings."""
|
"""Context manager for HTTP session with optimized settings."""
|
||||||
connector = aiohttp.TCPConnector(
|
connector = aiohttp.TCPConnector(
|
||||||
limit=10, # Total connection limit
|
limit=20, # Increased total connection limit for better throughput
|
||||||
limit_per_host=5, # Per-host connection limit
|
limit_per_host=10, # Increased per-host limit for API endpoints
|
||||||
ttl_dns_cache=300, # DNS cache TTL
|
ttl_dns_cache=600, # Longer DNS cache TTL (10 minutes)
|
||||||
use_dns_cache=True,
|
use_dns_cache=True,
|
||||||
keepalive_timeout=30,
|
keepalive_timeout=60, # Increased keepalive for connection reuse
|
||||||
enable_cleanup_closed=True,
|
enable_cleanup_closed=True,
|
||||||
|
force_close=False, # Allow connection reuse
|
||||||
|
resolver=aiohttp.AsyncResolver(), # Use async DNS resolver
|
||||||
|
)
|
||||||
|
|
||||||
|
timeout = aiohttp.ClientTimeout(
|
||||||
|
total=self.timeout,
|
||||||
|
connect=30, # Connection timeout
|
||||||
|
sock_read=60, # Socket read timeout
|
||||||
)
|
)
|
||||||
|
|
||||||
async with aiohttp.ClientSession(
|
async with aiohttp.ClientSession(
|
||||||
connector=connector,
|
connector=connector,
|
||||||
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
timeout=timeout,
|
||||||
headers={"User-Agent": "OpenWebUI-MistralLoader/2.0"},
|
headers={"User-Agent": "OpenWebUI-MistralLoader/2.0"},
|
||||||
|
raise_for_status=False, # We handle status codes manually
|
||||||
) as session:
|
) as session:
|
||||||
yield session
|
yield session
|
||||||
|
|
||||||
def _process_results(self, ocr_response: Dict[str, Any]) -> List[Document]:
|
def _process_results(self, ocr_response: Dict[str, Any]) -> List[Document]:
|
||||||
"""Process OCR results into Document objects with enhanced metadata."""
|
"""Process OCR results into Document objects with enhanced metadata and memory efficiency."""
|
||||||
pages_data = ocr_response.get("pages")
|
pages_data = ocr_response.get("pages")
|
||||||
if not pages_data:
|
if not pages_data:
|
||||||
log.warning("No pages found in OCR response.")
|
log.warning("No pages found in OCR response.")
|
||||||
return [
|
return [
|
||||||
Document(
|
Document(
|
||||||
page_content="No text content found", metadata={"error": "no_pages"}
|
page_content="No text content found",
|
||||||
|
metadata={"error": "no_pages", "file_name": self.file_name},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -418,41 +526,44 @@ class MistralLoader:
|
||||||
total_pages = len(pages_data)
|
total_pages = len(pages_data)
|
||||||
skipped_pages = 0
|
skipped_pages = 0
|
||||||
|
|
||||||
|
# Process pages in a memory-efficient way
|
||||||
for page_data in pages_data:
|
for page_data in pages_data:
|
||||||
page_content = page_data.get("markdown")
|
page_content = page_data.get("markdown")
|
||||||
page_index = page_data.get("index") # API uses 0-based index
|
page_index = page_data.get("index") # API uses 0-based index
|
||||||
|
|
||||||
if page_content is not None and page_index is not None:
|
if page_content is None or page_index is None:
|
||||||
# Clean up content efficiently
|
|
||||||
cleaned_content = (
|
|
||||||
page_content.strip()
|
|
||||||
if isinstance(page_content, str)
|
|
||||||
else str(page_content)
|
|
||||||
)
|
|
||||||
|
|
||||||
if cleaned_content: # Only add non-empty pages
|
|
||||||
documents.append(
|
|
||||||
Document(
|
|
||||||
page_content=cleaned_content,
|
|
||||||
metadata={
|
|
||||||
"page": page_index, # 0-based index from API
|
|
||||||
"page_label": page_index
|
|
||||||
+ 1, # 1-based label for convenience
|
|
||||||
"total_pages": total_pages,
|
|
||||||
"file_name": self.file_name,
|
|
||||||
"file_size": self.file_size,
|
|
||||||
"processing_engine": "mistral-ocr",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
skipped_pages += 1
|
|
||||||
self._debug_log(f"Skipping empty page {page_index}")
|
|
||||||
else:
|
|
||||||
skipped_pages += 1
|
skipped_pages += 1
|
||||||
self._debug_log(
|
self._debug_log(
|
||||||
f"Skipping page due to missing 'markdown' or 'index'. Data: {page_data}"
|
f"Skipping page due to missing 'markdown' or 'index'. Data keys: {list(page_data.keys())}"
|
||||||
)
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Clean up content efficiently with early exit for empty content
|
||||||
|
if isinstance(page_content, str):
|
||||||
|
cleaned_content = page_content.strip()
|
||||||
|
else:
|
||||||
|
cleaned_content = str(page_content).strip()
|
||||||
|
|
||||||
|
if not cleaned_content:
|
||||||
|
skipped_pages += 1
|
||||||
|
self._debug_log(f"Skipping empty page {page_index}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Create document with optimized metadata
|
||||||
|
documents.append(
|
||||||
|
Document(
|
||||||
|
page_content=cleaned_content,
|
||||||
|
metadata={
|
||||||
|
"page": page_index, # 0-based index from API
|
||||||
|
"page_label": page_index + 1, # 1-based label for convenience
|
||||||
|
"total_pages": total_pages,
|
||||||
|
"file_name": self.file_name,
|
||||||
|
"file_size": self.file_size,
|
||||||
|
"processing_engine": "mistral-ocr",
|
||||||
|
"content_length": len(cleaned_content),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if skipped_pages > 0:
|
if skipped_pages > 0:
|
||||||
log.info(
|
log.info(
|
||||||
|
|
@ -467,7 +578,11 @@ class MistralLoader:
|
||||||
return [
|
return [
|
||||||
Document(
|
Document(
|
||||||
page_content="No valid text content found in document",
|
page_content="No valid text content found in document",
|
||||||
metadata={"error": "no_valid_pages", "total_pages": total_pages},
|
metadata={
|
||||||
|
"error": "no_valid_pages",
|
||||||
|
"total_pages": total_pages,
|
||||||
|
"file_name": self.file_name,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -585,12 +700,14 @@ class MistralLoader:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def load_multiple_async(
|
async def load_multiple_async(
|
||||||
loaders: List["MistralLoader"],
|
loaders: List["MistralLoader"],
|
||||||
|
max_concurrent: int = 5, # Limit concurrent requests
|
||||||
) -> List[List[Document]]:
|
) -> List[List[Document]]:
|
||||||
"""
|
"""
|
||||||
Process multiple files concurrently for maximum performance.
|
Process multiple files concurrently with controlled concurrency.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
loaders: List of MistralLoader instances
|
loaders: List of MistralLoader instances
|
||||||
|
max_concurrent: Maximum number of concurrent requests
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of document lists, one for each loader
|
List of document lists, one for each loader
|
||||||
|
|
@ -598,11 +715,20 @@ class MistralLoader:
|
||||||
if not loaders:
|
if not loaders:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
log.info(f"Starting concurrent processing of {len(loaders)} files")
|
log.info(
|
||||||
|
f"Starting concurrent processing of {len(loaders)} files with max {max_concurrent} concurrent"
|
||||||
|
)
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
# Process all files concurrently
|
# Use semaphore to control concurrency
|
||||||
tasks = [loader.load_async() for loader in loaders]
|
semaphore = asyncio.Semaphore(max_concurrent)
|
||||||
|
|
||||||
|
async def process_with_semaphore(loader: "MistralLoader") -> List[Document]:
|
||||||
|
async with semaphore:
|
||||||
|
return await loader.load_async()
|
||||||
|
|
||||||
|
# Process all files with controlled concurrency
|
||||||
|
tasks = [process_with_semaphore(loader) for loader in loaders]
|
||||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
# Handle any exceptions in results
|
# Handle any exceptions in results
|
||||||
|
|
@ -624,10 +750,18 @@ class MistralLoader:
|
||||||
else:
|
else:
|
||||||
processed_results.append(result)
|
processed_results.append(result)
|
||||||
|
|
||||||
|
# MONITORING: Log comprehensive batch processing statistics
|
||||||
total_time = time.time() - start_time
|
total_time = time.time() - start_time
|
||||||
total_docs = sum(len(docs) for docs in processed_results)
|
total_docs = sum(len(docs) for docs in processed_results)
|
||||||
|
success_count = sum(
|
||||||
|
1 for result in results if not isinstance(result, Exception)
|
||||||
|
)
|
||||||
|
failure_count = len(results) - success_count
|
||||||
|
|
||||||
log.info(
|
log.info(
|
||||||
f"Batch processing completed in {total_time:.2f}s, produced {total_docs} total documents"
|
f"Batch processing completed in {total_time:.2f}s: "
|
||||||
|
f"{success_count} files succeeded, {failure_count} files failed, "
|
||||||
|
f"produced {total_docs} total documents"
|
||||||
)
|
)
|
||||||
|
|
||||||
return processed_results
|
return processed_results
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from xml.etree.ElementTree import ParseError
|
||||||
|
|
||||||
from typing import Any, Dict, Generator, List, Optional, Sequence, Union
|
from typing import Any, Dict, Generator, List, Optional, Sequence, Union
|
||||||
from urllib.parse import parse_qs, urlparse
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
@ -93,7 +94,6 @@ class YoutubeLoader:
|
||||||
"http": self.proxy_url,
|
"http": self.proxy_url,
|
||||||
"https": 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]}...")
|
log.debug(f"Using proxy URL: {self.proxy_url[:14]}...")
|
||||||
else:
|
else:
|
||||||
youtube_proxies = None
|
youtube_proxies = None
|
||||||
|
|
@ -110,11 +110,37 @@ class YoutubeLoader:
|
||||||
for lang in self.language:
|
for lang in self.language:
|
||||||
try:
|
try:
|
||||||
transcript = transcript_list.find_transcript([lang])
|
transcript = transcript_list.find_transcript([lang])
|
||||||
|
if transcript.is_generated:
|
||||||
|
log.debug(f"Found generated transcript for language '{lang}'")
|
||||||
|
try:
|
||||||
|
transcript = transcript_list.find_manually_created_transcript(
|
||||||
|
[lang]
|
||||||
|
)
|
||||||
|
log.debug(f"Found manual transcript for language '{lang}'")
|
||||||
|
except NoTranscriptFound:
|
||||||
|
log.debug(
|
||||||
|
f"No manual transcript found for language '{lang}', using generated"
|
||||||
|
)
|
||||||
|
pass
|
||||||
|
|
||||||
log.debug(f"Found transcript for language '{lang}'")
|
log.debug(f"Found transcript for language '{lang}'")
|
||||||
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
|
try:
|
||||||
|
transcript_pieces: List[Dict[str, Any]] = transcript.fetch()
|
||||||
|
except ParseError:
|
||||||
|
log.debug(f"Empty or invalid transcript for language '{lang}'")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not transcript_pieces:
|
||||||
|
log.debug(f"Empty transcript for language '{lang}'")
|
||||||
|
continue
|
||||||
|
|
||||||
transcript_text = " ".join(
|
transcript_text = " ".join(
|
||||||
map(
|
map(
|
||||||
lambda transcript_piece: transcript_piece.text.strip(" "),
|
lambda transcript_piece: (
|
||||||
|
transcript_piece.text.strip(" ")
|
||||||
|
if hasattr(transcript_piece, "text")
|
||||||
|
else ""
|
||||||
|
),
|
||||||
transcript_pieces,
|
transcript_pieces,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -131,6 +157,4 @@ class YoutubeLoader:
|
||||||
log.warning(
|
log.warning(
|
||||||
f"No transcript found for any of the specified languages: {languages_tried}. Verify if the video has transcripts, add more languages if needed."
|
f"No transcript found for any of the specified languages: {languages_tried}. Verify if the video has transcripts, add more languages if needed."
|
||||||
)
|
)
|
||||||
raise NoTranscriptFound(
|
raise NoTranscriptFound(self.video_id, self.language, list(transcript_list))
|
||||||
f"No transcript found for any supported language. Verify if the video has transcripts, add more languages if needed."
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,19 @@ import logging
|
||||||
import time # for measuring elapsed time
|
import time # for measuring elapsed time
|
||||||
from pinecone import Pinecone, ServerlessSpec
|
from pinecone import Pinecone, ServerlessSpec
|
||||||
|
|
||||||
|
# Add gRPC support for better performance (Pinecone best practice)
|
||||||
|
try:
|
||||||
|
from pinecone.grpc import PineconeGRPC
|
||||||
|
|
||||||
|
GRPC_AVAILABLE = True
|
||||||
|
except ImportError:
|
||||||
|
GRPC_AVAILABLE = False
|
||||||
|
|
||||||
import asyncio # for async upserts
|
import asyncio # for async upserts
|
||||||
import functools # for partial binding in async tasks
|
import functools # for partial binding in async tasks
|
||||||
|
|
||||||
import concurrent.futures # for parallel batch upserts
|
import concurrent.futures # for parallel batch upserts
|
||||||
|
import random # for jitter in retry backoff
|
||||||
|
|
||||||
from open_webui.retrieval.vector.main import (
|
from open_webui.retrieval.vector.main import (
|
||||||
VectorDBBase,
|
VectorDBBase,
|
||||||
|
|
@ -47,7 +56,24 @@ class PineconeClient(VectorDBBase):
|
||||||
self.cloud = PINECONE_CLOUD
|
self.cloud = PINECONE_CLOUD
|
||||||
|
|
||||||
# Initialize Pinecone client for improved performance
|
# Initialize Pinecone client for improved performance
|
||||||
self.client = Pinecone(api_key=self.api_key)
|
if GRPC_AVAILABLE:
|
||||||
|
# Use gRPC client for better performance (Pinecone recommendation)
|
||||||
|
self.client = PineconeGRPC(
|
||||||
|
api_key=self.api_key,
|
||||||
|
pool_threads=20, # Improved connection pool size
|
||||||
|
timeout=30, # Reasonable timeout for operations
|
||||||
|
)
|
||||||
|
self.using_grpc = True
|
||||||
|
log.info("Using Pinecone gRPC client for optimal performance")
|
||||||
|
else:
|
||||||
|
# Fallback to HTTP client with enhanced connection pooling
|
||||||
|
self.client = Pinecone(
|
||||||
|
api_key=self.api_key,
|
||||||
|
pool_threads=20, # Improved connection pool size
|
||||||
|
timeout=30, # Reasonable timeout for operations
|
||||||
|
)
|
||||||
|
self.using_grpc = False
|
||||||
|
log.info("Using Pinecone HTTP client (gRPC not available)")
|
||||||
|
|
||||||
# Persistent executor for batch operations
|
# Persistent executor for batch operations
|
||||||
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
|
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
|
||||||
|
|
@ -91,12 +117,53 @@ class PineconeClient(VectorDBBase):
|
||||||
log.info(f"Using existing Pinecone index '{self.index_name}'")
|
log.info(f"Using existing Pinecone index '{self.index_name}'")
|
||||||
|
|
||||||
# Connect to the index
|
# Connect to the index
|
||||||
self.index = self.client.Index(self.index_name)
|
self.index = self.client.Index(
|
||||||
|
self.index_name,
|
||||||
|
pool_threads=20, # Enhanced connection pool for index operations
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Failed to initialize Pinecone index: {e}")
|
log.error(f"Failed to initialize Pinecone index: {e}")
|
||||||
raise RuntimeError(f"Failed to initialize Pinecone index: {e}")
|
raise RuntimeError(f"Failed to initialize Pinecone index: {e}")
|
||||||
|
|
||||||
|
def _retry_pinecone_operation(self, operation_func, max_retries=3):
|
||||||
|
"""Retry Pinecone operations with exponential backoff for rate limits and network issues."""
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
return operation_func()
|
||||||
|
except Exception as e:
|
||||||
|
error_str = str(e).lower()
|
||||||
|
# Check if it's a retryable error (rate limits, network issues, timeouts)
|
||||||
|
is_retryable = any(
|
||||||
|
keyword in error_str
|
||||||
|
for keyword in [
|
||||||
|
"rate limit",
|
||||||
|
"quota",
|
||||||
|
"timeout",
|
||||||
|
"network",
|
||||||
|
"connection",
|
||||||
|
"unavailable",
|
||||||
|
"internal error",
|
||||||
|
"429",
|
||||||
|
"500",
|
||||||
|
"502",
|
||||||
|
"503",
|
||||||
|
"504",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not is_retryable or attempt == max_retries - 1:
|
||||||
|
# Don't retry for non-retryable errors or on final attempt
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Exponential backoff with jitter
|
||||||
|
delay = (2**attempt) + random.uniform(0, 1)
|
||||||
|
log.warning(
|
||||||
|
f"Pinecone operation failed (attempt {attempt + 1}/{max_retries}), "
|
||||||
|
f"retrying in {delay:.2f}s: {e}"
|
||||||
|
)
|
||||||
|
time.sleep(delay)
|
||||||
|
|
||||||
def _create_points(
|
def _create_points(
|
||||||
self, items: List[VectorItem], collection_name_with_prefix: str
|
self, items: List[VectorItem], collection_name_with_prefix: str
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
|
|
@ -223,7 +290,8 @@ class PineconeClient(VectorDBBase):
|
||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
log.debug(f"Insert of {len(points)} vectors took {elapsed:.2f} seconds")
|
log.debug(f"Insert of {len(points)} vectors took {elapsed:.2f} seconds")
|
||||||
log.info(
|
log.info(
|
||||||
f"Successfully inserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'"
|
f"Successfully inserted {len(points)} vectors in parallel batches "
|
||||||
|
f"into '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
|
def upsert(self, collection_name: str, items: List[VectorItem]) -> None:
|
||||||
|
|
@ -254,7 +322,8 @@ class PineconeClient(VectorDBBase):
|
||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
log.debug(f"Upsert of {len(points)} vectors took {elapsed:.2f} seconds")
|
log.debug(f"Upsert of {len(points)} vectors took {elapsed:.2f} seconds")
|
||||||
log.info(
|
log.info(
|
||||||
f"Successfully upserted {len(points)} vectors in parallel batches into '{collection_name_with_prefix}'"
|
f"Successfully upserted {len(points)} vectors in parallel batches "
|
||||||
|
f"into '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None:
|
async def insert_async(self, collection_name: str, items: List[VectorItem]) -> None:
|
||||||
|
|
@ -285,7 +354,8 @@ class PineconeClient(VectorDBBase):
|
||||||
log.error(f"Error in async insert batch: {result}")
|
log.error(f"Error in async insert batch: {result}")
|
||||||
raise result
|
raise result
|
||||||
log.info(
|
log.info(
|
||||||
f"Successfully async inserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
|
f"Successfully async inserted {len(points)} vectors in batches "
|
||||||
|
f"into '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None:
|
async def upsert_async(self, collection_name: str, items: List[VectorItem]) -> None:
|
||||||
|
|
@ -316,7 +386,8 @@ class PineconeClient(VectorDBBase):
|
||||||
log.error(f"Error in async upsert batch: {result}")
|
log.error(f"Error in async upsert batch: {result}")
|
||||||
raise result
|
raise result
|
||||||
log.info(
|
log.info(
|
||||||
f"Successfully async upserted {len(points)} vectors in batches into '{collection_name_with_prefix}'"
|
f"Successfully async upserted {len(points)} vectors in batches "
|
||||||
|
f"into '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
def search(
|
def search(
|
||||||
|
|
@ -457,10 +528,12 @@ class PineconeClient(VectorDBBase):
|
||||||
# This is a limitation of Pinecone - be careful with ID uniqueness
|
# This is a limitation of Pinecone - be careful with ID uniqueness
|
||||||
self.index.delete(ids=batch_ids)
|
self.index.delete(ids=batch_ids)
|
||||||
log.debug(
|
log.debug(
|
||||||
f"Deleted batch of {len(batch_ids)} vectors by ID from '{collection_name_with_prefix}'"
|
f"Deleted batch of {len(batch_ids)} vectors by ID "
|
||||||
|
f"from '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
log.info(
|
log.info(
|
||||||
f"Successfully deleted {len(ids)} vectors by ID from '{collection_name_with_prefix}'"
|
f"Successfully deleted {len(ids)} vectors by ID "
|
||||||
|
f"from '{collection_name_with_prefix}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
elif filter:
|
elif filter:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List
|
from typing import Optional, Literal
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
|
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
|
||||||
from open_webui.env import SRC_LOG_LEVELS
|
from open_webui.env import SRC_LOG_LEVELS
|
||||||
|
|
||||||
|
MODELS = Literal[
|
||||||
|
"sonar",
|
||||||
|
"sonar-pro",
|
||||||
|
"sonar-reasoning",
|
||||||
|
"sonar-reasoning-pro",
|
||||||
|
"sonar-deep-research",
|
||||||
|
]
|
||||||
|
SEARCH_CONTEXT_USAGE_LEVELS = Literal["low", "medium", "high"]
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.setLevel(SRC_LOG_LEVELS["RAG"])
|
log.setLevel(SRC_LOG_LEVELS["RAG"])
|
||||||
|
|
||||||
|
|
@ -14,6 +24,8 @@ def search_perplexity(
|
||||||
query: str,
|
query: str,
|
||||||
count: int,
|
count: int,
|
||||||
filter_list: Optional[list[str]] = None,
|
filter_list: Optional[list[str]] = None,
|
||||||
|
model: MODELS = "sonar",
|
||||||
|
search_context_usage: SEARCH_CONTEXT_USAGE_LEVELS = "medium",
|
||||||
) -> list[SearchResult]:
|
) -> list[SearchResult]:
|
||||||
"""Search using Perplexity API and return the results as a list of SearchResult objects.
|
"""Search using Perplexity API and return the results as a list of SearchResult objects.
|
||||||
|
|
||||||
|
|
@ -21,6 +33,9 @@ def search_perplexity(
|
||||||
api_key (str): A Perplexity API key
|
api_key (str): A Perplexity API key
|
||||||
query (str): The query to search for
|
query (str): The query to search for
|
||||||
count (int): Maximum number of results to return
|
count (int): Maximum number of results to return
|
||||||
|
filter_list (Optional[list[str]]): List of domains to filter results
|
||||||
|
model (str): The Perplexity model to use (sonar, sonar-pro)
|
||||||
|
search_context_usage (str): Search context usage level (low, medium, high)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -33,7 +48,7 @@ def search_perplexity(
|
||||||
|
|
||||||
# Create payload for the API call
|
# Create payload for the API call
|
||||||
payload = {
|
payload = {
|
||||||
"model": "sonar",
|
"model": model,
|
||||||
"messages": [
|
"messages": [
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
|
|
@ -43,6 +58,9 @@ def search_perplexity(
|
||||||
],
|
],
|
||||||
"temperature": 0.2, # Lower temperature for more factual responses
|
"temperature": 0.2, # Lower temperature for more factual responses
|
||||||
"stream": False,
|
"stream": False,
|
||||||
|
"web_search_options": {
|
||||||
|
"search_context_usage": search_context_usage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
|
|
|
||||||
|
|
@ -420,7 +420,7 @@ def load_b64_image_data(b64_str):
|
||||||
try:
|
try:
|
||||||
if "," in b64_str:
|
if "," in b64_str:
|
||||||
header, encoded = b64_str.split(",", 1)
|
header, encoded = b64_str.split(",", 1)
|
||||||
mime_type = header.split(";")[0]
|
mime_type = header.split(";")[0].lstrip("data:")
|
||||||
img_data = base64.b64decode(encoded)
|
img_data = base64.b64decode(encoded)
|
||||||
else:
|
else:
|
||||||
mime_type = "image/png"
|
mime_type = "image/png"
|
||||||
|
|
@ -428,7 +428,7 @@ def load_b64_image_data(b64_str):
|
||||||
return img_data, mime_type
|
return img_data, mime_type
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(f"Error loading image data: {e}")
|
log.exception(f"Error loading image data: {e}")
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
def load_url_image_data(url, headers=None):
|
def load_url_image_data(url, headers=None):
|
||||||
|
|
|
||||||
|
|
@ -124,9 +124,8 @@ async def get_note_by_id(request: Request, id: str, user=Depends(get_verified_us
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if user.role != "admin" or (
|
||||||
user.role != "admin"
|
user.id != note.user_id
|
||||||
and user.id != note.user_id
|
|
||||||
and not has_access(user.id, type="read", access_control=note.access_control)
|
and not has_access(user.id, type="read", access_control=note.access_control)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -159,9 +158,8 @@ async def update_note_by_id(
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if user.role != "admin" or (
|
||||||
user.role != "admin"
|
user.id != note.user_id
|
||||||
and user.id != note.user_id
|
|
||||||
and not has_access(user.id, type="write", access_control=note.access_control)
|
and not has_access(user.id, type="write", access_control=note.access_control)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -199,9 +197,8 @@ async def delete_note_by_id(request: Request, id: str, user=Depends(get_verified
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if user.role != "admin" or (
|
||||||
user.role != "admin"
|
user.id != note.user_id
|
||||||
and user.id != note.user_id
|
|
||||||
and not has_access(user.id, type="write", access_control=note.access_control)
|
and not has_access(user.id, type="write", access_control=note.access_control)
|
||||||
):
|
):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,88 @@ async def generate_chat_completion(
|
||||||
await session.close()
|
await session.close()
|
||||||
|
|
||||||
|
|
||||||
|
async def embeddings(request: Request, form_data: dict, user):
|
||||||
|
"""
|
||||||
|
Calls the embeddings endpoint for OpenAI-compatible providers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): The FastAPI request context.
|
||||||
|
form_data (dict): OpenAI-compatible embeddings payload.
|
||||||
|
user (UserModel): The authenticated user.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: OpenAI-compatible embeddings response.
|
||||||
|
"""
|
||||||
|
idx = 0
|
||||||
|
# Prepare payload/body
|
||||||
|
body = json.dumps(form_data)
|
||||||
|
# Find correct backend url/key based on model
|
||||||
|
await get_all_models(request, user=user)
|
||||||
|
model_id = form_data.get("model")
|
||||||
|
models = request.app.state.OPENAI_MODELS
|
||||||
|
if model_id in models:
|
||||||
|
idx = models[model_id]["urlIdx"]
|
||||||
|
url = request.app.state.config.OPENAI_API_BASE_URLS[idx]
|
||||||
|
key = request.app.state.config.OPENAI_API_KEYS[idx]
|
||||||
|
r = None
|
||||||
|
session = None
|
||||||
|
streaming = False
|
||||||
|
try:
|
||||||
|
session = aiohttp.ClientSession(trust_env=True)
|
||||||
|
r = await session.request(
|
||||||
|
method="POST",
|
||||||
|
url=f"{url}/embeddings",
|
||||||
|
data=body,
|
||||||
|
headers={
|
||||||
|
"Authorization": f"Bearer {key}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
**(
|
||||||
|
{
|
||||||
|
"X-OpenWebUI-User-Name": user.name,
|
||||||
|
"X-OpenWebUI-User-Id": user.id,
|
||||||
|
"X-OpenWebUI-User-Email": user.email,
|
||||||
|
"X-OpenWebUI-User-Role": user.role,
|
||||||
|
}
|
||||||
|
if ENABLE_FORWARD_USER_INFO_HEADERS and user
|
||||||
|
else {}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
r.raise_for_status()
|
||||||
|
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)
|
||||||
|
detail = None
|
||||||
|
if r is not None:
|
||||||
|
try:
|
||||||
|
res = await r.json()
|
||||||
|
if "error" in res:
|
||||||
|
detail = f"External: {res['error']['message'] if 'message' in res['error'] else res['error']}"
|
||||||
|
except Exception:
|
||||||
|
detail = f"External: {e}"
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=r.status if r else 500,
|
||||||
|
detail=detail if detail else "Open WebUI: Server Connection Error",
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if not streaming and session:
|
||||||
|
if r:
|
||||||
|
r.close()
|
||||||
|
await session.close()
|
||||||
|
|
||||||
|
|
||||||
@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
|
@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"])
|
||||||
async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -414,6 +414,9 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
||||||
"DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE,
|
"DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE,
|
||||||
"DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG,
|
"DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG,
|
||||||
"DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
"DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_MODE": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_LOCAL": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_API": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_API,
|
||||||
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
||||||
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
||||||
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
|
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
|
||||||
|
|
@ -467,6 +470,8 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
||||||
"BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
|
"BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
|
||||||
"EXA_API_KEY": request.app.state.config.EXA_API_KEY,
|
"EXA_API_KEY": request.app.state.config.EXA_API_KEY,
|
||||||
"PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY,
|
"PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY,
|
||||||
|
"PERPLEXITY_MODEL": request.app.state.config.PERPLEXITY_MODEL,
|
||||||
|
"PERPLEXITY_SEARCH_CONTEXT_USAGE": request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE,
|
||||||
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
||||||
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
||||||
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
||||||
|
|
@ -520,6 +525,8 @@ class WebConfig(BaseModel):
|
||||||
BING_SEARCH_V7_SUBSCRIPTION_KEY: Optional[str] = None
|
BING_SEARCH_V7_SUBSCRIPTION_KEY: Optional[str] = None
|
||||||
EXA_API_KEY: Optional[str] = None
|
EXA_API_KEY: Optional[str] = None
|
||||||
PERPLEXITY_API_KEY: Optional[str] = None
|
PERPLEXITY_API_KEY: Optional[str] = None
|
||||||
|
PERPLEXITY_MODEL: Optional[str] = None
|
||||||
|
PERPLEXITY_SEARCH_CONTEXT_USAGE: Optional[str] = None
|
||||||
SOUGOU_API_SID: Optional[str] = None
|
SOUGOU_API_SID: Optional[str] = None
|
||||||
SOUGOU_API_SK: Optional[str] = None
|
SOUGOU_API_SK: Optional[str] = None
|
||||||
WEB_LOADER_ENGINE: Optional[str] = None
|
WEB_LOADER_ENGINE: Optional[str] = None
|
||||||
|
|
@ -571,6 +578,9 @@ class ConfigForm(BaseModel):
|
||||||
DOCLING_OCR_ENGINE: Optional[str] = None
|
DOCLING_OCR_ENGINE: Optional[str] = None
|
||||||
DOCLING_OCR_LANG: Optional[str] = None
|
DOCLING_OCR_LANG: Optional[str] = None
|
||||||
DOCLING_DO_PICTURE_DESCRIPTION: Optional[bool] = None
|
DOCLING_DO_PICTURE_DESCRIPTION: Optional[bool] = None
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_MODE: Optional[str] = None
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_LOCAL: Optional[dict] = None
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_API: Optional[dict] = None
|
||||||
DOCUMENT_INTELLIGENCE_ENDPOINT: Optional[str] = None
|
DOCUMENT_INTELLIGENCE_ENDPOINT: Optional[str] = None
|
||||||
DOCUMENT_INTELLIGENCE_KEY: Optional[str] = None
|
DOCUMENT_INTELLIGENCE_KEY: Optional[str] = None
|
||||||
MISTRAL_OCR_API_KEY: Optional[str] = None
|
MISTRAL_OCR_API_KEY: Optional[str] = None
|
||||||
|
|
@ -744,6 +754,22 @@ async def update_rag_config(
|
||||||
else request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION
|
else request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION
|
||||||
)
|
)
|
||||||
|
|
||||||
|
request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE = (
|
||||||
|
form_data.DOCLING_PICTURE_DESCRIPTION_MODE
|
||||||
|
if form_data.DOCLING_PICTURE_DESCRIPTION_MODE is not None
|
||||||
|
else request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE
|
||||||
|
)
|
||||||
|
request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL = (
|
||||||
|
form_data.DOCLING_PICTURE_DESCRIPTION_LOCAL
|
||||||
|
if form_data.DOCLING_PICTURE_DESCRIPTION_LOCAL is not None
|
||||||
|
else request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL
|
||||||
|
)
|
||||||
|
request.app.state.config.DOCLING_PICTURE_DESCRIPTION_API = (
|
||||||
|
form_data.DOCLING_PICTURE_DESCRIPTION_API
|
||||||
|
if form_data.DOCLING_PICTURE_DESCRIPTION_API is not None
|
||||||
|
else request.app.state.config.DOCLING_PICTURE_DESCRIPTION_API
|
||||||
|
)
|
||||||
|
|
||||||
request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = (
|
request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT = (
|
||||||
form_data.DOCUMENT_INTELLIGENCE_ENDPOINT
|
form_data.DOCUMENT_INTELLIGENCE_ENDPOINT
|
||||||
if form_data.DOCUMENT_INTELLIGENCE_ENDPOINT is not None
|
if form_data.DOCUMENT_INTELLIGENCE_ENDPOINT is not None
|
||||||
|
|
@ -907,6 +933,10 @@ async def update_rag_config(
|
||||||
)
|
)
|
||||||
request.app.state.config.EXA_API_KEY = form_data.web.EXA_API_KEY
|
request.app.state.config.EXA_API_KEY = form_data.web.EXA_API_KEY
|
||||||
request.app.state.config.PERPLEXITY_API_KEY = form_data.web.PERPLEXITY_API_KEY
|
request.app.state.config.PERPLEXITY_API_KEY = form_data.web.PERPLEXITY_API_KEY
|
||||||
|
request.app.state.config.PERPLEXITY_MODEL = form_data.web.PERPLEXITY_MODEL
|
||||||
|
request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE = (
|
||||||
|
form_data.web.PERPLEXITY_SEARCH_CONTEXT_USAGE
|
||||||
|
)
|
||||||
request.app.state.config.SOUGOU_API_SID = form_data.web.SOUGOU_API_SID
|
request.app.state.config.SOUGOU_API_SID = form_data.web.SOUGOU_API_SID
|
||||||
request.app.state.config.SOUGOU_API_SK = form_data.web.SOUGOU_API_SK
|
request.app.state.config.SOUGOU_API_SK = form_data.web.SOUGOU_API_SK
|
||||||
|
|
||||||
|
|
@ -977,6 +1007,9 @@ async def update_rag_config(
|
||||||
"DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE,
|
"DOCLING_OCR_ENGINE": request.app.state.config.DOCLING_OCR_ENGINE,
|
||||||
"DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG,
|
"DOCLING_OCR_LANG": request.app.state.config.DOCLING_OCR_LANG,
|
||||||
"DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
"DOCLING_DO_PICTURE_DESCRIPTION": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_MODE": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_LOCAL": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL,
|
||||||
|
"DOCLING_PICTURE_DESCRIPTION_API": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_API,
|
||||||
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
"DOCUMENT_INTELLIGENCE_ENDPOINT": request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
||||||
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
"DOCUMENT_INTELLIGENCE_KEY": request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
||||||
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
|
"MISTRAL_OCR_API_KEY": request.app.state.config.MISTRAL_OCR_API_KEY,
|
||||||
|
|
@ -1030,6 +1063,8 @@ async def update_rag_config(
|
||||||
"BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
|
"BING_SEARCH_V7_SUBSCRIPTION_KEY": request.app.state.config.BING_SEARCH_V7_SUBSCRIPTION_KEY,
|
||||||
"EXA_API_KEY": request.app.state.config.EXA_API_KEY,
|
"EXA_API_KEY": request.app.state.config.EXA_API_KEY,
|
||||||
"PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY,
|
"PERPLEXITY_API_KEY": request.app.state.config.PERPLEXITY_API_KEY,
|
||||||
|
"PERPLEXITY_MODEL": request.app.state.config.PERPLEXITY_MODEL,
|
||||||
|
"PERPLEXITY_SEARCH_CONTEXT_USAGE": request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE,
|
||||||
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
||||||
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
||||||
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
||||||
|
|
@ -1321,9 +1356,14 @@ def process_file(
|
||||||
EXTERNAL_DOCUMENT_LOADER_API_KEY=request.app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY,
|
EXTERNAL_DOCUMENT_LOADER_API_KEY=request.app.state.config.EXTERNAL_DOCUMENT_LOADER_API_KEY,
|
||||||
TIKA_SERVER_URL=request.app.state.config.TIKA_SERVER_URL,
|
TIKA_SERVER_URL=request.app.state.config.TIKA_SERVER_URL,
|
||||||
DOCLING_SERVER_URL=request.app.state.config.DOCLING_SERVER_URL,
|
DOCLING_SERVER_URL=request.app.state.config.DOCLING_SERVER_URL,
|
||||||
DOCLING_OCR_ENGINE=request.app.state.config.DOCLING_OCR_ENGINE,
|
DOCLING_PARAMS={
|
||||||
DOCLING_OCR_LANG=request.app.state.config.DOCLING_OCR_LANG,
|
"ocr_engine": request.app.state.config.DOCLING_OCR_ENGINE,
|
||||||
DOCLING_DO_PICTURE_DESCRIPTION=request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
"ocr_lang": request.app.state.config.DOCLING_OCR_LANG,
|
||||||
|
"do_picture_description": request.app.state.config.DOCLING_DO_PICTURE_DESCRIPTION,
|
||||||
|
"picture_description_mode": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_MODE,
|
||||||
|
"picture_description_local": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_LOCAL,
|
||||||
|
"picture_description_api": request.app.state.config.DOCLING_PICTURE_DESCRIPTION_API,
|
||||||
|
},
|
||||||
PDF_EXTRACT_IMAGES=request.app.state.config.PDF_EXTRACT_IMAGES,
|
PDF_EXTRACT_IMAGES=request.app.state.config.PDF_EXTRACT_IMAGES,
|
||||||
DOCUMENT_INTELLIGENCE_ENDPOINT=request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
DOCUMENT_INTELLIGENCE_ENDPOINT=request.app.state.config.DOCUMENT_INTELLIGENCE_ENDPOINT,
|
||||||
DOCUMENT_INTELLIGENCE_KEY=request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
DOCUMENT_INTELLIGENCE_KEY=request.app.state.config.DOCUMENT_INTELLIGENCE_KEY,
|
||||||
|
|
@ -1740,19 +1780,14 @@ def search_web(request: Request, engine: str, query: str) -> list[SearchResult]:
|
||||||
request.app.state.config.WEB_SEARCH_RESULT_COUNT,
|
request.app.state.config.WEB_SEARCH_RESULT_COUNT,
|
||||||
request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST,
|
request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST,
|
||||||
)
|
)
|
||||||
elif engine == "exa":
|
|
||||||
return search_exa(
|
|
||||||
request.app.state.config.EXA_API_KEY,
|
|
||||||
query,
|
|
||||||
request.app.state.config.WEB_SEARCH_RESULT_COUNT,
|
|
||||||
request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST,
|
|
||||||
)
|
|
||||||
elif engine == "perplexity":
|
elif engine == "perplexity":
|
||||||
return search_perplexity(
|
return search_perplexity(
|
||||||
request.app.state.config.PERPLEXITY_API_KEY,
|
request.app.state.config.PERPLEXITY_API_KEY,
|
||||||
query,
|
query,
|
||||||
request.app.state.config.WEB_SEARCH_RESULT_COUNT,
|
request.app.state.config.WEB_SEARCH_RESULT_COUNT,
|
||||||
request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST,
|
request.app.state.config.WEB_SEARCH_DOMAIN_FILTER_LIST,
|
||||||
|
model=request.app.state.config.PERPLEXITY_MODEL,
|
||||||
|
search_context_usage=request.app.state.config.PERPLEXITY_SEARCH_CONTEXT_USAGE,
|
||||||
)
|
)
|
||||||
elif engine == "sougou":
|
elif engine == "sougou":
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import re
|
||||||
from open_webui.utils.chat import generate_chat_completion
|
from open_webui.utils.chat import generate_chat_completion
|
||||||
from open_webui.utils.task import (
|
from open_webui.utils.task import (
|
||||||
title_generation_template,
|
title_generation_template,
|
||||||
|
follow_up_generation_template,
|
||||||
query_generation_template,
|
query_generation_template,
|
||||||
image_prompt_generation_template,
|
image_prompt_generation_template,
|
||||||
autocomplete_generation_template,
|
autocomplete_generation_template,
|
||||||
|
|
@ -25,6 +26,7 @@ from open_webui.utils.task import get_task_model_id
|
||||||
|
|
||||||
from open_webui.config import (
|
from open_webui.config import (
|
||||||
DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE,
|
DEFAULT_TITLE_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
DEFAULT_FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||||
DEFAULT_TAGS_GENERATION_PROMPT_TEMPLATE,
|
DEFAULT_TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||||
DEFAULT_IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
DEFAULT_IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE,
|
||||||
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
|
DEFAULT_QUERY_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
|
@ -58,6 +60,8 @@ async def get_task_config(request: Request, user=Depends(get_verified_user)):
|
||||||
"ENABLE_AUTOCOMPLETE_GENERATION": request.app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
|
"ENABLE_AUTOCOMPLETE_GENERATION": request.app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,
|
||||||
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": request.app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
|
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": request.app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
|
||||||
"TAGS_GENERATION_PROMPT_TEMPLATE": request.app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
|
"TAGS_GENERATION_PROMPT_TEMPLATE": request.app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
"FOLLOW_UP_GENERATION_PROMPT_TEMPLATE": request.app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
"ENABLE_FOLLOW_UP_GENERATION": request.app.state.config.ENABLE_FOLLOW_UP_GENERATION,
|
||||||
"ENABLE_TAGS_GENERATION": request.app.state.config.ENABLE_TAGS_GENERATION,
|
"ENABLE_TAGS_GENERATION": request.app.state.config.ENABLE_TAGS_GENERATION,
|
||||||
"ENABLE_TITLE_GENERATION": request.app.state.config.ENABLE_TITLE_GENERATION,
|
"ENABLE_TITLE_GENERATION": request.app.state.config.ENABLE_TITLE_GENERATION,
|
||||||
"ENABLE_SEARCH_QUERY_GENERATION": request.app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
|
"ENABLE_SEARCH_QUERY_GENERATION": request.app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
|
||||||
|
|
@ -76,6 +80,8 @@ class TaskConfigForm(BaseModel):
|
||||||
ENABLE_AUTOCOMPLETE_GENERATION: bool
|
ENABLE_AUTOCOMPLETE_GENERATION: bool
|
||||||
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: int
|
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: int
|
||||||
TAGS_GENERATION_PROMPT_TEMPLATE: str
|
TAGS_GENERATION_PROMPT_TEMPLATE: str
|
||||||
|
FOLLOW_UP_GENERATION_PROMPT_TEMPLATE: str
|
||||||
|
ENABLE_FOLLOW_UP_GENERATION: bool
|
||||||
ENABLE_TAGS_GENERATION: bool
|
ENABLE_TAGS_GENERATION: bool
|
||||||
ENABLE_SEARCH_QUERY_GENERATION: bool
|
ENABLE_SEARCH_QUERY_GENERATION: bool
|
||||||
ENABLE_RETRIEVAL_QUERY_GENERATION: bool
|
ENABLE_RETRIEVAL_QUERY_GENERATION: bool
|
||||||
|
|
@ -94,6 +100,13 @@ async def update_task_config(
|
||||||
form_data.TITLE_GENERATION_PROMPT_TEMPLATE
|
form_data.TITLE_GENERATION_PROMPT_TEMPLATE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
request.app.state.config.ENABLE_FOLLOW_UP_GENERATION = (
|
||||||
|
form_data.ENABLE_FOLLOW_UP_GENERATION
|
||||||
|
)
|
||||||
|
request.app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE = (
|
||||||
|
form_data.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE
|
||||||
|
)
|
||||||
|
|
||||||
request.app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = (
|
request.app.state.config.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE = (
|
||||||
form_data.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE
|
form_data.IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE
|
||||||
)
|
)
|
||||||
|
|
@ -133,6 +146,8 @@ async def update_task_config(
|
||||||
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": request.app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
|
"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": request.app.state.config.AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH,
|
||||||
"TAGS_GENERATION_PROMPT_TEMPLATE": request.app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
|
"TAGS_GENERATION_PROMPT_TEMPLATE": request.app.state.config.TAGS_GENERATION_PROMPT_TEMPLATE,
|
||||||
"ENABLE_TAGS_GENERATION": request.app.state.config.ENABLE_TAGS_GENERATION,
|
"ENABLE_TAGS_GENERATION": request.app.state.config.ENABLE_TAGS_GENERATION,
|
||||||
|
"ENABLE_FOLLOW_UP_GENERATION": request.app.state.config.ENABLE_FOLLOW_UP_GENERATION,
|
||||||
|
"FOLLOW_UP_GENERATION_PROMPT_TEMPLATE": request.app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE,
|
||||||
"ENABLE_SEARCH_QUERY_GENERATION": request.app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
|
"ENABLE_SEARCH_QUERY_GENERATION": request.app.state.config.ENABLE_SEARCH_QUERY_GENERATION,
|
||||||
"ENABLE_RETRIEVAL_QUERY_GENERATION": request.app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION,
|
"ENABLE_RETRIEVAL_QUERY_GENERATION": request.app.state.config.ENABLE_RETRIEVAL_QUERY_GENERATION,
|
||||||
"QUERY_GENERATION_PROMPT_TEMPLATE": request.app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE,
|
"QUERY_GENERATION_PROMPT_TEMPLATE": request.app.state.config.QUERY_GENERATION_PROMPT_TEMPLATE,
|
||||||
|
|
@ -231,6 +246,86 @@ async def generate_title(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/follow_up/completions")
|
||||||
|
async def generate_follow_ups(
|
||||||
|
request: Request, form_data: dict, user=Depends(get_verified_user)
|
||||||
|
):
|
||||||
|
|
||||||
|
if not request.app.state.config.ENABLE_FOLLOW_UP_GENERATION:
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=status.HTTP_200_OK,
|
||||||
|
content={"detail": "Follow-up generation is disabled"},
|
||||||
|
)
|
||||||
|
|
||||||
|
if getattr(request.state, "direct", False) and hasattr(request.state, "model"):
|
||||||
|
models = {
|
||||||
|
request.state.model["id"]: request.state.model,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
models = request.app.state.MODELS
|
||||||
|
|
||||||
|
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,
|
||||||
|
request.app.state.config.TASK_MODEL,
|
||||||
|
request.app.state.config.TASK_MODEL_EXTERNAL,
|
||||||
|
models,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
f"generating chat title using model {task_model_id} for user {user.email} "
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE != "":
|
||||||
|
template = request.app.state.config.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE
|
||||||
|
else:
|
||||||
|
template = DEFAULT_FOLLOW_UP_GENERATION_PROMPT_TEMPLATE
|
||||||
|
|
||||||
|
content = follow_up_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,
|
||||||
|
"metadata": {
|
||||||
|
**(request.state.metadata if hasattr(request.state, "metadata") else {}),
|
||||||
|
"task": str(TASKS.FOLLOW_UP_GENERATION),
|
||||||
|
"task_body": form_data,
|
||||||
|
"chat_id": form_data.get("chat_id", None),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process the payload through the pipeline
|
||||||
|
try:
|
||||||
|
payload = await process_pipeline_inlet_filter(request, payload, user, models)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
try:
|
||||||
|
return await generate_chat_completion(request, form_data=payload, user=user)
|
||||||
|
except Exception as e:
|
||||||
|
log.error("Exception occurred", exc_info=True)
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
content={"detail": "An internal error has occurred."},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/tags/completions")
|
@router.post("/tags/completions")
|
||||||
async def generate_chat_tags(
|
async def generate_chat_tags(
|
||||||
request: Request, form_data: dict, user=Depends(get_verified_user)
|
request: Request, form_data: dict, user=Depends(get_verified_user)
|
||||||
|
|
|
||||||
|
|
@ -165,22 +165,6 @@ async def update_default_user_permissions(
|
||||||
return request.app.state.config.USER_PERMISSIONS
|
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
|
# GetUserSettingsBySessionUser
|
||||||
############################
|
############################
|
||||||
|
|
@ -333,11 +317,22 @@ async def update_user_by_id(
|
||||||
# Prevent modification of the primary admin user by other admins
|
# Prevent modification of the primary admin user by other admins
|
||||||
try:
|
try:
|
||||||
first_user = Users.get_first_user()
|
first_user = Users.get_first_user()
|
||||||
if first_user and user_id == first_user.id and session_user.id != user_id:
|
if first_user:
|
||||||
raise HTTPException(
|
if user_id == first_user.id:
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
if session_user.id != user_id:
|
||||||
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
# If the user trying to update is the primary admin, and they are not the primary admin themselves
|
||||||
)
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
if form_data.role != "admin":
|
||||||
|
# If the primary admin is trying to change their own role, prevent it
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(f"Error checking primary admin status: {e}")
|
log.error(f"Error checking primary admin status: {e}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -365,6 +360,7 @@ async def update_user_by_id(
|
||||||
updated_user = Users.update_user_by_id(
|
updated_user = Users.update_user_by_id(
|
||||||
user_id,
|
user_id,
|
||||||
{
|
{
|
||||||
|
"role": form_data.role,
|
||||||
"name": form_data.name,
|
"name": form_data.name,
|
||||||
"email": form_data.email.lower(),
|
"email": form_data.email.lower(),
|
||||||
"profile_image_url": form_data.profile_image_url,
|
"profile_image_url": form_data.profile_image_url,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class CodeForm(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
@router.post("/code/format")
|
@router.post("/code/format")
|
||||||
async def format_code(form_data: CodeForm, user=Depends(get_verified_user)):
|
async def format_code(form_data: CodeForm, user=Depends(get_admin_user)):
|
||||||
try:
|
try:
|
||||||
formatted_code = black.format_str(form_data.code, mode=black.Mode())
|
formatted_code = black.format_str(form_data.code, mode=black.Mode())
|
||||||
return {"code": formatted_code}
|
return {"code": formatted_code}
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,90 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
import json
|
||||||
|
from redis import Redis
|
||||||
|
from fastapi import Request
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
# A dictionary to keep track of active tasks
|
# A dictionary to keep track of active tasks
|
||||||
tasks: Dict[str, asyncio.Task] = {}
|
tasks: Dict[str, asyncio.Task] = {}
|
||||||
chat_tasks = {}
|
chat_tasks = {}
|
||||||
|
|
||||||
|
|
||||||
def cleanup_task(task_id: str, id=None):
|
REDIS_TASKS_KEY = "open-webui:tasks"
|
||||||
|
REDIS_CHAT_TASKS_KEY = "open-webui:tasks:chat"
|
||||||
|
REDIS_PUBSUB_CHANNEL = "open-webui:tasks:commands"
|
||||||
|
|
||||||
|
|
||||||
|
def is_redis(request: Request) -> bool:
|
||||||
|
# Called everywhere a request is available to check Redis
|
||||||
|
return hasattr(request.app.state, "redis") and isinstance(
|
||||||
|
request.app.state.redis, Redis
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def redis_task_command_listener(app):
|
||||||
|
redis: Redis = app.state.redis
|
||||||
|
pubsub = redis.pubsub()
|
||||||
|
await pubsub.subscribe(REDIS_PUBSUB_CHANNEL)
|
||||||
|
print("Subscribed to Redis task command channel")
|
||||||
|
|
||||||
|
async for message in pubsub.listen():
|
||||||
|
if message["type"] != "message":
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
command = json.loads(message["data"])
|
||||||
|
if command.get("action") == "stop":
|
||||||
|
task_id = command.get("task_id")
|
||||||
|
local_task = tasks.get(task_id)
|
||||||
|
if local_task:
|
||||||
|
local_task.cancel()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error handling distributed task command: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
### ------------------------------
|
||||||
|
### REDIS-ENABLED HANDLERS
|
||||||
|
### ------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def redis_save_task(redis: Redis, task_id: str, chat_id: Optional[str]):
|
||||||
|
pipe = redis.pipeline()
|
||||||
|
pipe.hset(REDIS_TASKS_KEY, task_id, chat_id or "")
|
||||||
|
if chat_id:
|
||||||
|
pipe.sadd(f"{REDIS_CHAT_TASKS_KEY}:{chat_id}", task_id)
|
||||||
|
pipe.execute()
|
||||||
|
|
||||||
|
|
||||||
|
def redis_cleanup_task(redis: Redis, task_id: str, chat_id: Optional[str]):
|
||||||
|
pipe = redis.pipeline()
|
||||||
|
pipe.hdel(REDIS_TASKS_KEY, task_id)
|
||||||
|
if chat_id:
|
||||||
|
pipe.srem(f"{REDIS_CHAT_TASKS_KEY}:{chat_id}", task_id)
|
||||||
|
if pipe.scard(f"{REDIS_CHAT_TASKS_KEY}:{chat_id}").execute()[-1] == 0:
|
||||||
|
pipe.delete(f"{REDIS_CHAT_TASKS_KEY}:{chat_id}") # Remove if empty set
|
||||||
|
pipe.execute()
|
||||||
|
|
||||||
|
|
||||||
|
def redis_list_tasks(redis: Redis) -> List[str]:
|
||||||
|
return list(redis.hkeys(REDIS_TASKS_KEY))
|
||||||
|
|
||||||
|
|
||||||
|
def redis_list_chat_tasks(redis: Redis, chat_id: str) -> List[str]:
|
||||||
|
return list(redis.smembers(f"{REDIS_CHAT_TASKS_KEY}:{chat_id}"))
|
||||||
|
|
||||||
|
|
||||||
|
def redis_send_command(redis: Redis, command: dict):
|
||||||
|
redis.publish(REDIS_PUBSUB_CHANNEL, json.dumps(command))
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_task(request, task_id: str, id=None):
|
||||||
"""
|
"""
|
||||||
Remove a completed or canceled task from the global `tasks` dictionary.
|
Remove a completed or canceled task from the global `tasks` dictionary.
|
||||||
"""
|
"""
|
||||||
|
if is_redis(request):
|
||||||
|
redis_cleanup_task(request.app.state.redis, task_id, id)
|
||||||
|
|
||||||
tasks.pop(task_id, None) # Remove the task if it exists
|
tasks.pop(task_id, None) # Remove the task if it exists
|
||||||
|
|
||||||
# If an ID is provided, remove the task from the chat_tasks dictionary
|
# If an ID is provided, remove the task from the chat_tasks dictionary
|
||||||
|
|
@ -21,7 +95,7 @@ def cleanup_task(task_id: str, id=None):
|
||||||
chat_tasks.pop(id, None)
|
chat_tasks.pop(id, None)
|
||||||
|
|
||||||
|
|
||||||
def create_task(coroutine, id=None):
|
def create_task(request, coroutine, id=None):
|
||||||
"""
|
"""
|
||||||
Create a new asyncio task and add it to the global task dictionary.
|
Create a new asyncio task and add it to the global task dictionary.
|
||||||
"""
|
"""
|
||||||
|
|
@ -29,7 +103,7 @@ def create_task(coroutine, id=None):
|
||||||
task = asyncio.create_task(coroutine) # Create the task
|
task = asyncio.create_task(coroutine) # Create the task
|
||||||
|
|
||||||
# Add a done callback for cleanup
|
# Add a done callback for cleanup
|
||||||
task.add_done_callback(lambda t: cleanup_task(task_id, id))
|
task.add_done_callback(lambda t: cleanup_task(request, task_id, id))
|
||||||
tasks[task_id] = task
|
tasks[task_id] = task
|
||||||
|
|
||||||
# If an ID is provided, associate the task with that ID
|
# If an ID is provided, associate the task with that ID
|
||||||
|
|
@ -38,34 +112,46 @@ def create_task(coroutine, id=None):
|
||||||
else:
|
else:
|
||||||
chat_tasks[id] = [task_id]
|
chat_tasks[id] = [task_id]
|
||||||
|
|
||||||
|
if is_redis(request):
|
||||||
|
redis_save_task(request.app.state.redis, task_id, id)
|
||||||
|
|
||||||
return task_id, task
|
return task_id, task
|
||||||
|
|
||||||
|
|
||||||
def get_task(task_id: str):
|
def list_tasks(request):
|
||||||
"""
|
|
||||||
Retrieve a task by its task ID.
|
|
||||||
"""
|
|
||||||
return tasks.get(task_id)
|
|
||||||
|
|
||||||
|
|
||||||
def list_tasks():
|
|
||||||
"""
|
"""
|
||||||
List all currently active task IDs.
|
List all currently active task IDs.
|
||||||
"""
|
"""
|
||||||
|
if is_redis(request):
|
||||||
|
return redis_list_tasks(request.app.state.redis)
|
||||||
return list(tasks.keys())
|
return list(tasks.keys())
|
||||||
|
|
||||||
|
|
||||||
def list_task_ids_by_chat_id(id):
|
def list_task_ids_by_chat_id(request, id):
|
||||||
"""
|
"""
|
||||||
List all tasks associated with a specific ID.
|
List all tasks associated with a specific ID.
|
||||||
"""
|
"""
|
||||||
|
if is_redis(request):
|
||||||
|
return redis_list_chat_tasks(request.app.state.redis, id)
|
||||||
return chat_tasks.get(id, [])
|
return chat_tasks.get(id, [])
|
||||||
|
|
||||||
|
|
||||||
async def stop_task(task_id: str):
|
async def stop_task(request, task_id: str):
|
||||||
"""
|
"""
|
||||||
Cancel a running task and remove it from the global task list.
|
Cancel a running task and remove it from the global task list.
|
||||||
"""
|
"""
|
||||||
|
if is_redis(request):
|
||||||
|
# PUBSUB: All instances check if they have this task, and stop if so.
|
||||||
|
redis_send_command(
|
||||||
|
request.app.state.redis,
|
||||||
|
{
|
||||||
|
"action": "stop",
|
||||||
|
"task_id": task_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Optionally check if task_id still in Redis a few moments later for feedback?
|
||||||
|
return {"status": True, "message": f"Stop signal sent for {task_id}"}
|
||||||
|
|
||||||
task = tasks.get(task_id)
|
task = tasks.get(task_id)
|
||||||
if not task:
|
if not task:
|
||||||
raise ValueError(f"Task with ID {task_id} not found.")
|
raise ValueError(f"Task with ID {task_id} not found.")
|
||||||
|
|
|
||||||
|
|
@ -320,12 +320,7 @@ async def chat_completed(request: Request, form_data: dict, user: Any):
|
||||||
extra_params = {
|
extra_params = {
|
||||||
"__event_emitter__": get_event_emitter(metadata),
|
"__event_emitter__": get_event_emitter(metadata),
|
||||||
"__event_call__": get_event_call(metadata),
|
"__event_call__": get_event_call(metadata),
|
||||||
"__user__": {
|
"__user__": user.model_dump() if isinstance(user, UserModel) else {},
|
||||||
"id": user.id,
|
|
||||||
"email": user.email,
|
|
||||||
"name": user.name,
|
|
||||||
"role": user.role,
|
|
||||||
},
|
|
||||||
"__metadata__": metadata,
|
"__metadata__": metadata,
|
||||||
"__request__": request,
|
"__request__": request,
|
||||||
"__model__": model,
|
"__model__": model,
|
||||||
|
|
@ -424,12 +419,7 @@ async def chat_action(request: Request, action_id: str, form_data: dict, user: A
|
||||||
params[key] = value
|
params[key] = value
|
||||||
|
|
||||||
if "__user__" in sig.parameters:
|
if "__user__" in sig.parameters:
|
||||||
__user__ = {
|
__user__ = (user.model_dump() if isinstance(user, UserModel) else {},)
|
||||||
"id": user.id,
|
|
||||||
"email": user.email,
|
|
||||||
"name": user.name,
|
|
||||||
"role": user.role,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(function_module, "UserValves"):
|
if hasattr(function_module, "UserValves"):
|
||||||
|
|
|
||||||
90
backend/open_webui/utils/embeddings.py
Normal file
90
backend/open_webui/utils/embeddings.py
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
import random
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from fastapi import Request
|
||||||
|
from open_webui.models.users import UserModel
|
||||||
|
from open_webui.models.models import Models
|
||||||
|
from open_webui.utils.models import check_model_access
|
||||||
|
from open_webui.env import SRC_LOG_LEVELS, GLOBAL_LOG_LEVEL, BYPASS_MODEL_ACCESS_CONTROL
|
||||||
|
|
||||||
|
from open_webui.routers.openai import embeddings as openai_embeddings
|
||||||
|
from open_webui.routers.ollama import (
|
||||||
|
embeddings as ollama_embeddings,
|
||||||
|
GenerateEmbeddingsForm,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
from open_webui.utils.payload import convert_embedding_payload_openai_to_ollama
|
||||||
|
from open_webui.utils.response import convert_embedding_response_ollama_to_openai
|
||||||
|
|
||||||
|
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log.setLevel(SRC_LOG_LEVELS["MAIN"])
|
||||||
|
|
||||||
|
|
||||||
|
async def generate_embeddings(
|
||||||
|
request: Request,
|
||||||
|
form_data: dict,
|
||||||
|
user: UserModel,
|
||||||
|
bypass_filter: bool = False,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Dispatch and handle embeddings generation based on the model type (OpenAI, Ollama).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request (Request): The FastAPI request context.
|
||||||
|
form_data (dict): The input data sent to the endpoint.
|
||||||
|
user (UserModel): The authenticated user.
|
||||||
|
bypass_filter (bool): If True, disables access filtering (default False).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The embeddings response, following OpenAI API compatibility.
|
||||||
|
"""
|
||||||
|
if BYPASS_MODEL_ACCESS_CONTROL:
|
||||||
|
bypass_filter = True
|
||||||
|
|
||||||
|
# Attach extra metadata from request.state if present
|
||||||
|
if hasattr(request.state, "metadata"):
|
||||||
|
if "metadata" not in form_data:
|
||||||
|
form_data["metadata"] = request.state.metadata
|
||||||
|
else:
|
||||||
|
form_data["metadata"] = {
|
||||||
|
**form_data["metadata"],
|
||||||
|
**request.state.metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
# If "direct" flag present, use only that model
|
||||||
|
if getattr(request.state, "direct", False) and hasattr(request.state, "model"):
|
||||||
|
models = {
|
||||||
|
request.state.model["id"]: request.state.model,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
models = request.app.state.MODELS
|
||||||
|
|
||||||
|
model_id = form_data.get("model")
|
||||||
|
if model_id not in models:
|
||||||
|
raise Exception("Model not found")
|
||||||
|
model = models[model_id]
|
||||||
|
|
||||||
|
# Access filtering
|
||||||
|
if not getattr(request.state, "direct", False):
|
||||||
|
if not bypass_filter and user.role == "user":
|
||||||
|
check_model_access(user, model)
|
||||||
|
|
||||||
|
# Ollama backend
|
||||||
|
if model.get("owned_by") == "ollama":
|
||||||
|
ollama_payload = convert_embedding_payload_openai_to_ollama(form_data)
|
||||||
|
response = await ollama_embeddings(
|
||||||
|
request=request,
|
||||||
|
form_data=GenerateEmbeddingsForm(**ollama_payload),
|
||||||
|
user=user,
|
||||||
|
)
|
||||||
|
return convert_embedding_response_ollama_to_openai(response)
|
||||||
|
|
||||||
|
# Default: OpenAI or compatible backend
|
||||||
|
return await openai_embeddings(
|
||||||
|
request=request,
|
||||||
|
form_data=form_data,
|
||||||
|
user=user,
|
||||||
|
)
|
||||||
|
|
@ -32,11 +32,17 @@ from open_webui.socket.main import (
|
||||||
from open_webui.routers.tasks import (
|
from open_webui.routers.tasks import (
|
||||||
generate_queries,
|
generate_queries,
|
||||||
generate_title,
|
generate_title,
|
||||||
|
generate_follow_ups,
|
||||||
generate_image_prompt,
|
generate_image_prompt,
|
||||||
generate_chat_tags,
|
generate_chat_tags,
|
||||||
)
|
)
|
||||||
from open_webui.routers.retrieval import process_web_search, SearchForm
|
from open_webui.routers.retrieval import process_web_search, SearchForm
|
||||||
from open_webui.routers.images import image_generations, GenerateImageForm
|
from open_webui.routers.images import (
|
||||||
|
load_b64_image_data,
|
||||||
|
image_generations,
|
||||||
|
GenerateImageForm,
|
||||||
|
upload_image,
|
||||||
|
)
|
||||||
from open_webui.routers.pipelines import (
|
from open_webui.routers.pipelines import (
|
||||||
process_pipeline_inlet_filter,
|
process_pipeline_inlet_filter,
|
||||||
process_pipeline_outlet_filter,
|
process_pipeline_outlet_filter,
|
||||||
|
|
@ -726,12 +732,7 @@ async def process_chat_payload(request, form_data, user, metadata, model):
|
||||||
extra_params = {
|
extra_params = {
|
||||||
"__event_emitter__": event_emitter,
|
"__event_emitter__": event_emitter,
|
||||||
"__event_call__": event_call,
|
"__event_call__": event_call,
|
||||||
"__user__": {
|
"__user__": user.model_dump() if isinstance(user, UserModel) else {},
|
||||||
"id": user.id,
|
|
||||||
"email": user.email,
|
|
||||||
"name": user.name,
|
|
||||||
"role": user.role,
|
|
||||||
},
|
|
||||||
"__metadata__": metadata,
|
"__metadata__": metadata,
|
||||||
"__request__": request,
|
"__request__": request,
|
||||||
"__model__": model,
|
"__model__": model,
|
||||||
|
|
@ -1048,6 +1049,59 @@ async def process_chat_response(
|
||||||
)
|
)
|
||||||
|
|
||||||
if tasks and messages:
|
if tasks and messages:
|
||||||
|
if (
|
||||||
|
TASKS.FOLLOW_UP_GENERATION in tasks
|
||||||
|
and tasks[TASKS.FOLLOW_UP_GENERATION]
|
||||||
|
):
|
||||||
|
res = await generate_follow_ups(
|
||||||
|
request,
|
||||||
|
{
|
||||||
|
"model": message["model"],
|
||||||
|
"messages": messages,
|
||||||
|
"message_id": metadata["message_id"],
|
||||||
|
"chat_id": metadata["chat_id"],
|
||||||
|
},
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
|
||||||
|
if res and isinstance(res, dict):
|
||||||
|
if len(res.get("choices", [])) == 1:
|
||||||
|
follow_ups_string = (
|
||||||
|
res.get("choices", [])[0]
|
||||||
|
.get("message", {})
|
||||||
|
.get("content", "")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
follow_ups_string = ""
|
||||||
|
|
||||||
|
follow_ups_string = follow_ups_string[
|
||||||
|
follow_ups_string.find("{") : follow_ups_string.rfind("}")
|
||||||
|
+ 1
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
follow_ups = json.loads(follow_ups_string).get(
|
||||||
|
"follow_ups", []
|
||||||
|
)
|
||||||
|
Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||||
|
metadata["chat_id"],
|
||||||
|
metadata["message_id"],
|
||||||
|
{
|
||||||
|
"followUps": follow_ups,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await event_emitter(
|
||||||
|
{
|
||||||
|
"type": "chat:message:follow_ups",
|
||||||
|
"data": {
|
||||||
|
"follow_ups": follow_ups,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
if TASKS.TITLE_GENERATION in tasks:
|
if TASKS.TITLE_GENERATION in tasks:
|
||||||
if tasks[TASKS.TITLE_GENERATION]:
|
if tasks[TASKS.TITLE_GENERATION]:
|
||||||
res = await generate_title(
|
res = await generate_title(
|
||||||
|
|
@ -1273,12 +1327,7 @@ async def process_chat_response(
|
||||||
extra_params = {
|
extra_params = {
|
||||||
"__event_emitter__": event_emitter,
|
"__event_emitter__": event_emitter,
|
||||||
"__event_call__": event_caller,
|
"__event_call__": event_caller,
|
||||||
"__user__": {
|
"__user__": user.model_dump() if isinstance(user, UserModel) else {},
|
||||||
"id": user.id,
|
|
||||||
"email": user.email,
|
|
||||||
"name": user.name,
|
|
||||||
"role": user.role,
|
|
||||||
},
|
|
||||||
"__metadata__": metadata,
|
"__metadata__": metadata,
|
||||||
"__request__": request,
|
"__request__": request,
|
||||||
"__model__": model,
|
"__model__": model,
|
||||||
|
|
@ -2215,28 +2264,21 @@ async def process_chat_response(
|
||||||
stdoutLines = stdout.split("\n")
|
stdoutLines = stdout.split("\n")
|
||||||
for idx, line in enumerate(stdoutLines):
|
for idx, line in enumerate(stdoutLines):
|
||||||
if "data:image/png;base64" in line:
|
if "data:image/png;base64" in line:
|
||||||
id = str(uuid4())
|
image_url = ""
|
||||||
|
# Extract base64 image data from the line
|
||||||
# ensure the path exists
|
image_data, content_type = (
|
||||||
os.makedirs(
|
load_b64_image_data(line)
|
||||||
os.path.join(CACHE_DIR, "images"),
|
|
||||||
exist_ok=True,
|
|
||||||
)
|
)
|
||||||
|
if image_data is not None:
|
||||||
image_path = os.path.join(
|
image_url = upload_image(
|
||||||
CACHE_DIR,
|
request,
|
||||||
f"images/{id}.png",
|
image_data,
|
||||||
)
|
content_type,
|
||||||
|
metadata,
|
||||||
with open(image_path, "wb") as f:
|
user,
|
||||||
f.write(
|
|
||||||
base64.b64decode(
|
|
||||||
line.split(",")[1]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
stdoutLines[idx] = (
|
stdoutLines[idx] = (
|
||||||
f""
|
f""
|
||||||
)
|
)
|
||||||
|
|
||||||
output["stdout"] = "\n".join(stdoutLines)
|
output["stdout"] = "\n".join(stdoutLines)
|
||||||
|
|
@ -2247,30 +2289,22 @@ async def process_chat_response(
|
||||||
resultLines = result.split("\n")
|
resultLines = result.split("\n")
|
||||||
for idx, line in enumerate(resultLines):
|
for idx, line in enumerate(resultLines):
|
||||||
if "data:image/png;base64" in line:
|
if "data:image/png;base64" in line:
|
||||||
id = str(uuid4())
|
image_url = ""
|
||||||
|
# Extract base64 image data from the line
|
||||||
# ensure the path exists
|
image_data, content_type = (
|
||||||
os.makedirs(
|
load_b64_image_data(line)
|
||||||
os.path.join(CACHE_DIR, "images"),
|
|
||||||
exist_ok=True,
|
|
||||||
)
|
)
|
||||||
|
if image_data is not None:
|
||||||
image_path = os.path.join(
|
image_url = upload_image(
|
||||||
CACHE_DIR,
|
request,
|
||||||
f"images/{id}.png",
|
image_data,
|
||||||
)
|
content_type,
|
||||||
|
metadata,
|
||||||
with open(image_path, "wb") as f:
|
user,
|
||||||
f.write(
|
|
||||||
base64.b64decode(
|
|
||||||
line.split(",")[1]
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
resultLines[idx] = (
|
resultLines[idx] = (
|
||||||
f""
|
f""
|
||||||
)
|
)
|
||||||
|
|
||||||
output["result"] = "\n".join(resultLines)
|
output["result"] = "\n".join(resultLines)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
output = str(e)
|
output = str(e)
|
||||||
|
|
@ -2380,7 +2414,7 @@ async def process_chat_response(
|
||||||
|
|
||||||
# background_tasks.add_task(post_response_handler, response, events)
|
# background_tasks.add_task(post_response_handler, response, events)
|
||||||
task_id, _ = create_task(
|
task_id, _ = create_task(
|
||||||
post_response_handler(response, events), id=metadata["chat_id"]
|
request, post_response_handler(response, events), id=metadata["chat_id"]
|
||||||
)
|
)
|
||||||
return {"status": True, "task_id": task_id}
|
return {"status": True, "task_id": task_id}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -538,7 +538,7 @@ class OAuthManager:
|
||||||
# Redirect back to the frontend with the JWT token
|
# Redirect back to the frontend with the JWT token
|
||||||
|
|
||||||
redirect_base_url = request.app.state.config.WEBUI_URL or request.base_url
|
redirect_base_url = request.app.state.config.WEBUI_URL or request.base_url
|
||||||
if redirect_base_url.endswith("/"):
|
if isinstance(redirect_base_url, str) and redirect_base_url.endswith("/"):
|
||||||
redirect_base_url = redirect_base_url[:-1]
|
redirect_base_url = redirect_base_url[:-1]
|
||||||
redirect_url = f"{redirect_base_url}/auth#token={jwt_token}"
|
redirect_url = f"{redirect_base_url}/auth#token={jwt_token}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -329,3 +329,32 @@ def convert_payload_openai_to_ollama(openai_payload: dict) -> dict:
|
||||||
ollama_payload["format"] = format
|
ollama_payload["format"] = format
|
||||||
|
|
||||||
return ollama_payload
|
return ollama_payload
|
||||||
|
|
||||||
|
|
||||||
|
def convert_embedding_payload_openai_to_ollama(openai_payload: dict) -> dict:
|
||||||
|
"""
|
||||||
|
Convert an embeddings request payload from OpenAI format to Ollama format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
openai_payload (dict): The original payload designed for OpenAI API usage.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A payload compatible with the Ollama API embeddings endpoint.
|
||||||
|
"""
|
||||||
|
ollama_payload = {"model": openai_payload.get("model")}
|
||||||
|
input_value = openai_payload.get("input")
|
||||||
|
|
||||||
|
# Ollama expects 'input' as a list, and 'prompt' as a single string.
|
||||||
|
if isinstance(input_value, list):
|
||||||
|
ollama_payload["input"] = input_value
|
||||||
|
ollama_payload["prompt"] = "\n".join(str(x) for x in input_value)
|
||||||
|
else:
|
||||||
|
ollama_payload["input"] = [input_value]
|
||||||
|
ollama_payload["prompt"] = str(input_value)
|
||||||
|
|
||||||
|
# Optionally forward other fields if present
|
||||||
|
for optional_key in ("options", "truncate", "keep_alive"):
|
||||||
|
if optional_key in openai_payload:
|
||||||
|
ollama_payload[optional_key] = openai_payload[optional_key]
|
||||||
|
|
||||||
|
return ollama_payload
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import socketio
|
||||||
import redis
|
import redis
|
||||||
from redis import asyncio as aioredis
|
from redis import asyncio as aioredis
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def parse_redis_service_url(redis_url):
|
def parse_redis_service_url(redis_url):
|
||||||
|
|
@ -18,7 +19,9 @@ def parse_redis_service_url(redis_url):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_redis_connection(redis_url, redis_sentinels, decode_responses=True):
|
def get_redis_connection(
|
||||||
|
redis_url, redis_sentinels, decode_responses=True
|
||||||
|
) -> Optional[redis.Redis]:
|
||||||
if redis_sentinels:
|
if redis_sentinels:
|
||||||
redis_config = parse_redis_service_url(redis_url)
|
redis_config = parse_redis_service_url(redis_url)
|
||||||
sentinel = redis.sentinel.Sentinel(
|
sentinel = redis.sentinel.Sentinel(
|
||||||
|
|
@ -32,9 +35,11 @@ def get_redis_connection(redis_url, redis_sentinels, decode_responses=True):
|
||||||
|
|
||||||
# Get a master connection from Sentinel
|
# Get a master connection from Sentinel
|
||||||
return sentinel.master_for(redis_config["service"])
|
return sentinel.master_for(redis_config["service"])
|
||||||
else:
|
elif redis_url:
|
||||||
# Standard Redis connection
|
# Standard Redis connection
|
||||||
return redis.Redis.from_url(redis_url, decode_responses=decode_responses)
|
return redis.Redis.from_url(redis_url, decode_responses=decode_responses)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_sentinels_from_env(sentinel_hosts_env, sentinel_port_env):
|
def get_sentinels_from_env(sentinel_hosts_env, sentinel_port_env):
|
||||||
|
|
|
||||||
|
|
@ -125,3 +125,64 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response)
|
||||||
yield line
|
yield line
|
||||||
|
|
||||||
yield "data: [DONE]\n\n"
|
yield "data: [DONE]\n\n"
|
||||||
|
|
||||||
|
|
||||||
|
def convert_embedding_response_ollama_to_openai(response) -> dict:
|
||||||
|
"""
|
||||||
|
Convert the response from Ollama embeddings endpoint to the OpenAI-compatible format.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
response (dict): The response from the Ollama API,
|
||||||
|
e.g. {"embedding": [...], "model": "..."}
|
||||||
|
or {"embeddings": [{"embedding": [...], "index": 0}, ...], "model": "..."}
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Response adapted to OpenAI's embeddings API format.
|
||||||
|
e.g. {
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{"object": "embedding", "embedding": [...], "index": 0},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"model": "...",
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
# Ollama batch-style output
|
||||||
|
if isinstance(response, dict) and "embeddings" in response:
|
||||||
|
openai_data = []
|
||||||
|
for i, emb in enumerate(response["embeddings"]):
|
||||||
|
openai_data.append(
|
||||||
|
{
|
||||||
|
"object": "embedding",
|
||||||
|
"embedding": emb.get("embedding"),
|
||||||
|
"index": emb.get("index", i),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"object": "list",
|
||||||
|
"data": openai_data,
|
||||||
|
"model": response.get("model"),
|
||||||
|
}
|
||||||
|
# Ollama single output
|
||||||
|
elif isinstance(response, dict) and "embedding" in response:
|
||||||
|
return {
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"object": "embedding",
|
||||||
|
"embedding": response["embedding"],
|
||||||
|
"index": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"model": response.get("model"),
|
||||||
|
}
|
||||||
|
# Already OpenAI-compatible?
|
||||||
|
elif (
|
||||||
|
isinstance(response, dict)
|
||||||
|
and "data" in response
|
||||||
|
and isinstance(response["data"], list)
|
||||||
|
):
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Fallback: return as is if unrecognized
|
||||||
|
return response
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,24 @@ def title_generation_template(
|
||||||
return template
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
def follow_up_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(
|
def tags_generation_template(
|
||||||
template: str, messages: list[dict], user: Optional[dict] = None
|
template: str, messages: list[dict], user: Optional[dict] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
fastapi==0.115.7
|
fastapi==0.115.7
|
||||||
uvicorn[standard]==0.34.0
|
uvicorn[standard]==0.34.2
|
||||||
pydantic==2.10.6
|
pydantic==2.10.6
|
||||||
python-multipart==0.0.20
|
python-multipart==0.0.20
|
||||||
|
|
||||||
|
|
@ -76,13 +76,13 @@ pandas==2.2.3
|
||||||
openpyxl==3.1.5
|
openpyxl==3.1.5
|
||||||
pyxlsb==1.0.10
|
pyxlsb==1.0.10
|
||||||
xlrd==2.0.1
|
xlrd==2.0.1
|
||||||
validators==0.34.0
|
validators==0.35.0
|
||||||
psutil
|
psutil
|
||||||
sentencepiece
|
sentencepiece
|
||||||
soundfile==0.13.1
|
soundfile==0.13.1
|
||||||
azure-ai-documentintelligence==1.0.0
|
azure-ai-documentintelligence==1.0.2
|
||||||
|
|
||||||
pillow==11.1.0
|
pillow==11.2.1
|
||||||
opencv-python-headless==4.11.0.86
|
opencv-python-headless==4.11.0.86
|
||||||
rapidocr-onnxruntime==1.4.4
|
rapidocr-onnxruntime==1.4.4
|
||||||
rank-bm25==0.2.2
|
rank-bm25==0.2.2
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@ if [[ "${WEB_LOADER_ENGINE,,}" == "playwright" ]]; then
|
||||||
python -c "import nltk; nltk.download('punkt_tab')"
|
python -c "import nltk; nltk.download('punkt_tab')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
KEY_FILE=.webui_secret_key
|
if [ -n "${WEBUI_SECRET_KEY_FILE}" ]; then
|
||||||
|
KEY_FILE="${WEBUI_SECRET_KEY_FILE}"
|
||||||
|
else
|
||||||
|
KEY_FILE=".webui_secret_key"
|
||||||
|
fi
|
||||||
|
|
||||||
PORT="${PORT:-8080}"
|
PORT="${PORT:-8080}"
|
||||||
HOST="${HOST:-0.0.0.0}"
|
HOST="${HOST:-0.0.0.0}"
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ IF /I "%WEB_LOADER_ENGINE%" == "playwright" (
|
||||||
)
|
)
|
||||||
|
|
||||||
SET "KEY_FILE=.webui_secret_key"
|
SET "KEY_FILE=.webui_secret_key"
|
||||||
|
IF NOT "%WEBUI_SECRET_KEY_FILE%" == "" (
|
||||||
|
SET "KEY_FILE=%WEBUI_SECRET_KEY_FILE%"
|
||||||
|
)
|
||||||
|
|
||||||
IF "%PORT%"=="" SET PORT=8080
|
IF "%PORT%"=="" SET PORT=8080
|
||||||
IF "%HOST%"=="" SET HOST=0.0.0.0
|
IF "%HOST%"=="" SET HOST=0.0.0.0
|
||||||
SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
|
SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
|
||||||
|
|
|
||||||
8
package-lock.json
generated
8
package-lock.json
generated
|
|
@ -51,7 +51,7 @@
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"js-sha256": "^0.10.1",
|
"js-sha256": "^0.10.1",
|
||||||
"jspdf": "^3.0.0",
|
"jspdf": "^3.0.0",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.22",
|
||||||
"kokoro-js": "^1.1.1",
|
"kokoro-js": "^1.1.1",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
|
|
@ -7930,9 +7930,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/katex": {
|
"node_modules/katex": {
|
||||||
"version": "0.16.21",
|
"version": "0.16.22",
|
||||||
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz",
|
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz",
|
||||||
"integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==",
|
"integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/katex",
|
"https://opencollective.com/katex",
|
||||||
"https://github.com/sponsors/katex"
|
"https://github.com/sponsors/katex"
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"idb": "^7.1.1",
|
"idb": "^7.1.1",
|
||||||
"js-sha256": "^0.10.1",
|
"js-sha256": "^0.10.1",
|
||||||
"jspdf": "^3.0.0",
|
"jspdf": "^3.0.0",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.22",
|
||||||
"kokoro-js": "^1.1.1",
|
"kokoro-js": "^1.1.1",
|
||||||
"marked": "^9.1.0",
|
"marked": "^9.1.0",
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ const packages = [
|
||||||
'sympy',
|
'sympy',
|
||||||
'tiktoken',
|
'tiktoken',
|
||||||
'seaborn',
|
'seaborn',
|
||||||
'pytz'
|
'pytz',
|
||||||
|
'black'
|
||||||
];
|
];
|
||||||
|
|
||||||
import { loadPyodide } from 'pyodide';
|
import { loadPyodide } from 'pyodide';
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ code {
|
||||||
font-family: 'InstrumentSerif', sans-serif;
|
font-family: 'InstrumentSerif', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.marked a {
|
||||||
|
@apply underline;
|
||||||
|
}
|
||||||
|
|
||||||
math {
|
math {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ export const userSignOut = async () => {
|
||||||
})
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
if (!res.ok) throw await res.json();
|
if (!res.ok) throw await res.json();
|
||||||
return res;
|
return res.json();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
||||||
|
|
@ -612,6 +612,78 @@ export const generateTitle = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const generateFollowUps = async (
|
||||||
|
token: string = '',
|
||||||
|
model: string,
|
||||||
|
messages: string,
|
||||||
|
chat_id?: string
|
||||||
|
) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_BASE_URL}/api/v1/tasks/follow_ups/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.error(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 "follow_ups" key, return the follow_ups array; otherwise, return an empty array
|
||||||
|
if (parsed && parsed.follow_ups) {
|
||||||
|
return Array.isArray(parsed.follow_ups) ? parsed.follow_ups : [];
|
||||||
|
} 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 generateTags = async (
|
export const generateTags = async (
|
||||||
token: string = '',
|
token: string = '',
|
||||||
model: string,
|
model: string,
|
||||||
|
|
|
||||||
|
|
@ -393,6 +393,7 @@ export const updateUserById = async (token: string, userId: string, user: UserUp
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
profile_image_url: user.profile_image_url,
|
profile_image_url: user.profile_image_url,
|
||||||
|
role: user.role,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
password: user.password !== '' ? user.password : undefined
|
password: user.password !== '' ? user.password : undefined
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
const verifyOllamaHandler = async () => {
|
const verifyOllamaHandler = async () => {
|
||||||
|
// remove trailing slash from url
|
||||||
|
url = url.replace(/\/$/, '');
|
||||||
|
|
||||||
const res = await verifyOllamaConnection(localStorage.token, {
|
const res = await verifyOllamaConnection(localStorage.token, {
|
||||||
url,
|
url,
|
||||||
key
|
key
|
||||||
|
|
@ -62,6 +65,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const verifyOpenAIHandler = async () => {
|
const verifyOpenAIHandler = async () => {
|
||||||
|
// remove trailing slash from url
|
||||||
|
url = url.replace(/\/$/, '');
|
||||||
|
|
||||||
const res = await verifyOpenAIConnection(
|
const res = await verifyOpenAIConnection(
|
||||||
localStorage.token,
|
localStorage.token,
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, tick, onMount } from 'svelte';
|
import { getContext, tick, onMount } from 'svelte';
|
||||||
import { toast } from 'svelte-sonner';
|
import { goto } from '$app/navigation';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
import Leaderboard from './Evaluations/Leaderboard.svelte';
|
import Leaderboard from './Evaluations/Leaderboard.svelte';
|
||||||
import Feedbacks from './Evaluations/Feedbacks.svelte';
|
import Feedbacks from './Evaluations/Feedbacks.svelte';
|
||||||
|
|
||||||
|
|
@ -8,7 +10,24 @@
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
let selectedTab = 'leaderboard';
|
let selectedTab;
|
||||||
|
$: {
|
||||||
|
const pathParts = $page.url.pathname.split('/');
|
||||||
|
const tabFromPath = pathParts[pathParts.length - 1];
|
||||||
|
selectedTab = ['leaderboard', 'feedbacks'].includes(tabFromPath) ? tabFromPath : 'leaderboard';
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (selectedTab) {
|
||||||
|
// scroll to selectedTab
|
||||||
|
scrollToTab(selectedTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToTab = (tabId) => {
|
||||||
|
const tabElement = document.getElementById(tabId);
|
||||||
|
if (tabElement) {
|
||||||
|
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
let feedbacks = [];
|
let feedbacks = [];
|
||||||
|
|
@ -27,6 +46,9 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll to the selected tab on mount
|
||||||
|
scrollToTab(selectedTab);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -37,12 +59,13 @@
|
||||||
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"
|
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
|
<button
|
||||||
|
id="leaderboard"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||||
'leaderboard'
|
'leaderboard'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'leaderboard';
|
goto('/admin/evaluations/leaderboard');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -63,12 +86,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="feedbacks"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||||
'feedbacks'
|
'feedbacks'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'feedbacks';
|
goto('/admin/evaluations/feedbacks');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, tick, onMount } from 'svelte';
|
import { getContext, tick, onMount } from 'svelte';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
import { config } from '$lib/stores';
|
import { config } from '$lib/stores';
|
||||||
|
|
@ -26,6 +28,41 @@
|
||||||
|
|
||||||
let selectedTab = 'general';
|
let selectedTab = 'general';
|
||||||
|
|
||||||
|
// Get current tab from URL pathname, default to 'general'
|
||||||
|
$: {
|
||||||
|
const pathParts = $page.url.pathname.split('/');
|
||||||
|
const tabFromPath = pathParts[pathParts.length - 1];
|
||||||
|
selectedTab = [
|
||||||
|
'general',
|
||||||
|
'connections',
|
||||||
|
'models',
|
||||||
|
'evaluations',
|
||||||
|
'tools',
|
||||||
|
'documents',
|
||||||
|
'web',
|
||||||
|
'code-execution',
|
||||||
|
'interface',
|
||||||
|
'audio',
|
||||||
|
'images',
|
||||||
|
'pipelines',
|
||||||
|
'db'
|
||||||
|
].includes(tabFromPath)
|
||||||
|
? tabFromPath
|
||||||
|
: 'general';
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (selectedTab) {
|
||||||
|
// scroll to selectedTab
|
||||||
|
scrollToTab(selectedTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToTab = (tabId) => {
|
||||||
|
const tabElement = document.getElementById(tabId);
|
||||||
|
if (tabElement) {
|
||||||
|
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const containerElement = document.getElementById('admin-settings-tabs-container');
|
const containerElement = document.getElementById('admin-settings-tabs-container');
|
||||||
|
|
||||||
|
|
@ -37,6 +74,9 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll to the selected tab on mount
|
||||||
|
scrollToTab(selectedTab);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -46,12 +86,13 @@
|
||||||
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"
|
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
|
<button
|
||||||
|
id="general"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
|
||||||
'general'
|
'general'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'general';
|
goto('/admin/settings/general');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -72,12 +113,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="connections"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'connections'
|
'connections'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'connections';
|
goto('/admin/settings/connections');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -96,12 +138,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="models"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'models'
|
'models'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'models';
|
goto('/admin/settings/models');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -122,12 +165,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="evaluations"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'evaluations'
|
'evaluations'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'evaluations';
|
goto('/admin/settings/evaluations');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -137,12 +181,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="tools"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'tools'
|
'tools'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'tools';
|
goto('/admin/settings/tools');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -163,12 +208,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="documents"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'documents'
|
'documents'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'documents';
|
goto('/admin/settings/documents');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -193,12 +239,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="web"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'web'
|
'web'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'web';
|
goto('/admin/settings/web');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -217,12 +264,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="code-execution"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'code-execution'
|
'code-execution'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'code-execution';
|
goto('/admin/settings/code-execution');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -243,12 +291,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="interface"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'interface'
|
'interface'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'interface';
|
goto('/admin/settings/interface');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -269,12 +318,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="audio"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'audio'
|
'audio'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'audio';
|
goto('/admin/settings/audio');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -296,12 +346,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="images"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'images'
|
'images'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'images';
|
goto('/admin/settings/images');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -322,12 +373,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="pipelines"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'pipelines'
|
'pipelines'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'pipelines';
|
goto('/admin/settings/pipelines');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -352,12 +404,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="db"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||||
'db'
|
'db'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'db';
|
goto('/admin/settings/db');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
|
||||||
|
|
@ -194,17 +194,20 @@
|
||||||
await embeddingModelUpdateHandler();
|
await embeddingModelUpdateHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
RAGConfig.ALLOWED_FILE_EXTENSIONS = (RAGConfig?.ALLOWED_FILE_EXTENSIONS ?? '')
|
const res = await updateRAGConfig(localStorage.token, {
|
||||||
.split(',')
|
...RAGConfig,
|
||||||
.map((ext) => ext.trim())
|
ALLOWED_FILE_EXTENSIONS: RAGConfig.ALLOWED_FILE_EXTENSIONS.split(',')
|
||||||
.filter((ext) => ext !== '');
|
.map((ext) => ext.trim())
|
||||||
|
.filter((ext) => ext !== ''),
|
||||||
RAGConfig.DATALAB_MARKER_LANGS = RAGConfig.DATALAB_MARKER_LANGS.split(',')
|
DATALAB_MARKER_LANGS: RAGConfig.DATALAB_MARKER_LANGS.split(',')
|
||||||
.map((code) => code.trim())
|
.map((code) => code.trim())
|
||||||
.filter((code) => code !== '')
|
.filter((code) => code !== '')
|
||||||
.join(', ');
|
.join(', '),
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_LOCAL: JSON.parse(
|
||||||
const res = await updateRAGConfig(localStorage.token, RAGConfig);
|
RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL || '{}'
|
||||||
|
),
|
||||||
|
DOCLING_PICTURE_DESCRIPTION_API: JSON.parse(RAGConfig.DOCLING_PICTURE_DESCRIPTION_API || '{}')
|
||||||
|
});
|
||||||
dispatch('save');
|
dispatch('save');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -232,6 +235,18 @@
|
||||||
|
|
||||||
const config = await getRAGConfig(localStorage.token);
|
const config = await getRAGConfig(localStorage.token);
|
||||||
config.ALLOWED_FILE_EXTENSIONS = (config?.ALLOWED_FILE_EXTENSIONS ?? []).join(', ');
|
config.ALLOWED_FILE_EXTENSIONS = (config?.ALLOWED_FILE_EXTENSIONS ?? []).join(', ');
|
||||||
|
|
||||||
|
config.DOCLING_PICTURE_DESCRIPTION_LOCAL = JSON.stringify(
|
||||||
|
config.DOCLING_PICTURE_DESCRIPTION_LOCAL ?? {},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
config.DOCLING_PICTURE_DESCRIPTION_API = JSON.stringify(
|
||||||
|
config.DOCLING_PICTURE_DESCRIPTION_API ?? {},
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
RAGConfig = config;
|
RAGConfig = config;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -510,6 +525,71 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{#if RAGConfig.DOCLING_DO_PICTURE_DESCRIPTION}
|
||||||
|
<div class="flex justify-between w-full mt-2">
|
||||||
|
<div class="self-center text-xs font-medium">
|
||||||
|
<Tooltip content={''} placement="top-start">
|
||||||
|
{$i18n.t('Picture Description Mode')}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<select
|
||||||
|
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
|
||||||
|
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE}
|
||||||
|
>
|
||||||
|
<option value="">{$i18n.t('Default')}</option>
|
||||||
|
<option value="local">{$i18n.t('Local')}</option>
|
||||||
|
<option value="api">{$i18n.t('API')}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'local'}
|
||||||
|
<div class="flex flex-col gap-2 mt-2">
|
||||||
|
<div class=" flex flex-col w-full justify-between">
|
||||||
|
<div class=" mb-1 text-xs font-medium">
|
||||||
|
{$i18n.t('Picture Description Local Config')}
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full items-center relative">
|
||||||
|
<Tooltip
|
||||||
|
content={$i18n.t(
|
||||||
|
'Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.'
|
||||||
|
)}
|
||||||
|
placement="top-start"
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL}
|
||||||
|
placeholder={$i18n.t('Enter Config in JSON format')}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'api'}
|
||||||
|
<div class="flex flex-col gap-2 mt-2">
|
||||||
|
<div class=" flex flex-col w-full justify-between">
|
||||||
|
<div class=" mb-1 text-xs font-medium">
|
||||||
|
{$i18n.t('Picture Description API Config')}
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full items-center relative">
|
||||||
|
<Tooltip
|
||||||
|
content={$i18n.t(
|
||||||
|
'API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.'
|
||||||
|
)}
|
||||||
|
placement="top-start"
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<Textarea
|
||||||
|
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_API}
|
||||||
|
placeholder={$i18n.t('Enter Config in JSON format')}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
|
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
|
||||||
<div class="my-0.5 flex gap-2 pr-2">
|
<div class="my-0.5 flex gap-2 pr-2">
|
||||||
<input
|
<input
|
||||||
|
|
@ -830,12 +910,7 @@
|
||||||
<div class=" mb-2.5 flex w-full justify-between">
|
<div class=" mb-2.5 flex w-full justify-between">
|
||||||
<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
|
<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
|
||||||
<div class="flex items-center relative">
|
<div class="flex items-center relative">
|
||||||
<Switch
|
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} />
|
||||||
bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH}
|
|
||||||
on:change={() => {
|
|
||||||
submitHandler();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@
|
||||||
const updateHandler = async () => {
|
const updateHandler = async () => {
|
||||||
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
|
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
|
||||||
const res = await updateAdminConfig(localStorage.token, adminConfig);
|
const res = await updateAdminConfig(localStorage.token, adminConfig);
|
||||||
|
await updateLdapConfig(localStorage.token, ENABLE_LDAP);
|
||||||
await updateLdapServerHandler();
|
await updateLdapServerHandler();
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
@ -311,7 +312,6 @@
|
||||||
{$i18n.t('Pending User Overlay Title')}
|
{$i18n.t('Pending User Overlay Title')}
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
rows={2}
|
|
||||||
placeholder={$i18n.t(
|
placeholder={$i18n.t(
|
||||||
'Enter a title for the pending user info overlay. Leave empty for default.'
|
'Enter a title for the pending user info overlay. Leave empty for default.'
|
||||||
)}
|
)}
|
||||||
|
|
@ -401,12 +401,7 @@
|
||||||
<div class=" font-medium">{$i18n.t('LDAP')}</div>
|
<div class=" font-medium">{$i18n.t('LDAP')}</div>
|
||||||
|
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<Switch
|
<Switch bind:state={ENABLE_LDAP} />
|
||||||
bind:state={ENABLE_LDAP}
|
|
||||||
on:change={async () => {
|
|
||||||
updateLdapConfig(localStorage.token, ENABLE_LDAP);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@
|
||||||
TASK_MODEL_EXTERNAL: '',
|
TASK_MODEL_EXTERNAL: '',
|
||||||
ENABLE_TITLE_GENERATION: true,
|
ENABLE_TITLE_GENERATION: true,
|
||||||
TITLE_GENERATION_PROMPT_TEMPLATE: '',
|
TITLE_GENERATION_PROMPT_TEMPLATE: '',
|
||||||
|
ENABLE_FOLLOW_UP_GENERATION: true,
|
||||||
|
FOLLOW_UP_GENERATION_PROMPT_TEMPLATE: '',
|
||||||
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE: '',
|
IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE: '',
|
||||||
ENABLE_AUTOCOMPLETE_GENERATION: true,
|
ENABLE_AUTOCOMPLETE_GENERATION: true,
|
||||||
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: -1,
|
AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH: -1,
|
||||||
|
|
@ -235,6 +237,32 @@
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<div class="mb-2.5 flex w-full items-center justify-between">
|
||||||
|
<div class=" self-center text-xs font-medium">
|
||||||
|
{$i18n.t('Follow Up Generation')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Switch bind:state={taskConfig.ENABLE_FOLLOW_UP_GENERATION} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if taskConfig.ENABLE_FOLLOW_UP_GENERATION}
|
||||||
|
<div class="mb-2.5">
|
||||||
|
<div class=" mb-1 text-xs font-medium">{$i18n.t('Follow Up 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.FOLLOW_UP_GENERATION_PROMPT_TEMPLATE}
|
||||||
|
placeholder={$i18n.t(
|
||||||
|
'Leave empty to use the default prompt, or enter a custom prompt'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="mb-2.5 flex w-full items-center justify-between">
|
<div class="mb-2.5 flex w-full items-center justify-between">
|
||||||
<div class=" self-center text-xs font-medium">
|
<div class=" self-center text-xs font-medium">
|
||||||
{$i18n.t('Tags Generation')}
|
{$i18n.t('Tags Generation')}
|
||||||
|
|
|
||||||
|
|
@ -446,15 +446,54 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if webConfig.WEB_SEARCH_ENGINE === 'perplexity'}
|
{:else if webConfig.WEB_SEARCH_ENGINE === 'perplexity'}
|
||||||
<div>
|
<div class="mb-2.5 flex w-full flex-col">
|
||||||
<div class=" self-center text-xs font-medium mb-1">
|
<div>
|
||||||
{$i18n.t('Perplexity API Key')}
|
<div class=" self-center text-xs font-medium mb-1">
|
||||||
</div>
|
{$i18n.t('Perplexity API Key')}
|
||||||
|
</div>
|
||||||
|
|
||||||
<SensitiveInput
|
<SensitiveInput
|
||||||
placeholder={$i18n.t('Enter Perplexity API Key')}
|
placeholder={$i18n.t('Enter Perplexity API Key')}
|
||||||
bind:value={webConfig.PERPLEXITY_API_KEY}
|
bind:value={webConfig.PERPLEXITY_API_KEY}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2.5 flex w-full flex-col">
|
||||||
|
<div>
|
||||||
|
<div class="self-center text-xs font-medium mb-1">
|
||||||
|
{$i18n.t('Perplexity Model')}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
list="perplexity-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-hidden"
|
||||||
|
bind:value={webConfig.PERPLEXITY_MODEL}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<datalist id="perplexity-model-list">
|
||||||
|
<option value="sonar">Sonar</option>
|
||||||
|
<option value="sonar-pro">Sonar Pro</option>
|
||||||
|
<option value="sonar-reasoning">Sonar Reasoning</option>
|
||||||
|
<option value="sonar-reasoning-pro">Sonar Reasoning Pro</option>
|
||||||
|
<option value="sonar-deep-research">Sonar Deep Research</option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2.5 flex w-full flex-col">
|
||||||
|
<div>
|
||||||
|
<div class=" self-center text-xs font-medium mb-1">
|
||||||
|
{$i18n.t('Perplexity Search Context Usage')}
|
||||||
|
</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-hidden"
|
||||||
|
bind:value={webConfig.PERPLEXITY_SEARCH_CONTEXT_USAGE}
|
||||||
|
>
|
||||||
|
<option value="low">Low</option>
|
||||||
|
<option value="medium">Medium</option>
|
||||||
|
<option value="high">High</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else if webConfig.WEB_SEARCH_ENGINE === 'sougou'}
|
{:else if webConfig.WEB_SEARCH_ENGINE === 'sougou'}
|
||||||
<div class="mb-2.5 flex w-full flex-col">
|
<div class="mb-2.5 flex w-full flex-col">
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,32 @@
|
||||||
|
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { user } from '$lib/stores';
|
import { user } from '$lib/stores';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
import UserList from './Users/UserList.svelte';
|
import UserList from './Users/UserList.svelte';
|
||||||
import Groups from './Users/Groups.svelte';
|
import Groups from './Users/Groups.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
let selectedTab = 'overview';
|
let selectedTab;
|
||||||
|
$: {
|
||||||
|
const pathParts = $page.url.pathname.split('/');
|
||||||
|
const tabFromPath = pathParts[pathParts.length - 1];
|
||||||
|
selectedTab = ['overview', 'groups'].includes(tabFromPath) ? tabFromPath : 'overview';
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (selectedTab) {
|
||||||
|
// scroll to selectedTab
|
||||||
|
scrollToTab(selectedTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToTab = (tabId) => {
|
||||||
|
const tabElement = document.getElementById(tabId);
|
||||||
|
if (tabElement) {
|
||||||
|
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
|
@ -30,6 +49,9 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll to the selected tab on mount
|
||||||
|
scrollToTab(selectedTab);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -39,12 +61,13 @@
|
||||||
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"
|
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
|
<button
|
||||||
|
id="overview"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||||
'overview'
|
'overview'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'overview';
|
goto('/admin/users/overview');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
@ -63,12 +86,13 @@
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
id="groups"
|
||||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||||
'groups'
|
'groups'
|
||||||
? ''
|
? ''
|
||||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedTab = 'groups';
|
goto('/admin/users/groups');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class=" self-center mr-2">
|
<div class=" self-center mr-2">
|
||||||
|
|
|
||||||
|
|
@ -52,27 +52,6 @@
|
||||||
|
|
||||||
let showUserChatsModal = false;
|
let showUserChatsModal = false;
|
||||||
let showEditUserModal = false;
|
let showEditUserModal = false;
|
||||||
let showUpdateRoleModal = false;
|
|
||||||
|
|
||||||
const onUpdateRole = (user) => {
|
|
||||||
if (user.role === 'user') {
|
|
||||||
updateRoleHandler(user.id, 'admin');
|
|
||||||
} else if (user.role === 'pending') {
|
|
||||||
updateRoleHandler(user.id, 'user');
|
|
||||||
} else {
|
|
||||||
updateRoleHandler(user.id, 'pending');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const updateRoleHandler = async (id, role) => {
|
|
||||||
const res = await updateUserRole(localStorage.token, id, role).catch((error) => {
|
|
||||||
toast.error(`${error}`);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
getUserList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteUserHandler = async (id) => {
|
const deleteUserHandler = async (id) => {
|
||||||
const res = await deleteUserById(localStorage.token, id).catch((error) => {
|
const res = await deleteUserById(localStorage.token, id).catch((error) => {
|
||||||
|
|
@ -133,21 +112,6 @@
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RoleUpdateConfirmDialog
|
|
||||||
bind:show={showUpdateRoleModal}
|
|
||||||
on:confirm={() => {
|
|
||||||
onUpdateRole(selectedUser);
|
|
||||||
}}
|
|
||||||
message={$i18n.t(`Are you sure you want to update this user\'s role to **{{ROLE}}**?`, {
|
|
||||||
ROLE:
|
|
||||||
selectedUser?.role === 'user'
|
|
||||||
? 'admin'
|
|
||||||
: selectedUser?.role === 'pending'
|
|
||||||
? 'user'
|
|
||||||
: 'pending'
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{#key selectedUser}
|
{#key selectedUser}
|
||||||
<EditUserModal
|
<EditUserModal
|
||||||
bind:show={showEditUserModal}
|
bind:show={showEditUserModal}
|
||||||
|
|
@ -415,7 +379,7 @@
|
||||||
class=" translate-y-0.5"
|
class=" translate-y-0.5"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
selectedUser = user;
|
selectedUser = user;
|
||||||
showUpdateRoleModal = true;
|
showEditUserModal = !showEditUserModal;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Badge
|
<Badge
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
let _user = {
|
let _user = {
|
||||||
profile_image_url: '',
|
profile_image_url: '',
|
||||||
|
role: 'pending',
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: ''
|
||||||
|
|
@ -95,6 +96,23 @@
|
||||||
|
|
||||||
<div class=" px-5 pt-3 pb-5">
|
<div class=" px-5 pt-3 pb-5">
|
||||||
<div class=" flex flex-col space-y-1.5">
|
<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('Role')}</div>
|
||||||
|
|
||||||
|
<div class="flex-1">
|
||||||
|
<select
|
||||||
|
class="w-full rounded-sm text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-hidden"
|
||||||
|
bind:value={_user.role}
|
||||||
|
disabled={_user.id == sessionUser.id}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option value="admin">{$i18n.t('Admin')}</option>
|
||||||
|
<option value="user">{$i18n.t('User')}</option>
|
||||||
|
<option value="pending">{$i18n.t('Pending')}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
|
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,6 +239,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const showMessage = async (message) => {
|
const showMessage = async (message) => {
|
||||||
|
await tick();
|
||||||
|
|
||||||
const _chatId = JSON.parse(JSON.stringify($chatId));
|
const _chatId = JSON.parse(JSON.stringify($chatId));
|
||||||
let _messageId = JSON.parse(JSON.stringify(message.id));
|
let _messageId = JSON.parse(JSON.stringify(message.id));
|
||||||
|
|
||||||
|
|
@ -298,6 +300,12 @@
|
||||||
message.content = data.content;
|
message.content = data.content;
|
||||||
} else if (type === 'chat:message:files' || type === 'files') {
|
} else if (type === 'chat:message:files' || type === 'files') {
|
||||||
message.files = data.files;
|
message.files = data.files;
|
||||||
|
} else if (type === 'chat:message:follow_ups') {
|
||||||
|
message.followUps = data.follow_ups;
|
||||||
|
|
||||||
|
if (autoScroll) {
|
||||||
|
scrollToBottom('smooth');
|
||||||
|
}
|
||||||
} else if (type === 'chat:title') {
|
} else if (type === 'chat:title') {
|
||||||
chatTitle.set(data);
|
chatTitle.set(data);
|
||||||
currentChatPage.set(1);
|
currentChatPage.set(1);
|
||||||
|
|
@ -774,6 +782,7 @@
|
||||||
|
|
||||||
autoScroll = true;
|
autoScroll = true;
|
||||||
|
|
||||||
|
resetInput();
|
||||||
await chatId.set('');
|
await chatId.set('');
|
||||||
await chatTitle.set('');
|
await chatTitle.set('');
|
||||||
|
|
||||||
|
|
@ -911,10 +920,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollToBottom = async () => {
|
const scrollToBottom = async (behavior = 'auto') => {
|
||||||
await tick();
|
await tick();
|
||||||
if (messagesContainerElement) {
|
if (messagesContainerElement) {
|
||||||
messagesContainerElement.scrollTop = messagesContainerElement.scrollHeight;
|
messagesContainerElement.scrollTo({
|
||||||
|
top: messagesContainerElement.scrollHeight,
|
||||||
|
behavior
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
|
const chatCompletedHandler = async (chatId, modelId, responseMessageId, messages) => {
|
||||||
|
|
@ -1676,19 +1688,20 @@
|
||||||
chat_id: $chatId,
|
chat_id: $chatId,
|
||||||
id: responseMessageId,
|
id: responseMessageId,
|
||||||
|
|
||||||
...(!$temporaryChatEnabled &&
|
background_tasks: {
|
||||||
(messages.length == 1 ||
|
...(!$temporaryChatEnabled &&
|
||||||
(messages.length == 2 &&
|
(messages.length == 1 ||
|
||||||
messages.at(0)?.role === 'system' &&
|
(messages.length == 2 &&
|
||||||
messages.at(1)?.role === 'user')) &&
|
messages.at(0)?.role === 'system' &&
|
||||||
(selectedModels[0] === model.id || atSelectedModel !== undefined)
|
messages.at(1)?.role === 'user')) &&
|
||||||
? {
|
(selectedModels[0] === model.id || atSelectedModel !== undefined)
|
||||||
background_tasks: {
|
? {
|
||||||
title_generation: $settings?.title?.auto ?? true,
|
title_generation: $settings?.title?.auto ?? true,
|
||||||
tags_generation: $settings?.autoTags ?? true
|
tags_generation: $settings?.autoTags ?? true
|
||||||
}
|
}
|
||||||
}
|
: {}),
|
||||||
: {}),
|
follow_up_generation: $settings?.autoFollowUps ?? true
|
||||||
|
},
|
||||||
|
|
||||||
...(stream && (model.info?.meta?.capabilities?.usage ?? false)
|
...(stream && (model.info?.meta?.capabilities?.usage ?? false)
|
||||||
? {
|
? {
|
||||||
|
|
@ -2072,7 +2085,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class=" pb-[1rem]">
|
<div class=" pb-2">
|
||||||
<MessageInput
|
<MessageInput
|
||||||
{history}
|
{history}
|
||||||
{taskIds}
|
{taskIds}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if selectedId}
|
{#if selectedId}
|
||||||
<hr class="dark:border-gray-800 my-1 w-full" />
|
<hr class="border-gray-50 dark:border-gray-800 my-1 w-full" />
|
||||||
|
|
||||||
<div class="my-2 text-xs">
|
<div class="my-2 text-xs">
|
||||||
{#if !loading}
|
{#if !loading}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import DOMPurify from 'dompurify';
|
||||||
|
import { marked } from 'marked';
|
||||||
|
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { createPicker, getAuthToken } from '$lib/utils/google-drive-picker';
|
import { createPicker, getAuthToken } from '$lib/utils/google-drive-picker';
|
||||||
import { pickAndDownloadFile } from '$lib/utils/onedrive-file-picker';
|
import { pickAndDownloadFile } from '$lib/utils/onedrive-file-picker';
|
||||||
|
|
@ -595,7 +599,7 @@
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<form
|
<form
|
||||||
class="w-full flex gap-1.5"
|
class="w-full flex flex-col gap-1.5"
|
||||||
on:submit|preventDefault={() => {
|
on:submit|preventDefault={() => {
|
||||||
// check if selectedModels support image input
|
// check if selectedModels support image input
|
||||||
dispatch('submit', prompt);
|
dispatch('submit', prompt);
|
||||||
|
|
@ -1520,6 +1524,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if $config?.license_metadata?.input_footer}
|
||||||
|
<div class=" text-xs text-gray-500 text-center line-clamp-1 marked">
|
||||||
|
{@html DOMPurify.sanitize(marked($config?.license_metadata?.input_footer))}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="mb-1" />
|
||||||
|
{/if}
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -205,8 +205,10 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mineTypes = ['audio/webm; codecs=opus', 'audio/mp4'];
|
||||||
|
|
||||||
mediaRecorder = new MediaRecorder(stream, {
|
mediaRecorder = new MediaRecorder(stream, {
|
||||||
mimeType: 'audio/webm; codecs=opus'
|
mimeType: mineTypes.find((type) => MediaRecorder.isTypeSupported(type))
|
||||||
});
|
});
|
||||||
|
|
||||||
mediaRecorder.onstart = () => {
|
mediaRecorder.onstart = () => {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
<!-- svelte-ignore a11y-media-has-caption -->
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
<video
|
<video
|
||||||
class="w-full my-2"
|
class="w-full my-2"
|
||||||
src={videoSrc}
|
src={videoSrc.replaceAll('&', '&')}
|
||||||
title="Video player"
|
title="Video player"
|
||||||
frameborder="0"
|
frameborder="0"
|
||||||
referrerpolicy="strict-origin-when-cross-origin"
|
referrerpolicy="strict-origin-when-cross-origin"
|
||||||
|
|
@ -38,6 +38,20 @@
|
||||||
{:else}
|
{:else}
|
||||||
{token.text}
|
{token.text}
|
||||||
{/if}
|
{/if}
|
||||||
|
{:else if html && html.includes('<audio')}
|
||||||
|
{@const audio = html.match(/<audio[^>]*>([\s\S]*?)<\/audio>/)}
|
||||||
|
{@const audioSrc = audio && audio[1]}
|
||||||
|
{#if audioSrc}
|
||||||
|
<!-- svelte-ignore a11y-media-has-caption -->
|
||||||
|
<audio
|
||||||
|
class="w-full my-2"
|
||||||
|
src={audioSrc.replaceAll('&', '&')}
|
||||||
|
title="Audio player"
|
||||||
|
controls
|
||||||
|
></audio>
|
||||||
|
{:else}
|
||||||
|
{token.text}
|
||||||
|
{/if}
|
||||||
{:else if token.text && token.text.match(/<iframe\s+[^>]*src="https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})(?:\?[^"]*)?"[^>]*><\/iframe>/)}
|
{:else if token.text && token.text.match(/<iframe\s+[^>]*src="https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})(?:\?[^"]*)?"[^>]*><\/iframe>/)}
|
||||||
{@const match = token.text.match(
|
{@const match = token.text.match(
|
||||||
/<iframe\s+[^>]*src="https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})(?:\?[^"]*)?"[^>]*><\/iframe>/
|
/<iframe\s+[^>]*src="https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})(?:\?[^"]*)?"[^>]*><\/iframe>/
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
src.startsWith('/')
|
src.startsWith('/')
|
||||||
? src
|
? src
|
||||||
: `/user.png`}
|
: `/user.png`}
|
||||||
class=" {className} object-cover rounded-full -translate-y-[1px]"
|
class=" {className} object-cover rounded-full"
|
||||||
alt="profile"
|
alt="profile"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full flex justify-center">
|
<div class="w-full flex justify-center">
|
||||||
<div class=" relative w-fit">
|
<div class=" relative w-fit overflow-x-auto scrollbar-none">
|
||||||
<div class="mt-1.5 w-fit flex gap-1 pb-5">
|
<div class="mt-1.5 w-fit flex gap-1 pb-2">
|
||||||
<!-- 1-10 scale -->
|
<!-- 1-10 scale -->
|
||||||
{#each Array.from({ length: 10 }).map((_, i) => i + 1) as rating}
|
{#each Array.from({ length: 10 }).map((_, i) => i + 1) as rating}
|
||||||
<button
|
<button
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="absolute bottom-0 left-0 right-0 flex justify-between text-xs">
|
<div class="sticky top-0 bottom-0 left-0 right-0 flex justify-between text-xs">
|
||||||
<div>
|
<div>
|
||||||
1 - {$i18n.t('Awful')}
|
1 - {$i18n.t('Awful')}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,9 @@
|
||||||
import ContentRenderer from './ContentRenderer.svelte';
|
import ContentRenderer from './ContentRenderer.svelte';
|
||||||
import { KokoroWorker } from '$lib/workers/KokoroWorker';
|
import { KokoroWorker } from '$lib/workers/KokoroWorker';
|
||||||
import FileItem from '$lib/components/common/FileItem.svelte';
|
import FileItem from '$lib/components/common/FileItem.svelte';
|
||||||
|
import FollowUps from './ResponseMessage/FollowUps.svelte';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { flyAndScale } from '$lib/utils/transitions';
|
||||||
|
|
||||||
interface MessageType {
|
interface MessageType {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -606,7 +609,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-auto w-0 pl-1 relative">
|
<div class="flex-auto w-0 pl-1 relative -translate-y-0.5">
|
||||||
<Name>
|
<Name>
|
||||||
<Tooltip content={model?.name ?? message.model} placement="top-start">
|
<Tooltip content={model?.name ?? message.model} placement="top-start">
|
||||||
<span class="line-clamp-1 text-black dark:text-white">
|
<span class="line-clamp-1 text-black dark:text-white">
|
||||||
|
|
@ -866,12 +869,14 @@
|
||||||
{#if siblings.length > 1}
|
{#if siblings.length > 1}
|
||||||
<div class="flex self-center min-w-fit" dir="ltr">
|
<div class="flex self-center min-w-fit" dir="ltr">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Previous message')}
|
||||||
class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
|
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={() => {
|
on:click={() => {
|
||||||
showPreviousMessage(message);
|
showPreviousMessage(message);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -937,10 +942,12 @@
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
showNextMessage(message);
|
showNextMessage(message);
|
||||||
}}
|
}}
|
||||||
|
aria-label={$i18n.t('Next message')}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
aria-hidden="true"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2.5"
|
stroke-width="2.5"
|
||||||
|
|
@ -961,6 +968,7 @@
|
||||||
{#if $user?.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
|
{#if $user?.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
|
||||||
<Tooltip content={$i18n.t('Edit')} placement="bottom">
|
<Tooltip content={$i18n.t('Edit')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Edit')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -973,6 +981,7 @@
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
>
|
>
|
||||||
|
|
@ -989,6 +998,7 @@
|
||||||
|
|
||||||
<Tooltip content={$i18n.t('Copy')} placement="bottom">
|
<Tooltip content={$i18n.t('Copy')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Copy')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -999,6 +1009,7 @@
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
aria-hidden="true"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
@ -1016,6 +1027,7 @@
|
||||||
{#if $user?.role === 'admin' || ($user?.permissions?.chat?.tts ?? true)}
|
{#if $user?.role === 'admin' || ($user?.permissions?.chat?.tts ?? true)}
|
||||||
<Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
|
<Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Read Aloud')}
|
||||||
id="speak-button-{message.id}"
|
id="speak-button-{message.id}"
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? 'visible'
|
||||||
|
|
@ -1031,6 +1043,7 @@
|
||||||
class=" w-4 h-4"
|
class=" w-4 h-4"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -1063,6 +1076,7 @@
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
|
|
@ -1078,6 +1092,7 @@
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
|
|
@ -1096,6 +1111,7 @@
|
||||||
{#if $config?.features.enable_image_generation && ($user?.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly}
|
{#if $config?.features.enable_image_generation && ($user?.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly}
|
||||||
<Tooltip content={$i18n.t('Generate Image')} placement="bottom">
|
<Tooltip content={$i18n.t('Generate Image')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Generate Image')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -1107,6 +1123,7 @@
|
||||||
>
|
>
|
||||||
{#if generatingImage}
|
{#if generatingImage}
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
class=" w-4 h-4"
|
class=" w-4 h-4"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -1141,6 +1158,7 @@
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
aria-hidden="true"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
@ -1173,6 +1191,7 @@
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
aria-hidden="true"
|
||||||
class=" {isLastMessage
|
class=" {isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -1182,6 +1201,7 @@
|
||||||
id="info-{message.id}"
|
id="info-{message.id}"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -1203,6 +1223,7 @@
|
||||||
{#if !$temporaryChatEnabled && ($config?.features.enable_message_rating ?? true)}
|
{#if !$temporaryChatEnabled && ($config?.features.enable_message_rating ?? true)}
|
||||||
<Tooltip content={$i18n.t('Good Response')} placement="bottom">
|
<Tooltip content={$i18n.t('Good Response')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Good Response')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? 'visible'
|
||||||
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
|
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
|
||||||
|
|
@ -1221,6 +1242,7 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
|
|
@ -1239,6 +1261,7 @@
|
||||||
|
|
||||||
<Tooltip content={$i18n.t('Bad Response')} placement="bottom">
|
<Tooltip content={$i18n.t('Bad Response')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Bad Response')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? 'visible'
|
||||||
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
|
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
|
||||||
|
|
@ -1257,6 +1280,7 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
|
|
@ -1277,6 +1301,7 @@
|
||||||
{#if isLastMessage}
|
{#if isLastMessage}
|
||||||
<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
|
<Tooltip content={$i18n.t('Continue Response')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
|
aria-label={$i18n.t('Continue Response')}
|
||||||
type="button"
|
type="button"
|
||||||
id="continue-response-button"
|
id="continue-response-button"
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
|
|
@ -1287,6 +1312,7 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|
@ -1312,6 +1338,7 @@
|
||||||
<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
|
<Tooltip content={$i18n.t('Regenerate')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={$i18n.t('Regenerate')}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -1337,6 +1364,7 @@
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2.3"
|
stroke-width="2.3"
|
||||||
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
>
|
>
|
||||||
|
|
@ -1353,6 +1381,7 @@
|
||||||
<Tooltip content={$i18n.t('Delete')} placement="bottom">
|
<Tooltip content={$i18n.t('Delete')} placement="bottom">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={$i18n.t('Delete')}
|
||||||
id="delete-response-button"
|
id="delete-response-button"
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? 'visible'
|
||||||
|
|
@ -1367,6 +1396,7 @@
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
aria-hidden="true"
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
|
|
@ -1384,6 +1414,7 @@
|
||||||
<Tooltip content={action.name} placement="bottom">
|
<Tooltip content={action.name} placement="bottom">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
aria-label={action.name}
|
||||||
class="{isLastMessage
|
class="{isLastMessage
|
||||||
? 'visible'
|
? '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"
|
: '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"
|
||||||
|
|
@ -1425,6 +1456,17 @@
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if isLastMessage && message.done && !readOnly && (message?.followUps ?? []).length > 0}
|
||||||
|
<div class="mt-2.5" in:fade={{ duration: 100 }}>
|
||||||
|
<FollowUps
|
||||||
|
followUps={message?.followUps}
|
||||||
|
onClick={(prompt) => {
|
||||||
|
submitMessage(message?.id, prompt);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import ArrowTurnDownRight from '$lib/components/icons/ArrowTurnDownRight.svelte';
|
||||||
|
import { onMount, tick, getContext } from 'svelte';
|
||||||
|
|
||||||
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
export let followUps: string[] = [];
|
||||||
|
export let onClick: (followUp: string) => void = () => {};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<div class="text-sm font-medium">
|
||||||
|
{$i18n.t('Follow up')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col text-left gap-1 mt-1.5">
|
||||||
|
{#each followUps as followUp, idx (idx)}
|
||||||
|
<button
|
||||||
|
class=" mr-2 py-1.5 bg-transparent text-left text-sm flex items-center gap-2 px-1.5 text-gray-500 dark:text-gray-400 hover:text-black dark:hover:text-white transition"
|
||||||
|
on:click={() => onClick(followUp)}
|
||||||
|
title={followUp}
|
||||||
|
aria-label={followUp}
|
||||||
|
>
|
||||||
|
<ArrowTurnDownRight className="size-3.5" />
|
||||||
|
|
||||||
|
<div class="line-clamp-1">
|
||||||
|
{followUp}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{#if idx < followUps.length - 1}
|
||||||
|
<hr class="border-gray-100 dark:border-gray-850" />
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
// Addons
|
// Addons
|
||||||
let titleAutoGenerate = true;
|
let titleAutoGenerate = true;
|
||||||
|
let autoFollowUps = true;
|
||||||
let autoTags = true;
|
let autoTags = true;
|
||||||
|
|
||||||
let responseAutoCopy = false;
|
let responseAutoCopy = false;
|
||||||
|
|
@ -197,6 +198,11 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleAutoFollowUps = async () => {
|
||||||
|
autoFollowUps = !autoFollowUps;
|
||||||
|
saveSettings({ autoFollowUps });
|
||||||
|
};
|
||||||
|
|
||||||
const toggleAutoTags = async () => {
|
const toggleAutoTags = async () => {
|
||||||
autoTags = !autoTags;
|
autoTags = !autoTags;
|
||||||
saveSettings({ autoTags });
|
saveSettings({ autoTags });
|
||||||
|
|
@ -287,6 +293,7 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
titleAutoGenerate = $settings?.title?.auto ?? true;
|
titleAutoGenerate = $settings?.title?.auto ?? true;
|
||||||
autoTags = $settings.autoTags ?? true;
|
autoTags = $settings.autoTags ?? true;
|
||||||
|
autoFollowUps = $settings.autoFollowUps ?? true;
|
||||||
|
|
||||||
highContrastMode = $settings.highContrastMode ?? false;
|
highContrastMode = $settings.highContrastMode ?? false;
|
||||||
|
|
||||||
|
|
@ -619,6 +626,26 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class=" py-0.5 flex w-full justify-between">
|
||||||
|
<div class=" self-center text-xs">{$i18n.t('Follow-Up Auto-Generation')}</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||||
|
on:click={() => {
|
||||||
|
toggleAutoFollowUps();
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{#if autoFollowUps === 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=" py-0.5 flex w-full justify-between">
|
<div class=" py-0.5 flex w-full justify-between">
|
||||||
<div class=" self-center text-xs">{$i18n.t('Chat Tags Auto-Generation')}</div>
|
<div class=" self-center text-xs">{$i18n.t('Chat Tags Auto-Generation')}</div>
|
||||||
|
|
|
||||||
|
|
@ -34,93 +34,160 @@
|
||||||
id: 'general',
|
id: 'general',
|
||||||
title: 'General',
|
title: 'General',
|
||||||
keywords: [
|
keywords: [
|
||||||
'general',
|
'advancedparams',
|
||||||
'theme',
|
'advancedparameters',
|
||||||
'language',
|
'advanced params',
|
||||||
'notifications',
|
'advanced parameters',
|
||||||
'system',
|
|
||||||
'systemprompt',
|
|
||||||
'prompt',
|
|
||||||
'advanced',
|
|
||||||
'settings',
|
|
||||||
'defaultsettings',
|
|
||||||
'configuration',
|
'configuration',
|
||||||
'systemsettings',
|
|
||||||
'notificationsettings',
|
|
||||||
'systempromptconfig',
|
|
||||||
'languageoptions',
|
|
||||||
'defaultparameters',
|
'defaultparameters',
|
||||||
'systemparameters'
|
'default parameters',
|
||||||
|
'defaultsettings',
|
||||||
|
'default settings',
|
||||||
|
'general',
|
||||||
|
'keepalive',
|
||||||
|
'keep alive',
|
||||||
|
'languages',
|
||||||
|
'notifications',
|
||||||
|
'requestmode',
|
||||||
|
'request mode',
|
||||||
|
'systemparameters',
|
||||||
|
'system parameters',
|
||||||
|
'systemprompt',
|
||||||
|
'system prompt',
|
||||||
|
'systemsettings',
|
||||||
|
'system settings',
|
||||||
|
'theme',
|
||||||
|
'translate',
|
||||||
|
'webuisettings',
|
||||||
|
'webui settings'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'interface',
|
id: 'interface',
|
||||||
title: 'Interface',
|
title: 'Interface',
|
||||||
keywords: [
|
keywords: [
|
||||||
'defaultmodel',
|
'allow user location',
|
||||||
'selectmodel',
|
'allow voice interruption in call',
|
||||||
'ui',
|
'allowuserlocation',
|
||||||
'userinterface',
|
'allowvoiceinterruptionincall',
|
||||||
'display',
|
'always collapse codeblocks',
|
||||||
'layout',
|
'always collapse code blocks',
|
||||||
'design',
|
'always expand details',
|
||||||
'landingpage',
|
'always on web search',
|
||||||
'landingpagemode',
|
'always play notification sound',
|
||||||
'default',
|
'alwayscollapsecodeblocks',
|
||||||
'chat',
|
'alwaysexpanddetails',
|
||||||
'chatbubble',
|
'alwaysonwebsearch',
|
||||||
'chatui',
|
'alwaysplaynotificationsound',
|
||||||
'username',
|
'android',
|
||||||
'showusername',
|
'auto chat tags',
|
||||||
'displayusername',
|
'auto copy response to clipboard',
|
||||||
'widescreen',
|
'auto title',
|
||||||
'widescreenmode',
|
'autochattags',
|
||||||
'fullscreen',
|
'autocopyresponsetoclipboard',
|
||||||
'expandmode',
|
'autotitle',
|
||||||
|
'beta',
|
||||||
|
'call',
|
||||||
|
'chat background image',
|
||||||
|
'chat bubble ui',
|
||||||
|
'chat direction',
|
||||||
|
'chat tags autogen',
|
||||||
|
'chat tags autogeneration',
|
||||||
|
'chat ui',
|
||||||
|
'chatbackgroundimage',
|
||||||
|
'chatbubbleui',
|
||||||
'chatdirection',
|
'chatdirection',
|
||||||
|
'chat tags autogeneration',
|
||||||
|
'chattagsautogeneration',
|
||||||
|
'chatui',
|
||||||
|
'copy formatted text',
|
||||||
|
'copyformattedtext',
|
||||||
|
'default model',
|
||||||
|
'defaultmodel',
|
||||||
|
'design',
|
||||||
|
'detect artifacts automatically',
|
||||||
|
'detectartifactsautomatically',
|
||||||
|
'display emoji in call',
|
||||||
|
'display username',
|
||||||
|
'displayemojiincall',
|
||||||
|
'displayusername',
|
||||||
|
'enter key behavior',
|
||||||
|
'enterkeybehavior',
|
||||||
|
'expand mode',
|
||||||
|
'expandmode',
|
||||||
|
'file',
|
||||||
|
'followup autogeneration',
|
||||||
|
'followupautogeneration',
|
||||||
|
'fullscreen',
|
||||||
|
'fullwidthmode',
|
||||||
|
'full width mode',
|
||||||
|
'haptic feedback',
|
||||||
|
'hapticfeedback',
|
||||||
|
'high contrast mode',
|
||||||
|
'highcontrastmode',
|
||||||
|
'iframe sandbox allow forms',
|
||||||
|
'iframe sandbox allow same origin',
|
||||||
|
'iframesandboxallowforms',
|
||||||
|
'iframesandboxallowsameorigin',
|
||||||
|
'imagecompression',
|
||||||
|
'image compression',
|
||||||
|
'imagemaxcompressionsize',
|
||||||
|
'image max compression size',
|
||||||
|
'interface customization',
|
||||||
|
'interface options',
|
||||||
|
'interfacecustomization',
|
||||||
|
'interfaceoptions',
|
||||||
|
'landing page mode',
|
||||||
|
'landingpagemode',
|
||||||
|
'layout',
|
||||||
|
'left to right',
|
||||||
|
'left-to-right',
|
||||||
'lefttoright',
|
'lefttoright',
|
||||||
'ltr',
|
'ltr',
|
||||||
|
'paste large text as file',
|
||||||
|
'pastelargetextasfile',
|
||||||
|
'reset background',
|
||||||
|
'resetbackground',
|
||||||
|
'response auto copy',
|
||||||
|
'responseautocopy',
|
||||||
|
'rich text input for chat',
|
||||||
|
'richtextinputforchat',
|
||||||
|
'right to left',
|
||||||
|
'right-to-left',
|
||||||
'righttoleft',
|
'righttoleft',
|
||||||
'rtl',
|
'rtl',
|
||||||
'notifications',
|
'scroll behavior',
|
||||||
'toast',
|
'scroll on branch change',
|
||||||
'toastnotifications',
|
|
||||||
'largechunks',
|
|
||||||
'streamlargechunks',
|
|
||||||
'scroll',
|
|
||||||
'scrollonbranchchange',
|
|
||||||
'scrollbehavior',
|
'scrollbehavior',
|
||||||
'richtext',
|
'scrollonbranchchange',
|
||||||
'richtextinput',
|
'select model',
|
||||||
'background',
|
'selectmodel',
|
||||||
'chatbackground',
|
'settings',
|
||||||
'chatbackgroundimage',
|
'show username',
|
||||||
'backgroundimage',
|
'showusername',
|
||||||
'uploadbackground',
|
'stream large chunks',
|
||||||
'resetbackground',
|
'streamlargechunks',
|
||||||
'titleautogen',
|
'stylized pdf export',
|
||||||
|
'stylizedpdfexport',
|
||||||
|
'title autogeneration',
|
||||||
'titleautogeneration',
|
'titleautogeneration',
|
||||||
'autotitle',
|
'toast notifications for new updates',
|
||||||
'chattags',
|
'toastnotificationsfornewupdates',
|
||||||
'autochattags',
|
'upload background',
|
||||||
'responseautocopy',
|
'uploadbackground',
|
||||||
'clipboard',
|
'user interface',
|
||||||
'location',
|
'user location access',
|
||||||
'userlocation',
|
'userinterface',
|
||||||
'userlocationaccess',
|
'userlocationaccess',
|
||||||
'haptic',
|
|
||||||
'hapticfeedback',
|
|
||||||
'vibration',
|
'vibration',
|
||||||
'voice',
|
'voice control',
|
||||||
'voicecontrol',
|
'voicecontrol',
|
||||||
'voiceinterruption',
|
'widescreen mode',
|
||||||
'call',
|
'widescreenmode',
|
||||||
'emojis',
|
'whatsnew',
|
||||||
'displayemoji',
|
'whats new',
|
||||||
'save',
|
'websearchinchat',
|
||||||
'interfaceoptions',
|
'web search in chat'
|
||||||
'interfacecustomization',
|
|
||||||
'alwaysonwebsearch'
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
...($user?.role === 'admin' ||
|
...($user?.role === 'admin' ||
|
||||||
|
|
@ -129,7 +196,15 @@
|
||||||
{
|
{
|
||||||
id: 'connections',
|
id: 'connections',
|
||||||
title: 'Connections',
|
title: 'Connections',
|
||||||
keywords: []
|
keywords: [
|
||||||
|
'addconnection',
|
||||||
|
'add connection',
|
||||||
|
'manageconnections',
|
||||||
|
'manage connections',
|
||||||
|
'manage direct connections',
|
||||||
|
'managedirectconnections',
|
||||||
|
'settings'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
@ -140,7 +215,15 @@
|
||||||
{
|
{
|
||||||
id: 'tools',
|
id: 'tools',
|
||||||
title: 'Tools',
|
title: 'Tools',
|
||||||
keywords: []
|
keywords: [
|
||||||
|
'addconnection',
|
||||||
|
'add connection',
|
||||||
|
'managetools',
|
||||||
|
'manage tools',
|
||||||
|
'manage tool servers',
|
||||||
|
'managetoolservers',
|
||||||
|
'settings'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
@ -149,159 +232,233 @@
|
||||||
id: 'personalization',
|
id: 'personalization',
|
||||||
title: 'Personalization',
|
title: 'Personalization',
|
||||||
keywords: [
|
keywords: [
|
||||||
'personalization',
|
'account preferences',
|
||||||
'memory',
|
'account settings',
|
||||||
'personalize',
|
'accountpreferences',
|
||||||
'preferences',
|
'accountsettings',
|
||||||
'profile',
|
'custom settings',
|
||||||
'personalsettings',
|
|
||||||
'customsettings',
|
'customsettings',
|
||||||
'userpreferences',
|
'experimental',
|
||||||
'accountpreferences'
|
'memories',
|
||||||
|
'memory',
|
||||||
|
'personalization',
|
||||||
|
'personalize',
|
||||||
|
'personal settings',
|
||||||
|
'personalsettings',
|
||||||
|
'profile',
|
||||||
|
'user preferences',
|
||||||
|
'userpreferences'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'audio',
|
id: 'audio',
|
||||||
title: 'Audio',
|
title: 'Audio',
|
||||||
keywords: [
|
keywords: [
|
||||||
'audio',
|
'audio config',
|
||||||
'sound',
|
'audio control',
|
||||||
'soundsettings',
|
'audio features',
|
||||||
'audiocontrol',
|
'audio input',
|
||||||
'volume',
|
'audio output',
|
||||||
'speech',
|
'audio playback',
|
||||||
'speechrecognition',
|
'audio voice',
|
||||||
'stt',
|
|
||||||
'speechtotext',
|
|
||||||
'tts',
|
|
||||||
'texttospeech',
|
|
||||||
'playback',
|
|
||||||
'playbackspeed',
|
|
||||||
'voiceplayback',
|
|
||||||
'speechplayback',
|
|
||||||
'audiooutput',
|
|
||||||
'speechengine',
|
|
||||||
'voicecontrol',
|
|
||||||
'audioplayback',
|
|
||||||
'transcription',
|
|
||||||
'autotranscribe',
|
|
||||||
'autosend',
|
|
||||||
'speechsettings',
|
|
||||||
'audiovoice',
|
|
||||||
'voiceoptions',
|
|
||||||
'setvoice',
|
|
||||||
'nonlocalvoices',
|
|
||||||
'savesettings',
|
|
||||||
'audioconfig',
|
'audioconfig',
|
||||||
'speechconfig',
|
'audiocontrol',
|
||||||
'voicerecognition',
|
|
||||||
'speechsynthesis',
|
|
||||||
'speechmode',
|
|
||||||
'voicespeed',
|
|
||||||
'speechrate',
|
|
||||||
'speechspeed',
|
|
||||||
'audioinput',
|
|
||||||
'audiofeatures',
|
'audiofeatures',
|
||||||
'voicemodes'
|
'audioinput',
|
||||||
|
'audiooutput',
|
||||||
|
'audioplayback',
|
||||||
|
'audiovoice',
|
||||||
|
'auto playback response',
|
||||||
|
'autoplaybackresponse',
|
||||||
|
'auto transcribe',
|
||||||
|
'autotranscribe',
|
||||||
|
'instant auto send after voice transcription',
|
||||||
|
'instantautosendaftervoicetranscription',
|
||||||
|
'language',
|
||||||
|
'non local voices',
|
||||||
|
'nonlocalvoices',
|
||||||
|
'save settings',
|
||||||
|
'savesettings',
|
||||||
|
'set voice',
|
||||||
|
'setvoice',
|
||||||
|
'sound settings',
|
||||||
|
'soundsettings',
|
||||||
|
'speech config',
|
||||||
|
'speech mode',
|
||||||
|
'speech playback speed',
|
||||||
|
'speech rate',
|
||||||
|
'speech recognition',
|
||||||
|
'speech settings',
|
||||||
|
'speech speed',
|
||||||
|
'speech synthesis',
|
||||||
|
'speech to text engine',
|
||||||
|
'speechconfig',
|
||||||
|
'speechmode',
|
||||||
|
'speechplaybackspeed',
|
||||||
|
'speechrate',
|
||||||
|
'speechrecognition',
|
||||||
|
'speechsettings',
|
||||||
|
'speechspeed',
|
||||||
|
'speechsynthesis',
|
||||||
|
'speechtotextengine',
|
||||||
|
'speedch playback rate',
|
||||||
|
'speedchplaybackrate',
|
||||||
|
'stt settings',
|
||||||
|
'sttsettings',
|
||||||
|
'text to speech engine',
|
||||||
|
'text to speech',
|
||||||
|
'textospeechengine',
|
||||||
|
'texttospeech',
|
||||||
|
'texttospeechvoice',
|
||||||
|
'text to speech voice',
|
||||||
|
'voice control',
|
||||||
|
'voice modes',
|
||||||
|
'voice options',
|
||||||
|
'voice playback',
|
||||||
|
'voice recognition',
|
||||||
|
'voice speed',
|
||||||
|
'voicecontrol',
|
||||||
|
'voicemodes',
|
||||||
|
'voiceoptions',
|
||||||
|
'voiceplayback',
|
||||||
|
'voicerecognition',
|
||||||
|
'voicespeed',
|
||||||
|
'volume'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'chats',
|
id: 'chats',
|
||||||
title: 'Chats',
|
title: 'Chats',
|
||||||
keywords: [
|
keywords: [
|
||||||
'chat',
|
'archive all chats',
|
||||||
'messages',
|
'archive chats',
|
||||||
'conversations',
|
'archiveallchats',
|
||||||
'chatsettings',
|
'archivechats',
|
||||||
'history',
|
'archived chats',
|
||||||
|
'archivedchats',
|
||||||
|
'chat activity',
|
||||||
|
'chat history',
|
||||||
|
'chat settings',
|
||||||
|
'chatactivity',
|
||||||
'chathistory',
|
'chathistory',
|
||||||
'messagehistory',
|
'chatsettings',
|
||||||
'messagearchive',
|
'conversation activity',
|
||||||
'convo',
|
'conversation history',
|
||||||
'chats',
|
'conversationactivity',
|
||||||
'conversationhistory',
|
'conversationhistory',
|
||||||
'exportmessages',
|
'conversations',
|
||||||
'chatactivity'
|
'convos',
|
||||||
|
'delete all chats',
|
||||||
|
'delete chats',
|
||||||
|
'deleteallchats',
|
||||||
|
'deletechats',
|
||||||
|
'export chats',
|
||||||
|
'exportchats',
|
||||||
|
'import chats',
|
||||||
|
'importchats',
|
||||||
|
'message activity',
|
||||||
|
'message archive',
|
||||||
|
'message history',
|
||||||
|
'messagearchive',
|
||||||
|
'messagehistory'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'account',
|
id: 'account',
|
||||||
title: 'Account',
|
title: 'Account',
|
||||||
keywords: [
|
keywords: [
|
||||||
'account',
|
'account preferences',
|
||||||
'profile',
|
'account settings',
|
||||||
'security',
|
'accountpreferences',
|
||||||
'privacy',
|
'accountsettings',
|
||||||
'settings',
|
'api keys',
|
||||||
|
'apikeys',
|
||||||
|
'change password',
|
||||||
|
'changepassword',
|
||||||
|
'jwt token',
|
||||||
|
'jwttoken',
|
||||||
'login',
|
'login',
|
||||||
|
'new password',
|
||||||
|
'newpassword',
|
||||||
|
'notification webhook url',
|
||||||
|
'notificationwebhookurl',
|
||||||
|
'personal settings',
|
||||||
|
'personalsettings',
|
||||||
|
'privacy settings',
|
||||||
|
'privacysettings',
|
||||||
|
'profileavatar',
|
||||||
|
'profile avatar',
|
||||||
|
'profile details',
|
||||||
|
'profile image',
|
||||||
|
'profile picture',
|
||||||
|
'profiledetails',
|
||||||
|
'profileimage',
|
||||||
|
'profilepicture',
|
||||||
|
'security settings',
|
||||||
|
'securitysettings',
|
||||||
|
'update account',
|
||||||
|
'update password',
|
||||||
|
'updateaccount',
|
||||||
|
'updatepassword',
|
||||||
|
'user account',
|
||||||
|
'user data',
|
||||||
|
'user preferences',
|
||||||
|
'user profile',
|
||||||
'useraccount',
|
'useraccount',
|
||||||
'userdata',
|
'userdata',
|
||||||
'api',
|
'username',
|
||||||
'apikey',
|
'userpreferences',
|
||||||
'userprofile',
|
'userprofile',
|
||||||
'profiledetails',
|
'webhook url',
|
||||||
'accountsettings',
|
'webhookurl'
|
||||||
'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',
|
id: 'about',
|
||||||
title: 'About',
|
title: 'About',
|
||||||
keywords: [
|
keywords: [
|
||||||
'about',
|
'about app',
|
||||||
'info',
|
'about me',
|
||||||
'information',
|
'about open webui',
|
||||||
'version',
|
'about page',
|
||||||
'documentation',
|
'about us',
|
||||||
'help',
|
|
||||||
'support',
|
|
||||||
'details',
|
|
||||||
'aboutus',
|
|
||||||
'softwareinfo',
|
|
||||||
'timothyjaeryangbaek',
|
|
||||||
'openwebui',
|
|
||||||
'release',
|
|
||||||
'updates',
|
|
||||||
'updateinfo',
|
|
||||||
'versioninfo',
|
|
||||||
'aboutapp',
|
'aboutapp',
|
||||||
'terms',
|
'aboutme',
|
||||||
'termsandconditions',
|
'aboutopenwebui',
|
||||||
|
'aboutpage',
|
||||||
|
'aboutus',
|
||||||
|
'check for updates',
|
||||||
|
'checkforupdates',
|
||||||
'contact',
|
'contact',
|
||||||
'aboutpage'
|
'copyright',
|
||||||
|
'details',
|
||||||
|
'discord',
|
||||||
|
'documentation',
|
||||||
|
'github',
|
||||||
|
'help',
|
||||||
|
'information',
|
||||||
|
'license',
|
||||||
|
'redistributions',
|
||||||
|
'release',
|
||||||
|
'see whats new',
|
||||||
|
'seewhatsnew',
|
||||||
|
'settings',
|
||||||
|
'software info',
|
||||||
|
'softwareinfo',
|
||||||
|
'support',
|
||||||
|
'terms and conditions',
|
||||||
|
'terms of use',
|
||||||
|
'termsandconditions',
|
||||||
|
'termsofuse',
|
||||||
|
'timothy jae ryang baek',
|
||||||
|
'timothy j baek',
|
||||||
|
'timothyjaeryangbaek',
|
||||||
|
'timothyjbaek',
|
||||||
|
'twitter',
|
||||||
|
'update info',
|
||||||
|
'updateinfo',
|
||||||
|
'version info',
|
||||||
|
'versioninfo'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -405,7 +562,7 @@
|
||||||
<div class="flex flex-col md:flex-row w-full px-4 pt-1 pb-4 md:space-x-4">
|
<div class="flex flex-col md:flex-row w-full px-4 pt-1 pb-4 md:space-x-4">
|
||||||
<div
|
<div
|
||||||
id="settings-tabs-container"
|
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"
|
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 md:min-h-[32rem] md:max-h-[32rem] 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="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">
|
<div class="self-center rounded-l-xl bg-transparent">
|
||||||
|
|
@ -647,35 +804,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class=" self-center">{$i18n.t('About')}</div>
|
<div class=" self-center">{$i18n.t('About')}</div>
|
||||||
</button>
|
</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}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
|
|
@ -683,6 +811,32 @@
|
||||||
{$i18n.t('No results found')}
|
{$i18n.t('No results found')}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#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 text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white mt-auto"
|
||||||
|
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}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 md:min-h-[32rem] max-h-[32rem]">
|
<div class="flex-1 md:min-h-[32rem] max-h-[32rem]">
|
||||||
{#if selectedTab === 'general'}
|
{#if selectedTab === 'general'}
|
||||||
|
|
@ -763,6 +917,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
|
appearance: textfield;
|
||||||
-moz-appearance: textfield; /* Firefox */
|
-moz-appearance: textfield; /* Firefox */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,13 @@
|
||||||
|
|
||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
|
||||||
import { onMount, createEventDispatcher, getContext, tick } from 'svelte';
|
import { onMount, createEventDispatcher, getContext, tick, onDestroy } from 'svelte';
|
||||||
|
|
||||||
|
import PyodideWorker from '$lib/workers/pyodide.worker?worker';
|
||||||
|
|
||||||
import { formatPythonCode } from '$lib/apis/utils';
|
import { formatPythonCode } from '$lib/apis/utils';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { user } from '$lib/stores';
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
@ -113,13 +116,82 @@
|
||||||
return await language?.load();
|
return await language?.load();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pyodideWorkerInstance = null;
|
||||||
|
|
||||||
|
const getPyodideWorker = () => {
|
||||||
|
if (!pyodideWorkerInstance) {
|
||||||
|
pyodideWorkerInstance = new PyodideWorker(); // Your worker constructor
|
||||||
|
}
|
||||||
|
return pyodideWorkerInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generate unique IDs for requests
|
||||||
|
let _formatReqId = 0;
|
||||||
|
|
||||||
|
const formatPythonCodePyodide = (code) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = `format-${++_formatReqId}`;
|
||||||
|
let timeout;
|
||||||
|
const worker = getPyodideWorker();
|
||||||
|
|
||||||
|
const script = `
|
||||||
|
import black
|
||||||
|
print(black.format_str("""${code.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/"/g, '\\"')}""", mode=black.Mode()))
|
||||||
|
`;
|
||||||
|
|
||||||
|
const packages = ['black'];
|
||||||
|
|
||||||
|
function handleMessage(event) {
|
||||||
|
const { id: eventId, stdout, stderr } = event.data;
|
||||||
|
if (eventId !== id) return; // Only handle our message
|
||||||
|
clearTimeout(timeout);
|
||||||
|
worker.removeEventListener('message', handleMessage);
|
||||||
|
worker.removeEventListener('error', handleError);
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
reject(stderr);
|
||||||
|
} else {
|
||||||
|
const formatted = stdout && typeof stdout === 'string' ? stdout.trim() : '';
|
||||||
|
resolve({ code: formatted });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(event) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
worker.removeEventListener('message', handleMessage);
|
||||||
|
worker.removeEventListener('error', handleError);
|
||||||
|
reject(event.message || 'Pyodide worker error');
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.addEventListener('message', handleMessage);
|
||||||
|
worker.addEventListener('error', handleError);
|
||||||
|
|
||||||
|
// Send to worker
|
||||||
|
worker.postMessage({ id, code: script, packages });
|
||||||
|
|
||||||
|
// Timeout
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
worker.removeEventListener('message', handleMessage);
|
||||||
|
worker.removeEventListener('error', handleError);
|
||||||
|
try {
|
||||||
|
worker.terminate();
|
||||||
|
} catch {}
|
||||||
|
pyodideWorkerInstance = null;
|
||||||
|
reject('Execution Time Limit Exceeded');
|
||||||
|
}, 60000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const formatPythonCodeHandler = async () => {
|
export const formatPythonCodeHandler = async () => {
|
||||||
if (codeEditor) {
|
if (codeEditor) {
|
||||||
const res = await formatPythonCode(localStorage.token, _value).catch((error) => {
|
const res = await (
|
||||||
|
$user?.role === 'admin'
|
||||||
|
? formatPythonCode(localStorage.token, _value)
|
||||||
|
: formatPythonCodePyodide(_value)
|
||||||
|
).catch((error) => {
|
||||||
toast.error(`${error}`);
|
toast.error(`${error}`);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res && res.code) {
|
if (res && res.code) {
|
||||||
const formattedCode = res.code;
|
const formattedCode = res.code;
|
||||||
codeEditor.dispatch({
|
codeEditor.dispatch({
|
||||||
|
|
@ -240,6 +312,12 @@
|
||||||
document.removeEventListener('keydown', keydownHandler);
|
document.removeEventListener('keydown', keydownHandler);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (pyodideWorkerInstance) {
|
||||||
|
pyodideWorkerInstance.terminate();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="code-textarea-{id}" class="h-full w-full text-sm" />
|
<div id="code-textarea-{id}" class="h-full w-full text-sm" />
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
async function loadLocale(locales) {
|
async function loadLocale(locales) {
|
||||||
|
if (!locales || !Array.isArray(locales)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const locale of locales) {
|
for (const locale of locales) {
|
||||||
try {
|
try {
|
||||||
dayjs.locale(locale);
|
dayjs.locale(locale);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
observer.disconnect();
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
if (intervalId) {
|
if (intervalId) {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
|
|
||||||
import { onDestroy } from 'svelte';
|
import { onDestroy } from 'svelte';
|
||||||
import { marked } from 'marked';
|
|
||||||
|
|
||||||
import tippy from 'tippy.js';
|
import tippy from 'tippy.js';
|
||||||
import { roundArrow } from 'tippy.js';
|
|
||||||
|
|
||||||
export let placement = 'top';
|
export let placement = 'top';
|
||||||
export let content = `I'm a tooltip!`;
|
export let content = `I'm a tooltip!`;
|
||||||
|
|
@ -47,6 +45,6 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={tooltipElement} aria-label={DOMPurify.sanitize(content)} class={className}>
|
<div bind:this={tooltipElement} class={className}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
12
src/lib/components/icons/ArrowTurnDownRight.svelte
Normal file
12
src/lib/components/icons/ArrowTurnDownRight.svelte
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<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 16 16" fill="currentColor" class={className}>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M2.75 2a.75.75 0 0 1 .75.75v6.5h7.94l-.97-.97a.75.75 0 0 1 1.06-1.06l2.25 2.25a.75.75 0 0 1 0 1.06l-2.25 2.25a.75.75 0 1 1-1.06-1.06l.97-.97H2.75A.75.75 0 0 1 2 10V2.75A.75.75 0 0 1 2.75 2Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
@ -785,7 +785,7 @@
|
||||||
<div
|
<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"
|
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}
|
{#each $pinnedChats as chat, idx (`pinned-chat-${chat?.id ?? idx}`)}
|
||||||
<ChatItem
|
<ChatItem
|
||||||
className=""
|
className=""
|
||||||
id={chat.id}
|
id={chat.id}
|
||||||
|
|
@ -831,7 +831,7 @@
|
||||||
<div class=" flex-1 flex flex-col overflow-y-auto scrollbar-hidden">
|
<div class=" flex-1 flex flex-col overflow-y-auto scrollbar-hidden">
|
||||||
<div class="pt-1.5">
|
<div class="pt-1.5">
|
||||||
{#if $chats}
|
{#if $chats}
|
||||||
{#each $chats as chat, idx}
|
{#each $chats as chat, idx (`chat-${chat?.id ?? idx}`)}
|
||||||
{#if idx === 0 || (idx > 0 && chat.time_range !== $chats[idx - 1].time_range)}
|
{#if idx === 0 || (idx > 0 && chat.time_range !== $chats[idx - 1].time_range)}
|
||||||
<div
|
<div
|
||||||
class="w-full pl-2.5 text-xs text-gray-500 dark:text-gray-500 font-medium {idx ===
|
class="w-full pl-2.5 text-xs text-gray-500 dark:text-gray-500 font-medium {idx ===
|
||||||
|
|
|
||||||
|
|
@ -204,9 +204,10 @@
|
||||||
const chatTitleInputKeydownHandler = (e) => {
|
const chatTitleInputKeydownHandler = (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
editChatTitle(id, chatTitle);
|
setTimeout(() => {
|
||||||
confirmEdit = false;
|
const input = document.getElementById(`chat-title-input-${id}`);
|
||||||
chatTitle = '';
|
if (input) input.blur();
|
||||||
|
}, 0);
|
||||||
} else if (e.key === 'Escape') {
|
} else if (e.key === 'Escape') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
confirmEdit = false;
|
confirmEdit = false;
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
|
|
||||||
<div class=" pt-1">
|
<div class=" pt-1">
|
||||||
<button
|
<button
|
||||||
class=" group-hover:text-gray-500 dark:text-gray-900 dark:hover:text-gray-300 transition"
|
class=" group-hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-300 transition"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
onDelete();
|
onDelete();
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
import ChevronRight from '../icons/ChevronRight.svelte';
|
import ChevronRight from '../icons/ChevronRight.svelte';
|
||||||
import Spinner from '../common/Spinner.svelte';
|
import Spinner from '../common/Spinner.svelte';
|
||||||
import Tooltip from '../common/Tooltip.svelte';
|
import Tooltip from '../common/Tooltip.svelte';
|
||||||
import { capitalizeFirstLetter } from '$lib/utils';
|
import { capitalizeFirstLetter, slugify } from '$lib/utils';
|
||||||
import XMark from '../icons/XMark.svelte';
|
import XMark from '../icons/XMark.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
@ -68,7 +68,15 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const cloneHandler = async (prompt) => {
|
const cloneHandler = async (prompt) => {
|
||||||
sessionStorage.prompt = JSON.stringify(prompt);
|
const clonedPrompt = { ...prompt };
|
||||||
|
|
||||||
|
clonedPrompt.title = `${clonedPrompt.title} (Clone)`;
|
||||||
|
const baseCommand = clonedPrompt.command.startsWith('/')
|
||||||
|
? clonedPrompt.command.substring(1)
|
||||||
|
: clonedPrompt.command;
|
||||||
|
clonedPrompt.command = slugify(`${baseCommand} clone`);
|
||||||
|
|
||||||
|
sessionStorage.prompt = JSON.stringify(clonedPrompt);
|
||||||
goto('/workspace/prompts/create');
|
goto('/workspace/prompts/create');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
export let onSubmit: Function;
|
export let onSubmit: Function;
|
||||||
export let edit = false;
|
export let edit = false;
|
||||||
export let prompt = null;
|
export let prompt = null;
|
||||||
|
export let clone = false;
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
|
"and create a new shared link.": "و أنشئ رابط مشترك جديد.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API الرابط الرئيسي",
|
"API Base URL": "API الرابط الرئيسي",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API مفتاح",
|
"API Key": "API مفتاح",
|
||||||
"API Key created.": "API تم أنشاء المفتاح",
|
"API Key created.": "API تم أنشاء المفتاح",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "هل أنت متأكد ؟",
|
"Are you sure?": "هل أنت متأكد ؟",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "أدخل الChunk Overlap",
|
"Enter Chunk Overlap": "أدخل الChunk Overlap",
|
||||||
"Enter Chunk Size": "أدخل Chunk الحجم",
|
"Enter Chunk Size": "أدخل Chunk الحجم",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "اتبعت التعليمات على أكمل وجه",
|
"Followed instructions perfectly": "اتبعت التعليمات على أكمل وجه",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "كلمة المرور الجديدة",
|
"New Password": "كلمة المرور الجديدة",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
|
"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "أو",
|
"or": "أو",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "آخر",
|
"Other": "آخر",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF ملف (.pdf)",
|
"PDF document (.pdf)": "PDF ملف (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF أستخرج الصور (OCR)",
|
"PDF Extract Images (OCR)": "PDF أستخرج الصور (OCR)",
|
||||||
"pending": "قيد الانتظار",
|
"pending": "قيد الانتظار",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "{{error}} تم رفض الإذن عند الوصول إلى الميكروفون ",
|
"Permission denied when accessing microphone: {{error}}": "{{error}} تم رفض الإذن عند الوصول إلى الميكروفون ",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "التخصيص",
|
"Personalization": "التخصيص",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "أخر 30 يوم",
|
"Previous 30 days": "أخر 30 يوم",
|
||||||
"Previous 7 days": "أخر 7 أيام",
|
"Previous 7 days": "أخر 7 أيام",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "صورة الملف الشخصي",
|
"Profile Image": "صورة الملف الشخصي",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "و{{COUNT}} المزيد",
|
"and {{COUNT}} more": "و{{COUNT}} المزيد",
|
||||||
"and create a new shared link.": "وإنشاء رابط مشترك جديد.",
|
"and create a new shared link.": "وإنشاء رابط مشترك جديد.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "الرابط الأساسي لواجهة API",
|
"API Base URL": "الرابط الأساسي لواجهة API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "مفتاح واجهة برمجة التطبيقات (API)",
|
"API Key": "مفتاح واجهة برمجة التطبيقات (API)",
|
||||||
"API Key created.": "تم إنشاء مفتاح واجهة API.",
|
"API Key created.": "تم إنشاء مفتاح واجهة API.",
|
||||||
"API Key Endpoint Restrictions": "قيود نقاط نهاية مفتاح API",
|
"API Key Endpoint Restrictions": "قيود نقاط نهاية مفتاح API",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "هل أنت متأكد من رغبتك في حذف هذه القناة؟",
|
"Are you sure you want to delete this channel?": "هل أنت متأكد من رغبتك في حذف هذه القناة؟",
|
||||||
"Are you sure you want to delete this message?": "هل أنت متأكد من رغبتك في حذف هذه الرسالة؟",
|
"Are you sure you want to delete this message?": "هل أنت متأكد من رغبتك في حذف هذه الرسالة؟",
|
||||||
"Are you sure you want to unarchive all archived chats?": "هل أنت متأكد من رغبتك في إلغاء أرشفة جميع المحادثات المؤرشفة؟",
|
"Are you sure you want to unarchive all archived chats?": "هل أنت متأكد من رغبتك في إلغاء أرشفة جميع المحادثات المؤرشفة؟",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "هل أنت متأكد؟",
|
"Are you sure?": "هل أنت متأكد؟",
|
||||||
"Arena Models": "نماذج الساحة",
|
"Arena Models": "نماذج الساحة",
|
||||||
"Artifacts": "القطع الأثرية",
|
"Artifacts": "القطع الأثرية",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "أدخل الChunk Overlap",
|
"Enter Chunk Overlap": "أدخل الChunk Overlap",
|
||||||
"Enter Chunk Size": "أدخل Chunk الحجم",
|
"Enter Chunk Size": "أدخل Chunk الحجم",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "أدخل أزواج \"الرمز:قيمة التحيز\" مفصولة بفواصل (مثال: 5432:100، 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "أدخل أزواج \"الرمز:قيمة التحيز\" مفصولة بفواصل (مثال: 5432:100، 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "أدخل الوصف",
|
"Enter description": "أدخل الوصف",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "تم حذف المجلد بنجاح",
|
"Folder deleted successfully": "تم حذف المجلد بنجاح",
|
||||||
"Folder name cannot be empty.": "لا يمكن أن يكون اسم المجلد فارغًا.",
|
"Folder name cannot be empty.": "لا يمكن أن يكون اسم المجلد فارغًا.",
|
||||||
"Folder name updated successfully": "تم تحديث اسم المجلد بنجاح",
|
"Folder name updated successfully": "تم تحديث اسم المجلد بنجاح",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "اتبعت التعليمات على أكمل وجه",
|
"Followed instructions perfectly": "اتبعت التعليمات على أكمل وجه",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "كلمة المرور الجديدة",
|
"New Password": "كلمة المرور الجديدة",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "قناة جديدة",
|
"new-channel": "قناة جديدة",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "تم تحديث إعدادات OpenAI API",
|
"OpenAI API settings updated": "تم تحديث إعدادات OpenAI API",
|
||||||
"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
|
"OpenAI URL/Key required.": "URL/مفتاح OpenAI.مطلوب عنوان ",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "أو",
|
"or": "أو",
|
||||||
"Organize your users": "تنظيم المستخدمين الخاصين بك",
|
"Organize your users": "تنظيم المستخدمين الخاصين بك",
|
||||||
"Other": "آخر",
|
"Other": "آخر",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF ملف (.pdf)",
|
"PDF document (.pdf)": "PDF ملف (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF أستخرج الصور (OCR)",
|
"PDF Extract Images (OCR)": "PDF أستخرج الصور (OCR)",
|
||||||
"pending": "قيد الانتظار",
|
"pending": "قيد الانتظار",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "تم رفض الإذن عند محاولة الوصول إلى أجهزة الوسائط",
|
"Permission denied when accessing media devices": "تم رفض الإذن عند محاولة الوصول إلى أجهزة الوسائط",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "{{error}} تم رفض الإذن عند الوصول إلى الميكروفون ",
|
"Permission denied when accessing microphone: {{error}}": "{{error}} تم رفض الإذن عند الوصول إلى الميكروفون ",
|
||||||
"Permissions": "الأذونات",
|
"Permissions": "الأذونات",
|
||||||
"Perplexity API Key": "مفتاح API لـ Perplexity",
|
"Perplexity API Key": "مفتاح API لـ Perplexity",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "التخصيص",
|
"Personalization": "التخصيص",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "تثبيت",
|
"Pin": "تثبيت",
|
||||||
"Pinned": "مثبت",
|
"Pinned": "مثبت",
|
||||||
"Pioneer insights": "رؤى رائدة",
|
"Pioneer insights": "رؤى رائدة",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "أخر 30 يوم",
|
"Previous 30 days": "أخر 30 يوم",
|
||||||
"Previous 7 days": "أخر 7 أيام",
|
"Previous 7 days": "أخر 7 أيام",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "صورة الملف الشخصي",
|
"Profile Image": "صورة الملف الشخصي",
|
||||||
"Prompt": "التوجيه",
|
"Prompt": "التوجيه",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "и още {{COUNT}}",
|
"and {{COUNT}} more": "и още {{COUNT}}",
|
||||||
"and create a new shared link.": "и създай нов общ линк.",
|
"and create a new shared link.": "и създай нов общ линк.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Базов URL",
|
"API Base URL": "API Базов URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API Ключ",
|
"API Key": "API Ключ",
|
||||||
"API Key created.": "API Ключ създаден.",
|
"API Key created.": "API Ключ създаден.",
|
||||||
"API Key Endpoint Restrictions": "Ограничения на крайните точки за API Ключ",
|
"API Key Endpoint Restrictions": "Ограничения на крайните точки за API Ключ",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Сигурни ли сте, че искате да изтриете този канал?",
|
"Are you sure you want to delete this channel?": "Сигурни ли сте, че искате да изтриете този канал?",
|
||||||
"Are you sure you want to delete this message?": "Сигурни ли сте, че искате да изтриете това съобщение?",
|
"Are you sure you want to delete this message?": "Сигурни ли сте, че искате да изтриете това съобщение?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Сигурни ли сте, че искате да разархивирате всички архивирани чатове?",
|
"Are you sure you want to unarchive all archived chats?": "Сигурни ли сте, че искате да разархивирате всички архивирани чатове?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Сигурни ли сте?",
|
"Are you sure?": "Сигурни ли сте?",
|
||||||
"Arena Models": "Арена Модели",
|
"Arena Models": "Арена Модели",
|
||||||
"Artifacts": "Артефакти",
|
"Artifacts": "Артефакти",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Въведете припокриване на чънкове",
|
"Enter Chunk Overlap": "Въведете припокриване на чънкове",
|
||||||
"Enter Chunk Size": "Въведете размер на чънк",
|
"Enter Chunk Size": "Въведете размер на чънк",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Въведете описание",
|
"Enter description": "Въведете описание",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Папката е изтрита успешно",
|
"Folder deleted successfully": "Папката е изтрита успешно",
|
||||||
"Folder name cannot be empty.": "Името на папката не може да бъде празно.",
|
"Folder name cannot be empty.": "Името на папката не може да бъде празно.",
|
||||||
"Folder name updated successfully": "Името на папката е актуализирано успешно",
|
"Folder name updated successfully": "Името на папката е актуализирано успешно",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Следвайте инструкциите перфектно",
|
"Followed instructions perfectly": "Следвайте инструкциите перфектно",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Нова парола",
|
"New Password": "Нова парола",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "нов-канал",
|
"new-channel": "нов-канал",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "Без съдържание",
|
"No content": "Без съдържание",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "Настройките на OpenAI API са актуализирани",
|
"OpenAI API settings updated": "Настройките на OpenAI API са актуализирани",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/Key е задължителен.",
|
"OpenAI URL/Key required.": "OpenAI URL/Key е задължителен.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "или",
|
"or": "или",
|
||||||
"Organize your users": "Организирайте вашите потребители",
|
"Organize your users": "Организирайте вашите потребители",
|
||||||
"Other": "Друго",
|
"Other": "Друго",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF документ (.pdf)",
|
"PDF document (.pdf)": "PDF документ (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Извличане на изображения от PDF (OCR)",
|
"PDF Extract Images (OCR)": "Извличане на изображения от PDF (OCR)",
|
||||||
"pending": "в очакване",
|
"pending": "в очакване",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Отказан достъп при опит за достъп до медийни устройства",
|
"Permission denied when accessing media devices": "Отказан достъп при опит за достъп до медийни устройства",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Отказан достъп при опит за достъп до микрофона: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Отказан достъп при опит за достъп до микрофона: {{error}}",
|
||||||
"Permissions": "Разрешения",
|
"Permissions": "Разрешения",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Персонализация",
|
"Personalization": "Персонализация",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Закачи",
|
"Pin": "Закачи",
|
||||||
"Pinned": "Закачено",
|
"Pinned": "Закачено",
|
||||||
"Pioneer insights": "Пионерски прозрения",
|
"Pioneer insights": "Пионерски прозрения",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Предишните 30 дни",
|
"Previous 30 days": "Предишните 30 дни",
|
||||||
"Previous 7 days": "Предишните 7 дни",
|
"Previous 7 days": "Предишните 7 дни",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Профилна снимка",
|
"Profile Image": "Профилна снимка",
|
||||||
"Prompt": "Промпт",
|
"Prompt": "Промпт",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
|
"and create a new shared link.": "এবং একটি নতুন শেয়ারে লিংক তৈরি করুন.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "এপিআই বেজ ইউআরএল",
|
"API Base URL": "এপিআই বেজ ইউআরএল",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "এপিআই কোড",
|
"API Key": "এপিআই কোড",
|
||||||
"API Key created.": "একটি এপিআই কোড তৈরি করা হয়েছে.",
|
"API Key created.": "একটি এপিআই কোড তৈরি করা হয়েছে.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "আপনি নিশ্চিত?",
|
"Are you sure?": "আপনি নিশ্চিত?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "চাঙ্ক ওভারল্যাপ লিখুন",
|
"Enter Chunk Overlap": "চাঙ্ক ওভারল্যাপ লিখুন",
|
||||||
"Enter Chunk Size": "চাংক সাইজ লিখুন",
|
"Enter Chunk Size": "চাংক সাইজ লিখুন",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "নির্দেশাবলী নিখুঁতভাবে অনুসরণ করা হয়েছে",
|
"Followed instructions perfectly": "নির্দেশাবলী নিখুঁতভাবে অনুসরণ করা হয়েছে",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "নতুন পাসওয়ার্ড",
|
"New Password": "নতুন পাসওয়ার্ড",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/Key আবশ্যক",
|
"OpenAI URL/Key required.": "OpenAI URL/Key আবশ্যক",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "অথবা",
|
"or": "অথবা",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "অন্যান্য",
|
"Other": "অন্যান্য",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF ডকুমেন্ট (.pdf)",
|
"PDF document (.pdf)": "PDF ডকুমেন্ট (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "পিডিএফ এর ছবি থেকে লেখা বের করুন (OCR)",
|
"PDF Extract Images (OCR)": "পিডিএফ এর ছবি থেকে লেখা বের করুন (OCR)",
|
||||||
"pending": "অপেক্ষমান",
|
"pending": "অপেক্ষমান",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "মাইক্রোফোন ব্যবহারের অনুমতি পাওয়া যায়নি: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "মাইক্রোফোন ব্যবহারের অনুমতি পাওয়া যায়নি: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "ডিজিটাল বাংলা",
|
"Personalization": "ডিজিটাল বাংলা",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "পূর্ব ৩০ দিন",
|
"Previous 30 days": "পূর্ব ৩০ দিন",
|
||||||
"Previous 7 days": "পূর্ব ৭ দিন",
|
"Previous 7 days": "পূর্ব ৭ দিন",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "প্রোফাইল ইমেজ",
|
"Profile Image": "প্রোফাইল ইমেজ",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "ད་དུང་ {{COUNT}}",
|
"and {{COUNT}} more": "ད་དུང་ {{COUNT}}",
|
||||||
"and create a new shared link.": "དང་མཉམ་སྤྱོད་སྦྲེལ་ཐག་གསར་པ་ཞིག་བཟོ་བ།",
|
"and create a new shared link.": "དང་མཉམ་སྤྱོད་སྦྲེལ་ཐག་གསར་པ་ཞིག་བཟོ་བ།",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API གཞི་རྩའི་ URL",
|
"API Base URL": "API གཞི་རྩའི་ URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API ལྡེ་མིག",
|
"API Key": "API ལྡེ་མིག",
|
||||||
"API Key created.": "API ལྡེ་མིག་བཟོས་ཟིན།",
|
"API Key created.": "API ལྡེ་མིག་བཟོས་ཟིན།",
|
||||||
"API Key Endpoint Restrictions": "API ལྡེ་མིག་མཇུག་མཐུད་ཚད་བཀག",
|
"API Key Endpoint Restrictions": "API ལྡེ་མིག་མཇུག་མཐུད་ཚད་བཀག",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "ཁྱེད་ཀྱིས་བགྲོ་གླེང་འདི་བསུབ་འདོད་ངེས་ཡིན་ནམ།",
|
"Are you sure you want to delete this channel?": "ཁྱེད་ཀྱིས་བགྲོ་གླེང་འདི་བསུབ་འདོད་ངེས་ཡིན་ནམ།",
|
||||||
"Are you sure you want to delete this message?": "འཕྲིན་འདི་བསུབ་འདོད་ངེས་ཡིན་ནམ།",
|
"Are you sure you want to delete this message?": "འཕྲིན་འདི་བསུབ་འདོད་ངེས་ཡིན་ནམ།",
|
||||||
"Are you sure you want to unarchive all archived chats?": "ཁྱེད་ཀྱིས་ཡིག་མཛོད་དུ་བཞག་པའི་ཁ་བརྡ་ཡོངས་རྫོགས་ཕྱིར་འདོན་འདོད་ངེས་ཡིན་ནམ།",
|
"Are you sure you want to unarchive all archived chats?": "ཁྱེད་ཀྱིས་ཡིག་མཛོད་དུ་བཞག་པའི་ཁ་བརྡ་ཡོངས་རྫོགས་ཕྱིར་འདོན་འདོད་ངེས་ཡིན་ནམ།",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "ཁྱོད་ངེས་པ་ཡིན་ནམ།",
|
"Are you sure?": "ཁྱོད་ངེས་པ་ཡིན་ནམ།",
|
||||||
"Arena Models": "Arena དཔེ་དབྱིབས།",
|
"Arena Models": "Arena དཔེ་དབྱིབས།",
|
||||||
"Artifacts": "རྫས་རྟེན།",
|
"Artifacts": "རྫས་རྟེན།",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "དུམ་བུ་བསྣོལ་བ་འཇུག་པ།",
|
"Enter Chunk Overlap": "དུམ་བུ་བསྣོལ་བ་འཇུག་པ།",
|
||||||
"Enter Chunk Size": "དུམ་བུའི་ཆེ་ཆུང་འཇུག་པ།",
|
"Enter Chunk Size": "དུམ་བུའི་ཆེ་ཆུང་འཇུག་པ།",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "ཚེག་བསྐུངས་ཀྱིས་ལོགས་སུ་བཀར་བའི་ \"ཊོཀ་ཀེན།:ཕྱོགས་ཞེན་རིན་ཐང་།\" ཆ་འཇུག་པ། (དཔེར། 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "ཚེག་བསྐུངས་ཀྱིས་ལོགས་སུ་བཀར་བའི་ \"ཊོཀ་ཀེན།:ཕྱོགས་ཞེན་རིན་ཐང་།\" ཆ་འཇུག་པ། (དཔེར། 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "འགྲེལ་བཤད་འཇུག་པ།",
|
"Enter description": "འགྲེལ་བཤད་འཇུག་པ།",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "ཡིག་སྣོད་ལེགས་པར་བསུབས་ཟིན།",
|
"Folder deleted successfully": "ཡིག་སྣོད་ལེགས་པར་བསུབས་ཟིན།",
|
||||||
"Folder name cannot be empty.": "ཡིག་སྣོད་ཀྱི་མིང་སྟོང་པ་ཡིན་མི་ཆོག",
|
"Folder name cannot be empty.": "ཡིག་སྣོད་ཀྱི་མིང་སྟོང་པ་ཡིན་མི་ཆོག",
|
||||||
"Folder name updated successfully": "ཡིག་སྣོད་ཀྱི་མིང་ལེགས་པར་གསར་སྒྱུར་བྱས་ཟིན།",
|
"Folder name updated successfully": "ཡིག་སྣོད་ཀྱི་མིང་ལེགས་པར་གསར་སྒྱུར་བྱས་ཟིན།",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "ལམ་སྟོན་ཡང་དག་པར་བསྒྲུབས།",
|
"Followed instructions perfectly": "ལམ་སྟོན་ཡང་དག་པར་བསྒྲུབས།",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "གསང་གྲངས་གསར་པ།",
|
"New Password": "གསང་གྲངས་གསར་པ།",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "བགྲོ་གླེང་གསར་པ།",
|
"new-channel": "བགྲོ་གླེང་གསར་པ།",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI API སྒྲིག་འགོད་གསར་སྒྱུར་བྱས།",
|
"OpenAI API settings updated": "OpenAI API སྒྲིག་འགོད་གསར་སྒྱུར་བྱས།",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/ལྡེ་མིག་དགོས་ངེས།",
|
"OpenAI URL/Key required.": "OpenAI URL/ལྡེ་མིག་དགོས་ངེས།",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "ཡང་ན།",
|
"or": "ཡང་ན།",
|
||||||
"Organize your users": "ཁྱེད་ཀྱི་བེད་སྤྱོད་མཁན་སྒྲིག་འཛུགས།",
|
"Organize your users": "ཁྱེད་ཀྱི་བེད་སྤྱོད་མཁན་སྒྲིག་འཛུགས།",
|
||||||
"Other": "གཞན།",
|
"Other": "གཞན།",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF ཡིག་ཆ། (.pdf)",
|
"PDF document (.pdf)": "PDF ཡིག་ཆ། (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF པར་འདོན་སྤེལ། (OCR)",
|
"PDF Extract Images (OCR)": "PDF པར་འདོན་སྤེལ། (OCR)",
|
||||||
"pending": "སྒུག་བཞིན་པ།",
|
"pending": "སྒུག་བཞིན་པ།",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "བརྒྱུད་ལམ་སྒྲིག་ཆས་འཛུལ་སྤྱོད་སྐབས་དབང་ཚད་ཁས་མ་བླངས།",
|
"Permission denied when accessing media devices": "བརྒྱུད་ལམ་སྒྲིག་ཆས་འཛུལ་སྤྱོད་སྐབས་དབང་ཚད་ཁས་མ་བླངས།",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "སྐད་སྒྲ་འཛིན་ཆས་འཛུལ་སྤྱོད་སྐབས་དབང་ཚད་ཁས་མ་བླངས།: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "སྐད་སྒྲ་འཛིན་ཆས་འཛུལ་སྤྱོད་སྐབས་དབང་ཚད་ཁས་མ་བླངས།: {{error}}",
|
||||||
"Permissions": "དབང་ཚད།",
|
"Permissions": "དབང་ཚད།",
|
||||||
"Perplexity API Key": "Perplexity API ལྡེ་མིག",
|
"Perplexity API Key": "Perplexity API ལྡེ་མིག",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "སྒེར་སྤྱོད་ཅན།",
|
"Personalization": "སྒེར་སྤྱོད་ཅན།",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "གདབ་པ།",
|
"Pin": "གདབ་པ།",
|
||||||
"Pinned": "གདབ་ཟིན།",
|
"Pinned": "གདབ་ཟིན།",
|
||||||
"Pioneer insights": "སྔོན་དཔག་རིག་ནུས།",
|
"Pioneer insights": "སྔོན་དཔག་རིག་ནུས།",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "ཉིན་ ༣༠ སྔོན་མ།",
|
"Previous 30 days": "ཉིན་ ༣༠ སྔོན་མ།",
|
||||||
"Previous 7 days": "ཉིན་ ༧ སྔོན་མ།",
|
"Previous 7 days": "ཉིན་ ༧ སྔོན་མ།",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "སྒེར།",
|
"Private": "སྒེར།",
|
||||||
"Profile Image": "སྤྱི་ཐག་པར།",
|
"Profile Image": "སྤྱི་ཐག་པར།",
|
||||||
"Prompt": "འགུལ་སློང་།",
|
"Prompt": "འགུལ་སློང་།",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "i {{COUNT}} més",
|
"and {{COUNT}} more": "i {{COUNT}} més",
|
||||||
"and create a new shared link.": "i crear un nou enllaç compartit.",
|
"and create a new shared link.": "i crear un nou enllaç compartit.",
|
||||||
"Android": "Android",
|
"Android": "Android",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "URL Base de l'API",
|
"API Base URL": "URL Base de l'API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "clau API",
|
"API Key": "clau API",
|
||||||
"API Key created.": "clau API creada.",
|
"API Key created.": "clau API creada.",
|
||||||
"API Key Endpoint Restrictions": "Restriccions del punt d'accés de la Clau API",
|
"API Key Endpoint Restrictions": "Restriccions del punt d'accés de la Clau API",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Estàs segur que vols eliminar aquest canal?",
|
"Are you sure you want to delete this channel?": "Estàs segur que vols eliminar aquest canal?",
|
||||||
"Are you sure you want to delete this message?": "Estàs segur que vols eliminar aquest missatge?",
|
"Are you sure you want to delete this message?": "Estàs segur que vols eliminar aquest missatge?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Estàs segur que vols desarxivar tots els xats arxivats?",
|
"Are you sure you want to unarchive all archived chats?": "Estàs segur que vols desarxivar tots els xats arxivats?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "Estàs segur que vols actualitzar el rol de l'usuari a **{{ROLE}}**?",
|
|
||||||
"Are you sure?": "Estàs segur?",
|
"Are you sure?": "Estàs segur?",
|
||||||
"Arena Models": "Models de l'Arena",
|
"Arena Models": "Models de l'Arena",
|
||||||
"Artifacts": "Artefactes",
|
"Artifacts": "Artefactes",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Introdueix la mida de solapament de blocs",
|
"Enter Chunk Overlap": "Introdueix la mida de solapament de blocs",
|
||||||
"Enter Chunk Size": "Introdueix la mida del bloc",
|
"Enter Chunk Size": "Introdueix la mida del bloc",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Introdueix parelles de \"token:valor de biaix\" separats per comes (exemple: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Introdueix parelles de \"token:valor de biaix\" separats per comes (exemple: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Introdueix la descripció",
|
"Enter description": "Introdueix la descripció",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Carpeta eliminada correctament",
|
"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",
|
"Folder name updated successfully": "Nom de la carpeta actualitzat correctament",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "S'han seguit les instruccions perfectament",
|
"Followed instructions perfectly": "S'han seguit les instruccions perfectament",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nova contrasenya",
|
"New Password": "Nova contrasenya",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "nou-canal",
|
"new-channel": "nou-canal",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "No hi ha contingut",
|
"No content": "No hi ha contingut",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
|
"OpenAI API settings updated": "Configuració de l'API d'OpenAI actualitzada",
|
||||||
"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
|
"OpenAI URL/Key required.": "URL/Clau d'OpenAI requerides.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "o",
|
"or": "o",
|
||||||
"Organize your users": "Organitza els teus usuaris",
|
"Organize your users": "Organitza els teus usuaris",
|
||||||
"Other": "Altres",
|
"Other": "Altres",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Document PDF (.pdf)",
|
"PDF document (.pdf)": "Document PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)",
|
"PDF Extract Images (OCR)": "Extreu imatges del PDF (OCR)",
|
||||||
"pending": "pendent",
|
"pending": "pendent",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia",
|
"Permission denied when accessing media devices": "Permís denegat en accedir a dispositius multimèdia",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
|
||||||
"Permissions": "Permisos",
|
"Permissions": "Permisos",
|
||||||
"Perplexity API Key": "Clau API de Perplexity",
|
"Perplexity API Key": "Clau API de Perplexity",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalització",
|
"Personalization": "Personalització",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Fixar",
|
"Pin": "Fixar",
|
||||||
"Pinned": "Fixat",
|
"Pinned": "Fixat",
|
||||||
"Pioneer insights": "Perspectives pioneres",
|
"Pioneer insights": "Perspectives pioneres",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 dies anteriors",
|
"Previous 30 days": "30 dies anteriors",
|
||||||
"Previous 7 days": "7 dies anteriors",
|
"Previous 7 days": "7 dies anteriors",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Privat",
|
"Private": "Privat",
|
||||||
"Profile Image": "Imatge de perfil",
|
"Profile Image": "Imatge de perfil",
|
||||||
"Prompt": "Indicació",
|
"Prompt": "Indicació",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "",
|
"and create a new shared link.": "",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Base URL",
|
"API Base URL": "API Base URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "yawe sa API",
|
"API Key": "yawe sa API",
|
||||||
"API Key created.": "",
|
"API Key created.": "",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Sigurado ka ?",
|
"Are you sure?": "Sigurado ka ?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Pagsulod sa block overlap",
|
"Enter Chunk Overlap": "Pagsulod sa block overlap",
|
||||||
"Enter Chunk Size": "Isulod ang block size",
|
"Enter Chunk Size": "Isulod ang block size",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "",
|
"Followed instructions perfectly": "",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Bag-ong Password",
|
"New Password": "Bag-ong Password",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "",
|
"OpenAI URL/Key required.": "",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "O",
|
"or": "O",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "",
|
"Other": "",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "",
|
"PDF document (.pdf)": "",
|
||||||
"PDF Extract Images (OCR)": "PDF Image Extraction (OCR)",
|
"PDF Extract Images (OCR)": "PDF Image Extraction (OCR)",
|
||||||
"pending": "gipugngan",
|
"pending": "gipugngan",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Gidili ang pagtugot sa dihang nag-access sa mikropono: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Gidili ang pagtugot sa dihang nag-access sa mikropono: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "",
|
"Personalization": "",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "",
|
"Previous 30 days": "",
|
||||||
"Previous 7 days": "",
|
"Previous 7 days": "",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "",
|
"Profile Image": "",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "a {{COUNT}} další/ch",
|
"and {{COUNT}} more": "a {{COUNT}} další/ch",
|
||||||
"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
|
"and create a new shared link.": "a vytvořit nový sdílený odkaz.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "Základní URL adresa API",
|
"API Base URL": "Základní URL adresa API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Klíč API",
|
"API Key": "Klíč API",
|
||||||
"API Key created.": "API klíč byl vytvořen.",
|
"API Key created.": "API klíč byl vytvořen.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Jste si jistý?",
|
"Are you sure?": "Jste si jistý?",
|
||||||
"Arena Models": "Arena modely",
|
"Arena Models": "Arena modely",
|
||||||
"Artifacts": "Artefakty",
|
"Artifacts": "Artefakty",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Zadejte překryv části",
|
"Enter Chunk Overlap": "Zadejte překryv části",
|
||||||
"Enter Chunk Size": "Zadejte velikost bloku",
|
"Enter Chunk Size": "Zadejte velikost bloku",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Zadejte popis",
|
"Enter description": "Zadejte popis",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Složka byla úspěšně smazána",
|
"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.",
|
"Folder name updated successfully": "Název složky byl úspěšně aktualizován.",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Dodržel pokyny dokonale.",
|
"Followed instructions perfectly": "Dodržel pokyny dokonale.",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nové heslo",
|
"New Password": "Nové heslo",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "Je vyžadován odkaz/adresa URL nebo klíč OpenAI.",
|
"OpenAI URL/Key required.": "Je vyžadován odkaz/adresa URL nebo klíč OpenAI.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "nebo",
|
"or": "nebo",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "Jiné",
|
"Other": "Jiné",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Extrahování obrázků z PDF (OCR)",
|
"PDF Extract Images (OCR)": "Extrahování obrázků z PDF (OCR)",
|
||||||
"pending": "čeká na vyřízení",
|
"pending": "čeká na vyřízení",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Odmítnutí povolení při přístupu k mediálním zařízením",
|
"Permission denied when accessing media devices": "Odmítnutí povolení při přístupu k mediálním zařízením",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Oprávnění zamítnuto při přístupu k mikrofonu: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Oprávnění zamítnuto při přístupu k mikrofonu: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalizace",
|
"Personalization": "Personalizace",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Předchozích 30 dnů",
|
"Previous 30 days": "Předchozích 30 dnů",
|
||||||
"Previous 7 days": "Předchozích 7 dní",
|
"Previous 7 days": "Předchozích 7 dní",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Profilový obrázek",
|
"Profile Image": "Profilový obrázek",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "og {{COUNT}} mere",
|
"and {{COUNT}} more": "og {{COUNT}} mere",
|
||||||
"and create a new shared link.": "og lav et nyt link til deling",
|
"and create a new shared link.": "og lav et nyt link til deling",
|
||||||
"Android": "Android",
|
"Android": "Android",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Base URL",
|
"API Base URL": "API Base URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API nøgle",
|
"API Key": "API nøgle",
|
||||||
"API Key created.": "API nøgle lavet",
|
"API Key created.": "API nøgle lavet",
|
||||||
"API Key Endpoint Restrictions": "API nøgler endpoint forbehold",
|
"API Key Endpoint Restrictions": "API nøgler endpoint forbehold",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Er du sikker på du vil slette denne kanal?",
|
"Are you sure you want to delete this channel?": "Er du sikker på du vil slette denne kanal?",
|
||||||
"Are you sure you want to delete this message?": "Er du sikker på du vil slette denne besked?",
|
"Are you sure you want to delete this message?": "Er du sikker på du vil slette denne besked?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Er du sikker på du vil fjerne alle arkiverede chats?",
|
"Are you sure you want to unarchive all archived chats?": "Er du sikker på du vil fjerne alle arkiverede chats?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Er du sikker?",
|
"Are you sure?": "Er du sikker?",
|
||||||
"Arena Models": "Arena Modeller",
|
"Arena Models": "Arena Modeller",
|
||||||
"Artifacts": "Artifakter",
|
"Artifacts": "Artifakter",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Indtast overlapning af tekststykker",
|
"Enter Chunk Overlap": "Indtast overlapning af tekststykker",
|
||||||
"Enter Chunk Size": "Indtast størrelse af tekststykker",
|
"Enter Chunk Size": "Indtast størrelse af tekststykker",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Indtast beskrivelse",
|
"Enter description": "Indtast beskrivelse",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Mappe fjernet.",
|
"Folder deleted successfully": "Mappe fjernet.",
|
||||||
"Folder name cannot be empty.": "Mappenavn kan ikke være tom.",
|
"Folder name cannot be empty.": "Mappenavn kan ikke være tom.",
|
||||||
"Folder name updated successfully": "Mappenavn opdateret.",
|
"Folder name updated successfully": "Mappenavn opdateret.",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Fulgte instruktionerne perfekt",
|
"Followed instructions perfectly": "Fulgte instruktionerne perfekt",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Ny adgangskode",
|
"New Password": "Ny adgangskode",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "Næste besked",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "Intet indhold",
|
"No content": "Intet indhold",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/nøgle påkrævet.",
|
"OpenAI URL/Key required.": "OpenAI URL/nøgle påkrævet.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "eller",
|
"or": "eller",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "Andet",
|
"Other": "Andet",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF-dokument (.pdf)",
|
"PDF document (.pdf)": "PDF-dokument (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Udtræk billeder fra PDF (OCR)",
|
"PDF Extract Images (OCR)": "Udtræk billeder fra PDF (OCR)",
|
||||||
"pending": "afventer",
|
"pending": "afventer",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Tilladelse nægtet ved adgang til medieenheder",
|
"Permission denied when accessing media devices": "Tilladelse nægtet ved adgang til medieenheder",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Tilladelse nægtet ved adgang til mikrofon: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Tilladelse nægtet ved adgang til mikrofon: {{error}}",
|
||||||
"Permissions": "Tilladelser",
|
"Permissions": "Tilladelser",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalisering",
|
"Personalization": "Personalisering",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Fastgør",
|
"Pin": "Fastgør",
|
||||||
"Pinned": "Fastgjort",
|
"Pinned": "Fastgjort",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Seneste 30 dage",
|
"Previous 30 days": "Seneste 30 dage",
|
||||||
"Previous 7 days": "Seneste 7 dage",
|
"Previous 7 days": "Seneste 7 dage",
|
||||||
|
"Previous message": "Forrige besked",
|
||||||
"Private": "Privat",
|
"Private": "Privat",
|
||||||
"Profile Image": "Profilbillede",
|
"Profile Image": "Profilbillede",
|
||||||
"Prompt": "Prompt",
|
"Prompt": "Prompt",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "und {{COUNT}} mehr",
|
"and {{COUNT}} more": "und {{COUNT}} mehr",
|
||||||
"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
|
"and create a new shared link.": "und erstellen Sie einen neuen freigegebenen Link.",
|
||||||
"Android": "Android",
|
"Android": "Android",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API-Basis-URL",
|
"API Base URL": "API-Basis-URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API-Schlüssel",
|
"API Key": "API-Schlüssel",
|
||||||
"API Key created.": "API-Schlüssel erstellt.",
|
"API Key created.": "API-Schlüssel erstellt.",
|
||||||
"API Key Endpoint Restrictions": "API-Schlüssel Endpunkteinschränkungen",
|
"API Key Endpoint Restrictions": "API-Schlüssel Endpunkteinschränkungen",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Sind Sie sicher, dass Sie diesen Kanal löschen möchten?",
|
"Are you sure you want to delete this channel?": "Sind Sie sicher, dass Sie diesen Kanal löschen möchten?",
|
||||||
"Are you sure you want to delete this message?": "Sind Sie sicher, dass Sie diese Nachricht löschen möchten?",
|
"Are you sure you want to delete this message?": "Sind Sie sicher, dass Sie diese Nachricht löschen möchten?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Sind Sie sicher, dass Sie alle archivierten Chats wiederherstellen möchten?",
|
"Are you sure you want to unarchive all archived chats?": "Sind Sie sicher, dass Sie alle archivierten Chats wiederherstellen möchten?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "Sind Sie sicher, dass Sie die Rolle dieses Benutzers auf **{{ROLE}}** ändern möchten?",
|
|
||||||
"Are you sure?": "Sind Sie sicher?",
|
"Are you sure?": "Sind Sie sicher?",
|
||||||
"Arena Models": "Arena-Modelle",
|
"Arena Models": "Arena-Modelle",
|
||||||
"Artifacts": "Artefakte",
|
"Artifacts": "Artefakte",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Geben Sie die Blocküberlappung ein",
|
"Enter Chunk Overlap": "Geben Sie die Blocküberlappung ein",
|
||||||
"Enter Chunk Size": "Geben Sie die Blockgröße ein",
|
"Enter Chunk Size": "Geben Sie die Blockgröße ein",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Geben Sie kommagetrennte \"token:bias_value\"-Paare ein (Beispiel: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Geben Sie kommagetrennte \"token:bias_value\"-Paare ein (Beispiel: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "Geben Sie Inhalt für das Overlay 'Ausstehende Kontoaktivierung' ein. Für Standard leer lassen.",
|
"Enter content for the pending user info overlay. Leave empty for default.": "Geben Sie Inhalt für das Overlay 'Ausstehende Kontoaktivierung' ein. Für Standard leer lassen.",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Geben Sie eine Beschreibung ein",
|
"Enter description": "Geben Sie eine Beschreibung ein",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Ordner erfolgreich gelöscht",
|
"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",
|
"Folder name updated successfully": "Ordnername erfolgreich aktualisiert",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Anweisungen perfekt befolgt",
|
"Followed instructions perfectly": "Anweisungen perfekt befolgt",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Neues Passwort",
|
"New Password": "Neues Passwort",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "neuer-kanal",
|
"new-channel": "neuer-kanal",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "Kein Inhalt",
|
"No content": "Kein Inhalt",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert",
|
"OpenAI API settings updated": "OpenAI-API-Einstellungen aktualisiert",
|
||||||
"OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.",
|
"OpenAI URL/Key required.": "OpenAI-URL/Schlüssel erforderlich.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "oder",
|
"or": "oder",
|
||||||
"Organize your users": "Organisieren Sie Ihre Benutzer",
|
"Organize your users": "Organisieren Sie Ihre Benutzer",
|
||||||
"Other": "Andere",
|
"Other": "Andere",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF-Dokument (.pdf)",
|
"PDF document (.pdf)": "PDF-Dokument (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)",
|
"PDF Extract Images (OCR)": "Text von Bildern aus PDFs extrahieren (OCR)",
|
||||||
"pending": "ausstehend",
|
"pending": "ausstehend",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "Inhalt des Overlays 'Ausstehende Kontoaktivierung'",
|
"Pending User Overlay Content": "Inhalt des Overlays 'Ausstehende Kontoaktivierung'",
|
||||||
"Pending User Overlay Title": "Titel des Overlays 'Ausstehende Kontoaktivierung'",
|
"Pending User Overlay Title": "Titel des Overlays 'Ausstehende Kontoaktivierung'",
|
||||||
"Permission denied when accessing media devices": "Zugriff auf Mediengeräte verweigert",
|
"Permission denied when accessing media devices": "Zugriff auf Mediengeräte verweigert",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Zugriff auf das Mikrofon verweigert: {{error}}",
|
||||||
"Permissions": "Berechtigungen",
|
"Permissions": "Berechtigungen",
|
||||||
"Perplexity API Key": "Perplexity API-Schlüssel",
|
"Perplexity API Key": "Perplexity API-Schlüssel",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalisierung",
|
"Personalization": "Personalisierung",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Anheften",
|
"Pin": "Anheften",
|
||||||
"Pinned": "Angeheftet",
|
"Pinned": "Angeheftet",
|
||||||
"Pioneer insights": "Bahnbrechende Erkenntnisse",
|
"Pioneer insights": "Bahnbrechende Erkenntnisse",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "Vorschau",
|
"Preview": "Vorschau",
|
||||||
"Previous 30 days": "Vorherige 30 Tage",
|
"Previous 30 days": "Vorherige 30 Tage",
|
||||||
"Previous 7 days": "Vorherige 7 Tage",
|
"Previous 7 days": "Vorherige 7 Tage",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Privat",
|
"Private": "Privat",
|
||||||
"Profile Image": "Profilbild",
|
"Profile Image": "Profilbild",
|
||||||
"Prompt": "Prompt",
|
"Prompt": "Prompt",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "",
|
"and create a new shared link.": "",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Base URL",
|
"API Base URL": "API Base URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API Key",
|
"API Key": "API Key",
|
||||||
"API Key created.": "",
|
"API Key created.": "",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Such certainty?",
|
"Are you sure?": "Such certainty?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Enter Overlap of Chunks",
|
"Enter Chunk Overlap": "Enter Overlap of Chunks",
|
||||||
"Enter Chunk Size": "Enter Size of Chunk",
|
"Enter Chunk Size": "Enter Size of Chunk",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "",
|
"Followed instructions perfectly": "",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "New Barkword",
|
"New Password": "New Barkword",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "",
|
"OpenAI URL/Key required.": "",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "",
|
"Other": "",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "",
|
"PDF document (.pdf)": "",
|
||||||
"PDF Extract Images (OCR)": "PDF Extract Wowmages (OCR)",
|
"PDF Extract Images (OCR)": "PDF Extract Wowmages (OCR)",
|
||||||
"pending": "pending",
|
"pending": "pending",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Permission denied when accessing microphone: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalization",
|
"Personalization": "Personalization",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "",
|
"Previous 30 days": "",
|
||||||
"Previous 7 days": "",
|
"Previous 7 days": "",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "",
|
"Profile Image": "",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "και {{COUNT}} ακόμα",
|
"and {{COUNT}} more": "και {{COUNT}} ακόμα",
|
||||||
"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
|
"and create a new shared link.": "και δημιουργήστε έναν νέο κοινόχρηστο σύνδεσμο.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Βασικό URL",
|
"API Base URL": "API Βασικό URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Κλειδί API",
|
"API Key": "Κλειδί API",
|
||||||
"API Key created.": "Το κλειδί API δημιουργήθηκε.",
|
"API Key created.": "Το κλειδί API δημιουργήθηκε.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Είστε σίγουροι ότι θέλετε να απο-αρχειοθετήσετε όλες τις αρχειοθετημένες συνομιλίες;",
|
"Are you sure you want to unarchive all archived chats?": "Είστε σίγουροι ότι θέλετε να απο-αρχειοθετήσετε όλες τις αρχειοθετημένες συνομιλίες;",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Είστε σίγουροι;",
|
"Are you sure?": "Είστε σίγουροι;",
|
||||||
"Arena Models": "Μοντέλα Arena",
|
"Arena Models": "Μοντέλα Arena",
|
||||||
"Artifacts": "Αρχεία",
|
"Artifacts": "Αρχεία",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Εισάγετε την Επικάλυψη Τμημάτων",
|
"Enter Chunk Overlap": "Εισάγετε την Επικάλυψη Τμημάτων",
|
||||||
"Enter Chunk Size": "Εισάγετε το Μέγεθος Τμημάτων",
|
"Enter Chunk Size": "Εισάγετε το Μέγεθος Τμημάτων",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Εισάγετε την περιγραφή",
|
"Enter description": "Εισάγετε την περιγραφή",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Ο φάκελος διαγράφηκε με επιτυχία",
|
"Folder deleted successfully": "Ο φάκελος διαγράφηκε με επιτυχία",
|
||||||
"Folder name cannot be empty.": "Το όνομα του φακέλου δεν μπορεί να είναι κενό.",
|
"Folder name cannot be empty.": "Το όνομα του φακέλου δεν μπορεί να είναι κενό.",
|
||||||
"Folder name updated successfully": "Το όνομα του φακέλου ενημερώθηκε με επιτυχία",
|
"Folder name updated successfully": "Το όνομα του φακέλου ενημερώθηκε με επιτυχία",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Ακολούθησε τις οδηγίες τέλεια",
|
"Followed instructions perfectly": "Ακολούθησε τις οδηγίες τέλεια",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Νέος Κωδικός",
|
"New Password": "Νέος Κωδικός",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "Οι ρυθμίσεις API OpenAI ενημερώθηκαν",
|
"OpenAI API settings updated": "Οι ρυθμίσεις API OpenAI ενημερώθηκαν",
|
||||||
"OpenAI URL/Key required.": "Απαιτείται URL/Kλειδί OpenAI.",
|
"OpenAI URL/Key required.": "Απαιτείται URL/Kλειδί OpenAI.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "ή",
|
"or": "ή",
|
||||||
"Organize your users": "Οργανώστε τους χρήστες σας",
|
"Organize your users": "Οργανώστε τους χρήστες σας",
|
||||||
"Other": "Άλλο",
|
"Other": "Άλλο",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Έγγραφο PDF (.pdf)",
|
"PDF document (.pdf)": "Έγγραφο PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Εξαγωγή Εικόνων PDF (OCR)",
|
"PDF Extract Images (OCR)": "Εξαγωγή Εικόνων PDF (OCR)",
|
||||||
"pending": "εκκρεμεί",
|
"pending": "εκκρεμεί",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Άρνηση δικαιώματος κατά την πρόσβαση σε μέσα συσκευές",
|
"Permission denied when accessing media devices": "Άρνηση δικαιώματος κατά την πρόσβαση σε μέσα συσκευές",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Άρνηση δικαιώματος κατά την πρόσβαση σε μικρόφωνο: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Άρνηση δικαιώματος κατά την πρόσβαση σε μικρόφωνο: {{error}}",
|
||||||
"Permissions": "Δικαιώματα",
|
"Permissions": "Δικαιώματα",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Προσωποποίηση",
|
"Personalization": "Προσωποποίηση",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Καρφίτσωμα",
|
"Pin": "Καρφίτσωμα",
|
||||||
"Pinned": "Καρφιτσωμένο",
|
"Pinned": "Καρφιτσωμένο",
|
||||||
"Pioneer insights": "Συμβουλές πρωτοπόρων",
|
"Pioneer insights": "Συμβουλές πρωτοπόρων",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Προηγούμενες 30 ημέρες",
|
"Previous 30 days": "Προηγούμενες 30 ημέρες",
|
||||||
"Previous 7 days": "Προηγούμενες 7 ημέρες",
|
"Previous 7 days": "Προηγούμενες 7 ημέρες",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Εικόνα Προφίλ",
|
"Profile Image": "Εικόνα Προφίλ",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "",
|
"and create a new shared link.": "",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "",
|
"API Base URL": "",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "",
|
"API Key": "",
|
||||||
"API Key created.": "",
|
"API Key created.": "",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "",
|
"Are you sure?": "",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "",
|
"Enter Chunk Overlap": "",
|
||||||
"Enter Chunk Size": "",
|
"Enter Chunk Size": "",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "",
|
"Followed instructions perfectly": "",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "",
|
"New Password": "",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "",
|
"OpenAI URL/Key required.": "",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "",
|
"or": "",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "",
|
"Other": "",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "",
|
"PDF document (.pdf)": "",
|
||||||
"PDF Extract Images (OCR)": "",
|
"PDF Extract Images (OCR)": "",
|
||||||
"pending": "",
|
"pending": "",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "",
|
"Permission denied when accessing microphone: {{error}}": "",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "",
|
"Personalization": "",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "",
|
"Previous 30 days": "",
|
||||||
"Previous 7 days": "",
|
"Previous 7 days": "",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "",
|
"Profile Image": "",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "",
|
"and create a new shared link.": "",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "",
|
"API Base URL": "",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "",
|
"API Key": "",
|
||||||
"API Key created.": "",
|
"API Key created.": "",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "",
|
"Are you sure?": "",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "",
|
"Enter Chunk Overlap": "",
|
||||||
"Enter Chunk Size": "",
|
"Enter Chunk Size": "",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "",
|
"Followed instructions perfectly": "",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "",
|
"New Password": "",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "Next message",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "",
|
"OpenAI URL/Key required.": "",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "",
|
"or": "",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "",
|
"Other": "",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "",
|
"PDF document (.pdf)": "",
|
||||||
"PDF Extract Images (OCR)": "",
|
"PDF Extract Images (OCR)": "",
|
||||||
"pending": "",
|
"pending": "",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "",
|
"Permission denied when accessing microphone: {{error}}": "",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "",
|
"Personalization": "",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "",
|
"Previous 30 days": "",
|
||||||
"Previous 7 days": "",
|
"Previous 7 days": "",
|
||||||
|
"Previous message": "Previous message",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "",
|
"Profile Image": "",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "y {{COUNT}} más",
|
"and {{COUNT}} more": "y {{COUNT}} más",
|
||||||
"and create a new shared link.": "y crear un nuevo enlace compartido.",
|
"and create a new shared link.": "y crear un nuevo enlace compartido.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "URL Base API",
|
"API Base URL": "URL Base API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Clave API ",
|
"API Key": "Clave API ",
|
||||||
"API Key created.": "Clave API creada.",
|
"API Key created.": "Clave API creada.",
|
||||||
"API Key Endpoint Restrictions": "Clave API para Endpoints Restringidos",
|
"API Key Endpoint Restrictions": "Clave API para Endpoints Restringidos",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "¿Seguro de que quieres eliminar este canal?",
|
"Are you sure you want to delete this channel?": "¿Seguro de que quieres eliminar este canal?",
|
||||||
"Are you sure you want to delete this message?": "¿Seguro de que quieres eliminar este mensaje? ",
|
"Are you sure you want to delete this message?": "¿Seguro de que quieres eliminar este mensaje? ",
|
||||||
"Are you sure you want to unarchive all archived chats?": "¿Seguro de que quieres desarchivar todos los chats archivados?",
|
"Are you sure you want to unarchive all archived chats?": "¿Seguro de que quieres desarchivar todos los chats archivados?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "¿Estás seguro?",
|
"Are you sure?": "¿Estás seguro?",
|
||||||
"Arena Models": "Arena de Modelos",
|
"Arena Models": "Arena de Modelos",
|
||||||
"Artifacts": "Artefactos",
|
"Artifacts": "Artefactos",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Ingresar Superposición de los Fragmentos",
|
"Enter Chunk Overlap": "Ingresar Superposición de los Fragmentos",
|
||||||
"Enter Chunk Size": "Ingresar el Tamaño del Fragmento",
|
"Enter Chunk Size": "Ingresar el Tamaño del Fragmento",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Ingresar pares \"token:valor_sesgo\" separados por comas (ejemplo: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Ingresar pares \"token:valor_sesgo\" separados por comas (ejemplo: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Ingresar Descripción",
|
"Enter description": "Ingresar Descripción",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Carpeta eliminada correctamente",
|
"Folder deleted successfully": "Carpeta eliminada correctamente",
|
||||||
"Folder name cannot be empty.": "El nombre de la carpeta no puede estar vacío",
|
"Folder name cannot be empty.": "El nombre de la carpeta no puede estar vacío",
|
||||||
"Folder name updated successfully": "Nombre de la carpeta actualizado correctamente",
|
"Folder name updated successfully": "Nombre de la carpeta actualizado correctamente",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Siguió las instrucciones perfectamente",
|
"Followed instructions perfectly": "Siguió las instrucciones perfectamente",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nueva Contraseña",
|
"New Password": "Nueva Contraseña",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "nuevo-canal",
|
"new-channel": "nuevo-canal",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "Ajustes de API OpenAI actualizados",
|
"OpenAI API settings updated": "Ajustes de API OpenAI actualizados",
|
||||||
"OpenAI URL/Key required.": "URL/Clave de OpenAI requerida.",
|
"OpenAI URL/Key required.": "URL/Clave de OpenAI requerida.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "o",
|
"or": "o",
|
||||||
"Organize your users": "Organiza tus usuarios",
|
"Organize your users": "Organiza tus usuarios",
|
||||||
"Other": "Otro",
|
"Other": "Otro",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Documento PDF (.pdf)",
|
"PDF document (.pdf)": "Documento PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Extraer imágenes del PDF (OCR)",
|
"PDF Extract Images (OCR)": "Extraer imágenes del PDF (OCR)",
|
||||||
"pending": "pendiente",
|
"pending": "pendiente",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Permiso denegado accediendo a los dispositivos",
|
"Permission denied when accessing media devices": "Permiso denegado accediendo a los dispositivos",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Permiso denegado accediendo al micrófono: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Permiso denegado accediendo al micrófono: {{error}}",
|
||||||
"Permissions": "Permisos",
|
"Permissions": "Permisos",
|
||||||
"Perplexity API Key": "Clave API de Perplexity",
|
"Perplexity API Key": "Clave API de Perplexity",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalización",
|
"Personalization": "Personalización",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Fijar",
|
"Pin": "Fijar",
|
||||||
"Pinned": "Fijado",
|
"Pinned": "Fijado",
|
||||||
"Pioneer insights": "Descubrir nuevas perspectivas",
|
"Pioneer insights": "Descubrir nuevas perspectivas",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 días previos",
|
"Previous 30 days": "30 días previos",
|
||||||
"Previous 7 days": "7 días previos",
|
"Previous 7 days": "7 días previos",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Privado",
|
"Private": "Privado",
|
||||||
"Profile Image": "Imagen del Perfil",
|
"Profile Image": "Imagen del Perfil",
|
||||||
"Prompt": "Prompt",
|
"Prompt": "Prompt",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "ja veel {{COUNT}}",
|
"and {{COUNT}} more": "ja veel {{COUNT}}",
|
||||||
"and create a new shared link.": "ja looge uus jagatud link.",
|
"and create a new shared link.": "ja looge uus jagatud link.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API baas-URL",
|
"API Base URL": "API baas-URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API võti",
|
"API Key": "API võti",
|
||||||
"API Key created.": "API võti loodud.",
|
"API Key created.": "API võti loodud.",
|
||||||
"API Key Endpoint Restrictions": "API võtme lõpp-punkti piirangud",
|
"API Key Endpoint Restrictions": "API võtme lõpp-punkti piirangud",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Kas olete kindel, et soovite selle kanali kustutada?",
|
"Are you sure you want to delete this channel?": "Kas olete kindel, et soovite selle kanali kustutada?",
|
||||||
"Are you sure you want to delete this message?": "Kas olete kindel, et soovite selle sõnumi kustutada?",
|
"Are you sure you want to delete this message?": "Kas olete kindel, et soovite selle sõnumi kustutada?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Kas olete kindel, et soovite kõik arhiveeritud vestlused arhiivist eemaldada?",
|
"Are you sure you want to unarchive all archived chats?": "Kas olete kindel, et soovite kõik arhiveeritud vestlused arhiivist eemaldada?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Kas olete kindel?",
|
"Are you sure?": "Kas olete kindel?",
|
||||||
"Arena Models": "Areena mudelid",
|
"Arena Models": "Areena mudelid",
|
||||||
"Artifacts": "Tekkinud objektid",
|
"Artifacts": "Tekkinud objektid",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Sisestage tükkide ülekate",
|
"Enter Chunk Overlap": "Sisestage tükkide ülekate",
|
||||||
"Enter Chunk Size": "Sisestage tüki suurus",
|
"Enter Chunk Size": "Sisestage tüki suurus",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Sisestage komadega eraldatud \"token:kallutuse_väärtus\" paarid (näide: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Sisestage komadega eraldatud \"token:kallutuse_väärtus\" paarid (näide: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Sisestage kirjeldus",
|
"Enter description": "Sisestage kirjeldus",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Kaust edukalt kustutatud",
|
"Folder deleted successfully": "Kaust edukalt kustutatud",
|
||||||
"Folder name cannot be empty.": "Kausta nimi ei saa olla tühi.",
|
"Folder name cannot be empty.": "Kausta nimi ei saa olla tühi.",
|
||||||
"Folder name updated successfully": "Kausta nimi edukalt uuendatud",
|
"Folder name updated successfully": "Kausta nimi edukalt uuendatud",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Järgis juhiseid täiuslikult",
|
"Followed instructions perfectly": "Järgis juhiseid täiuslikult",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Uus parool",
|
"New Password": "Uus parool",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "uus-kanal",
|
"new-channel": "uus-kanal",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI API seaded uuendatud",
|
"OpenAI API settings updated": "OpenAI API seaded uuendatud",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/võti on nõutav.",
|
"OpenAI URL/Key required.": "OpenAI URL/võti on nõutav.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "või",
|
"or": "või",
|
||||||
"Organize your users": "Korraldage oma kasutajad",
|
"Organize your users": "Korraldage oma kasutajad",
|
||||||
"Other": "Muu",
|
"Other": "Muu",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF-ist piltide väljavõtmine (OCR)",
|
"PDF Extract Images (OCR)": "PDF-ist piltide väljavõtmine (OCR)",
|
||||||
"pending": "ootel",
|
"pending": "ootel",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Juurdepääs meediumiseadmetele keelatud",
|
"Permission denied when accessing media devices": "Juurdepääs meediumiseadmetele keelatud",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Juurdepääs mikrofonile keelatud: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Juurdepääs mikrofonile keelatud: {{error}}",
|
||||||
"Permissions": "Õigused",
|
"Permissions": "Õigused",
|
||||||
"Perplexity API Key": "Perplexity API võti",
|
"Perplexity API Key": "Perplexity API võti",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Isikupärastamine",
|
"Personalization": "Isikupärastamine",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Kinnita",
|
"Pin": "Kinnita",
|
||||||
"Pinned": "Kinnitatud",
|
"Pinned": "Kinnitatud",
|
||||||
"Pioneer insights": "Pioneeri arusaamad",
|
"Pioneer insights": "Pioneeri arusaamad",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Eelmised 30 päeva",
|
"Previous 30 days": "Eelmised 30 päeva",
|
||||||
"Previous 7 days": "Eelmised 7 päeva",
|
"Previous 7 days": "Eelmised 7 päeva",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Profiilipilt",
|
"Profile Image": "Profiilipilt",
|
||||||
"Prompt": "Vihje",
|
"Prompt": "Vihje",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "eta {{COUNT}} gehiago",
|
"and {{COUNT}} more": "eta {{COUNT}} gehiago",
|
||||||
"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
|
"and create a new shared link.": "eta sortu partekatutako esteka berri bat.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API Oinarri URLa",
|
"API Base URL": "API Oinarri URLa",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API Gakoa",
|
"API Key": "API Gakoa",
|
||||||
"API Key created.": "API Gakoa sortu da.",
|
"API Key created.": "API Gakoa sortu da.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Ziur zaude artxibatutako txat guztiak desartxibatu nahi dituzula?",
|
"Are you sure you want to unarchive all archived chats?": "Ziur zaude artxibatutako txat guztiak desartxibatu nahi dituzula?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Ziur zaude?",
|
"Are you sure?": "Ziur zaude?",
|
||||||
"Arena Models": "Arena Ereduak",
|
"Arena Models": "Arena Ereduak",
|
||||||
"Artifacts": "Artefaktuak",
|
"Artifacts": "Artefaktuak",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Sartu Zatien Gainjartzea (chunk overlap)",
|
"Enter Chunk Overlap": "Sartu Zatien Gainjartzea (chunk overlap)",
|
||||||
"Enter Chunk Size": "Sartu Zati Tamaina",
|
"Enter Chunk Size": "Sartu Zati Tamaina",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Sartu deskribapena",
|
"Enter description": "Sartu deskribapena",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Karpeta ongi ezabatu da",
|
"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",
|
"Folder name updated successfully": "Karpetaren izena ongi eguneratu da",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Jarraibideak perfektuki jarraitu ditu",
|
"Followed instructions perfectly": "Jarraibideak perfektuki jarraitu ditu",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Pasahitz berria",
|
"New Password": "Pasahitz berria",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI API ezarpenak eguneratu dira",
|
"OpenAI API settings updated": "OpenAI API ezarpenak eguneratu dira",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/Gakoa beharrezkoa da.",
|
"OpenAI URL/Key required.": "OpenAI URL/Gakoa beharrezkoa da.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "edo",
|
"or": "edo",
|
||||||
"Organize your users": "Antolatu zure erabiltzaileak",
|
"Organize your users": "Antolatu zure erabiltzaileak",
|
||||||
"Other": "Bestelakoa",
|
"Other": "Bestelakoa",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF dokumentua (.pdf)",
|
"PDF document (.pdf)": "PDF dokumentua (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF irudiak erauzi (OCR)",
|
"PDF Extract Images (OCR)": "PDF irudiak erauzi (OCR)",
|
||||||
"pending": "zain",
|
"pending": "zain",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Baimena ukatu da multimedia gailuak atzitzean",
|
"Permission denied when accessing media devices": "Baimena ukatu da multimedia gailuak atzitzean",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Baimena ukatu da mikrofonoa atzitzean: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Baimena ukatu da mikrofonoa atzitzean: {{error}}",
|
||||||
"Permissions": "Baimenak",
|
"Permissions": "Baimenak",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Pertsonalizazioa",
|
"Personalization": "Pertsonalizazioa",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Ainguratu",
|
"Pin": "Ainguratu",
|
||||||
"Pinned": "Ainguratuta",
|
"Pinned": "Ainguratuta",
|
||||||
"Pioneer insights": "Ikuspegi aitzindariak",
|
"Pioneer insights": "Ikuspegi aitzindariak",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Aurreko 30 egunak",
|
"Previous 30 days": "Aurreko 30 egunak",
|
||||||
"Previous 7 days": "Aurreko 7 egunak",
|
"Previous 7 days": "Aurreko 7 egunak",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Profil irudia",
|
"Profile Image": "Profil irudia",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
|
"and {{COUNT}} more": "و {{COUNT}} مورد دیگر",
|
||||||
"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
|
"and create a new shared link.": "و یک پیوند اشتراک\u200cگذاری جدید ایجاد کنید.",
|
||||||
"Android": "اندروید",
|
"Android": "اندروید",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "نشانی پایهٔ API",
|
"API Base URL": "نشانی پایهٔ API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "کلید API",
|
"API Key": "کلید API",
|
||||||
"API Key created.": "کلید API ساخته شد.",
|
"API Key created.": "کلید API ساخته شد.",
|
||||||
"API Key Endpoint Restrictions": "محدودیت\u200cهای نقطه پایانی کلید API",
|
"API Key Endpoint Restrictions": "محدودیت\u200cهای نقطه پایانی کلید API",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "آیا مطمئن هستید که می\u200cخواهید این کانال را حذف کنید؟",
|
"Are you sure you want to delete this channel?": "آیا مطمئن هستید که می\u200cخواهید این کانال را حذف کنید؟",
|
||||||
"Are you sure you want to delete this message?": "آیا مطمئن هستید که می\u200cخواهید این پیام را حذف کنید؟",
|
"Are you sure you want to delete this message?": "آیا مطمئن هستید که می\u200cخواهید این پیام را حذف کنید؟",
|
||||||
"Are you sure you want to unarchive all archived chats?": "آیا مطمئن هستید که می\u200cخواهید همه گفتگوهای بایگانی شده را از بایگانی خارج کنید؟",
|
"Are you sure you want to unarchive all archived chats?": "آیا مطمئن هستید که می\u200cخواهید همه گفتگوهای بایگانی شده را از بایگانی خارج کنید؟",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "مطمئنید؟",
|
"Are you sure?": "مطمئنید؟",
|
||||||
"Arena Models": "مدل\u200cهای آرنا",
|
"Arena Models": "مدل\u200cهای آرنا",
|
||||||
"Artifacts": "مصنوعات",
|
"Artifacts": "مصنوعات",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "مقدار Chunk Overlap را وارد کنید",
|
"Enter Chunk Overlap": "مقدار Chunk Overlap را وارد کنید",
|
||||||
"Enter Chunk Size": "مقدار Chunk Size را وارد کنید",
|
"Enter Chunk Size": "مقدار Chunk Size را وارد کنید",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "جفت\u200cهای \"توکن:مقدار_بایاس\" را با کاما جدا شده وارد کنید (مثال: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "جفت\u200cهای \"توکن:مقدار_بایاس\" را با کاما جدا شده وارد کنید (مثال: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "توضیحات را وارد کنید",
|
"Enter description": "توضیحات را وارد کنید",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "پوشه با موفقیت حذف شد",
|
"Folder deleted successfully": "پوشه با موفقیت حذف شد",
|
||||||
"Folder name cannot be empty.": "نام پوشه نمی\u200cتواند خالی باشد.",
|
"Folder name cannot be empty.": "نام پوشه نمی\u200cتواند خالی باشد.",
|
||||||
"Folder name updated successfully": "نام پوشه با موفقیت به\u200cروز شد",
|
"Folder name updated successfully": "نام پوشه با موفقیت به\u200cروز شد",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "دستورالعمل ها را کاملا دنبال کرد",
|
"Followed instructions perfectly": "دستورالعمل ها را کاملا دنبال کرد",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "رمز عبور جدید",
|
"New Password": "رمز عبور جدید",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "کانال-جدید",
|
"new-channel": "کانال-جدید",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "تنظیمات API اوپن\u200cای\u200cآی به\u200cروز شد",
|
"OpenAI API settings updated": "تنظیمات API اوپن\u200cای\u200cآی به\u200cروز شد",
|
||||||
"OpenAI URL/Key required.": "URL/Key OpenAI مورد نیاز است.",
|
"OpenAI URL/Key required.": "URL/Key OpenAI مورد نیاز است.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "یا",
|
"or": "یا",
|
||||||
"Organize your users": "کاربران خود را سازماندهی کنید",
|
"Organize your users": "کاربران خود را سازماندهی کنید",
|
||||||
"Other": "دیگر",
|
"Other": "دیگر",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF سند (.pdf)",
|
"PDF document (.pdf)": "PDF سند (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "استخراج تصاویر از PDF (OCR)",
|
"PDF Extract Images (OCR)": "استخراج تصاویر از PDF (OCR)",
|
||||||
"pending": "در انتظار",
|
"pending": "در انتظار",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "دسترسی به دستگاه\u200cهای رسانه رد شد",
|
"Permission denied when accessing media devices": "دسترسی به دستگاه\u200cهای رسانه رد شد",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "هنگام دسترسی به میکروفون، اجازه داده نشد: {{error}}",
|
||||||
"Permissions": "مجوزها",
|
"Permissions": "مجوزها",
|
||||||
"Perplexity API Key": "کلید API پرپلکسیتی",
|
"Perplexity API Key": "کلید API پرپلکسیتی",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "شخصی سازی",
|
"Personalization": "شخصی سازی",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "پین کردن",
|
"Pin": "پین کردن",
|
||||||
"Pinned": "پین شده",
|
"Pinned": "پین شده",
|
||||||
"Pioneer insights": "بینش\u200cهای پیشگام",
|
"Pioneer insights": "بینش\u200cهای پیشگام",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 روز قبل",
|
"Previous 30 days": "30 روز قبل",
|
||||||
"Previous 7 days": "7 روز قبل",
|
"Previous 7 days": "7 روز قبل",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "خصوصی",
|
"Private": "خصوصی",
|
||||||
"Profile Image": "تصویر پروفایل",
|
"Profile Image": "تصویر پروفایل",
|
||||||
"Prompt": "پرامپت",
|
"Prompt": "پرامپت",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "ja {{COUNT}} muuta",
|
"and {{COUNT}} more": "ja {{COUNT}} muuta",
|
||||||
"and create a new shared link.": "ja luo uusi jaettu linkki.",
|
"and create a new shared link.": "ja luo uusi jaettu linkki.",
|
||||||
"Android": "Android",
|
"Android": "Android",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API:n verkko-osoite",
|
"API Base URL": "API:n verkko-osoite",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API-avain",
|
"API Key": "API-avain",
|
||||||
"API Key created.": "API-avain luotu.",
|
"API Key created.": "API-avain luotu.",
|
||||||
"API Key Endpoint Restrictions": "API-avaimen päätepiste rajoitukset",
|
"API Key Endpoint Restrictions": "API-avaimen päätepiste rajoitukset",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Haluatko varmasti poistaa tämän kanavan?",
|
"Are you sure you want to delete this channel?": "Haluatko varmasti poistaa tämän kanavan?",
|
||||||
"Are you sure you want to delete this message?": "Haluatko varmasti poistaa tämän viestin?",
|
"Are you sure you want to delete this message?": "Haluatko varmasti poistaa tämän viestin?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Haluatko varmasti purkaa kaikkien arkistoitujen keskustelujen arkistoinnin?",
|
"Are you sure you want to unarchive all archived chats?": "Haluatko varmasti purkaa kaikkien arkistoitujen keskustelujen arkistoinnin?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "Haluatko varmasti päivittää tämän käyttäjän roolin **{{ROLE}}** tähän?",
|
|
||||||
"Are you sure?": "Oletko varma?",
|
"Are you sure?": "Oletko varma?",
|
||||||
"Arena Models": "Arena-mallit",
|
"Arena Models": "Arena-mallit",
|
||||||
"Artifacts": "Artefaktit",
|
"Artifacts": "Artefaktit",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Syötä osien päällekkäisyys",
|
"Enter Chunk Overlap": "Syötä osien päällekkäisyys",
|
||||||
"Enter Chunk Size": "Syötä osien koko",
|
"Enter Chunk Size": "Syötä osien koko",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Syötä pilkulla erottaen \"token:bias_value\" parit (esim. 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Syötä pilkulla erottaen \"token:bias_value\" parit (esim. 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "Kirjoita odottavien käyttäjien infon tekstisisältö. Käytä oletusta jättämällä tyhjäksi.",
|
"Enter content for the pending user info overlay. Leave empty for default.": "Kirjoita odottavien käyttäjien infon tekstisisältö. Käytä oletusta jättämällä tyhjäksi.",
|
||||||
"Enter Datalab Marker API Key": "Kirjoita Datalab Marker API-avain",
|
"Enter Datalab Marker API Key": "Kirjoita Datalab Marker API-avain",
|
||||||
"Enter description": "Kirjoita kuvaus",
|
"Enter description": "Kirjoita kuvaus",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Kansio poistettu onnistuneesti",
|
"Folder deleted successfully": "Kansio poistettu onnistuneesti",
|
||||||
"Folder name cannot be empty.": "Kansion nimi ei voi olla tyhjä.",
|
"Folder name cannot be empty.": "Kansion nimi ei voi olla tyhjä.",
|
||||||
"Folder name updated successfully": "Kansion nimi päivitetty onnistuneesti",
|
"Folder name updated successfully": "Kansion nimi päivitetty onnistuneesti",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Noudatti ohjeita täydellisesti",
|
"Followed instructions perfectly": "Noudatti ohjeita täydellisesti",
|
||||||
"Force OCR": "Pakota OCR",
|
"Force OCR": "Pakota OCR",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "Pakota OCR:n käyttö kaikilla PDF-sivuilla. Tämä voi johtaa huonompiin tuloksiin jos PDF:n tekstisisältö on laadukasta. Oletusarvo, ei käytöstä.",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "Pakota OCR:n käyttö kaikilla PDF-sivuilla. Tämä voi johtaa huonompiin tuloksiin jos PDF:n tekstisisältö on laadukasta. Oletusarvo, ei käytöstä.",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Uusi salasana",
|
"New Password": "Uusi salasana",
|
||||||
"New Tool": "Uusi työkalu",
|
"New Tool": "Uusi työkalu",
|
||||||
"new-channel": "uusi-kanava",
|
"new-channel": "uusi-kanava",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "Käyttäjän keskusteluja ei löytynyt.",
|
"No chats found for this user.": "Käyttäjän keskusteluja ei löytynyt.",
|
||||||
"No chats found.": "Keskusteluja ei löytynyt",
|
"No chats found.": "Keskusteluja ei löytynyt",
|
||||||
"No content": "Ei sisältöä",
|
"No content": "Ei sisältöä",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI API -asetukset päivitetty",
|
"OpenAI API settings updated": "OpenAI API -asetukset päivitetty",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/avain vaaditaan.",
|
"OpenAI URL/Key required.": "OpenAI URL/avain vaaditaan.",
|
||||||
"openapi.json URL or Path": "openapi.json verkko-osoite tai polku",
|
"openapi.json URL or Path": "openapi.json verkko-osoite tai polku",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "tai",
|
"or": "tai",
|
||||||
"Organize your users": "Järjestä käyttäjäsi",
|
"Organize your users": "Järjestä käyttäjäsi",
|
||||||
"Other": "Muu",
|
"Other": "Muu",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF-asiakirja (.pdf)",
|
"PDF document (.pdf)": "PDF-asiakirja (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Poimi kuvat PDF:stä (OCR)",
|
"PDF Extract Images (OCR)": "Poimi kuvat PDF:stä (OCR)",
|
||||||
"pending": "odottaa",
|
"pending": "odottaa",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "Odottavien käyttäjien sisältö",
|
"Pending User Overlay Content": "Odottavien käyttäjien sisältö",
|
||||||
"Pending User Overlay Title": "Odottavien käyttäjien otsikko",
|
"Pending User Overlay Title": "Odottavien käyttäjien otsikko",
|
||||||
"Permission denied when accessing media devices": "Käyttöoikeus evätty media-laitteille",
|
"Permission denied when accessing media devices": "Käyttöoikeus evätty media-laitteille",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Käyttöoikeus evätty mikrofonille: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Käyttöoikeus evätty mikrofonille: {{error}}",
|
||||||
"Permissions": "Käyttöoikeudet",
|
"Permissions": "Käyttöoikeudet",
|
||||||
"Perplexity API Key": "Perplexity API-avain",
|
"Perplexity API Key": "Perplexity API-avain",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personointi",
|
"Personalization": "Personointi",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Kiinnitä",
|
"Pin": "Kiinnitä",
|
||||||
"Pinned": "Kiinnitetty",
|
"Pinned": "Kiinnitetty",
|
||||||
"Pioneer insights": "Pioneerin oivalluksia",
|
"Pioneer insights": "Pioneerin oivalluksia",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "Esikatselu",
|
"Preview": "Esikatselu",
|
||||||
"Previous 30 days": "Edelliset 30 päivää",
|
"Previous 30 days": "Edelliset 30 päivää",
|
||||||
"Previous 7 days": "Edelliset 7 päivää",
|
"Previous 7 days": "Edelliset 7 päivää",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Yksityinen",
|
"Private": "Yksityinen",
|
||||||
"Profile Image": "Profiilikuva",
|
"Profile Image": "Profiilikuva",
|
||||||
"Prompt": "Kehote",
|
"Prompt": "Kehote",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "et créer un nouveau lien partagé.",
|
"and create a new shared link.": "et créer un nouveau lien partagé.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "URL de base de l'API",
|
"API Base URL": "URL de base de l'API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Clé d'API",
|
"API Key": "Clé d'API",
|
||||||
"API Key created.": "Clé d'API générée.",
|
"API Key created.": "Clé d'API générée.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Êtes-vous certain ?",
|
"Are you sure?": "Êtes-vous certain ?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Entrez le chevauchement de chunk",
|
"Enter Chunk Overlap": "Entrez le chevauchement de chunk",
|
||||||
"Enter Chunk Size": "Entrez la taille de bloc",
|
"Enter Chunk Size": "Entrez la taille de bloc",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "A parfaitement suivi les instructions",
|
"Followed instructions perfectly": "A parfaitement suivi les instructions",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nouveau mot de passe",
|
"New Password": "Nouveau mot de passe",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
|
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "ou",
|
"or": "ou",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "Autre",
|
"Other": "Autre",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Document au format PDF (.pdf)",
|
"PDF document (.pdf)": "Document au format PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
||||||
"pending": "en attente",
|
"pending": "en attente",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
|
"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Permission refusée lors de l'accès au microphone : {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personnalisation",
|
"Personalization": "Personnalisation",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Épingler",
|
"Pin": "Épingler",
|
||||||
"Pinned": "Épinglé",
|
"Pinned": "Épinglé",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 derniers jours",
|
"Previous 30 days": "30 derniers jours",
|
||||||
"Previous 7 days": "7 derniers jours",
|
"Previous 7 days": "7 derniers jours",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Image de profil",
|
"Profile Image": "Image de profil",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "et {{COUNT}} autres",
|
"and {{COUNT}} more": "et {{COUNT}} autres",
|
||||||
"and create a new shared link.": "et créer un nouveau lien partagé.",
|
"and create a new shared link.": "et créer un nouveau lien partagé.",
|
||||||
"Android": "Android",
|
"Android": "Android",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "URL de base de l'API",
|
"API Base URL": "URL de base de l'API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Clé d'API",
|
"API Key": "Clé d'API",
|
||||||
"API Key created.": "Clé d'API générée.",
|
"API Key created.": "Clé d'API générée.",
|
||||||
"API Key Endpoint Restrictions": "Restrictions des points de terminaison de la clé API",
|
"API Key Endpoint Restrictions": "Restrictions des points de terminaison de la clé API",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Êtes-vous sûr de vouloir supprimer ce canal ?",
|
"Are you sure you want to delete this channel?": "Êtes-vous sûr de vouloir supprimer ce canal ?",
|
||||||
"Are you sure you want to delete this message?": "Êtes-vous sûr de vouloir supprimer ce message ?",
|
"Are you sure you want to delete this message?": "Êtes-vous sûr de vouloir supprimer ce message ?",
|
||||||
"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 you want to unarchive all archived chats?": "Êtes-vous sûr de vouloir désarchiver toutes les conversations archivées?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Êtes-vous certain ?",
|
"Are you sure?": "Êtes-vous certain ?",
|
||||||
"Arena Models": "Modèles d'arène",
|
"Arena Models": "Modèles d'arène",
|
||||||
"Artifacts": "Artéfacts",
|
"Artifacts": "Artéfacts",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Entrez le chevauchement des chunks",
|
"Enter Chunk Overlap": "Entrez le chevauchement des chunks",
|
||||||
"Enter Chunk Size": "Entrez la taille des chunks",
|
"Enter Chunk Size": "Entrez la taille des chunks",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Entrez la description",
|
"Enter description": "Entrez la description",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Dossier supprimé avec succès",
|
"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",
|
"Folder name updated successfully": "Le nom du dossier a été mis à jour avec succès",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "A parfaitement suivi les instructions",
|
"Followed instructions perfectly": "A parfaitement suivi les instructions",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nouveau mot de passe",
|
"New Password": "Nouveau mot de passe",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "nouveau-canal",
|
"new-channel": "nouveau-canal",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "Paramètres de l'API OpenAI mis à jour",
|
"OpenAI API settings updated": "Paramètres de l'API OpenAI mis à jour",
|
||||||
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
|
"OpenAI URL/Key required.": "URL/Clé OpenAI requise.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "ou",
|
"or": "ou",
|
||||||
"Organize your users": "Organisez vos utilisateurs",
|
"Organize your users": "Organisez vos utilisateurs",
|
||||||
"Other": "Autre",
|
"Other": "Autre",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Document au format PDF (.pdf)",
|
"PDF document (.pdf)": "Document au format PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
"PDF Extract Images (OCR)": "Extraction d'images PDF (OCR)",
|
||||||
"pending": "en attente",
|
"pending": "en attente",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
|
"Permission denied when accessing media devices": "Accès aux appareils multimédias refusé",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Accès au microphone refusé : {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Accès au microphone refusé : {{error}}",
|
||||||
"Permissions": "Permissions",
|
"Permissions": "Permissions",
|
||||||
"Perplexity API Key": "Clé d'API de Perplexity",
|
"Perplexity API Key": "Clé d'API de Perplexity",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personnalisation",
|
"Personalization": "Personnalisation",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Épingler",
|
"Pin": "Épingler",
|
||||||
"Pinned": "Épinglé",
|
"Pinned": "Épinglé",
|
||||||
"Pioneer insights": "Explorer de nouvelles perspectives",
|
"Pioneer insights": "Explorer de nouvelles perspectives",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 derniers jours",
|
"Previous 30 days": "30 derniers jours",
|
||||||
"Previous 7 days": "7 derniers jours",
|
"Previous 7 days": "7 derniers jours",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Privé",
|
"Private": "Privé",
|
||||||
"Profile Image": "Image de profil",
|
"Profile Image": "Image de profil",
|
||||||
"Prompt": "Prompt",
|
"Prompt": "Prompt",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
"(e.g. `sh webui.sh --api`)": "(למשל `sh webui.sh --api`)",
|
"(e.g. `sh webui.sh --api`)": "(למשל `sh webui.sh --api`)",
|
||||||
"(latest)": "(האחרון)",
|
"(latest)": "(האחרון)",
|
||||||
"(leave blank for to use commercial endpoint)": "",
|
"(leave blank for to use commercial endpoint)": "",
|
||||||
"{{ models }}": "{{ דגמים }}",
|
"{{ models }}": "{{ מודלים }}",
|
||||||
"{{COUNT}} Available Tools": "",
|
"{{COUNT}} Available Tools": "",
|
||||||
"{{COUNT}} hidden lines": "",
|
"{{COUNT}} hidden lines": "",
|
||||||
"{{COUNT}} Replies": "",
|
"{{COUNT}} Replies": "",
|
||||||
|
|
@ -17,39 +17,39 @@
|
||||||
"a user": "משתמש",
|
"a user": "משתמש",
|
||||||
"About": "אודות",
|
"About": "אודות",
|
||||||
"Accept autocomplete generation / Jump to prompt variable": "",
|
"Accept autocomplete generation / Jump to prompt variable": "",
|
||||||
"Access": "",
|
"Access": "גישה",
|
||||||
"Access Control": "",
|
"Access Control": "בקרת גישה",
|
||||||
"Accessible to all users": "",
|
"Accessible to all users": "",
|
||||||
"Account": "חשבון",
|
"Account": "חשבון",
|
||||||
"Account Activation Pending": "",
|
"Account Activation Pending": "",
|
||||||
"Accurate information": "מידע מדויק",
|
"Accurate information": "מידע מדויק",
|
||||||
"Actions": "",
|
"Actions": "פעולה",
|
||||||
"Activate": "",
|
"Activate": "",
|
||||||
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
|
"Activate this command by typing \"/{{COMMAND}}\" to chat input.": "",
|
||||||
"Active Users": "",
|
"Active Users": "משתמשים מחוברים",
|
||||||
"Add": "הוסף",
|
"Add": "הוסף",
|
||||||
"Add a model ID": "",
|
"Add a model ID": "",
|
||||||
"Add a short description about what this model does": "הוסף תיאור קצר אודות אופן הפעולה של מודל זה",
|
"Add a short description about what this model does": "הוסף תיאור קצר אודות אופן הפעולה של מודל זה",
|
||||||
"Add a tag": "הוסף תג",
|
"Add a tag": "הוסף תג",
|
||||||
"Add Arena Model": "",
|
"Add Arena Model": "",
|
||||||
"Add Connection": "",
|
"Add Connection": "",
|
||||||
"Add Content": "",
|
"Add Content": "הוסף תוכן",
|
||||||
"Add content here": "",
|
"Add content here": "הוסף תוכן כאן",
|
||||||
"Add Custom Parameter": "",
|
"Add Custom Parameter": "",
|
||||||
"Add custom prompt": "הוסף פקודה מותאמת אישית",
|
"Add custom prompt": "הוסף פקודה מותאמת אישית",
|
||||||
"Add Files": "הוסף קבצים",
|
"Add Files": "הוסף קבצים",
|
||||||
"Add Group": "",
|
"Add Group": "הוסף קבוצה",
|
||||||
"Add Memory": "הוסף זיכרון",
|
"Add Memory": "הוסף זיכרון",
|
||||||
"Add Model": "הוסף מודל",
|
"Add Model": "הוסף מודל",
|
||||||
"Add Reaction": "",
|
"Add Reaction": "",
|
||||||
"Add Tag": "",
|
"Add Tag": "הוסף תג",
|
||||||
"Add Tags": "הוסף תגים",
|
"Add Tags": "הוסף תגים",
|
||||||
"Add text content": "",
|
"Add text content": "",
|
||||||
"Add User": "הוסף משתמש",
|
"Add User": "הוסף משתמש",
|
||||||
"Add User Group": "",
|
"Add User Group": "",
|
||||||
"Adjusting these settings will apply changes universally to all users.": "התאמת הגדרות אלו תחול על כל המשתמשים.",
|
"Adjusting these settings will apply changes universally to all users.": "התאמת הגדרות אלו תחול על כל המשתמשים.",
|
||||||
"admin": "מנהל",
|
"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.": "",
|
"Admins have access to all tools at all times; users need tools assigned per model in the workspace.": "",
|
||||||
|
|
@ -57,15 +57,15 @@
|
||||||
"Advanced Params": "פרמטרים מתקדמים",
|
"Advanced Params": "פרמטרים מתקדמים",
|
||||||
"All": "",
|
"All": "",
|
||||||
"All Documents": "כל המסמכים",
|
"All Documents": "כל המסמכים",
|
||||||
"All models deleted successfully": "",
|
"All models deleted successfully": "כל המודלים נמחקו בהצלחה",
|
||||||
"Allow Call": "",
|
"Allow Call": "",
|
||||||
"Allow Chat Controls": "",
|
"Allow Chat Controls": "",
|
||||||
"Allow Chat Delete": "",
|
"Allow Chat Delete": "",
|
||||||
"Allow Chat Deletion": "אפשר מחיקת צ'אט",
|
"Allow Chat Deletion": "אפשר מחיקת צ'אט",
|
||||||
"Allow Chat Edit": "",
|
"Allow Chat Edit": "אפשר עריכת צ'אט",
|
||||||
"Allow Chat Export": "",
|
"Allow Chat Export": "",
|
||||||
"Allow Chat Share": "",
|
"Allow Chat Share": "אפשר שיתוף צ'אט",
|
||||||
"Allow File Upload": "",
|
"Allow File Upload": "אפשר העלאת קובץ",
|
||||||
"Allow Multiple Models in Chat": "",
|
"Allow Multiple Models in Chat": "",
|
||||||
"Allow non-local voices": "",
|
"Allow non-local voices": "",
|
||||||
"Allow Speech to Text": "",
|
"Allow Speech to Text": "",
|
||||||
|
|
@ -78,11 +78,11 @@
|
||||||
"Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.": "",
|
"Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.": "",
|
||||||
"Already have an account?": "כבר יש לך חשבון?",
|
"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.": "",
|
"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.": "",
|
||||||
"Always": "",
|
"Always": "תמיד",
|
||||||
"Always Collapse Code Blocks": "",
|
"Always Collapse Code Blocks": "",
|
||||||
"Always Expand Details": "",
|
"Always Expand Details": "",
|
||||||
"Always Play Notification Sound": "",
|
"Always Play Notification Sound": "",
|
||||||
"Amazing": "",
|
"Amazing": "מדהים",
|
||||||
"an assistant": "עוזר",
|
"an assistant": "עוזר",
|
||||||
"Analyzed": "",
|
"Analyzed": "",
|
||||||
"Analyzing...": "",
|
"Analyzing...": "",
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "וצור קישור משותף חדש.",
|
"and create a new shared link.": "וצור קישור משותף חדש.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "כתובת URL בסיסית ל-API",
|
"API Base URL": "כתובת URL בסיסית ל-API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "מפתח API",
|
"API Key": "מפתח API",
|
||||||
"API Key created.": "מפתח API נוצר.",
|
"API Key created.": "מפתח API נוצר.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "האם אתה בטוח?",
|
"Are you sure?": "האם אתה בטוח?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -124,7 +125,7 @@
|
||||||
"Auth": "",
|
"Auth": "",
|
||||||
"Authenticate": "",
|
"Authenticate": "",
|
||||||
"Authentication": "",
|
"Authentication": "",
|
||||||
"Auto": "",
|
"Auto": "אוטומטי",
|
||||||
"Auto-Copy Response to Clipboard": "העתקה אוטומטית של תגובה ללוח",
|
"Auto-Copy Response to Clipboard": "העתקה אוטומטית של תגובה ללוח",
|
||||||
"Auto-playback response": "תגובת השמעה אוטומטית",
|
"Auto-playback response": "תגובת השמעה אוטומטית",
|
||||||
"Autocomplete Generation": "",
|
"Autocomplete Generation": "",
|
||||||
|
|
@ -134,7 +135,7 @@
|
||||||
"AUTOMATIC1111 Base URL": "כתובת URL בסיסית של AUTOMATIC1111",
|
"AUTOMATIC1111 Base URL": "כתובת URL בסיסית של AUTOMATIC1111",
|
||||||
"AUTOMATIC1111 Base URL is required.": "נדרשת כתובת URL בסיסית של AUTOMATIC1111",
|
"AUTOMATIC1111 Base URL is required.": "נדרשת כתובת URL בסיסית של AUTOMATIC1111",
|
||||||
"Available list": "",
|
"Available list": "",
|
||||||
"Available Tools": "",
|
"Available Tools": "כלים זמינים",
|
||||||
"available!": "זמין!",
|
"available!": "זמין!",
|
||||||
"Awful": "",
|
"Awful": "",
|
||||||
"Azure AI Speech": "",
|
"Azure AI Speech": "",
|
||||||
|
|
@ -145,7 +146,7 @@
|
||||||
"Base Model (From)": "דגם בסיס (מ)",
|
"Base Model (From)": "דגם בסיס (מ)",
|
||||||
"before": "לפני",
|
"before": "לפני",
|
||||||
"Being lazy": "להיות עצלן",
|
"Being lazy": "להיות עצלן",
|
||||||
"Beta": "",
|
"Beta": "בטא",
|
||||||
"Bing Search V7 Endpoint": "",
|
"Bing Search V7 Endpoint": "",
|
||||||
"Bing Search V7 Subscription Key": "",
|
"Bing Search V7 Subscription Key": "",
|
||||||
"Bocha Search API Key": "",
|
"Bocha Search API Key": "",
|
||||||
|
|
@ -155,10 +156,10 @@
|
||||||
"By {{name}}": "",
|
"By {{name}}": "",
|
||||||
"Bypass Embedding and Retrieval": "",
|
"Bypass Embedding and Retrieval": "",
|
||||||
"Bypass Web Loader": "",
|
"Bypass Web Loader": "",
|
||||||
"Calendar": "",
|
"Calendar": "לוח שנה",
|
||||||
"Call": "",
|
"Call": "",
|
||||||
"Call feature is not supported when using Web STT engine": "",
|
"Call feature is not supported when using Web STT engine": "",
|
||||||
"Camera": "",
|
"Camera": "מצלמה",
|
||||||
"Cancel": "בטל",
|
"Cancel": "בטל",
|
||||||
"Capabilities": "יכולות",
|
"Capabilities": "יכולות",
|
||||||
"Capture": "",
|
"Capture": "",
|
||||||
|
|
@ -188,18 +189,18 @@
|
||||||
"Ciphers": "",
|
"Ciphers": "",
|
||||||
"Citation": "ציטוט",
|
"Citation": "ציטוט",
|
||||||
"Citations": "",
|
"Citations": "",
|
||||||
"Clear memory": "",
|
"Clear memory": "נקה זיכרון",
|
||||||
"Clear Memory": "",
|
"Clear Memory": "נקה",
|
||||||
"click here": "",
|
"click here": "לחץ פה",
|
||||||
"Click here for filter guides.": "",
|
"Click here for filter guides.": "",
|
||||||
"Click here for help.": "לחץ כאן לעזרה.",
|
"Click here for help.": "לחץ כאן לעזרה.",
|
||||||
"Click here to": "לחץ כאן כדי",
|
"Click here to": "לחץ כאן כדי",
|
||||||
"Click here to download user import template file.": "",
|
"Click here to download user import template file.": "",
|
||||||
"Click here to learn more about faster-whisper and see the available models.": "",
|
"Click here to learn more about faster-whisper and see the available models.": "לחץ כאן כדי ללמוד עוד על faster-whisper ולראות מודלים זמינים",
|
||||||
"Click here to see available models.": "",
|
"Click here to see available models.": "לחץ כאן כדי לראות מודלים זמינים",
|
||||||
"Click here to select": "לחץ כאן לבחירה",
|
"Click here to select": "לחץ כאן לבחירה",
|
||||||
"Click here to select a csv file.": "לחץ כאן לבחירת קובץ csv.",
|
"Click here to select a csv file.": "לחץ כאן לבחירת קובץ csv.",
|
||||||
"Click here to select a py file.": "",
|
"Click here to select a py file.": "לחץ כאן כדי לבחירת קובץ py",
|
||||||
"Click here to upload a workflow.json file.": "",
|
"Click here to upload a workflow.json file.": "",
|
||||||
"click here.": "לחץ כאן.",
|
"click here.": "לחץ כאן.",
|
||||||
"Click on the user role button to change a user's role.": "לחץ על כפתור תפקיד המשתמש כדי לשנות את תפקיד המשתמש.",
|
"Click on the user role button to change a user's role.": "לחץ על כפתור תפקיד המשתמש כדי לשנות את תפקיד המשתמש.",
|
||||||
|
|
@ -208,9 +209,9 @@
|
||||||
"Clone Chat": "",
|
"Clone Chat": "",
|
||||||
"Clone of {{TITLE}}": "",
|
"Clone of {{TITLE}}": "",
|
||||||
"Close": "סגור",
|
"Close": "סגור",
|
||||||
"Code execution": "",
|
"Code execution": "הרצת קוד",
|
||||||
"Code Execution": "",
|
"Code Execution": "הרצת קוד",
|
||||||
"Code Execution Engine": "",
|
"Code Execution Engine": "מנוע הרצת קוד",
|
||||||
"Code Execution Timeout": "",
|
"Code Execution Timeout": "",
|
||||||
"Code formatted successfully": "",
|
"Code formatted successfully": "",
|
||||||
"Code Interpreter": "",
|
"Code Interpreter": "",
|
||||||
|
|
@ -218,9 +219,9 @@
|
||||||
"Code Interpreter Prompt Template": "",
|
"Code Interpreter Prompt Template": "",
|
||||||
"Collapse": "",
|
"Collapse": "",
|
||||||
"Collection": "אוסף",
|
"Collection": "אוסף",
|
||||||
"Color": "",
|
"Color": "צבע",
|
||||||
"ComfyUI": "ComfyUI",
|
"ComfyUI": "ComfyUI",
|
||||||
"ComfyUI API Key": "",
|
"ComfyUI API Key": "מפתח API כל ComfyUI",
|
||||||
"ComfyUI Base URL": "כתובת URL בסיסית של ComfyUI",
|
"ComfyUI Base URL": "כתובת URL בסיסית של ComfyUI",
|
||||||
"ComfyUI Base URL is required.": "נדרשת כתובת URL בסיסית של ComfyUI",
|
"ComfyUI Base URL is required.": "נדרשת כתובת URL בסיסית של ComfyUI",
|
||||||
"ComfyUI Workflow": "",
|
"ComfyUI Workflow": "",
|
||||||
|
|
@ -231,31 +232,31 @@
|
||||||
"Configure": "",
|
"Configure": "",
|
||||||
"Confirm": "",
|
"Confirm": "",
|
||||||
"Confirm Password": "אשר סיסמה",
|
"Confirm Password": "אשר סיסמה",
|
||||||
"Confirm your action": "",
|
"Confirm your action": "אשר את הפעולה שלך",
|
||||||
"Confirm your new password": "",
|
"Confirm your new password": "אשר את הסיסמה החדשה שלך",
|
||||||
"Connect to your own OpenAI compatible API endpoints.": "",
|
"Connect to your own OpenAI compatible API endpoints.": "",
|
||||||
"Connect to your own OpenAPI compatible external tool servers.": "",
|
"Connect to your own OpenAPI compatible external tool servers.": "",
|
||||||
"Connection failed": "",
|
"Connection failed": "החיבור נכשל",
|
||||||
"Connection successful": "",
|
"Connection successful": "החיבור הצליח",
|
||||||
"Connection Type": "",
|
"Connection Type": "סוג חיבור",
|
||||||
"Connections": "חיבורים",
|
"Connections": "חיבורים",
|
||||||
"Connections saved successfully": "",
|
"Connections saved successfully": "החיבור נשמר בהצלחה",
|
||||||
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
|
"Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.": "",
|
||||||
"Contact Admin for WebUI Access": "",
|
"Contact Admin for WebUI Access": "",
|
||||||
"Content": "תוכן",
|
"Content": "תוכן",
|
||||||
"Content Extraction Engine": "",
|
"Content Extraction Engine": "",
|
||||||
"Continue Response": "המשך תגובה",
|
"Continue Response": "המשך תגובה",
|
||||||
"Continue with {{provider}}": "",
|
"Continue with {{provider}}": "המשך עם {{provider}}",
|
||||||
"Continue with Email": "",
|
"Continue with Email": "המשך עם מייל",
|
||||||
"Continue with LDAP": "",
|
"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.": "",
|
"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.": "",
|
||||||
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.": "",
|
"Control the repetition of token sequences in the generated text. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 1.1) will be more lenient. At 1, it is disabled.": "",
|
||||||
"Controls": "",
|
"Controls": "",
|
||||||
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "",
|
"Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text.": "",
|
||||||
"Copied": "",
|
"Copied": "הועתק",
|
||||||
"Copied link to clipboard": "",
|
"Copied link to clipboard": "",
|
||||||
"Copied shared chat URL to clipboard!": "העתקת כתובת URL של צ'אט משותף ללוח!",
|
"Copied shared chat URL to clipboard!": "העתקת כתובת URL של צ'אט משותף ללוח!",
|
||||||
"Copied to clipboard": "",
|
"Copied to clipboard": "הועתק ללוח",
|
||||||
"Copy": "העתק",
|
"Copy": "העתק",
|
||||||
"Copy Formatted Text": "",
|
"Copy Formatted Text": "",
|
||||||
"Copy last code block": "העתק את בלוק הקוד האחרון",
|
"Copy last code block": "העתק את בלוק הקוד האחרון",
|
||||||
|
|
@ -270,11 +271,11 @@
|
||||||
"Create Account": "צור חשבון",
|
"Create Account": "צור חשבון",
|
||||||
"Create Admin Account": "",
|
"Create Admin Account": "",
|
||||||
"Create Channel": "",
|
"Create Channel": "",
|
||||||
"Create Group": "",
|
"Create Group": "יצירת קבוצה",
|
||||||
"Create Knowledge": "",
|
"Create Knowledge": "",
|
||||||
"Create new key": "צור מפתח חדש",
|
"Create new key": "צור מפתח חדש",
|
||||||
"Create new secret key": "צור מפתח סודי חדש",
|
"Create new secret key": "צור מפתח סודי חדש",
|
||||||
"Create Note": "",
|
"Create Note": "יצירת פתק",
|
||||||
"Create your first note by clicking on the plus button below.": "",
|
"Create your first note by clicking on the plus button below.": "",
|
||||||
"Created at": "נוצר ב",
|
"Created at": "נוצר ב",
|
||||||
"Created At": "נוצר ב",
|
"Created At": "נוצר ב",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "הזן חפיפת נתונים",
|
"Enter Chunk Overlap": "הזן חפיפת נתונים",
|
||||||
"Enter Chunk Size": "הזן גודל נתונים",
|
"Enter Chunk Size": "הזן גודל נתונים",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "עקב אחר ההוראות במושלמות",
|
"Followed instructions perfectly": "עקב אחר ההוראות במושלמות",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -642,10 +648,10 @@
|
||||||
"Generate Image": "",
|
"Generate Image": "",
|
||||||
"Generate prompt pair": "",
|
"Generate prompt pair": "",
|
||||||
"Generating search query": "יצירת שאילתת חיפוש",
|
"Generating search query": "יצירת שאילתת חיפוש",
|
||||||
"Generating...": "",
|
"Generating...": "מג'נרט...",
|
||||||
"Get started": "",
|
"Get started": "",
|
||||||
"Get started with {{WEBUI_NAME}}": "",
|
"Get started with {{WEBUI_NAME}}": "",
|
||||||
"Global": "",
|
"Global": "גלובלי",
|
||||||
"Good Response": "תגובה טובה",
|
"Good Response": "תגובה טובה",
|
||||||
"Google Drive": "",
|
"Google Drive": "",
|
||||||
"Google PSE API Key": "מפתח API של Google PSE",
|
"Google PSE API Key": "מפתח API של Google PSE",
|
||||||
|
|
@ -655,7 +661,7 @@
|
||||||
"Group Description": "",
|
"Group Description": "",
|
||||||
"Group Name": "",
|
"Group Name": "",
|
||||||
"Group updated successfully": "",
|
"Group updated successfully": "",
|
||||||
"Groups": "",
|
"Groups": "קבוצות",
|
||||||
"Haptic Feedback": "",
|
"Haptic Feedback": "",
|
||||||
"Hello, {{name}}": "שלום, {{name}}",
|
"Hello, {{name}}": "שלום, {{name}}",
|
||||||
"Help": "עזרה",
|
"Help": "עזרה",
|
||||||
|
|
@ -663,9 +669,9 @@
|
||||||
"Hex Color": "",
|
"Hex Color": "",
|
||||||
"Hex Color - Leave empty for default color": "",
|
"Hex Color - Leave empty for default color": "",
|
||||||
"Hide": "הסתר",
|
"Hide": "הסתר",
|
||||||
"Hide Model": "",
|
"Hide Model": "הסתר מודל",
|
||||||
"High Contrast Mode": "",
|
"High Contrast Mode": "",
|
||||||
"Home": "",
|
"Home": "בית",
|
||||||
"Host": "",
|
"Host": "",
|
||||||
"How can I help you today?": "כיצד אוכל לעזור לך היום?",
|
"How can I help you today?": "כיצד אוכל לעזור לך היום?",
|
||||||
"How would you rate this response?": "",
|
"How would you rate this response?": "",
|
||||||
|
|
@ -676,7 +682,7 @@
|
||||||
"iframe Sandbox Allow Forms": "",
|
"iframe Sandbox Allow Forms": "",
|
||||||
"iframe Sandbox Allow Same Origin": "",
|
"iframe Sandbox Allow Same Origin": "",
|
||||||
"Ignite curiosity": "",
|
"Ignite curiosity": "",
|
||||||
"Image": "",
|
"Image": "תמונה",
|
||||||
"Image Compression": "",
|
"Image Compression": "",
|
||||||
"Image Generation": "",
|
"Image Generation": "",
|
||||||
"Image Generation (Experimental)": "יצירת תמונות (ניסיוני)",
|
"Image Generation (Experimental)": "יצירת תמונות (ניסיוני)",
|
||||||
|
|
@ -692,11 +698,11 @@
|
||||||
"Import From Link": "",
|
"Import From Link": "",
|
||||||
"Import Functions": "",
|
"Import Functions": "",
|
||||||
"Import Models": "ייבוא דגמים",
|
"Import Models": "ייבוא דגמים",
|
||||||
"Import Notes": "",
|
"Import Notes": "ייבוא פתקים",
|
||||||
"Import Presets": "",
|
"Import Presets": "",
|
||||||
"Import Prompt Suggestions": "",
|
"Import Prompt Suggestions": "",
|
||||||
"Import Prompts": "יבוא פקודות",
|
"Import Prompts": "ייבוא פקודות",
|
||||||
"Import Tools": "",
|
"Import Tools": "ייבוא כלים",
|
||||||
"Include": "",
|
"Include": "",
|
||||||
"Include `--api-auth` flag when running stable-diffusion-webui": "",
|
"Include `--api-auth` flag when running stable-diffusion-webui": "",
|
||||||
"Include `--api` flag when running stable-diffusion-webui": "כלול את הדגל `--api` בעת הרצת stable-diffusion-webui",
|
"Include `--api` flag when running stable-diffusion-webui": "כלול את הדגל `--api` בעת הרצת stable-diffusion-webui",
|
||||||
|
|
@ -713,7 +719,7 @@
|
||||||
"Invalid JSON file": "",
|
"Invalid JSON file": "",
|
||||||
"Invalid JSON schema": "",
|
"Invalid JSON schema": "",
|
||||||
"Invalid Tag": "תג לא חוקי",
|
"Invalid Tag": "תג לא חוקי",
|
||||||
"is typing...": "",
|
"is typing...": "מקליד...",
|
||||||
"January": "ינואר",
|
"January": "ינואר",
|
||||||
"Jina API Key": "",
|
"Jina API Key": "",
|
||||||
"join our Discord for help.": "הצטרף ל-Discord שלנו לעזרה.",
|
"join our Discord for help.": "הצטרף ל-Discord שלנו לעזרה.",
|
||||||
|
|
@ -757,7 +763,7 @@
|
||||||
"Leave empty to include all models or select specific models": "",
|
"Leave empty to include all models or select specific models": "",
|
||||||
"Leave empty to use the default prompt, or enter a custom prompt": "",
|
"Leave empty to use the default prompt, or enter a custom prompt": "",
|
||||||
"Leave model field empty to use the default model.": "",
|
"Leave model field empty to use the default model.": "",
|
||||||
"License": "",
|
"License": "רישיון",
|
||||||
"Light": "בהיר",
|
"Light": "בהיר",
|
||||||
"Listening...": "",
|
"Listening...": "",
|
||||||
"Llama.cpp": "",
|
"Llama.cpp": "",
|
||||||
|
|
@ -795,7 +801,7 @@
|
||||||
"Memory updated successfully": "",
|
"Memory updated successfully": "",
|
||||||
"Merge Responses": "",
|
"Merge Responses": "",
|
||||||
"Merged Response": "תגובה ממוזגת",
|
"Merged Response": "תגובה ממוזגת",
|
||||||
"Message rating should be enabled to use this feature": "",
|
"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.": "הודעות שתשלח לאחר יצירת הקישור לא ישותפו. משתמשים עם כתובת האתר יוכלו לצפות בצ'אט המשותף.",
|
"Messages you send after creating your link won't be shared. Users with the URL will be able to view the shared chat.": "הודעות שתשלח לאחר יצירת הקישור לא ישותפו. משתמשים עם כתובת האתר יוכלו לצפות בצ'אט המשותף.",
|
||||||
"Microsoft OneDrive": "",
|
"Microsoft OneDrive": "",
|
||||||
"Microsoft OneDrive (personal)": "",
|
"Microsoft OneDrive (personal)": "",
|
||||||
|
|
@ -820,9 +826,9 @@
|
||||||
"Model Filtering": "",
|
"Model Filtering": "",
|
||||||
"Model ID": "מזהה דגם",
|
"Model ID": "מזהה דגם",
|
||||||
"Model IDs": "",
|
"Model IDs": "",
|
||||||
"Model Name": "",
|
"Model Name": "שם המודל",
|
||||||
"Model not selected": "לא נבחר מודל",
|
"Model not selected": "לא נבחר מודל",
|
||||||
"Model Params": "פרמס מודל",
|
"Model Params": "פרמטרי המודל",
|
||||||
"Model Permissions": "",
|
"Model Permissions": "",
|
||||||
"Model unloaded successfully": "",
|
"Model unloaded successfully": "",
|
||||||
"Model updated successfully": "",
|
"Model updated successfully": "",
|
||||||
|
|
@ -835,22 +841,23 @@
|
||||||
"Mojeek Search API Key": "",
|
"Mojeek Search API Key": "",
|
||||||
"more": "",
|
"more": "",
|
||||||
"More": "עוד",
|
"More": "עוד",
|
||||||
"My Notes": "",
|
"My Notes": "הפתקים שלי",
|
||||||
"Name": "שם",
|
"Name": "שם",
|
||||||
"Name your knowledge base": "",
|
"Name your knowledge base": "",
|
||||||
"Native": "",
|
"Native": "",
|
||||||
"New Chat": "צ'אט חדש",
|
"New Chat": "צ'אט חדש",
|
||||||
"New Folder": "",
|
"New Folder": "תיקייה חדשה",
|
||||||
"New Function": "",
|
"New Function": "פונקציה חדשה",
|
||||||
"New Note": "",
|
"New Note": "פתק חדש",
|
||||||
"New Password": "סיסמה חדשה",
|
"New Password": "סיסמה חדשה",
|
||||||
"New Tool": "",
|
"New Tool": "כלי חדש",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
"No chats found for this user.": "",
|
"Next message": "",
|
||||||
"No chats found.": "",
|
"No chats found for this user.": "לא נמצאו צ'אטים ליוזר הזה.",
|
||||||
"No content": "",
|
"No chats found.": "לא נמצאו צ'אטים",
|
||||||
"No content found": "",
|
"No content": "אין תוכן",
|
||||||
"No content found in file.": "",
|
"No content found": "תוכן לא נמצא",
|
||||||
|
"No content found in file.": "לא נמצא תוכן בקובץ.",
|
||||||
"No content to speak": "",
|
"No content to speak": "",
|
||||||
"No distance available": "",
|
"No distance available": "",
|
||||||
"No feedbacks found": "",
|
"No feedbacks found": "",
|
||||||
|
|
@ -867,14 +874,14 @@
|
||||||
"No results found": "לא נמצאו תוצאות",
|
"No results found": "לא נמצאו תוצאות",
|
||||||
"No search query generated": "לא נוצרה שאילתת חיפוש",
|
"No search query generated": "לא נוצרה שאילתת חיפוש",
|
||||||
"No source available": "אין מקור זמין",
|
"No source available": "אין מקור זמין",
|
||||||
"No users were found.": "",
|
"No users were found.": "לא נמצאו יוזרים",
|
||||||
"No valves to update": "",
|
"No valves to update": "",
|
||||||
"None": "ללא",
|
"None": "ללא",
|
||||||
"Not factually correct": "לא נכון מבחינה עובדתית",
|
"Not factually correct": "לא נכון מבחינה עובדתית",
|
||||||
"Not helpful": "",
|
"Not helpful": "",
|
||||||
"Note deleted successfully": "",
|
"Note deleted successfully": "פתק נמחק בהצלחה",
|
||||||
"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: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "הערה: אם תקבע ציון מינימלי, החיפוש יחזיר רק מסמכים עם ציון שגבוה או שווה לציון המינימלי.",
|
||||||
"Notes": "",
|
"Notes": "פתקים",
|
||||||
"Notification Sound": "",
|
"Notification Sound": "",
|
||||||
"Notification Webhook": "",
|
"Notification Webhook": "",
|
||||||
"Notifications": "התראות",
|
"Notifications": "התראות",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "נדרשת כתובת URL/מפתח של OpenAI.",
|
"OpenAI URL/Key required.": "נדרשת כתובת URL/מפתח של OpenAI.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "או",
|
"or": "או",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "אחר",
|
"Other": "אחר",
|
||||||
|
|
@ -920,7 +928,7 @@
|
||||||
"Output format": "",
|
"Output format": "",
|
||||||
"Output Format": "",
|
"Output Format": "",
|
||||||
"Overview": "",
|
"Overview": "",
|
||||||
"page": "",
|
"page": "עמוד",
|
||||||
"Paginate": "",
|
"Paginate": "",
|
||||||
"Parameters": "",
|
"Parameters": "",
|
||||||
"Password": "סיסמה",
|
"Password": "סיסמה",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "מסמך PDF (.pdf)",
|
"PDF document (.pdf)": "מסמך PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "חילוץ תמונות מ-PDF (OCR)",
|
"PDF Extract Images (OCR)": "חילוץ תמונות מ-PDF (OCR)",
|
||||||
"pending": "ממתין",
|
"pending": "ממתין",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "ההרשאה נדחתה בעת גישה למיקרופון: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "ההרשאה נדחתה בעת גישה למיקרופון: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "תאור",
|
"Personalization": "תאור",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 הימים הקודמים",
|
"Previous 30 days": "30 הימים הקודמים",
|
||||||
"Previous 7 days": "7 הימים הקודמים",
|
"Previous 7 days": "7 הימים הקודמים",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "תמונת פרופיל",
|
"Profile Image": "תמונת פרופיל",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
@ -1068,7 +1083,7 @@
|
||||||
"Select a model": "בחר מודל",
|
"Select a model": "בחר מודל",
|
||||||
"Select a pipeline": "בחר קו צינור",
|
"Select a pipeline": "בחר קו צינור",
|
||||||
"Select a pipeline url": "בחר כתובת URL של קו צינור",
|
"Select a pipeline url": "בחר כתובת URL של קו צינור",
|
||||||
"Select a tool": "",
|
"Select a tool": "בחר כלי",
|
||||||
"Select an auth method": "",
|
"Select an auth method": "",
|
||||||
"Select an Ollama instance": "",
|
"Select an Ollama instance": "",
|
||||||
"Select Engine": "",
|
"Select Engine": "",
|
||||||
|
|
@ -1188,7 +1203,7 @@
|
||||||
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "ציון צריך להיות ערך בין 0.0 (0%) ל-1.0 (100%)",
|
"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.": "",
|
"The temperature of the model. Increasing the temperature will make the model answer more creatively.": "",
|
||||||
"Theme": "נושא",
|
"Theme": "נושא",
|
||||||
"Thinking...": "",
|
"Thinking...": "חושב...",
|
||||||
"This action cannot be undone. Do you wish to continue?": "",
|
"This action cannot be undone. Do you wish to continue?": "",
|
||||||
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
|
"This channel was created on {{createdAt}}. This is the very beginning of the {{channelName}} channel.": "",
|
||||||
"This chat won’t appear in history and your messages will not be saved.": "",
|
"This chat won’t appear in history and your messages will not be saved.": "",
|
||||||
|
|
@ -1216,7 +1231,7 @@
|
||||||
"Title Auto-Generation": "יצירת שם אוטומטית",
|
"Title Auto-Generation": "יצירת שם אוטומטית",
|
||||||
"Title cannot be an empty string.": "שם לא יכול להיות מחרוזת ריקה.",
|
"Title cannot be an empty string.": "שם לא יכול להיות מחרוזת ריקה.",
|
||||||
"Title Generation": "",
|
"Title Generation": "",
|
||||||
"Title Generation Prompt": "שאלה ליצירת שם",
|
"Title Generation Prompt": "פרומפט ליצירת כותרת",
|
||||||
"TLS": "",
|
"TLS": "",
|
||||||
"To access the available model names for downloading,": "כדי לגשת לשמות הדגמים הזמינים להורדה,",
|
"To access the available model names for downloading,": "כדי לגשת לשמות הדגמים הזמינים להורדה,",
|
||||||
"To access the GGUF models available for downloading,": "כדי לגשת לדגמי GGUF הזמינים להורדה,",
|
"To access the GGUF models available for downloading,": "כדי לגשת לדגמי GGUF הזמינים להורדה,",
|
||||||
|
|
@ -1242,7 +1257,7 @@
|
||||||
"Tool Name": "",
|
"Tool Name": "",
|
||||||
"Tool Servers": "",
|
"Tool Servers": "",
|
||||||
"Tool updated successfully": "",
|
"Tool updated successfully": "",
|
||||||
"Tools": "",
|
"Tools": "כלים",
|
||||||
"Tools Access": "",
|
"Tools Access": "",
|
||||||
"Tools are a function calling system with arbitrary code execution": "",
|
"Tools are a function calling system with arbitrary code execution": "",
|
||||||
"Tools Function Calling Prompt": "",
|
"Tools Function Calling Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "और एक नई साझा लिंक बनाएं.",
|
"and create a new shared link.": "और एक नई साझा लिंक बनाएं.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "एपीआई बेस यूआरएल",
|
"API Base URL": "एपीआई बेस यूआरएल",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "एपीआई कुंजी",
|
"API Key": "एपीआई कुंजी",
|
||||||
"API Key created.": "एपीआई कुंजी बनाई गई",
|
"API Key created.": "एपीआई कुंजी बनाई गई",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "क्या आपको यकीन है?",
|
"Are you sure?": "क्या आपको यकीन है?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "चंक ओवरलैप दर्ज करें",
|
"Enter Chunk Overlap": "चंक ओवरलैप दर्ज करें",
|
||||||
"Enter Chunk Size": "खंड आकार दर्ज करें",
|
"Enter Chunk Size": "खंड आकार दर्ज करें",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "निर्देशों का पूर्णतः पालन किया",
|
"Followed instructions perfectly": "निर्देशों का पूर्णतः पालन किया",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "नया पासवर्ड",
|
"New Password": "नया पासवर्ड",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/Key आवश्यक है।",
|
"OpenAI URL/Key required.": "OpenAI URL/Key आवश्यक है।",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "या",
|
"or": "या",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "अन्य",
|
"Other": "अन्य",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF दस्तावेज़ (.pdf)",
|
"PDF document (.pdf)": "PDF दस्तावेज़ (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF छवियाँ निकालें (OCR)",
|
"PDF Extract Images (OCR)": "PDF छवियाँ निकालें (OCR)",
|
||||||
"pending": "लंबित",
|
"pending": "लंबित",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "",
|
"Permission denied when accessing media devices": "",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "माइक्रोफ़ोन तक पहुँचने पर अनुमति अस्वीकृत: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "माइक्रोफ़ोन तक पहुँचने पर अनुमति अस्वीकृत: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "पेरसनलाइज़मेंट",
|
"Personalization": "पेरसनलाइज़मेंट",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "पिछले 30 दिन",
|
"Previous 30 days": "पिछले 30 दिन",
|
||||||
"Previous 7 days": "पिछले 7 दिन",
|
"Previous 7 days": "पिछले 7 दिन",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "प्रोफ़ाइल छवि",
|
"Profile Image": "प्रोफ़ाइल छवि",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "i stvorite novu dijeljenu vezu.",
|
"and create a new shared link.": "i stvorite novu dijeljenu vezu.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "Osnovni URL API-ja",
|
"API Base URL": "Osnovni URL API-ja",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API ključ",
|
"API Key": "API ključ",
|
||||||
"API Key created.": "API ključ je stvoren.",
|
"API Key created.": "API ključ je stvoren.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Jeste li sigurni?",
|
"Are you sure?": "Jeste li sigurni?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Unesite preklapanje dijelova",
|
"Enter Chunk Overlap": "Unesite preklapanje dijelova",
|
||||||
"Enter Chunk Size": "Unesite veličinu dijela",
|
"Enter Chunk Size": "Unesite veličinu dijela",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Savršeno slijedio upute",
|
"Followed instructions perfectly": "Savršeno slijedio upute",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Nova lozinka",
|
"New Password": "Nova lozinka",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "Potreban je OpenAI URL/ključ.",
|
"OpenAI URL/Key required.": "Potreban je OpenAI URL/ključ.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "ili",
|
"or": "ili",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "Ostalo",
|
"Other": "Ostalo",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
"PDF document (.pdf)": "PDF dokument (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF izdvajanje slika (OCR)",
|
"PDF Extract Images (OCR)": "PDF izdvajanje slika (OCR)",
|
||||||
"pending": "u tijeku",
|
"pending": "u tijeku",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Dopuštenje je odbijeno prilikom pristupa medijskim uređajima",
|
"Permission denied when accessing media devices": "Dopuštenje je odbijeno prilikom pristupa medijskim uređajima",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Pristup mikrofonu odbijen: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Pristup mikrofonu odbijen: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Prilagodba",
|
"Personalization": "Prilagodba",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Prethodnih 30 dana",
|
"Previous 30 days": "Prethodnih 30 dana",
|
||||||
"Previous 7 days": "Prethodnih 7 dana",
|
"Previous 7 days": "Prethodnih 7 dana",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Profilna slika",
|
"Profile Image": "Profilna slika",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "és még {{COUNT}} db",
|
"and {{COUNT}} more": "és még {{COUNT}} db",
|
||||||
"and create a new shared link.": "és hozz létre egy új megosztott linket.",
|
"and create a new shared link.": "és hozz létre egy új megosztott linket.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "API alap URL",
|
"API Base URL": "API alap URL",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "API kulcs",
|
"API Key": "API kulcs",
|
||||||
"API Key created.": "API kulcs létrehozva.",
|
"API Key created.": "API kulcs létrehozva.",
|
||||||
"API Key Endpoint Restrictions": "API kulcs végpont korlátozások",
|
"API Key Endpoint Restrictions": "API kulcs végpont korlátozások",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "Biztosan törölni szeretnéd ezt a csatornát?",
|
"Are you sure you want to delete this channel?": "Biztosan törölni szeretnéd ezt a csatornát?",
|
||||||
"Are you sure you want to delete this message?": "Biztosan törölni szeretnéd ezt az üzenetet?",
|
"Are you sure you want to delete this message?": "Biztosan törölni szeretnéd ezt az üzenetet?",
|
||||||
"Are you sure you want to unarchive all archived chats?": "Biztosan vissza szeretnéd állítani az összes archivált csevegést?",
|
"Are you sure you want to unarchive all archived chats?": "Biztosan vissza szeretnéd állítani az összes archivált csevegést?",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Biztos vagy benne?",
|
"Are you sure?": "Biztos vagy benne?",
|
||||||
"Arena Models": "Arena modellek",
|
"Arena Models": "Arena modellek",
|
||||||
"Artifacts": "Műtermékek",
|
"Artifacts": "Műtermékek",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Add meg a darab átfedést",
|
"Enter Chunk Overlap": "Add meg a darab átfedést",
|
||||||
"Enter Chunk Size": "Add meg a darab méretet",
|
"Enter Chunk Size": "Add meg a darab méretet",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Add meg vesszővel elválasztott \"token:bias_érték\" párokat (példa: 5432:100, 413:-100)",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "Add meg vesszővel elválasztott \"token:bias_érték\" párokat (példa: 5432:100, 413:-100)",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "Add meg a leírást",
|
"Enter description": "Add meg a leírást",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "Mappa sikeresen törölve",
|
"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",
|
"Folder name updated successfully": "Mappa neve sikeresen frissítve",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Tökéletesen követte az utasításokat",
|
"Followed instructions perfectly": "Tökéletesen követte az utasításokat",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Új jelszó",
|
"New Password": "Új jelszó",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "új csatorna",
|
"new-channel": "új csatorna",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "OpenAI API beállítások frissítve",
|
"OpenAI API settings updated": "OpenAI API beállítások frissítve",
|
||||||
"OpenAI URL/Key required.": "OpenAI URL/kulcs szükséges.",
|
"OpenAI URL/Key required.": "OpenAI URL/kulcs szükséges.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "vagy",
|
"or": "vagy",
|
||||||
"Organize your users": "Szervezd meg a felhasználóidat",
|
"Organize your users": "Szervezd meg a felhasználóidat",
|
||||||
"Other": "Egyéb",
|
"Other": "Egyéb",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "PDF dokumentum (.pdf)",
|
"PDF document (.pdf)": "PDF dokumentum (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "PDF képek kinyerése (OCR)",
|
"PDF Extract Images (OCR)": "PDF képek kinyerése (OCR)",
|
||||||
"pending": "függőben",
|
"pending": "függőben",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Hozzáférés megtagadva a médiaeszközökhöz",
|
"Permission denied when accessing media devices": "Hozzáférés megtagadva a médiaeszközökhöz",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Hozzáférés megtagadva a mikrofonhoz: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Hozzáférés megtagadva a mikrofonhoz: {{error}}",
|
||||||
"Permissions": "Engedélyek",
|
"Permissions": "Engedélyek",
|
||||||
"Perplexity API Key": "Perplexity API kulcs",
|
"Perplexity API Key": "Perplexity API kulcs",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Személyre szabás",
|
"Personalization": "Személyre szabás",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "Rögzítés",
|
"Pin": "Rögzítés",
|
||||||
"Pinned": "Rögzítve",
|
"Pinned": "Rögzítve",
|
||||||
"Pioneer insights": "Úttörő betekintések",
|
"Pioneer insights": "Úttörő betekintések",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "Előző 30 nap",
|
"Previous 30 days": "Előző 30 nap",
|
||||||
"Previous 7 days": "Előző 7 nap",
|
"Previous 7 days": "Előző 7 nap",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "Privát",
|
"Private": "Privát",
|
||||||
"Profile Image": "Profilkép",
|
"Profile Image": "Profilkép",
|
||||||
"Prompt": "Prompt",
|
"Prompt": "Prompt",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,9 @@
|
||||||
"and {{COUNT}} more": "",
|
"and {{COUNT}} more": "",
|
||||||
"and create a new shared link.": "dan membuat tautan bersama baru.",
|
"and create a new shared link.": "dan membuat tautan bersama baru.",
|
||||||
"Android": "",
|
"Android": "",
|
||||||
|
"API": "",
|
||||||
"API Base URL": "URL Dasar API",
|
"API Base URL": "URL Dasar API",
|
||||||
|
"API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.": "",
|
||||||
"API Key": "Kunci API",
|
"API Key": "Kunci API",
|
||||||
"API Key created.": "Kunci API dibuat.",
|
"API Key created.": "Kunci API dibuat.",
|
||||||
"API Key Endpoint Restrictions": "",
|
"API Key Endpoint Restrictions": "",
|
||||||
|
|
@ -108,7 +110,6 @@
|
||||||
"Are you sure you want to delete this channel?": "",
|
"Are you sure you want to delete this channel?": "",
|
||||||
"Are you sure you want to delete this message?": "",
|
"Are you sure you want to delete this message?": "",
|
||||||
"Are you sure you want to unarchive all archived chats?": "",
|
"Are you sure you want to unarchive all archived chats?": "",
|
||||||
"Are you sure you want to update this user's role to **{{ROLE}}**?": "",
|
|
||||||
"Are you sure?": "Apakah Anda yakin?",
|
"Are you sure?": "Apakah Anda yakin?",
|
||||||
"Arena Models": "",
|
"Arena Models": "",
|
||||||
"Artifacts": "",
|
"Artifacts": "",
|
||||||
|
|
@ -443,6 +444,7 @@
|
||||||
"Enter Chunk Overlap": "Masukkan Tumpang Tindih Chunk",
|
"Enter Chunk Overlap": "Masukkan Tumpang Tindih Chunk",
|
||||||
"Enter Chunk Size": "Masukkan Ukuran Potongan",
|
"Enter Chunk Size": "Masukkan Ukuran Potongan",
|
||||||
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
"Enter comma-separated \"token:bias_value\" pairs (example: 5432:100, 413:-100)": "",
|
||||||
|
"Enter Config in JSON format": "",
|
||||||
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
"Enter content for the pending user info overlay. Leave empty for default.": "",
|
||||||
"Enter Datalab Marker API Key": "",
|
"Enter Datalab Marker API Key": "",
|
||||||
"Enter description": "",
|
"Enter description": "",
|
||||||
|
|
@ -611,6 +613,10 @@
|
||||||
"Folder deleted successfully": "",
|
"Folder deleted successfully": "",
|
||||||
"Folder name cannot be empty.": "",
|
"Folder name cannot be empty.": "",
|
||||||
"Folder name updated successfully": "",
|
"Folder name updated successfully": "",
|
||||||
|
"Follow up": "",
|
||||||
|
"Follow Up Generation": "",
|
||||||
|
"Follow Up Generation Prompt": "",
|
||||||
|
"Follow-Up Auto-Generation": "",
|
||||||
"Followed instructions perfectly": "Mengikuti instruksi dengan sempurna",
|
"Followed instructions perfectly": "Mengikuti instruksi dengan sempurna",
|
||||||
"Force OCR": "",
|
"Force OCR": "",
|
||||||
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
"Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.": "",
|
||||||
|
|
@ -846,6 +852,7 @@
|
||||||
"New Password": "Kata Sandi Baru",
|
"New Password": "Kata Sandi Baru",
|
||||||
"New Tool": "",
|
"New Tool": "",
|
||||||
"new-channel": "",
|
"new-channel": "",
|
||||||
|
"Next message": "",
|
||||||
"No chats found for this user.": "",
|
"No chats found for this user.": "",
|
||||||
"No chats found.": "",
|
"No chats found.": "",
|
||||||
"No content": "",
|
"No content": "",
|
||||||
|
|
@ -913,6 +920,7 @@
|
||||||
"OpenAI API settings updated": "",
|
"OpenAI API settings updated": "",
|
||||||
"OpenAI URL/Key required.": "Diperlukan URL/Kunci OpenAI.",
|
"OpenAI URL/Key required.": "Diperlukan URL/Kunci OpenAI.",
|
||||||
"openapi.json URL or Path": "",
|
"openapi.json URL or Path": "",
|
||||||
|
"Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.": "",
|
||||||
"or": "atau",
|
"or": "atau",
|
||||||
"Organize your users": "",
|
"Organize your users": "",
|
||||||
"Other": "Lainnya",
|
"Other": "Lainnya",
|
||||||
|
|
@ -928,6 +936,7 @@
|
||||||
"PDF document (.pdf)": "Dokumen PDF (.pdf)",
|
"PDF document (.pdf)": "Dokumen PDF (.pdf)",
|
||||||
"PDF Extract Images (OCR)": "Ekstrak Gambar PDF (OCR)",
|
"PDF Extract Images (OCR)": "Ekstrak Gambar PDF (OCR)",
|
||||||
"pending": "tertunda",
|
"pending": "tertunda",
|
||||||
|
"Pending": "",
|
||||||
"Pending User Overlay Content": "",
|
"Pending User Overlay Content": "",
|
||||||
"Pending User Overlay Title": "",
|
"Pending User Overlay Title": "",
|
||||||
"Permission denied when accessing media devices": "Izin ditolak saat mengakses perangkat media",
|
"Permission denied when accessing media devices": "Izin ditolak saat mengakses perangkat media",
|
||||||
|
|
@ -935,7 +944,12 @@
|
||||||
"Permission denied when accessing microphone: {{error}}": "Izin ditolak saat mengakses mikrofon: {{error}}",
|
"Permission denied when accessing microphone: {{error}}": "Izin ditolak saat mengakses mikrofon: {{error}}",
|
||||||
"Permissions": "",
|
"Permissions": "",
|
||||||
"Perplexity API Key": "",
|
"Perplexity API Key": "",
|
||||||
|
"Perplexity Model": "",
|
||||||
|
"Perplexity Search Context Usage": "",
|
||||||
"Personalization": "Personalisasi",
|
"Personalization": "Personalisasi",
|
||||||
|
"Picture Description API Config": "",
|
||||||
|
"Picture Description Local Config": "",
|
||||||
|
"Picture Description Mode": "",
|
||||||
"Pin": "",
|
"Pin": "",
|
||||||
"Pinned": "",
|
"Pinned": "",
|
||||||
"Pioneer insights": "",
|
"Pioneer insights": "",
|
||||||
|
|
@ -965,6 +979,7 @@
|
||||||
"Preview": "",
|
"Preview": "",
|
||||||
"Previous 30 days": "30 hari sebelumnya",
|
"Previous 30 days": "30 hari sebelumnya",
|
||||||
"Previous 7 days": "7 hari sebelumnya",
|
"Previous 7 days": "7 hari sebelumnya",
|
||||||
|
"Previous message": "",
|
||||||
"Private": "",
|
"Private": "",
|
||||||
"Profile Image": "Gambar Profil",
|
"Profile Image": "Gambar Profil",
|
||||||
"Prompt": "",
|
"Prompt": "",
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue