mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-13 01:02:25 -06:00
0.9.2 (#24081)
* refac * fix: remove reactive label from onDestroy in Markdown * Update fi-FI translation.json (#24010) Added missing translations. * refac * i18n: update ko-KR translations (conflict solved) (#23949) * i18n: update ko-KR translations * i18n: fix missing ko-KR translations and reviewed pr-bot recommendation * i18n: add pt-BR translations for newly added UI items and consistency pass (#23954) New **pt-BR** translations for items introduced in the latest releases, plus a consistency/quality pass across existing strings (grammar, tone, capitalization, pluralization). Placeholders and hotkeys preserved. No logic changes. * fix(utils): Switch throttle decorator to async (#23979) After migration to async db operations, the throttle decorator also needs to support async. Since the decorator is only used for async funcs now, we can just switch it to async instead of supporting sync and async at the same time. Signed-off-by: Adam Tao <tcx4c70@gmail.com> * refac * refac * refac * refac * feat: add PaddleOCR-vl loader support and implement retrieval router infrastructure (#23945) Co-authored-by: Tim Baek <tim@openwebui.com> Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com> * refac * refac * Enhance image loading performance by adding preload links and setting loading attributes for logos in app.html (#24011) * feat(ui): add citation source overflow badge (#23918) * refac * i18n: enhance and expand Dutch language translations (#23944) * refac * refac * refac * perf: redirect default model profile image to canonical static URL (#24015) - Return 302 to /static/favicon.png instead of streaming the same PNG per model id so browsers can cache one asset for default avatars. - Validate stored /static/ paths with decode, normpath, and /static prefix checks; invalid paths fall back to favicon. Made-with: Cursor * refac * refac * refac * refac * refac * refac * refac * feat: enhance RichTextInput configuration to prevent duplicate extensions when rich text is enabled (#24009) * refac * refac * refac * refac * refac * refac * refac * refac * refac * refac * refac * refac * refac * changelog (#24072) * refactor(firecrawl): use v2 API directly (#23934) Co-authored-by: Tim Baek <tim@openwebui.com> * chore: bump * perf(chats): drop redundant db.refresh after commit in update_chat_by_id (#24024) The chat table has no computed columns (no DEFAULT, SERIAL/IDENTITY, or TRIGGER that populate server-side values on UPDATE), and every column modified by update_chat_by_id is set explicitly from Python values earlier in the function. db.refresh therefore issues a SELECT that replaces those just-written Python values with the round-tripped database representation of the same values, which is a no-op for functional purposes but pulls the entire chat.chat JSON blob back over the network and through the driver's JSON decoder. On large, active chats where chat.chat can reach tens of megabytes, skipping the refresh measurably reduces latency and eliminates one ~JSON-sized transient allocation per write. * refac * chore: format * chore: i18n * refac --------- Signed-off-by: Adam Tao <tcx4c70@gmail.com> Co-authored-by: Algorithm5838 <108630393+Algorithm5838@users.noreply.github.com> Co-authored-by: Kylapaallikko <Kylapaallikko@users.noreply.github.com> Co-authored-by: Teay <pythontogoplease@gmail.com> Co-authored-by: joaoback <156559121+joaoback@users.noreply.github.com> Co-authored-by: tcx4c70 <tcx4c70@gmail.com> Co-authored-by: goodbey857 <76645482+goodbey857@users.noreply.github.com> Co-authored-by: Jacob Leksan <63938553+jmleksan@users.noreply.github.com> Co-authored-by: RomualdYT <romuald@gameurnews.fr> Co-authored-by: Lucas <lucas@vanosenbruggen.com> Co-authored-by: Classic298 <27028174+Classic298@users.noreply.github.com> Co-authored-by: Constantine <Runixer@gmail.com>
This commit is contained in:
@@ -5,6 +5,51 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.9.2] - 2026-04-24
|
||||
|
||||
### Added
|
||||
|
||||
- 🧠 **PaddleOCR-vl document extraction.** Administrators can now use PaddleOCR-vl as a content extraction engine for document processing, with configurable API URL and token settings in document retrieval configuration. [#23945](https://github.com/open-webui/open-webui/pull/23945)
|
||||
- 🔥 **Firecrawl v2 API.** Firecrawl web loading now uses the v2 API directly with proper retry logic, exponential backoff on rate limits, and configurable timeout handling, improving reliability for both cloud and self-hosted Firecrawl setups. [#23934](https://github.com/open-webui/open-webui/pull/23934)
|
||||
- ⏰ **Calendar event reminder customization.** Calendar events now support a configurable `reminder_minutes` parameter, allowing models to set custom reminder durations instead of the default 10-minute notification.
|
||||
- 🔑 **Custom API key header.** Administrators can now configure a custom header name for API key authentication via the `CUSTOM_API_KEY_HEADER` environment variable, enabling compatibility with reverse proxies that use the `Authorization` header for their own authentication.
|
||||
- 🔌 **OAuth session disconnection.** Users can now disconnect OAuth sessions for specific providers (e.g., MCP connections) through a new API endpoint, enabling cleaner re-authentication workflows.
|
||||
- 📚 **Source overflow indicator.** The Sources button now shows a +N badge when more than three sources are available, so hidden sources are clearly indicated in chat responses. [#23918](https://github.com/open-webui/open-webui/pull/23918)
|
||||
- ⚡ **Model list performance.** Model list API responses now strip base64 profile image data from paginated results, and model tags are fetched via a dedicated efficient query instead of loading all models. This significantly reduces payload sizes and improves workspace Models page responsiveness.
|
||||
- ⚡ **Model avatar cache reuse.** Default model profile images now redirect to a shared static path instead of reading files from disk per-request, reducing repeated I/O and improving loading efficiency when multiple models use the fallback icon. [#24015](https://github.com/open-webui/open-webui/pull/24015)
|
||||
- 🚀 **Faster splash image loading.** Splash screen images are now prioritized earlier during page load with preload links, improving first-load LCP behavior and reducing delayed image discovery. [#24011](https://github.com/open-webui/open-webui/pull/24011)
|
||||
- 🧵 **Streaming markdown performance stability.** Streaming responses now stay more memory-efficient by preventing repeated cleanup callback registration during markdown updates. [#24048](https://github.com/open-webui/open-webui/pull/24048)
|
||||
- 📊 **Telemetry gauge reliability.** OpenTelemetry user gauge callbacks now use synchronous database queries directly, eliminating cross-thread async bridging issues that could cause silent failures in metric collection.
|
||||
- 🔄 **General improvements.** Various improvements were implemented across the application to enhance performance, stability, and security.
|
||||
- 🌐 **Translation updates.** Translations for Finnish, Korean, Portuguese (Brazil), and Dutch were enhanced and expanded.
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🔧 **MCP task cancellation stability.** Interrupted MCP tool calls no longer cause CPU spikes or runaway cleanup behavior. MCP client disconnection now runs in the same asyncio task as connection, respecting cancel scope constraints, and chat-active events are properly shielded during cancellation.
|
||||
- 🧠 **Persistent chat skill injection.** Skills mentioned in persisted chats now inject into the system prompt reliably. Skill ID extraction from `<$skillId|label>` message tags is now handled server-side, and tags are stripped before messages reach the model.
|
||||
- 🗄️ **Async database driver migration.** The async database backend now uses psycopg (v3) instead of asyncpg, eliminating brittle SSL parameter translation and supporting native libpq connection strings including `sslmode`, `options`, and `target_session_attrs` without any stripping or conversion.
|
||||
- 🐳 **Docker ARM64 reliability.** Docker images built for arm64 via QEMU cross-compilation no longer produce 0-byte corrupted Python dependencies. `UV_LINK_MODE=copy` is now set in the Dockerfile to force reliable file installation.
|
||||
- 🛠️ **Throttle request handling.** Request handling no longer fails when user activity status updates are throttled with a non-zero interval. [#23979](https://github.com/open-webui/open-webui/pull/23979)
|
||||
- ✍️ **Rich text extension conflicts.** Rich text editing no longer triggers duplicate extension conflicts for lists and code blocks, improving editor stability. [#24009](https://github.com/open-webui/open-webui/pull/24009)
|
||||
- 🔇 **Fetch URL null content guard.** The `fetch_url` built-in tool now safely handles `None` content returned by web loaders instead of crashing with a `TypeError`.
|
||||
- 🌐 **OAuth discovery fallback.** OAuth protected resource discovery now falls back to well-known RFC 9728 URIs when the `WWW-Authenticate` header doesn't contain a `resource_metadata` link, improving compatibility with more MCP server implementations.
|
||||
- 🔐 **Session token resolution.** Session user endpoints now gracefully handle missing `Authorization` headers by falling back to cookie and request state tokens, preventing errors when used behind forward-auth proxies.
|
||||
- 🚫 **Direct API error responses.** Chat completion requests without a WebSocket channel (direct API calls) now return proper HTTP error responses instead of silently returning null on failure.
|
||||
- 📡 **Cancelled response stream cleanup.** Cancelled chat generation now explicitly closes the upstream response body iterator, preventing orphaned async generators from spinning in anyio internals.
|
||||
- 🔒 **Model profile image path safety.** Model profile image endpoints now validate and sanitize static asset redirect paths, preventing path traversal through encoded dots or malicious URL patterns.
|
||||
- 📊 **RAG template validation UI.** The Documents settings page now displays a warning when RAG templates contain multiple `[context]` or `{{CONTEXT}}` placeholders, helping administrators avoid accidental redundant context injection.
|
||||
- 🧩 **Automation model detection.** The `create_automation` tool now correctly detects the current model ID even when `model_id` is not yet set in metadata, falling back to the model dict.
|
||||
- 🔄 **MCP resource content handling.** MCP tool results with the `resource` content type are now correctly detected and their `resource.text` payload is extracted, instead of being silently ignored.
|
||||
- 🔄 **Ollama and OpenAI metadata forwarding.** Ollama and OpenAI proxy routes now forward request metadata to downstream handlers, ensuring consistent context propagation.
|
||||
- 🧹 **Browser-native message virtualization.** The custom JavaScript-based message culling system (spacers, height caching, scroll listeners) was replaced with CSS `content-visibility: auto`, letting the browser natively skip rendering of off-screen messages without destroying component trees. This eliminates scroll jump artifacts and mount/destroy thrashing while preserving memory efficiency in long conversations.
|
||||
- 📻 **Redis notification compatibility.** Redis pub/sub now handles missing or incompatible `client_name` support more gracefully, preventing connection errors with certain Redis configurations.
|
||||
|
||||
### Changed
|
||||
|
||||
- ⚙️ **psycopg v3 async driver.** The async database driver has been migrated from `asyncpg` to `psycopg` (v3). This is a transparent change for most deployments, but custom connection strings with `asyncpg`-specific parameters may need adjustment.
|
||||
- 🔑 **Brotli dependency update.** Brotli has been updated to address CVE-2025-6176.
|
||||
- 🖥️ **Windows startup script.** The Windows startup batch script has been updated for improved compatibility.
|
||||
|
||||
## [0.9.1] - 2026-04-21
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -135,6 +135,9 @@ RUN apt-get update && \
|
||||
# install python dependencies
|
||||
COPY --chown=$UID:$GID ./backend/requirements.txt ./requirements.txt
|
||||
|
||||
# Set UV_LINK_MODE to copy to prevent 0-byte file corruption in QEMU arm64 cross-builds
|
||||
ENV UV_LINK_MODE=copy
|
||||
|
||||
RUN set -e; \
|
||||
pip3 install --no-cache-dir uv; \
|
||||
if [ "$USE_CUDA" = "true" ]; then \
|
||||
|
||||
@@ -47,7 +47,7 @@ For more information, be sure to check out our [Open WebUI Documentation](https:
|
||||
|
||||
- 💾 **Persistent Artifact Storage**: Built-in key-value storage API for artifacts, enabling features like journals, trackers, leaderboards, and collaborative tools with both personal and shared data scopes across sessions.
|
||||
|
||||
- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support using your choice of 9 vector databases and multiple content extraction engines (Tika, Docling, Document Intelligence, Mistral OCR, External loaders). Load documents directly into chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
|
||||
- 📚 **Local RAG Integration**: Dive into the future of chat interactions with groundbreaking Retrieval Augmented Generation (RAG) support using your choice of 9 vector databases and multiple content extraction engines (Tika, Docling, Document Intelligence, Mistral OCR, PaddleOCR-vl, External loaders). Load documents directly into chat or add files to your document library, effortlessly accessing them using the `#` command before a query.
|
||||
|
||||
- 🔍 **Web Search for RAG**: Perform web searches using 15+ providers including `SearXNG`, `Google PSE`, `Brave Search`, `Kagi`, `Mojeek`, `Tavily`, `Perplexity`, `serpstack`, `serper`, `Serply`, `DuckDuckGo`, `SearchApi`, `SerpApi`, `Bing`, `Jina`, `Exa`, `Sougou`, `Azure AI Search`, and `Ollama Cloud`, injecting results directly into your chat experience.
|
||||
|
||||
|
||||
@@ -2827,6 +2827,18 @@ MISTRAL_OCR_API_KEY = PersistentConfig(
|
||||
os.getenv('MISTRAL_OCR_API_KEY', ''),
|
||||
)
|
||||
|
||||
PADDLEOCR_VL_BASE_URL = PersistentConfig(
|
||||
'PADDLEOCR_VL_BASE_URL',
|
||||
'rag.paddleocr_vl_base_url',
|
||||
os.getenv('PADDLEOCR_VL_BASE_URL', 'http://localhost:8080'),
|
||||
)
|
||||
|
||||
PADDLEOCR_VL_TOKEN = PersistentConfig(
|
||||
'PADDLEOCR_VL_TOKEN',
|
||||
'rag.paddleocr_vl_token',
|
||||
os.getenv('PADDLEOCR_VL_TOKEN', ''),
|
||||
)
|
||||
|
||||
BYPASS_EMBEDDING_AND_RETRIEVAL = PersistentConfig(
|
||||
'BYPASS_EMBEDDING_AND_RETRIEVAL',
|
||||
'rag.bypass_embedding_and_retrieval',
|
||||
|
||||
@@ -525,6 +525,12 @@ WEBUI_AUTH_TRUSTED_NAME_HEADER = os.environ.get('WEBUI_AUTH_TRUSTED_NAME_HEADER'
|
||||
WEBUI_AUTH_TRUSTED_GROUPS_HEADER = os.environ.get('WEBUI_AUTH_TRUSTED_GROUPS_HEADER', None)
|
||||
WEBUI_AUTH_TRUSTED_ROLE_HEADER = os.environ.get('WEBUI_AUTH_TRUSTED_ROLE_HEADER', None)
|
||||
|
||||
# Custom header name for API key authentication. Defaults to 'x-api-key'.
|
||||
# Useful when Open WebUI sits behind a reverse proxy / API gateway that
|
||||
# already uses the Authorization header for its own authentication — set
|
||||
# this to a unique header (e.g. 'X-OpenWebUI-Key') so the middleware
|
||||
# checks the custom header instead and avoids the 401 short-circuit.
|
||||
CUSTOM_API_KEY_HEADER = os.environ.get('CUSTOM_API_KEY_HEADER', 'x-api-key')
|
||||
|
||||
ENABLE_PASSWORD_VALIDATION = os.environ.get('ENABLE_PASSWORD_VALIDATION', 'False').lower() == 'true'
|
||||
PASSWORD_VALIDATION_REGEX_PATTERN = os.environ.get(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import ssl as _stdlib_ssl
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from typing import Any, Optional
|
||||
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
|
||||
@@ -37,90 +36,84 @@ from typing_extensions import Self
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_ssl_mode_from_url(url: str) -> tuple[str, str | None]:
|
||||
# ── SSL URL normalization (used by sync engine & Alembic migrations) ─
|
||||
#
|
||||
# psycopg2 (sync) needs ``sslmode=`` in the connection string (it does
|
||||
# not recognise the bare ``ssl=`` key that some ORMs emit). The helpers
|
||||
# below strip all SSL-related query params, normalise them, and
|
||||
# reattach them in the canonical libpq form.
|
||||
#
|
||||
# The **async** engine now uses psycopg (v3), which speaks libpq
|
||||
# natively, so it needs no translation at all — the DATABASE_URL is
|
||||
# passed through as-is.
|
||||
# ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _pop_first(params: dict[str, list[str]], key: str) -> str | None:
|
||||
"""Pop a single-valued query param, returning ``None`` if absent."""
|
||||
values = params.pop(key, None)
|
||||
return values[0] if values else None
|
||||
|
||||
|
||||
def _is_postgres_url(url: str) -> bool:
|
||||
"""Return True if *url* looks like a PostgreSQL connection string."""
|
||||
return bool(url) and any(url.startswith(p) for p in ('postgresql://', 'postgresql+', 'postgres://'))
|
||||
|
||||
|
||||
def extract_ssl_params_from_url(url: str) -> tuple[str, dict[str, str]]:
|
||||
"""Strip SSL query-string parameters from a PostgreSQL URL.
|
||||
|
||||
asyncpg and psycopg2 use different query-string keys for SSL
|
||||
(``ssl`` vs ``sslmode``). This helper removes **both** from the
|
||||
URL so that each driver can receive the correct parameter through
|
||||
its own mechanism (query-string re-injection for psycopg2,
|
||||
``connect_args`` for asyncpg).
|
||||
|
||||
Returns
|
||||
-------
|
||||
(url_without_ssl, ssl_mode)
|
||||
*url_without_ssl* is the original URL with ``ssl`` / ``sslmode``
|
||||
query parameters removed. *ssl_mode* is the extracted mode
|
||||
string (e.g. ``'require'``), or ``None`` if neither parameter
|
||||
was present.
|
||||
|
||||
Non-PostgreSQL URLs are returned unchanged with ``ssl_mode=None``.
|
||||
Returns ``(url_without_ssl, ssl_dict)`` where *ssl_dict* maps
|
||||
canonical libpq key names (``sslmode``, ``sslrootcert``, …) to
|
||||
their values. Non-PostgreSQL URLs are returned unchanged with an
|
||||
empty dict.
|
||||
"""
|
||||
if not url or not any(url.startswith(prefix) for prefix in ('postgresql://', 'postgresql+', 'postgres://')):
|
||||
return url, None
|
||||
if not _is_postgres_url(url):
|
||||
return url, {}
|
||||
|
||||
parsed = urlparse(url)
|
||||
query_params = parse_qs(parsed.query, keep_blank_values=True)
|
||||
qp = parse_qs(parsed.query, keep_blank_values=True)
|
||||
|
||||
# Prefer sslmode (libpq canonical) over the asyncpg-only ssl key.
|
||||
ssl_mode: str | None = None
|
||||
for key in ('sslmode', 'ssl'):
|
||||
values = query_params.pop(key, None)
|
||||
if values and ssl_mode is None:
|
||||
ssl_mode = values[0]
|
||||
# Prefer sslmode (libpq canonical) over the bare ``ssl`` key.
|
||||
sslmode_val = _pop_first(qp, 'sslmode')
|
||||
ssl_val = _pop_first(qp, 'ssl')
|
||||
ssl_mode = sslmode_val or ssl_val
|
||||
|
||||
if ssl_mode is None:
|
||||
# Nothing to strip — return the URL untouched.
|
||||
return url, None
|
||||
ssl_dict: dict[str, str] = {}
|
||||
if ssl_mode:
|
||||
ssl_dict['sslmode'] = ssl_mode
|
||||
for key in ('sslrootcert', 'sslcert', 'sslkey', 'sslcrl'):
|
||||
val = _pop_first(qp, key)
|
||||
if val:
|
||||
ssl_dict[key] = val
|
||||
|
||||
# Rebuild the query string without the SSL keys.
|
||||
remaining_query = urlencode(query_params, doseq=True)
|
||||
url_without_ssl = urlunparse(parsed._replace(query=remaining_query))
|
||||
return url_without_ssl, ssl_mode
|
||||
if not ssl_dict:
|
||||
return url, ssl_dict
|
||||
|
||||
cleaned_query = urlencode(qp, doseq=True)
|
||||
return urlunparse(parsed._replace(query=cleaned_query)), ssl_dict
|
||||
|
||||
|
||||
def build_asyncpg_ssl_args(ssl_mode: str | None) -> dict:
|
||||
"""Convert a libpq-style SSL mode value to asyncpg ``connect_args``.
|
||||
def reattach_ssl_params_to_url(url_without_ssl: str, ssl_dict: dict[str, str]) -> str:
|
||||
"""Re-append SSL query-string parameters to a cleaned PostgreSQL URL.
|
||||
|
||||
Returns a dict suitable for unpacking into
|
||||
``create_async_engine(..., connect_args=...)``.
|
||||
Used for psycopg2/libpq consumers that expect ``sslmode`` and the
|
||||
certificate-file keys in the connection string.
|
||||
"""
|
||||
if ssl_mode is None:
|
||||
return {}
|
||||
|
||||
mode = ssl_mode.lower()
|
||||
if mode == 'disable':
|
||||
return {'connect_args': {'ssl': False}}
|
||||
if mode in ('allow', 'prefer'):
|
||||
# asyncpg has no direct equivalent — omit to let it try without.
|
||||
return {}
|
||||
if mode == 'require':
|
||||
# SSL required but no certificate verification (matches libpq).
|
||||
ctx = _stdlib_ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = _stdlib_ssl.CERT_NONE
|
||||
return {'connect_args': {'ssl': ctx}}
|
||||
if mode in ('verify-ca', 'verify-full'):
|
||||
# Full verification — use the system trust store.
|
||||
ctx = _stdlib_ssl.create_default_context()
|
||||
if mode == 'verify-ca':
|
||||
ctx.check_hostname = False
|
||||
return {'connect_args': {'ssl': ctx}}
|
||||
|
||||
# Unknown value — pass through as-is and let asyncpg decide.
|
||||
return {'connect_args': {'ssl': ssl_mode}}
|
||||
|
||||
|
||||
def reattach_ssl_mode_to_url(url_without_ssl: str, ssl_mode: str | None) -> str:
|
||||
"""Re-append ``sslmode=<value>`` to a cleaned PostgreSQL URL.
|
||||
|
||||
Used for psycopg2 / libpq consumers that expect the canonical
|
||||
``sslmode`` query-string key.
|
||||
"""
|
||||
if ssl_mode is None:
|
||||
if not ssl_dict:
|
||||
return url_without_ssl
|
||||
separator = '&' if '?' in url_without_ssl else '?'
|
||||
return f'{url_without_ssl}{separator}sslmode={ssl_mode}'
|
||||
|
||||
parts = [f'{k}={v}' for k, v in ssl_dict.items() if v]
|
||||
if not parts:
|
||||
return url_without_ssl
|
||||
|
||||
sep = '&' if '?' in url_without_ssl else '?'
|
||||
return f'{url_without_ssl}{sep}{"&".join(parts)}'
|
||||
|
||||
|
||||
# Backwards-compatible aliases for external callers.
|
||||
extract_ssl_mode_from_url = extract_ssl_params_from_url
|
||||
reattach_ssl_mode_to_url = reattach_ssl_params_to_url
|
||||
|
||||
|
||||
class JSONField(types.TypeDecorator):
|
||||
@@ -150,9 +143,10 @@ class JSONField(types.TypeDecorator):
|
||||
def handle_peewee_migration(DATABASE_URL):
|
||||
db = None
|
||||
try:
|
||||
# Normalize SSL params so psycopg2 always sees `sslmode=` (never `ssl=`).
|
||||
url_without_ssl, ssl_mode = extract_ssl_mode_from_url(DATABASE_URL)
|
||||
normalized_url = reattach_ssl_mode_to_url(url_without_ssl, ssl_mode)
|
||||
# Normalize SSL params so psycopg2 always sees `sslmode=` (never `ssl=`)
|
||||
# and cert-file params are preserved in the connection string.
|
||||
url_without_ssl, ssl_params = extract_ssl_params_from_url(DATABASE_URL)
|
||||
normalized_url = reattach_ssl_params_to_url(url_without_ssl, ssl_params)
|
||||
|
||||
# Replace the postgresql:// with postgres:// to handle the peewee migration
|
||||
db = register_connection(normalized_url.replace('postgresql://', 'postgres://'))
|
||||
@@ -179,32 +173,36 @@ if ENABLE_DB_MIGRATIONS:
|
||||
handle_peewee_migration(DATABASE_URL)
|
||||
|
||||
|
||||
# Normalize SSL params from the URL once; each engine branch re-injects
|
||||
# the driver-appropriate form.
|
||||
DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE = extract_ssl_mode_from_url(DATABASE_URL)
|
||||
# Normalize SSL params from the URL once; the sync engine needs them
|
||||
# reattached in canonical libpq form for psycopg2.
|
||||
_url_without_ssl, _ssl_dict = extract_ssl_params_from_url(DATABASE_URL)
|
||||
|
||||
# For psycopg2 (sync engine), re-append sslmode=<value>.
|
||||
SQLALCHEMY_DATABASE_URL = (
|
||||
reattach_ssl_mode_to_url(DATABASE_URL_WITHOUT_SSL, DATABASE_SSL_MODE) if DATABASE_SSL_MODE else DATABASE_URL
|
||||
)
|
||||
# For psycopg2 (sync engine), re-append sslmode + cert-file params.
|
||||
SQLALCHEMY_DATABASE_URL = reattach_ssl_params_to_url(_url_without_ssl, _ssl_dict) if _ssl_dict else DATABASE_URL
|
||||
|
||||
|
||||
def _make_async_url(url: str) -> str:
|
||||
"""Convert a sync database URL to its async driver equivalent."""
|
||||
"""Convert a sync database URL to its async driver equivalent.
|
||||
|
||||
The async engine uses psycopg (v3) which speaks libpq natively,
|
||||
so all standard connection-string parameters (``sslmode``,
|
||||
``options``, ``target_session_attrs``, etc.) are passed through
|
||||
without any translation.
|
||||
"""
|
||||
if url.startswith('sqlite+sqlcipher://'):
|
||||
# SQLCipher has no async driver — not supported for async
|
||||
raise ValueError(
|
||||
'sqlite+sqlcipher:// URLs are not supported with async engine. '
|
||||
'Use standard sqlite:// or postgresql:// instead.'
|
||||
)
|
||||
if url.startswith('sqlite:///') or url.startswith('sqlite://'):
|
||||
return url.replace('sqlite://', 'sqlite+aiosqlite://', 1)
|
||||
# psycopg v3 — auto-selects async mode with create_async_engine
|
||||
if url.startswith('postgresql+psycopg2://'):
|
||||
return url.replace('postgresql+psycopg2://', 'postgresql+asyncpg://', 1)
|
||||
return url.replace('postgresql+psycopg2://', 'postgresql+psycopg://', 1)
|
||||
if url.startswith('postgresql://'):
|
||||
return url.replace('postgresql://', 'postgresql+asyncpg://', 1)
|
||||
return url.replace('postgresql://', 'postgresql+psycopg://', 1)
|
||||
if url.startswith('postgres://'):
|
||||
return url.replace('postgres://', 'postgresql+asyncpg://', 1)
|
||||
return url.replace('postgres://', 'postgresql+psycopg://', 1)
|
||||
# For other dialects, return as-is and let SQLAlchemy handle it
|
||||
return url
|
||||
|
||||
@@ -329,10 +327,10 @@ get_db = contextmanager(get_session)
|
||||
# ASYNC ENGINE (used for ALL runtime database operations)
|
||||
# ============================================================
|
||||
|
||||
# Use the SSL-stripped URL for asyncpg — SSL is injected via connect_args.
|
||||
ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(
|
||||
DATABASE_URL_WITHOUT_SSL if DATABASE_SSL_MODE else SQLALCHEMY_DATABASE_URL
|
||||
)
|
||||
# psycopg (v3) speaks libpq natively — the full DATABASE_URL is passed
|
||||
# through as-is. SSL params, ``options``, ``target_session_attrs``, etc.
|
||||
# all work without any stripping or translation.
|
||||
ASYNC_SQLALCHEMY_DATABASE_URL = _make_async_url(SQLALCHEMY_DATABASE_URL)
|
||||
|
||||
if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL:
|
||||
# Generous default — async coroutines + no session sharing = high connection demand.
|
||||
@@ -350,10 +348,6 @@ if 'sqlite' in ASYNC_SQLALCHEMY_DATABASE_URL:
|
||||
def _set_sqlite_pragmas(dbapi_connection, connection_record):
|
||||
_apply_sqlite_pragmas(dbapi_connection)
|
||||
else:
|
||||
# Inject asyncpg-compatible SSL connect_args when the user specified
|
||||
# sslmode/ssl in DATABASE_URL.
|
||||
asyncpg_ssl_args = build_asyncpg_ssl_args(DATABASE_SSL_MODE)
|
||||
|
||||
if isinstance(DATABASE_POOL_SIZE, int):
|
||||
if DATABASE_POOL_SIZE > 0:
|
||||
async_engine = create_async_engine(
|
||||
@@ -363,20 +357,17 @@ else:
|
||||
pool_timeout=DATABASE_POOL_TIMEOUT,
|
||||
pool_recycle=DATABASE_POOL_RECYCLE,
|
||||
pool_pre_ping=True,
|
||||
**asyncpg_ssl_args,
|
||||
)
|
||||
else:
|
||||
async_engine = create_async_engine(
|
||||
ASYNC_SQLALCHEMY_DATABASE_URL,
|
||||
pool_pre_ping=True,
|
||||
poolclass=NullPool,
|
||||
**asyncpg_ssl_args,
|
||||
)
|
||||
else:
|
||||
async_engine = create_async_engine(
|
||||
ASYNC_SQLALCHEMY_DATABASE_URL,
|
||||
pool_pre_ping=True,
|
||||
**asyncpg_ssl_args,
|
||||
)
|
||||
|
||||
|
||||
|
||||
+55
-35
@@ -303,6 +303,8 @@ from open_webui.config import (
|
||||
DOCUMENT_INTELLIGENCE_MODEL,
|
||||
MISTRAL_OCR_API_BASE_URL,
|
||||
MISTRAL_OCR_API_KEY,
|
||||
PADDLEOCR_VL_BASE_URL,
|
||||
PADDLEOCR_VL_TOKEN,
|
||||
RAG_TEXT_SPLITTER,
|
||||
ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER,
|
||||
TIKTOKEN_ENCODING_NAME,
|
||||
@@ -1023,6 +1025,8 @@ app.state.config.DOCUMENT_INTELLIGENCE_KEY = DOCUMENT_INTELLIGENCE_KEY
|
||||
app.state.config.DOCUMENT_INTELLIGENCE_MODEL = DOCUMENT_INTELLIGENCE_MODEL
|
||||
app.state.config.MISTRAL_OCR_API_BASE_URL = MISTRAL_OCR_API_BASE_URL
|
||||
app.state.config.MISTRAL_OCR_API_KEY = MISTRAL_OCR_API_KEY
|
||||
app.state.config.PADDLEOCR_VL_BASE_URL = PADDLEOCR_VL_BASE_URL
|
||||
app.state.config.PADDLEOCR_VL_TOKEN = PADDLEOCR_VL_TOKEN
|
||||
app.state.config.MINERU_API_MODE = MINERU_API_MODE
|
||||
app.state.config.MINERU_API_URL = MINERU_API_URL
|
||||
app.state.config.MINERU_API_KEY = MINERU_API_KEY
|
||||
@@ -1832,7 +1836,7 @@ async def chat_completion(
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.debug(f'Error processing chat metadata: {e}')
|
||||
log.warning(f'Error processing chat metadata: {e}')
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e),
|
||||
@@ -1865,17 +1869,16 @@ async def chat_completion(
|
||||
except asyncio.CancelledError:
|
||||
log.info('Chat processing was cancelled')
|
||||
try:
|
||||
event_emitter = await get_event_emitter(metadata)
|
||||
if event_emitter:
|
||||
await asyncio.shield(
|
||||
event_emitter(
|
||||
{'type': 'chat:tasks:cancel'},
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
async def emit_cancel_event():
|
||||
event_emitter = await get_event_emitter(metadata)
|
||||
if event_emitter:
|
||||
await event_emitter({'type': 'chat:tasks:cancel'})
|
||||
|
||||
await asyncio.shield(emit_cancel_event())
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
raise # re-raise to ensure proper task cancellation handling
|
||||
raise # re-raise to ensure proper task cancellation handling
|
||||
except Exception as e:
|
||||
error_detail = e.detail if isinstance(e, HTTPException) else str(e)
|
||||
log.error('Error processing chat payload: %s', error_detail)
|
||||
@@ -1906,37 +1909,54 @@ async def chat_completion(
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# No chat_id/message_id → legacy/direct API path with no
|
||||
# WebSocket error channel. We must surface the error as
|
||||
# a proper HTTP response; without this the function would
|
||||
# return None which FastAPI serializes as null. #23924
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=error_detail,
|
||||
)
|
||||
finally:
|
||||
# Clean up MCP clients. Shield the entire block from
|
||||
# CancelledError so disconnect() can finish even when the
|
||||
# task is being stopped. Each client is isolated so one
|
||||
# failure doesn't skip the rest.
|
||||
# MCP cleanup — MUST run in the SAME asyncio task as
|
||||
# connect() because the MCP SDK's streamablehttp_client
|
||||
# uses anyio task groups whose cancel scopes enforce
|
||||
# same-task exit. Do NOT wrap in asyncio.shield() or
|
||||
# asyncio.wait_for() — both create a new task.
|
||||
try:
|
||||
if mcp_clients := metadata.get('mcp_clients'):
|
||||
|
||||
async def _cleanup_mcp():
|
||||
for client in reversed(list(mcp_clients.values())):
|
||||
try:
|
||||
await client.disconnect()
|
||||
except Exception as e:
|
||||
log.debug(f'Error disconnecting MCP client: {e}')
|
||||
|
||||
await asyncio.wait_for(
|
||||
asyncio.shield(_cleanup_mcp()),
|
||||
timeout=10.0,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
log.warning('MCP client cleanup timed out after 10 s')
|
||||
for client in reversed(list(mcp_clients.values())):
|
||||
try:
|
||||
await client.disconnect()
|
||||
except Exception as e:
|
||||
log.debug(f'Error disconnecting MCP client: {e}')
|
||||
except asyncio.CancelledError:
|
||||
# Let the client close asynchronously by GC
|
||||
pass
|
||||
except Exception as e:
|
||||
log.debug(f'Error cleaning up MCP clients: {e}')
|
||||
# Emit chat:active=false when task completes
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
try:
|
||||
if metadata.get('chat_id'):
|
||||
event_emitter = await get_event_emitter(metadata, update_db=False)
|
||||
if event_emitter:
|
||||
await event_emitter({'type': 'chat:active', 'data': {'active': False}})
|
||||
except Exception as e:
|
||||
log.debug(f'Error emitting chat:active: {e}')
|
||||
|
||||
async def emit_inactive_event():
|
||||
try:
|
||||
event_emitter = await get_event_emitter(metadata, update_db=False)
|
||||
if event_emitter:
|
||||
await event_emitter({'type': 'chat:active', 'data': {'active': False}})
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
# Shield the event emission so it finishes even if the main task is cancelled
|
||||
await asyncio.shield(emit_inactive_event())
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fan out: one task per model
|
||||
if metadata.get('session_id') and metadata.get('chat_id'):
|
||||
|
||||
@@ -5,7 +5,7 @@ from alembic import context
|
||||
from open_webui.models.auths import Auth
|
||||
from open_webui.models.calendar import Calendar, CalendarEvent, CalendarEventAttendee # noqa: F401
|
||||
from open_webui.env import DATABASE_URL, DATABASE_PASSWORD, LOG_FORMAT
|
||||
from open_webui.internal.db import extract_ssl_mode_from_url, reattach_ssl_mode_to_url
|
||||
from open_webui.internal.db import extract_ssl_params_from_url, reattach_ssl_params_to_url
|
||||
from sqlalchemy import engine_from_config, pool, create_engine
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
@@ -37,9 +37,9 @@ target_metadata = Auth.metadata
|
||||
|
||||
DB_URL = DATABASE_URL
|
||||
|
||||
# Normalize SSL query params for psycopg2 (Alembic uses psycopg2, not asyncpg).
|
||||
url_without_ssl, ssl_mode = extract_ssl_mode_from_url(DB_URL)
|
||||
DB_URL = reattach_ssl_mode_to_url(url_without_ssl, ssl_mode) if ssl_mode else DB_URL
|
||||
# Normalize SSL query params for psycopg2 (Alembic uses psycopg2 for sync migrations).
|
||||
url_without_ssl, ssl_params = extract_ssl_params_from_url(DB_URL)
|
||||
DB_URL = reattach_ssl_params_to_url(url_without_ssl, ssl_params) if ssl_params else DB_URL
|
||||
|
||||
if DB_URL:
|
||||
config.set_main_option('sqlalchemy.url', DB_URL.replace('%', '%%'))
|
||||
|
||||
@@ -393,7 +393,6 @@ class ChatTable:
|
||||
chat_item.updated_at = int(time.time())
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(chat_item)
|
||||
|
||||
return ChatModel.model_validate(chat_item)
|
||||
except Exception:
|
||||
|
||||
@@ -143,6 +143,8 @@ class ModelAccessListResponse(BaseModel):
|
||||
|
||||
|
||||
class ModelForm(BaseModel):
|
||||
model_config = ConfigDict(extra='ignore')
|
||||
|
||||
id: str
|
||||
base_model_id: Optional[str] = None
|
||||
name: str
|
||||
@@ -389,6 +391,52 @@ class ModelsTable:
|
||||
|
||||
return ModelListResponse(items=models, total=total)
|
||||
|
||||
async def get_model_meta_by_id(self, id: str, db: Optional[AsyncSession] = None) -> Optional[tuple[dict, int]]:
|
||||
"""Return (meta, updated_at) for a model, skipping access grant resolution."""
|
||||
try:
|
||||
async with get_async_db_context(db) as db:
|
||||
result = await db.execute(select(Model.meta, Model.updated_at).filter_by(id=id))
|
||||
return result.first()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def get_all_tags(
|
||||
self,
|
||||
user_id: str,
|
||||
is_admin: bool = False,
|
||||
db: Optional[AsyncSession] = None,
|
||||
) -> set[str]:
|
||||
"""Extract unique tag names from model meta, querying only the meta column."""
|
||||
async with get_async_db_context(db) as db:
|
||||
stmt = select(Model.meta).filter(Model.base_model_id != None)
|
||||
|
||||
if not is_admin:
|
||||
user_groups = await Groups.get_groups_by_member_id(user_id, db=db)
|
||||
user_group_ids = [group.id for group in user_groups]
|
||||
|
||||
filter_dict = {'user_id': user_id}
|
||||
if user_group_ids:
|
||||
filter_dict['group_ids'] = user_group_ids
|
||||
|
||||
stmt = self._has_permission(db, stmt, filter_dict, permission='read')
|
||||
|
||||
result = await db.execute(stmt)
|
||||
rows = result.scalars().all()
|
||||
|
||||
tags_set: set[str] = set()
|
||||
for meta in rows:
|
||||
if not meta:
|
||||
continue
|
||||
for tag in meta.get('tags', []):
|
||||
try:
|
||||
name = tag.get('name') if isinstance(tag, dict) else str(tag)
|
||||
if name:
|
||||
tags_set.add(name)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return tags_set
|
||||
|
||||
async def get_model_by_id(self, id: str, db: Optional[AsyncSession] = None) -> Optional[ModelModel]:
|
||||
try:
|
||||
async with get_async_db_context(db) as db:
|
||||
|
||||
@@ -320,6 +320,19 @@ class OAuthSessionTable:
|
||||
log.error(f'Error deleting OAuth sessions by user ID: {e}')
|
||||
return False
|
||||
|
||||
async def delete_sessions_by_user_id_and_provider(
|
||||
self, user_id: str, provider: str, db: Optional[AsyncSession] = None
|
||||
) -> bool:
|
||||
"""Delete all OAuth sessions for a specific user and provider"""
|
||||
try:
|
||||
async with get_async_db_context(db) as db:
|
||||
result = await db.execute(delete(OAuthSession).filter_by(user_id=user_id, provider=provider))
|
||||
await db.commit()
|
||||
return result.rowcount > 0
|
||||
except Exception as e:
|
||||
log.error(f'Error deleting OAuth sessions for user {user_id} and provider {provider}: {e}')
|
||||
return False
|
||||
|
||||
async def delete_sessions_by_provider(self, provider: str, db: Optional[AsyncSession] = None) -> bool:
|
||||
"""Delete all OAuth sessions for a provider"""
|
||||
try:
|
||||
|
||||
@@ -23,9 +23,9 @@ from open_webui.retrieval.loaders.external_document import ExternalDocumentLoade
|
||||
from open_webui.retrieval.loaders.mistral import MistralLoader
|
||||
from open_webui.retrieval.loaders.datalab_marker import DatalabMarkerLoader
|
||||
from open_webui.retrieval.loaders.mineru import MinerULoader
|
||||
from open_webui.retrieval.loaders.paddleocr_vl import PaddleOCRVLLoader
|
||||
|
||||
|
||||
from open_webui.env import GLOBAL_LOG_LEVEL, REQUESTS_VERIFY
|
||||
from open_webui.env import GLOBAL_LOG_LEVEL, REQUESTS_VERIFY, AIOHTTP_CLIENT_SESSION_SSL
|
||||
|
||||
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -205,6 +205,7 @@ class DoclingLoader:
|
||||
**self.params,
|
||||
},
|
||||
headers=headers,
|
||||
verify=AIOHTTP_CLIENT_SESSION_SSL,
|
||||
)
|
||||
if r.ok:
|
||||
result = r.json()
|
||||
@@ -399,6 +400,12 @@ class Loader:
|
||||
api_key=self.kwargs.get('MISTRAL_OCR_API_KEY'),
|
||||
file_path=file_path,
|
||||
)
|
||||
elif self.engine == 'paddleocr_vl' and self.kwargs.get('PADDLEOCR_VL_TOKEN') != '':
|
||||
loader = PaddleOCRVLLoader(
|
||||
api_url=self.kwargs.get('PADDLEOCR_VL_BASE_URL'),
|
||||
token=self.kwargs.get('PADDLEOCR_VL_TOKEN'),
|
||||
file_path=file_path,
|
||||
)
|
||||
else:
|
||||
if file_ext == 'pdf':
|
||||
loader = PyPDFLoader(
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import base64
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from open_webui.env import GLOBAL_LOG_LEVEL
|
||||
|
||||
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PaddleOCRVLLoader:
|
||||
"""Loader that uses PaddleOCR-vl API to extract text from PDF/images."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_url: str,
|
||||
token: str,
|
||||
file_path: str,
|
||||
):
|
||||
if not api_url or not token:
|
||||
raise ValueError('PaddleOCR-vl API URL and Token are required.')
|
||||
if not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f'File not found at {file_path}')
|
||||
|
||||
self.api_url = api_url.rstrip('/')
|
||||
self.token = token
|
||||
self.file_path = file_path
|
||||
self.file_name = os.path.basename(file_path)
|
||||
|
||||
def load(self) -> List[Document]:
|
||||
log.info(f'Processing with PaddleOCR-vl: {self.file_path}')
|
||||
|
||||
try:
|
||||
with open(self.file_path, 'rb') as file:
|
||||
file_bytes = file.read()
|
||||
file_data = base64.b64encode(file_bytes).decode('ascii')
|
||||
except Exception as e:
|
||||
log.error(f'Failed to read file {self.file_path}: {e}')
|
||||
raise
|
||||
|
||||
headers = {'Authorization': f'token {self.token}', 'Content-Type': 'application/json'}
|
||||
|
||||
# Detect fileType based on file extension
|
||||
ext = self.file_path.lower().split('.')[-1]
|
||||
image_extensions = ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'webp']
|
||||
file_type = 1 if ext in image_extensions else 0
|
||||
|
||||
payload = {
|
||||
'file': file_data,
|
||||
'fileType': file_type,
|
||||
'useDocOrientationClassify': False,
|
||||
'useDocUnwarping': False,
|
||||
'useChartRecognition': False,
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(f'{self.api_url}/layout-parsing', json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json().get('result', {})
|
||||
layout_results = result.get('layoutParsingResults', [])
|
||||
|
||||
documents = []
|
||||
total_pages = len(layout_results)
|
||||
skipped_pages = 0
|
||||
|
||||
for i, res in enumerate(layout_results):
|
||||
markdown_text = res.get('markdown', {}).get('text', '')
|
||||
|
||||
if isinstance(markdown_text, str):
|
||||
cleaned_content = markdown_text.strip()
|
||||
else:
|
||||
cleaned_content = str(markdown_text).strip()
|
||||
|
||||
if not cleaned_content:
|
||||
skipped_pages += 1
|
||||
continue
|
||||
|
||||
documents.append(
|
||||
Document(
|
||||
page_content=cleaned_content,
|
||||
metadata={
|
||||
'page': i,
|
||||
'page_label': i + 1,
|
||||
'total_pages': total_pages,
|
||||
'file_name': self.file_name,
|
||||
'processing_engine': 'paddleocr-vl',
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
if skipped_pages > 0:
|
||||
log.info(f'PaddleOCR-vl: Processed {len(documents)} pages, skipped {skipped_pages} empty pages.')
|
||||
|
||||
if not documents:
|
||||
log.warning('No valid text content found by PaddleOCR-vl.')
|
||||
return [
|
||||
Document(
|
||||
page_content='No valid text content found in document',
|
||||
metadata={
|
||||
'error': 'no_valid_pages',
|
||||
'file_name': self.file_name,
|
||||
'processing_engine': 'paddleocr-vl',
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
return documents
|
||||
|
||||
except Exception as e:
|
||||
log.error(f'Error calling PaddleOCR-vl: {e}')
|
||||
return [
|
||||
Document(
|
||||
page_content=f'Error during OCR processing: {e}',
|
||||
metadata={
|
||||
'error': 'processing_failed',
|
||||
'file_name': self.file_name,
|
||||
'processing_engine': 'paddleocr-vl',
|
||||
},
|
||||
)
|
||||
]
|
||||
@@ -114,6 +114,8 @@ def build_loader_from_config(request):
|
||||
DOCUMENT_INTELLIGENCE_MODEL=config.DOCUMENT_INTELLIGENCE_MODEL,
|
||||
MISTRAL_OCR_API_BASE_URL=config.MISTRAL_OCR_API_BASE_URL,
|
||||
MISTRAL_OCR_API_KEY=config.MISTRAL_OCR_API_KEY,
|
||||
PADDLEOCR_VL_BASE_URL=config.PADDLEOCR_VL_BASE_URL,
|
||||
PADDLEOCR_VL_TOKEN=config.PADDLEOCR_VL_TOKEN,
|
||||
MINERU_API_MODE=config.MINERU_API_MODE,
|
||||
MINERU_API_URL=config.MINERU_API_URL,
|
||||
MINERU_API_KEY=config.MINERU_API_KEY,
|
||||
@@ -564,6 +566,13 @@ async def query_collection(
|
||||
log.exception(f'Error when querying the collection: {e}')
|
||||
return None, e
|
||||
|
||||
# Sanitize: filter out None/empty queries to prevent embedding crashes
|
||||
# (e.g. when get_last_user_message returns None)
|
||||
queries = [q for q in queries if q]
|
||||
if not queries:
|
||||
log.warning('query_collection: all queries were None or empty, returning empty results')
|
||||
return {'distances': [[]], 'documents': [[]], 'metadatas': [[]]}
|
||||
|
||||
# Generate all query embeddings (in one call)
|
||||
query_embeddings = await embedding_function(queries, prefix=RAG_EMBEDDING_QUERY_PREFIX)
|
||||
log.debug(f'query_collection: processing {len(queries)} queries across {len(collection_names)} collections')
|
||||
|
||||
@@ -1,52 +1,229 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional, List
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import requests
|
||||
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
|
||||
from langchain_core.documents import Document
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from open_webui.retrieval.web.main import SearchResult
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_FIRECRAWL_API_BASE_URL = 'https://api.firecrawl.dev'
|
||||
FIRECRAWL_RETRY_STATUS_CODES = {429, 500, 502, 503, 504}
|
||||
FIRECRAWL_MAX_RETRIES = 2
|
||||
|
||||
|
||||
def build_firecrawl_url(base_url: str | None, path: str) -> str:
|
||||
base_url = (base_url or DEFAULT_FIRECRAWL_API_BASE_URL).rstrip('/')
|
||||
path = path.lstrip('/')
|
||||
|
||||
if base_url.endswith('/v2'):
|
||||
return f'{base_url}/{path}'
|
||||
|
||||
return f'{base_url}/v2/{path}'
|
||||
|
||||
|
||||
def build_firecrawl_headers(api_key: str | None) -> dict[str, str]:
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {api_key or ""}',
|
||||
}
|
||||
|
||||
|
||||
def get_firecrawl_timeout_seconds(timeout: Any) -> float | None:
|
||||
if timeout in (None, ''):
|
||||
return None
|
||||
|
||||
try:
|
||||
timeout = float(timeout)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
return timeout if timeout > 0 else None
|
||||
|
||||
|
||||
def get_firecrawl_scrape_timeout_ms(timeout: Any) -> int | None:
|
||||
timeout_seconds = get_firecrawl_timeout_seconds(timeout)
|
||||
if timeout_seconds is None:
|
||||
return None
|
||||
|
||||
# Firecrawl v2 expects scrape timeouts in milliseconds.
|
||||
return min(300000, max(1000, int(timeout_seconds * 1000)))
|
||||
|
||||
|
||||
def get_firecrawl_client_timeout_seconds(timeout: Any, fallback: float = 60) -> float:
|
||||
# Keep the local HTTP timeout slightly above Firecrawl's scrape timeout.
|
||||
return (get_firecrawl_timeout_seconds(timeout) or fallback) + 10
|
||||
|
||||
|
||||
def get_firecrawl_retry_delay(headers: Any, attempt: int) -> float:
|
||||
retry_after = headers.get('Retry-After') if headers else None
|
||||
if retry_after:
|
||||
try:
|
||||
return min(10.0, max(0.0, float(retry_after)))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
return min(8.0, float(2**attempt))
|
||||
|
||||
|
||||
def request_firecrawl_json(
|
||||
method: str,
|
||||
url: str,
|
||||
*,
|
||||
headers: dict[str, str],
|
||||
json: dict[str, Any] | None = None,
|
||||
timeout: float | None = None,
|
||||
verify: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
last_error = None
|
||||
|
||||
for attempt in range(FIRECRAWL_MAX_RETRIES + 1):
|
||||
try:
|
||||
response = requests.request(
|
||||
method,
|
||||
url,
|
||||
headers=headers,
|
||||
json=json,
|
||||
timeout=timeout,
|
||||
verify=verify,
|
||||
)
|
||||
|
||||
if response.status_code in FIRECRAWL_RETRY_STATUS_CODES and attempt < FIRECRAWL_MAX_RETRIES:
|
||||
delay = get_firecrawl_retry_delay(response.headers, attempt)
|
||||
log.warning(
|
||||
'Firecrawl %s %s returned HTTP %s; retrying in %.1fs',
|
||||
method,
|
||||
url,
|
||||
response.status_code,
|
||||
delay,
|
||||
)
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except (requests.ConnectionError, requests.Timeout) as e:
|
||||
last_error = e
|
||||
if attempt >= FIRECRAWL_MAX_RETRIES:
|
||||
break
|
||||
|
||||
delay = get_firecrawl_retry_delay(None, attempt)
|
||||
log.warning('Firecrawl %s %s failed; retrying in %.1fs: %s', method, url, delay, e)
|
||||
time.sleep(delay)
|
||||
|
||||
if last_error:
|
||||
raise last_error
|
||||
|
||||
raise RuntimeError(f'Firecrawl {method} {url} failed without a response')
|
||||
|
||||
|
||||
def get_firecrawl_result_url(result: dict[str, Any]) -> str:
|
||||
metadata = result.get('metadata') or {}
|
||||
return (
|
||||
result.get('url')
|
||||
or result.get('link')
|
||||
or metadata.get('url')
|
||||
or metadata.get('sourceURL')
|
||||
or metadata.get('source_url')
|
||||
or ''
|
||||
)
|
||||
|
||||
|
||||
def scrape_firecrawl_url(
|
||||
firecrawl_url: str,
|
||||
firecrawl_api_key: str,
|
||||
url: str,
|
||||
*,
|
||||
verify_ssl: bool = True,
|
||||
timeout: Any = None,
|
||||
params: dict[str, Any] | None = None,
|
||||
) -> Document | None:
|
||||
payload = {
|
||||
'url': url,
|
||||
'formats': ['markdown'],
|
||||
'skipTlsVerification': not verify_ssl,
|
||||
'removeBase64Images': True,
|
||||
**(params or {}),
|
||||
}
|
||||
scrape_timeout_ms = get_firecrawl_scrape_timeout_ms(timeout)
|
||||
if scrape_timeout_ms is not None:
|
||||
payload['timeout'] = scrape_timeout_ms
|
||||
|
||||
response = request_firecrawl_json(
|
||||
'POST',
|
||||
build_firecrawl_url(firecrawl_url, 'scrape'),
|
||||
headers=build_firecrawl_headers(firecrawl_api_key),
|
||||
json=payload,
|
||||
timeout=get_firecrawl_client_timeout_seconds(timeout),
|
||||
verify=verify_ssl,
|
||||
)
|
||||
data = response.get('data') or {}
|
||||
content = data.get('markdown') or ''
|
||||
if not isinstance(content, str) or not content.strip():
|
||||
return None
|
||||
|
||||
metadata = data.get('metadata') or {}
|
||||
document_metadata = {'source': get_firecrawl_result_url(data) or url}
|
||||
if metadata.get('title'):
|
||||
document_metadata['title'] = metadata['title']
|
||||
if metadata.get('description'):
|
||||
document_metadata['description'] = metadata['description']
|
||||
|
||||
return Document(page_content=content, metadata=document_metadata)
|
||||
|
||||
|
||||
def search_firecrawl(
|
||||
firecrawl_url: str,
|
||||
firecrawl_api_key: str,
|
||||
query: str,
|
||||
count: int,
|
||||
filter_list: Optional[List[str]] = None,
|
||||
) -> List[SearchResult]:
|
||||
filter_list: list[str] | None = None,
|
||||
) -> list[SearchResult]:
|
||||
try:
|
||||
url = firecrawl_url.rstrip('/')
|
||||
response = requests.post(
|
||||
f'{url}/v1/search',
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {firecrawl_api_key}',
|
||||
},
|
||||
response = request_firecrawl_json(
|
||||
'POST',
|
||||
build_firecrawl_url(firecrawl_url, 'search'),
|
||||
headers=build_firecrawl_headers(firecrawl_api_key),
|
||||
json={
|
||||
'query': query,
|
||||
'limit': count,
|
||||
'timeout': count * 3000,
|
||||
'ignoreInvalidURLs': True,
|
||||
},
|
||||
timeout=count * 3 + 10,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json().get('data', {})
|
||||
|
||||
results = [
|
||||
SearchResult(
|
||||
link=r.get('url', ''),
|
||||
title=r.get('title', ''),
|
||||
snippet=r.get('description', ''),
|
||||
)
|
||||
for r in data.get('web', [])
|
||||
]
|
||||
data = response.get('data') or {}
|
||||
results = data.get('web') or []
|
||||
|
||||
if filter_list:
|
||||
from open_webui.retrieval.web.main import get_filtered_results
|
||||
|
||||
results = get_filtered_results(results, filter_list)
|
||||
|
||||
results = results[:count]
|
||||
log.info(f'FireCrawl search results: {results}')
|
||||
return results
|
||||
from open_webui.retrieval.web.main import SearchResult
|
||||
|
||||
search_results = []
|
||||
for result in results[:count]:
|
||||
url = get_firecrawl_result_url(result)
|
||||
if not url:
|
||||
continue
|
||||
|
||||
metadata = result.get('metadata') or {}
|
||||
search_results.append(
|
||||
SearchResult(
|
||||
link=url,
|
||||
title=result.get('title') or metadata.get('title'),
|
||||
snippet=result.get('description') or result.get('snippet') or metadata.get('description'),
|
||||
)
|
||||
)
|
||||
|
||||
log.info(f'FireCrawl search results: {search_results}')
|
||||
return search_results
|
||||
except Exception as e:
|
||||
log.error(f'Error in FireCrawl search: {e}')
|
||||
return []
|
||||
|
||||
@@ -5,6 +5,8 @@ import socket
|
||||
import ssl
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
import requests
|
||||
from datetime import datetime, time, timedelta
|
||||
from typing import (
|
||||
Any,
|
||||
@@ -28,6 +30,7 @@ from langchain_core.documents import Document
|
||||
|
||||
from open_webui.retrieval.loaders.tavily import TavilyLoader
|
||||
from open_webui.retrieval.loaders.external_web import ExternalWebLoader
|
||||
from open_webui.retrieval.web.firecrawl import scrape_firecrawl_url
|
||||
from open_webui.constants import ERROR_MESSAGES
|
||||
from open_webui.config import (
|
||||
ENABLE_RAG_LOCAL_WEB_FETCH,
|
||||
@@ -216,39 +219,20 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
|
||||
|
||||
def lazy_load(self) -> Iterator[Document]:
|
||||
try:
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {self.api_key}',
|
||||
}
|
||||
|
||||
for url in self.web_paths:
|
||||
payload = {
|
||||
'url': url,
|
||||
'formats': ['markdown'],
|
||||
**self.params,
|
||||
}
|
||||
if self.timeout:
|
||||
payload['timeout'] = self.timeout * 1000
|
||||
|
||||
response = requests.post(
|
||||
f'{self.api_url}/v1/scrape',
|
||||
headers=headers,
|
||||
json=payload,
|
||||
timeout=self.timeout or 60,
|
||||
verify=self.verify_ssl,
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json().get('data', {})
|
||||
metadata = data.get('metadata', {})
|
||||
source = metadata.get('url') or metadata.get('sourceURL') or url
|
||||
|
||||
yield Document(
|
||||
page_content=data.get('markdown', ''),
|
||||
metadata={'source': source},
|
||||
doc = scrape_firecrawl_url(
|
||||
self.api_url,
|
||||
self.api_key,
|
||||
url,
|
||||
verify_ssl=self.verify_ssl,
|
||||
timeout=self.timeout,
|
||||
params=self.params,
|
||||
)
|
||||
if doc is not None:
|
||||
yield doc
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(f'Error extracting content from URLs: {e}')
|
||||
log.warning(f'Error extracting content from URLs with Firecrawl: {e}')
|
||||
else:
|
||||
raise e
|
||||
|
||||
@@ -259,7 +243,7 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
|
||||
yield doc
|
||||
except Exception as e:
|
||||
if self.continue_on_failure:
|
||||
log.exception(f'Error extracting content from URLs: {e}')
|
||||
log.warning(f'Error extracting content from URLs with Firecrawl: {e}')
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
@@ -576,7 +576,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
|
||||
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
|
||||
mistral_payload = {
|
||||
'input': payload.get('input', ''),
|
||||
'model': request.app.state.config.TTS_MODEL or 'mistral-tts-latest',
|
||||
'model': request.app.state.config.TTS_MODEL or 'voxtral-mini-tts-2603',
|
||||
'voice_id': payload.get('voice', ''),
|
||||
'response_format': 'mp3',
|
||||
}
|
||||
@@ -1345,7 +1345,7 @@ async def get_available_models(request: Request) -> list[dict]:
|
||||
except Exception as e:
|
||||
log.error(f'Error fetching models: {str(e)}')
|
||||
elif request.app.state.config.TTS_ENGINE == 'mistral':
|
||||
available_models = [{'id': 'mistral-tts-latest'}]
|
||||
available_models = [{'id': 'voxtral-mini-tts-2603'}]
|
||||
return available_models
|
||||
|
||||
|
||||
@@ -1431,11 +1431,14 @@ async def get_available_voices(request) -> dict:
|
||||
response.raise_for_status()
|
||||
voices_data = await response.json()
|
||||
|
||||
for voice in voices_data:
|
||||
voice_id = voice.get('voice_id', voice.get('id', ''))
|
||||
voice_name = voice.get('name', voice_id)
|
||||
if voice_id:
|
||||
available_voices[voice_id] = voice_name
|
||||
# Mistral returns a paginated response: {"items": [...], "page": ..., "total": ...}
|
||||
voices_list = voices_data.get('items', []) if isinstance(voices_data, dict) else voices_data
|
||||
for voice in voices_list:
|
||||
if isinstance(voice, dict):
|
||||
voice_id = voice.get('voice_id', voice.get('id', ''))
|
||||
voice_name = voice.get('name', voice_id)
|
||||
if voice_id:
|
||||
available_voices[voice_id] = voice_name
|
||||
except Exception as e:
|
||||
log.error(f'Error fetching Mistral voices: {str(e)}')
|
||||
|
||||
|
||||
@@ -172,10 +172,17 @@ async def get_session_user(
|
||||
user=Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
token = None
|
||||
auth_header = request.headers.get('Authorization')
|
||||
auth_token = get_http_authorization_cred(auth_header)
|
||||
token = auth_token.credentials
|
||||
data = decode_token(token)
|
||||
if auth_header:
|
||||
auth_token = get_http_authorization_cred(auth_header)
|
||||
if auth_token is not None:
|
||||
token = auth_token.credentials
|
||||
if token is None:
|
||||
token = request.cookies.get('token')
|
||||
if token is None and getattr(request.state, 'token', None):
|
||||
token = request.state.token.credentials
|
||||
data = decode_token(token) if token else None
|
||||
|
||||
expires_at = None
|
||||
|
||||
@@ -773,8 +780,9 @@ async def signout(request: Request, response: Response, db: AsyncSession = Depen
|
||||
auth_header = request.headers.get('Authorization')
|
||||
if auth_header:
|
||||
auth_cred = get_http_authorization_cred(auth_header)
|
||||
token = auth_cred.credentials
|
||||
else:
|
||||
if auth_cred is not None:
|
||||
token = auth_cred.credentials
|
||||
if token is None:
|
||||
token = request.cookies.get('token')
|
||||
|
||||
if token:
|
||||
@@ -853,6 +861,31 @@ async def signout(request: Request, response: Response, db: AsyncSession = Depen
|
||||
return JSONResponse(status_code=200, content={'status': True}, headers=response.headers)
|
||||
|
||||
|
||||
############################
|
||||
# OAuth Session Management
|
||||
############################
|
||||
|
||||
|
||||
@router.delete('/oauth/sessions/{provider:path}', response_model=bool)
|
||||
async def delete_oauth_session_by_provider(
|
||||
provider: str,
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
"""
|
||||
Disconnect the current user's OAuth session for a specific provider.
|
||||
The provider string matches the 'provider' field in the oauth_session table
|
||||
(e.g. 'mcp:server-id' for MCP connections).
|
||||
"""
|
||||
result = await OAuthSessions.delete_sessions_by_user_id_and_provider(user.id, provider, db=db)
|
||||
if not result:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail='No OAuth session found for this provider',
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
############################
|
||||
# AddUser
|
||||
############################
|
||||
|
||||
@@ -4,6 +4,8 @@ import base64
|
||||
import json
|
||||
import asyncio
|
||||
import logging
|
||||
import posixpath
|
||||
from urllib.parse import unquote
|
||||
|
||||
from open_webui.models.groups import Groups
|
||||
from open_webui.models.models import (
|
||||
@@ -29,12 +31,12 @@ from fastapi import (
|
||||
status,
|
||||
Response,
|
||||
)
|
||||
from fastapi.responses import FileResponse, StreamingResponse
|
||||
from fastapi.responses import RedirectResponse, StreamingResponse
|
||||
|
||||
|
||||
from open_webui.utils.auth import get_admin_user, get_verified_user
|
||||
from open_webui.utils.access_control import has_permission, filter_allowed_access_grants
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL, STATIC_DIR
|
||||
from open_webui.config import BYPASS_ADMIN_ACCESS_CONTROL
|
||||
from open_webui.internal.db import get_async_session
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -43,6 +45,34 @@ log = logging.getLogger(__name__)
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _safe_static_redirect_path(url: str) -> Optional[str]:
|
||||
"""
|
||||
If url is a same-origin static asset path, return a normalized path safe for
|
||||
RedirectResponse Location. Otherwise None (caller should fall back to default).
|
||||
Rejects traversal (..), encoded dots, query/fragment, and non-/static targets.
|
||||
"""
|
||||
if not url or not isinstance(url, str):
|
||||
return None
|
||||
path = url.split('?', 1)[0].split('#', 1)[0].strip()
|
||||
for _ in range(2):
|
||||
decoded = unquote(path)
|
||||
if decoded == path:
|
||||
break
|
||||
path = decoded
|
||||
if '\x00' in path or '\\' in path:
|
||||
return None
|
||||
if not path.startswith('/'):
|
||||
return None
|
||||
normalized = posixpath.normpath(path)
|
||||
if normalized in ('.', '/'):
|
||||
return None
|
||||
if not (normalized == '/static' or normalized.startswith('/static/')):
|
||||
return None
|
||||
if normalized == '/static':
|
||||
return '/static/'
|
||||
return normalized
|
||||
|
||||
|
||||
def is_valid_model_id(model_id: str) -> bool:
|
||||
return model_id and len(model_id) <= 256
|
||||
|
||||
@@ -108,18 +138,25 @@ async def get_models(
|
||||
db=db,
|
||||
)
|
||||
|
||||
return ModelAccessListResponse(
|
||||
items=[
|
||||
# Strip profile_image_url from meta — images are served via /model/profile/image.
|
||||
items = []
|
||||
for model in result.items:
|
||||
data = model.model_dump()
|
||||
if data.get('meta'):
|
||||
data['meta'].pop('profile_image_url', None)
|
||||
items.append(
|
||||
ModelAccessResponse(
|
||||
**model.model_dump(),
|
||||
**data,
|
||||
write_access=(
|
||||
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||
or user.id == model.user_id
|
||||
or model.id in writable_model_ids
|
||||
),
|
||||
)
|
||||
for model in result.items
|
||||
],
|
||||
)
|
||||
|
||||
return ModelAccessListResponse(
|
||||
items=items,
|
||||
total=result.total,
|
||||
)
|
||||
|
||||
@@ -141,25 +178,12 @@ async def get_base_models(user=Depends(get_admin_user), db: AsyncSession = Depen
|
||||
|
||||
@router.get('/tags', response_model=list[str])
|
||||
async def get_model_tags(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
|
||||
models = await Models.get_models(db=db)
|
||||
else:
|
||||
models = await Models.get_models_by_user_id(user.id, db=db)
|
||||
|
||||
tags_set = set()
|
||||
for model in models:
|
||||
if model.meta:
|
||||
meta = model.meta.model_dump()
|
||||
for tag in meta.get('tags', []):
|
||||
try:
|
||||
name = tag.get('name') if isinstance(tag, dict) else str(tag)
|
||||
if name:
|
||||
tags_set.add(name)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
tags = sorted(tags_set)
|
||||
return tags
|
||||
tags = await Models.get_all_tags(
|
||||
user_id=user.id,
|
||||
is_admin=(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL),
|
||||
db=db,
|
||||
)
|
||||
return sorted(tags)
|
||||
|
||||
|
||||
############################
|
||||
@@ -436,39 +460,48 @@ async def get_model_profile_image(
|
||||
user=Depends(get_verified_user),
|
||||
db: AsyncSession = Depends(get_async_session),
|
||||
):
|
||||
model = await Models.get_model_by_id(id, db=db)
|
||||
model_meta = await Models.get_model_meta_by_id(id, db=db)
|
||||
|
||||
if model:
|
||||
etag = f'"{model.updated_at}"' if model.updated_at else None
|
||||
if model_meta:
|
||||
meta, updated_at = model_meta
|
||||
profile_image_url = (meta or {}).get('profile_image_url')
|
||||
|
||||
if model.meta.profile_image_url:
|
||||
if model.meta.profile_image_url.startswith('http'):
|
||||
if profile_image_url:
|
||||
if profile_image_url.startswith('http'):
|
||||
return Response(
|
||||
status_code=status.HTTP_302_FOUND,
|
||||
headers={'Location': model.meta.profile_image_url},
|
||||
headers={'Location': profile_image_url},
|
||||
)
|
||||
elif model.meta.profile_image_url.startswith('data:image'):
|
||||
elif profile_image_url.startswith('data:image'):
|
||||
try:
|
||||
header, base64_data = model.meta.profile_image_url.split(',', 1)
|
||||
header, base64_data = profile_image_url.split(',', 1)
|
||||
image_data = base64.b64decode(base64_data)
|
||||
image_buffer = io.BytesIO(image_data)
|
||||
media_type = header.split(';')[0].lstrip('data:')
|
||||
|
||||
headers = {'Content-Disposition': 'inline'}
|
||||
if etag:
|
||||
headers['ETag'] = etag
|
||||
if updated_at:
|
||||
headers['ETag'] = f'"{updated_at}"'
|
||||
|
||||
return StreamingResponse(
|
||||
image_buffer,
|
||||
media_type=media_type,
|
||||
headers=headers,
|
||||
)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
safe_static = _safe_static_redirect_path(profile_image_url)
|
||||
if safe_static:
|
||||
return RedirectResponse(
|
||||
url=safe_static,
|
||||
status_code=status.HTTP_302_FOUND,
|
||||
)
|
||||
|
||||
return FileResponse(f'{STATIC_DIR}/favicon.png')
|
||||
else:
|
||||
return FileResponse(f'{STATIC_DIR}/favicon.png')
|
||||
return RedirectResponse(
|
||||
url='/static/favicon.png',
|
||||
status_code=status.HTTP_302_FOUND,
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
|
||||
@@ -86,6 +86,17 @@ log = logging.getLogger(__name__)
|
||||
#
|
||||
##########################################
|
||||
|
||||
# Headers that become stale after aiohttp auto-decompresses the upstream
|
||||
# response body. Forwarding them verbatim causes desktop / programmatic
|
||||
# clients to attempt decompression of an already-decoded payload, resulting
|
||||
# in ZlibError. See https://github.com/aio-libs/aiohttp/issues/4462.
|
||||
_STRIP_PROXY_HEADERS = frozenset({'Content-Encoding', 'Content-Length', 'Transfer-Encoding'})
|
||||
|
||||
|
||||
def _clean_proxy_headers(raw_headers) -> dict:
|
||||
"""Return a copy of *raw_headers* with stale encoding headers removed."""
|
||||
return {k: v for k, v in raw_headers.items() if k not in _STRIP_PROXY_HEADERS}
|
||||
|
||||
|
||||
async def send_get_request(url, key=None, user: UserModel = None):
|
||||
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
|
||||
@@ -163,7 +174,7 @@ async def send_request(
|
||||
r.raise_for_status()
|
||||
|
||||
if stream:
|
||||
response_headers = dict(r.headers)
|
||||
response_headers = _clean_proxy_headers(r.headers)
|
||||
if content_type:
|
||||
response_headers['Content-Type'] = content_type
|
||||
|
||||
|
||||
@@ -76,6 +76,17 @@ log = logging.getLogger(__name__)
|
||||
#
|
||||
##########################################
|
||||
|
||||
# Headers that become stale after aiohttp auto-decompresses the upstream
|
||||
# response body. Forwarding them verbatim causes desktop / programmatic
|
||||
# clients to attempt decompression of an already-decoded payload, resulting
|
||||
# in ZlibError. See https://github.com/aio-libs/aiohttp/issues/4462.
|
||||
_STRIP_PROXY_HEADERS = frozenset({'Content-Encoding', 'Content-Length', 'Transfer-Encoding'})
|
||||
|
||||
|
||||
def _clean_proxy_headers(raw_headers) -> dict:
|
||||
"""Return a copy of *raw_headers* with stale encoding headers removed."""
|
||||
return {k: v for k, v in raw_headers.items() if k not in _STRIP_PROXY_HEADERS}
|
||||
|
||||
|
||||
async def send_get_request(
|
||||
request: Request = None,
|
||||
@@ -1219,7 +1230,7 @@ async def generate_chat_completion(
|
||||
return StreamingResponse(
|
||||
stream_wrapper(r, content_handler=stream_chunks_handler),
|
||||
status_code=r.status,
|
||||
headers=dict(r.headers),
|
||||
headers=_clean_proxy_headers(r.headers),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@@ -1304,7 +1315,7 @@ async def embeddings(request: Request, form_data: dict, user):
|
||||
return StreamingResponse(
|
||||
stream_wrapper(r),
|
||||
status_code=r.status,
|
||||
headers=dict(r.headers),
|
||||
headers=_clean_proxy_headers(r.headers),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@@ -1425,7 +1436,7 @@ async def responses(
|
||||
return StreamingResponse(
|
||||
stream_wrapper(r),
|
||||
status_code=r.status,
|
||||
headers=dict(r.headers),
|
||||
headers=_clean_proxy_headers(r.headers),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@@ -1542,7 +1553,7 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||
return StreamingResponse(
|
||||
stream_wrapper(r),
|
||||
status_code=r.status,
|
||||
headers=dict(r.headers),
|
||||
headers=_clean_proxy_headers(r.headers),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
|
||||
@@ -480,6 +480,8 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
||||
'DOCUMENT_INTELLIGENCE_MODEL': request.app.state.config.DOCUMENT_INTELLIGENCE_MODEL,
|
||||
'MISTRAL_OCR_API_BASE_URL': request.app.state.config.MISTRAL_OCR_API_BASE_URL,
|
||||
'MISTRAL_OCR_API_KEY': request.app.state.config.MISTRAL_OCR_API_KEY,
|
||||
'PADDLEOCR_VL_BASE_URL': request.app.state.config.PADDLEOCR_VL_BASE_URL,
|
||||
'PADDLEOCR_VL_TOKEN': request.app.state.config.PADDLEOCR_VL_TOKEN,
|
||||
# MinerU settings
|
||||
'MINERU_API_MODE': request.app.state.config.MINERU_API_MODE,
|
||||
'MINERU_API_URL': request.app.state.config.MINERU_API_URL,
|
||||
@@ -686,6 +688,8 @@ class ConfigForm(BaseModel):
|
||||
DOCUMENT_INTELLIGENCE_MODEL: Optional[str] = None
|
||||
MISTRAL_OCR_API_BASE_URL: Optional[str] = None
|
||||
MISTRAL_OCR_API_KEY: Optional[str] = None
|
||||
PADDLEOCR_VL_BASE_URL: Optional[str] = None
|
||||
PADDLEOCR_VL_TOKEN: Optional[str] = None
|
||||
|
||||
# MinerU settings
|
||||
MINERU_API_MODE: Optional[str] = None
|
||||
@@ -887,6 +891,16 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
|
||||
if form_data.MISTRAL_OCR_API_KEY is not None
|
||||
else request.app.state.config.MISTRAL_OCR_API_KEY
|
||||
)
|
||||
request.app.state.config.PADDLEOCR_VL_BASE_URL = (
|
||||
form_data.PADDLEOCR_VL_BASE_URL
|
||||
if form_data.PADDLEOCR_VL_BASE_URL is not None
|
||||
else request.app.state.config.PADDLEOCR_VL_BASE_URL
|
||||
)
|
||||
request.app.state.config.PADDLEOCR_VL_TOKEN = (
|
||||
form_data.PADDLEOCR_VL_TOKEN
|
||||
if form_data.PADDLEOCR_VL_TOKEN is not None
|
||||
else request.app.state.config.PADDLEOCR_VL_TOKEN
|
||||
)
|
||||
|
||||
# MinerU settings
|
||||
request.app.state.config.MINERU_API_MODE = (
|
||||
@@ -1152,6 +1166,8 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
|
||||
'DOCUMENT_INTELLIGENCE_MODEL': request.app.state.config.DOCUMENT_INTELLIGENCE_MODEL,
|
||||
'MISTRAL_OCR_API_BASE_URL': request.app.state.config.MISTRAL_OCR_API_BASE_URL,
|
||||
'MISTRAL_OCR_API_KEY': request.app.state.config.MISTRAL_OCR_API_KEY,
|
||||
'PADDLEOCR_VL_BASE_URL': request.app.state.config.PADDLEOCR_VL_BASE_URL,
|
||||
'PADDLEOCR_VL_TOKEN': request.app.state.config.PADDLEOCR_VL_TOKEN,
|
||||
# MinerU settings
|
||||
'MINERU_API_MODE': request.app.state.config.MINERU_API_MODE,
|
||||
'MINERU_API_URL': request.app.state.config.MINERU_API_URL,
|
||||
|
||||
@@ -550,6 +550,8 @@ async def update_user_by_id(
|
||||
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f'Error checking primary admin status: {e}')
|
||||
raise HTTPException(
|
||||
@@ -631,6 +633,8 @@ async def delete_user_by_id(user_id: str, user=Depends(get_admin_user), db: Asyn
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.error(f'Error checking primary admin status: {e}')
|
||||
raise HTTPException(
|
||||
|
||||
@@ -238,9 +238,13 @@ async def fetch_url(
|
||||
content, _ = await asyncio.to_thread(get_content_from_url, __request__, url)
|
||||
|
||||
# Truncate if configured (WEB_FETCH_MAX_CONTENT_LENGTH)
|
||||
max_length = getattr(__request__.app.state.config, 'WEB_FETCH_MAX_CONTENT_LENGTH', None)
|
||||
if max_length and max_length > 0 and len(content) > max_length:
|
||||
content = content[:max_length] + '\n\n[Content truncated...]'
|
||||
# Guard: content may be None if the web loader silently failed
|
||||
if content is not None:
|
||||
max_length = getattr(__request__.app.state.config, 'WEB_FETCH_MAX_CONTENT_LENGTH', None)
|
||||
if max_length and max_length > 0 and len(content) > max_length:
|
||||
content = content[:max_length] + '\n\n[Content truncated...]'
|
||||
else:
|
||||
content = ''
|
||||
|
||||
return content
|
||||
except Exception as e:
|
||||
@@ -2568,8 +2572,11 @@ async def create_automation(
|
||||
if not user:
|
||||
return json.dumps({'error': 'User not found'})
|
||||
|
||||
# Always use the calling model for the automation
|
||||
model_id = (__metadata__ or {}).get('model_id')
|
||||
# Fall back to model dict ID since __metadata__ may predate model_id assignment
|
||||
metadata = __metadata__ or {}
|
||||
model_id = metadata.get('model_id') or (
|
||||
metadata.get('model', {}).get('id') if isinstance(metadata.get('model'), dict) else None
|
||||
)
|
||||
if not model_id:
|
||||
return json.dumps({'error': 'Could not detect current model'})
|
||||
|
||||
@@ -2888,6 +2895,9 @@ def _ns_to_dt(ns: int, tz) -> str:
|
||||
|
||||
def _event_to_dict(event, tz) -> dict:
|
||||
"""Convert a calendar event model to a human-friendly dict with local timestamps."""
|
||||
alert_minutes = None
|
||||
if event.meta and 'alert_minutes' in event.meta:
|
||||
alert_minutes = event.meta['alert_minutes']
|
||||
return {
|
||||
'id': event.id,
|
||||
'calendar_id': event.calendar_id,
|
||||
@@ -2897,6 +2907,7 @@ def _event_to_dict(event, tz) -> dict:
|
||||
'end': _ns_to_dt(event.end_at, tz) if event.end_at else None,
|
||||
'all_day': event.all_day,
|
||||
'location': event.location or '',
|
||||
'reminder_minutes': alert_minutes if alert_minutes is not None else 10,
|
||||
'color': event.color,
|
||||
'is_cancelled': event.is_cancelled,
|
||||
}
|
||||
@@ -3003,6 +3014,7 @@ async def create_calendar_event(
|
||||
calendar_id: Optional[str] = None,
|
||||
all_day: bool = False,
|
||||
location: Optional[str] = None,
|
||||
reminder_minutes: Optional[int] = None,
|
||||
__request__: Request = None,
|
||||
__user__: dict = None,
|
||||
) -> str:
|
||||
@@ -3017,6 +3029,7 @@ async def create_calendar_event(
|
||||
:param calendar_id: Target calendar ID (optional, uses default calendar if omitted)
|
||||
:param all_day: Whether this is an all-day event (default: false)
|
||||
:param location: Event location (optional)
|
||||
:param reminder_minutes: Minutes before the event to send a reminder notification (optional, default: 10). Use 0 for "at time of event", -1 for no reminder. Accepts any positive integer for custom timing (e.g. 120 for 2 hours before).
|
||||
:return: JSON with the created event details including id
|
||||
"""
|
||||
if __request__ is None:
|
||||
@@ -3079,6 +3092,18 @@ async def create_calendar_event(
|
||||
# Default to 1 hour duration
|
||||
end_ns = start_ns + 3_600_000_000_000
|
||||
|
||||
# Build meta with reminder setting
|
||||
meta = {}
|
||||
if reminder_minutes is not None:
|
||||
if isinstance(reminder_minutes, str):
|
||||
try:
|
||||
reminder_minutes = int(reminder_minutes)
|
||||
except ValueError:
|
||||
reminder_minutes = 10
|
||||
meta['alert_minutes'] = reminder_minutes
|
||||
else:
|
||||
meta['alert_minutes'] = 10
|
||||
|
||||
form = CalendarEventForm(
|
||||
calendar_id=calendar_id,
|
||||
title=title,
|
||||
@@ -3087,6 +3112,7 @@ async def create_calendar_event(
|
||||
end_at=end_ns,
|
||||
all_day=all_day,
|
||||
location=location,
|
||||
meta=meta,
|
||||
)
|
||||
|
||||
event = await CalendarEvents.insert_new_event(user_id, form)
|
||||
@@ -3114,6 +3140,7 @@ async def update_calendar_event(
|
||||
all_day: Optional[bool] = None,
|
||||
location: Optional[str] = None,
|
||||
is_cancelled: Optional[bool] = None,
|
||||
reminder_minutes: Optional[int] = None,
|
||||
__request__: Request = None,
|
||||
__user__: dict = None,
|
||||
) -> str:
|
||||
@@ -3129,6 +3156,7 @@ async def update_calendar_event(
|
||||
:param all_day: Whether this is an all-day event (optional)
|
||||
:param location: New event location (optional)
|
||||
:param is_cancelled: Set to true to cancel the event (optional)
|
||||
:param reminder_minutes: Minutes before the event to send a reminder notification (optional). Use 0 for "at time of event", -1 for no reminder. Accepts any positive integer for custom timing (e.g. 120 for 2 hours before).
|
||||
:return: JSON with the updated event details
|
||||
"""
|
||||
if __request__ is None:
|
||||
@@ -3183,6 +3211,17 @@ async def update_calendar_event(
|
||||
except (ValueError, TypeError) as e:
|
||||
return json.dumps({'error': f'Invalid end datetime: {e}'})
|
||||
|
||||
# Build meta update with reminder setting if provided
|
||||
meta = None
|
||||
if reminder_minutes is not None:
|
||||
if isinstance(reminder_minutes, str):
|
||||
try:
|
||||
reminder_minutes = int(reminder_minutes)
|
||||
except ValueError:
|
||||
reminder_minutes = None
|
||||
if reminder_minutes is not None:
|
||||
meta = {'alert_minutes': reminder_minutes}
|
||||
|
||||
form = CalendarEventUpdateForm(
|
||||
title=title,
|
||||
description=description,
|
||||
@@ -3191,6 +3230,7 @@ async def update_calendar_event(
|
||||
all_day=all_day,
|
||||
location=location,
|
||||
is_cancelled=is_cancelled,
|
||||
meta=meta,
|
||||
)
|
||||
|
||||
updated = await CalendarEvents.update_event_by_id(event_id, form)
|
||||
|
||||
@@ -41,6 +41,7 @@ from starlette.datastructures import MutableHeaders
|
||||
from starlette.requests import Request
|
||||
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
||||
|
||||
from open_webui.env import CUSTOM_API_KEY_HEADER
|
||||
from open_webui.internal.db import ScopedSession
|
||||
from open_webui.utils.auth import get_http_authorization_cred
|
||||
|
||||
@@ -119,9 +120,16 @@ class CommitSessionMiddleware:
|
||||
|
||||
|
||||
class AuthTokenMiddleware:
|
||||
"""Extract the bearer/cookie/x-api-key credential and stash it on
|
||||
"""Extract the bearer/cookie/API-key credential and stash it on
|
||||
`request.state.token`.
|
||||
|
||||
The header used for API-key transport is controlled by the
|
||||
``CUSTOM_API_KEY_HEADER`` environment variable (default ``x-api-key``).
|
||||
This is useful when Open WebUI sits behind a reverse proxy that
|
||||
consumes the ``Authorization`` header for its own authentication —
|
||||
set the env var to a unique header (e.g. ``X-OpenWebUI-Key``) so
|
||||
the middleware checks that instead and avoids the 401 short-circuit.
|
||||
|
||||
Routes that depend on `get_verified_user` etc. read this state.
|
||||
Also exposes `request.state.enable_api_keys` (snapshotted at request
|
||||
entry from runtime config) and stamps an `X-Process-Time` response
|
||||
@@ -146,7 +154,7 @@ class AuthTokenMiddleware:
|
||||
if cookie_token:
|
||||
token = HTTPAuthorizationCredentials(scheme='Bearer', credentials=cookie_token)
|
||||
if token is None:
|
||||
api_key = request.headers.get('x-api-key')
|
||||
api_key = request.headers.get(CUSTOM_API_KEY_HEADER)
|
||||
if api_key:
|
||||
token = HTTPAuthorizationCredentials(scheme='Bearer', credentials=api_key)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ async def get_function_module(request, function_id, load_from_db=True):
|
||||
"""
|
||||
Get the function module by its ID.
|
||||
"""
|
||||
function_module, _, _ = await get_function_module_from_cache(request, function_id, load_from_db)
|
||||
function_module, _, _ = await get_function_module_from_cache(request, function_id, load_from_db=load_from_db)
|
||||
return function_module
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ def create_httpx_client(headers=None, timeout=None, auth=None):
|
||||
return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=True)
|
||||
|
||||
|
||||
async def create_insecure_httpx_client(headers=None, timeout=None, auth=None):
|
||||
def create_insecure_httpx_client(headers=None, timeout=None, auth=None):
|
||||
return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=False)
|
||||
|
||||
|
||||
@@ -155,20 +155,18 @@ class MCPClient:
|
||||
self.session = None
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(
|
||||
asyncio.shield(exit_stack.aclose()),
|
||||
timeout=5.0,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
# IMPORTANT: Do NOT use asyncio.shield() or asyncio.wait_for()
|
||||
# because they create a new asyncio task, which violates the MCP SDK's
|
||||
# requirement that its TaskGroup be exited in the exact same task.
|
||||
# ALSO do NOT use anyio.CancelScope(shield=True) or anyio.fail_after(),
|
||||
# because they push a new cancel scope onto the task, violating LIFO
|
||||
# order when aclose() attempts to exit the inner TaskGroup.
|
||||
# We simply call aclose() directly. If the task is cancelled, the
|
||||
# sockets will eventually be cleaned up by garbage collection.
|
||||
await exit_stack.aclose()
|
||||
except TimeoutError:
|
||||
log.warning('MCPClient.disconnect() timed out after 5 s')
|
||||
except RuntimeError as exc:
|
||||
# The MCP SDK's streamable_http transport uses anyio task
|
||||
# groups and async generators internally. When we close
|
||||
# a session that was interrupted mid-flight these can
|
||||
# raise RuntimeError ("aclose(): asynchronous generator is
|
||||
# already running" or "Attempted to exit cancel scope in a
|
||||
# different task"). Swallowing the error here prevents the
|
||||
# orphaned coroutines from spinning at 100 % CPU.
|
||||
log.debug('MCPClient.disconnect() suppressed RuntimeError: %s', exc)
|
||||
except Exception as exc:
|
||||
log.debug('MCPClient.disconnect() error: %s', exc)
|
||||
|
||||
@@ -1164,6 +1164,15 @@ async def process_tool_result(
|
||||
'url': file_url,
|
||||
}
|
||||
)
|
||||
elif item.get('type') == 'resource':
|
||||
resource = item.get('resource', {})
|
||||
text = resource.get('text', '')
|
||||
if isinstance(text, str) and text:
|
||||
try:
|
||||
text = json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
tool_response.append(text)
|
||||
tool_result = tool_response[0] if len(tool_response) == 1 else tool_response
|
||||
else: # OpenAPI
|
||||
for item in tool_result:
|
||||
@@ -1546,11 +1555,11 @@ async def chat_web_search_handler(request: Request, form_data: dict, extra_param
|
||||
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
queries = [user_message]
|
||||
queries = [user_message or '']
|
||||
|
||||
# Check if generated queries are empty
|
||||
if len(queries) == 1 and queries[0].strip() == '':
|
||||
queries = [user_message]
|
||||
queries = [user_message or '']
|
||||
|
||||
# Check if queries are not found
|
||||
if len(queries) == 0:
|
||||
@@ -1991,7 +2000,7 @@ async def chat_completion_files_handler(
|
||||
)
|
||||
|
||||
if len(queries) == 0:
|
||||
queries = [get_last_user_message(body['messages'])]
|
||||
queries = [get_last_user_message(body['messages']) or '']
|
||||
|
||||
try:
|
||||
# Directly await async get_sources_from_items (no thread needed - fully async now)
|
||||
@@ -2180,6 +2189,43 @@ def process_messages_with_output(messages: list[dict]) -> list[dict]:
|
||||
return processed
|
||||
|
||||
|
||||
SKILL_MENTION_RE = re.compile(r'<\$([^|>]+)\|?[^>]*>')
|
||||
|
||||
|
||||
def _get_text_parts(message: dict) -> list[str]:
|
||||
"""Return all text segments from a message's content."""
|
||||
content = message.get('content')
|
||||
if isinstance(content, str):
|
||||
return [content]
|
||||
if isinstance(content, list):
|
||||
return [p.get('text', '') for p in content if isinstance(p, dict) and p.get('type') == 'text']
|
||||
return []
|
||||
|
||||
|
||||
def extract_skill_ids_from_messages(messages: list[dict]) -> set[str]:
|
||||
"""Extract skill IDs from <$skillId|label> mention tags in messages."""
|
||||
ids: set[str] = set()
|
||||
for message in messages:
|
||||
for text in _get_text_parts(message):
|
||||
ids.update(m.group(1) for m in SKILL_MENTION_RE.finditer(text))
|
||||
return ids
|
||||
|
||||
|
||||
def strip_skill_mentions(messages: list[dict]) -> None:
|
||||
"""Strip <$skillId|label> mention tags from message content in-place."""
|
||||
strip_re = re.compile(r'<\$[^>]+>')
|
||||
for message in messages:
|
||||
content = message.get('content')
|
||||
if isinstance(content, str) and strip_re.search(content):
|
||||
message['content'] = strip_re.sub('', content).strip()
|
||||
elif isinstance(content, list):
|
||||
for part in content:
|
||||
if isinstance(part, dict) and part.get('type') == 'text':
|
||||
text = part.get('text', '')
|
||||
if strip_re.search(text):
|
||||
part['text'] = strip_re.sub('', text).strip()
|
||||
|
||||
|
||||
async def process_chat_payload(request, form_data, user, metadata, model):
|
||||
# Pipeline Inlet -> Filter Inlet -> Chat Memory -> Chat Web Search -> Chat Image Generation
|
||||
# -> Chat Code Interpreter (Form Data Update) -> (Default) Chat Tools Function Calling
|
||||
@@ -2465,8 +2511,10 @@ async def process_chat_payload(request, form_data, user, metadata, model):
|
||||
# tool resolution (tool_ids, MCP servers, builtin tools).
|
||||
payload_tools = form_data.get('tools', None)
|
||||
|
||||
# Skills
|
||||
# Skills — extract IDs from message content (<$skillId|label> tags) so
|
||||
# persisted chats work without relying on the frontend to send skill_ids.
|
||||
user_skill_ids = set(form_data.pop('skill_ids', None) or [])
|
||||
user_skill_ids |= extract_skill_ids_from_messages(form_data.get('messages', []))
|
||||
model_skill_ids = set(model.get('info', {}).get('meta', {}).get('skillIds', []))
|
||||
|
||||
all_skill_ids = user_skill_ids | model_skill_ids
|
||||
@@ -2502,6 +2550,9 @@ async def process_chat_payload(request, form_data, user, metadata, model):
|
||||
append=True,
|
||||
)
|
||||
|
||||
# Strip <$skillId|label> mention tags so the model doesn't see raw markup.
|
||||
strip_skill_mentions(form_data.get('messages', []))
|
||||
|
||||
prompt = get_last_user_message(form_data['messages'])
|
||||
# TODO: re-enable URL extraction from prompt
|
||||
# urls = []
|
||||
@@ -4269,6 +4320,8 @@ async def streaming_chat_response_handler(response, ctx):
|
||||
'data': data,
|
||||
}
|
||||
)
|
||||
except (asyncio.CancelledError, KeyboardInterrupt):
|
||||
raise
|
||||
except Exception as e:
|
||||
done = 'data: [DONE]' in line
|
||||
if done:
|
||||
@@ -4656,6 +4709,7 @@ async def streaming_chat_response_handler(response, ctx):
|
||||
**form_data,
|
||||
'model': model_id,
|
||||
'stream': True,
|
||||
'metadata': metadata,
|
||||
}
|
||||
|
||||
if ENABLE_RESPONSES_API_STATEFUL and last_response_id:
|
||||
@@ -4879,6 +4933,7 @@ async def streaming_chat_response_handler(response, ctx):
|
||||
**form_data,
|
||||
'model': model_id,
|
||||
'stream': True,
|
||||
'metadata': metadata,
|
||||
'messages': [
|
||||
*form_data['messages'],
|
||||
*convert_output_to_messages(output, raw=True),
|
||||
@@ -4971,31 +5026,39 @@ async def streaming_chat_response_handler(response, ctx):
|
||||
await outlet_filter_handler(ctx)
|
||||
except asyncio.CancelledError:
|
||||
log.warning('Task was cancelled!')
|
||||
try:
|
||||
await asyncio.shield(event_emitter({'type': 'chat:tasks:cancel'}))
|
||||
|
||||
# Close the response body iterator to trigger cleanup
|
||||
# in stream_wrapper's finally block and release the
|
||||
# upstream connection. Without this, the async
|
||||
# generator is orphaned and may spin in anyio internals.
|
||||
if hasattr(response, 'body_iterator') and hasattr(response.body_iterator, 'aclose'):
|
||||
try:
|
||||
await asyncio.shield(response.body_iterator.aclose())
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
|
||||
async def save_cancelled_state():
|
||||
await event_emitter({'type': 'chat:tasks:cancel'})
|
||||
if not ENABLE_REALTIME_CHAT_SAVE:
|
||||
# Save message in the database
|
||||
await asyncio.shield(
|
||||
Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||
metadata['chat_id'],
|
||||
metadata['message_id'],
|
||||
{
|
||||
'done': True,
|
||||
'content': serialize_output(output),
|
||||
'output': output,
|
||||
},
|
||||
)
|
||||
await Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||
metadata['chat_id'],
|
||||
metadata['message_id'],
|
||||
{
|
||||
'done': True,
|
||||
'content': serialize_output(output),
|
||||
'output': output,
|
||||
},
|
||||
)
|
||||
else:
|
||||
await asyncio.shield(
|
||||
Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||
metadata['chat_id'],
|
||||
metadata['message_id'],
|
||||
{'done': True},
|
||||
)
|
||||
await Chats.upsert_message_to_chat_by_id_and_message_id(
|
||||
metadata['chat_id'],
|
||||
metadata['message_id'],
|
||||
{'done': True},
|
||||
)
|
||||
except Exception:
|
||||
|
||||
try:
|
||||
await asyncio.shield(save_cancelled_state())
|
||||
except (asyncio.CancelledError, Exception):
|
||||
pass
|
||||
raise # re-raise CancelledError for proper propagation
|
||||
|
||||
|
||||
@@ -597,6 +597,9 @@ def sanitize_text_for_db(text: str) -> str:
|
||||
"""Remove null bytes and invalid UTF-8 surrogates from text for PostgreSQL storage."""
|
||||
if not isinstance(text, str):
|
||||
return text
|
||||
# Fast path: skip work when there are no null bytes (the common case)
|
||||
if '\x00' not in text:
|
||||
return text
|
||||
# Remove null bytes
|
||||
text = text.replace('\x00', '').replace('\u0000', '')
|
||||
# Remove invalid UTF-8 surrogate characters that can cause encoding errors
|
||||
@@ -608,17 +611,38 @@ def sanitize_text_for_db(text: str) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def sanitize_data_for_db(obj):
|
||||
"""Recursively sanitize all strings in a data structure for database storage."""
|
||||
def _strip_null_bytes_deep(obj):
|
||||
"""Inner recursive walk — only called when null bytes are known to be present."""
|
||||
if isinstance(obj, str):
|
||||
return sanitize_text_for_db(obj)
|
||||
elif isinstance(obj, dict):
|
||||
return {k: sanitize_data_for_db(v) for k, v in obj.items()}
|
||||
return {k: _strip_null_bytes_deep(v) for k, v in obj.items()}
|
||||
elif isinstance(obj, list):
|
||||
return [sanitize_data_for_db(v) for v in obj]
|
||||
return [_strip_null_bytes_deep(v) for v in obj]
|
||||
return obj
|
||||
|
||||
|
||||
def sanitize_data_for_db(obj):
|
||||
"""Recursively sanitize all strings in a data structure for database storage.
|
||||
|
||||
Performs a fast pre-check: serializes the structure once and scans for
|
||||
null bytes. If none are found (the overwhelmingly common case), the
|
||||
original object is returned immediately, skipping the expensive
|
||||
recursive walk.
|
||||
"""
|
||||
if isinstance(obj, str):
|
||||
return sanitize_text_for_db(obj)
|
||||
# Fast path: check for null bytes in the serialized form.
|
||||
# json.dumps is implemented in C and much faster than a Python-level
|
||||
# recursive walk over every leaf string.
|
||||
try:
|
||||
if '\x00' not in json.dumps(obj, ensure_ascii=False):
|
||||
return obj
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return _strip_null_bytes_deep(obj)
|
||||
|
||||
|
||||
def sanitize_metadata(metadata: dict) -> dict:
|
||||
"""
|
||||
Return a JSON-safe copy of a metadata dict for database storage.
|
||||
@@ -843,9 +867,9 @@ def throttle(interval: float = 10.0):
|
||||
last_calls = {}
|
||||
lock = threading.Lock()
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
async def wrapper(*args, **kwargs):
|
||||
if interval is None:
|
||||
return func(*args, **kwargs)
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
key = (args, freeze(kwargs))
|
||||
now = time.time()
|
||||
@@ -855,7 +879,7 @@ def throttle(interval: float = 10.0):
|
||||
if now - last_calls.get(key, 0) < interval:
|
||||
return None
|
||||
last_calls[key] = now
|
||||
return func(*args, **kwargs)
|
||||
return await func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@@ -287,9 +287,9 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
|
||||
# imported/custom model configs may reference tools or filters the user
|
||||
# hasn't installed, and trying to load those would cause persistent
|
||||
# "Failed to load function module" log spam on every model refresh.
|
||||
for function_id in functions_by_id:
|
||||
for function_id, function in functions_by_id.items():
|
||||
try:
|
||||
await get_function_module_from_cache(request, function_id)
|
||||
await get_function_module_from_cache(request, function_id, function=function)
|
||||
except Exception as e:
|
||||
log.debug(f'Failed to load function module for {function_id}: {e}')
|
||||
|
||||
|
||||
@@ -304,57 +304,67 @@ async def get_authorization_server_discovery_urls(server_url: str) -> list[str]:
|
||||
ssl=AIOHTTP_CLIENT_SESSION_SSL,
|
||||
) as response:
|
||||
if response.status == 401:
|
||||
resource_metadata_urls = []
|
||||
match = re.search(
|
||||
r'resource_metadata=(?:"([^"]+)"|([^\s,]+))',
|
||||
response.headers.get('WWW-Authenticate', ''),
|
||||
)
|
||||
if match:
|
||||
resource_metadata_url = match.group(1) or match.group(2)
|
||||
log.debug(f'Found resource_metadata URL: {resource_metadata_url}')
|
||||
resource_metadata_urls = [match.group(1) or match.group(2)]
|
||||
log.debug(f'Found resource_metadata URL: {resource_metadata_urls[0]}')
|
||||
else:
|
||||
# Fall back to well-known resource metadata URIs (RFC 9728 §4.2)
|
||||
parsed, base_url = get_parsed_and_base_url(server_url)
|
||||
if parsed.path and parsed.path != '/':
|
||||
path = parsed.path.rstrip('/')
|
||||
resource_metadata_urls.append(
|
||||
urllib.parse.urljoin(base_url, f'/.well-known/oauth-protected-resource{path}')
|
||||
)
|
||||
resource_metadata_urls.append(
|
||||
urllib.parse.urljoin(base_url, '/.well-known/oauth-protected-resource')
|
||||
)
|
||||
log.debug(f'No resource_metadata in header, trying well-known URIs: {resource_metadata_urls}')
|
||||
|
||||
# Step 2: Fetch Protected Resource metadata
|
||||
async with session.get(
|
||||
resource_metadata_url, ssl=AIOHTTP_CLIENT_SESSION_SSL
|
||||
) as resource_response:
|
||||
if resource_response.status == 200:
|
||||
resource_metadata = await resource_response.json()
|
||||
# Fetch Protected Resource metadata from candidate URLs
|
||||
for resource_metadata_url in resource_metadata_urls:
|
||||
try:
|
||||
async with session.get(
|
||||
resource_metadata_url, ssl=AIOHTTP_CLIENT_SESSION_SSL
|
||||
) as resource_response:
|
||||
if resource_response.status == 200:
|
||||
resource_metadata = await resource_response.json()
|
||||
|
||||
# Step 3: Extract authorization_servers
|
||||
servers = resource_metadata.get('authorization_servers', [])
|
||||
if servers:
|
||||
authorization_servers = servers
|
||||
log.debug(f'Discovered authorization servers: {servers}')
|
||||
servers = resource_metadata.get('authorization_servers', [])
|
||||
if servers:
|
||||
authorization_servers = servers
|
||||
log.debug(f'Discovered authorization servers: {servers}')
|
||||
break
|
||||
except Exception as e:
|
||||
log.debug(f'Failed to fetch resource metadata from {resource_metadata_url}: {e}')
|
||||
continue
|
||||
except Exception as e:
|
||||
log.debug(f'MCP Protected Resource discovery failed: {e}')
|
||||
|
||||
discovery_urls = []
|
||||
for auth_server in authorization_servers:
|
||||
auth_server = auth_server.rstrip('/')
|
||||
discovery_urls.extend(
|
||||
[
|
||||
f'{auth_server}/.well-known/oauth-authorization-server',
|
||||
f'{auth_server}/.well-known/openid-configuration',
|
||||
]
|
||||
)
|
||||
discovery_urls.extend(_build_well_known_urls(auth_server))
|
||||
|
||||
return discovery_urls
|
||||
|
||||
|
||||
async def get_discovery_urls(server_url) -> list[str]:
|
||||
urls = await get_authorization_server_discovery_urls(server_url)
|
||||
def _build_well_known_urls(server_url: str) -> list[str]:
|
||||
"""Build RFC 8414 / OIDC Discovery well-known URLs for a server URL."""
|
||||
parsed, base_url = get_parsed_and_base_url(server_url)
|
||||
urls = []
|
||||
|
||||
if parsed.path and parsed.path != '/':
|
||||
# Generate discovery URLs based on https://modelcontextprotocol.io/specification/draft/basic/authorization#authorization-server-metadata-discovery
|
||||
tenant = parsed.path.rstrip('/')
|
||||
path = parsed.path.rstrip('/')
|
||||
urls.extend(
|
||||
[
|
||||
urllib.parse.urljoin(
|
||||
base_url,
|
||||
f'/.well-known/oauth-authorization-server{tenant}',
|
||||
),
|
||||
urllib.parse.urljoin(base_url, f'/.well-known/openid-configuration{tenant}'),
|
||||
urllib.parse.urljoin(base_url, f'{tenant}/.well-known/openid-configuration'),
|
||||
urllib.parse.urljoin(base_url, f'/.well-known/oauth-authorization-server{path}'),
|
||||
urllib.parse.urljoin(base_url, f'/.well-known/openid-configuration{path}'),
|
||||
urllib.parse.urljoin(base_url, f'{path}/.well-known/openid-configuration'),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -368,6 +378,12 @@ async def get_discovery_urls(server_url) -> list[str]:
|
||||
return urls
|
||||
|
||||
|
||||
async def get_discovery_urls(server_url) -> list[str]:
|
||||
urls = await get_authorization_server_discovery_urls(server_url)
|
||||
urls.extend(_build_well_known_urls(server_url))
|
||||
return urls
|
||||
|
||||
|
||||
# TODO: Some OAuth providers require Initial Access Tokens (IATs) for dynamic client registration.
|
||||
# This is not currently supported.
|
||||
async def get_oauth_client_info_with_dynamic_client_registration(
|
||||
|
||||
@@ -14,7 +14,7 @@ from open_webui.env import (
|
||||
OFFLINE_MODE,
|
||||
ENABLE_PIP_INSTALL_FRONTMATTER_REQUIREMENTS,
|
||||
)
|
||||
from open_webui.models.functions import Functions
|
||||
from open_webui.models.functions import FunctionModel, Functions
|
||||
from open_webui.models.tools import Tools
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -335,13 +335,16 @@ async def get_tool_module_from_cache(request, tool_id, load_from_db=True):
|
||||
return tool_module, frontmatter
|
||||
|
||||
|
||||
async def get_function_module_from_cache(request, function_id, load_from_db=True):
|
||||
async def get_function_module_from_cache(
|
||||
request, function_id, function: FunctionModel | None = None, load_from_db=True
|
||||
):
|
||||
if load_from_db:
|
||||
# Always load from the database by default
|
||||
# This is useful for hooks like "inlet" or "outlet" where the content might change
|
||||
# and we want to ensure the latest content is used.
|
||||
|
||||
function = await Functions.get_function_by_id(function_id)
|
||||
if function is None:
|
||||
function = await Functions.get_function_by_id(function_id)
|
||||
if not function:
|
||||
raise Exception(f'Function not found: {function_id}')
|
||||
content = function.content
|
||||
|
||||
@@ -21,6 +21,8 @@ from open_webui.env import (
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MAX_RETRY_COUNT = REDIS_SENTINEL_MAX_RETRY_COUNT
|
||||
|
||||
|
||||
# Let not our connections be timed out but deliver them from
|
||||
# partition. For the cache and the socket and the uptime
|
||||
@@ -38,7 +40,7 @@ class SentinelRedisProxy:
|
||||
def _master(self):
|
||||
return self._sentinel.master_for(self._service, **self._kw)
|
||||
|
||||
async def __getattr__(self, item):
|
||||
def __getattr__(self, item):
|
||||
master = self._master()
|
||||
orig_attr = getattr(master, item)
|
||||
|
||||
|
||||
@@ -135,6 +135,9 @@ def convert_response_ollama_to_openai(ollama_response: dict) -> dict:
|
||||
|
||||
async def convert_streaming_response_ollama_to_openai(ollama_streaming_response):
|
||||
has_tool_calls = False
|
||||
# All chunks in a single completion must share the same id (OpenAI spec).
|
||||
completion_id = f'chatcmpl-{str(uuid4())}'
|
||||
first = True
|
||||
async for data in ollama_streaming_response.body_iterator:
|
||||
data = json.loads(data)
|
||||
|
||||
@@ -155,6 +158,12 @@ async def convert_streaming_response_ollama_to_openai(ollama_streaming_response)
|
||||
usage = convert_ollama_usage_to_openai(data)
|
||||
|
||||
data = openai_chat_chunk_message_template(model, message_content, reasoning_content, openai_tool_calls, usage)
|
||||
data['id'] = completion_id
|
||||
|
||||
# First chunk must carry delta.role (OpenAI spec).
|
||||
if first:
|
||||
data['choices'][0]['delta']['role'] = 'assistant'
|
||||
first = False
|
||||
|
||||
if done and has_tool_calls:
|
||||
data['choices'][0]['finish_reason'] = 'tool_calls'
|
||||
|
||||
@@ -17,8 +17,10 @@ high-cardinality label sets.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
from typing import Dict, List, Sequence, Any
|
||||
from typing import Dict, Iterable, List, Optional
|
||||
from base64 import b64encode
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
@@ -36,6 +38,8 @@ from opentelemetry.sdk.metrics.export import (
|
||||
PeriodicExportingMetricReader,
|
||||
)
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from sqlalchemy import Engine, func, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from open_webui.env import (
|
||||
OTEL_SERVICE_NAME,
|
||||
@@ -46,7 +50,47 @@ from open_webui.env import (
|
||||
OTEL_METRICS_EXPORTER_OTLP_INSECURE,
|
||||
OTEL_METRICS_EXPORT_INTERVAL_MILLIS,
|
||||
)
|
||||
from open_webui.models.users import Users
|
||||
from open_webui.models.users import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sync DB helpers for OTel gauge callbacks
|
||||
#
|
||||
# The OTel Python SDK calls observable-instrument callbacks *synchronously*
|
||||
# from a background collection thread — async callbacks are NOT supported
|
||||
# (the SDK does not ``await`` the return value).
|
||||
#
|
||||
# Rather than bridging into the async event loop, we run plain synchronous
|
||||
# SQL queries using the sync engine that is already available at setup time.
|
||||
# This avoids any cross-thread / cross-loop concerns entirely.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _count_total_users(db_engine: Engine) -> Optional[int]:
|
||||
"""Return the total number of registered users (sync)."""
|
||||
with Session(db_engine) as session:
|
||||
return session.execute(select(func.count()).select_from(User)).scalar()
|
||||
|
||||
|
||||
def _count_active_users(db_engine: Engine) -> Optional[int]:
|
||||
"""Return the number of users active within the last 3 minutes (sync)."""
|
||||
three_minutes_ago = int(time.time()) - 180
|
||||
with Session(db_engine) as session:
|
||||
return session.execute(
|
||||
select(func.count()).select_from(User).filter(User.last_active_at >= three_minutes_ago)
|
||||
).scalar()
|
||||
|
||||
|
||||
def _count_users_active_today(db_engine: Engine) -> Optional[int]:
|
||||
"""Return the number of users active since midnight today (sync)."""
|
||||
now = int(datetime.datetime.now().timestamp())
|
||||
today_midnight = now - (now % 86400)
|
||||
with Session(db_engine) as session:
|
||||
return session.execute(
|
||||
select(func.count()).select_from(User).filter(User.last_active_at > today_midnight)
|
||||
).scalar()
|
||||
|
||||
|
||||
def _build_meter_provider(resource: Resource) -> MeterProvider:
|
||||
@@ -106,7 +150,7 @@ def _build_meter_provider(resource: Resource) -> MeterProvider:
|
||||
return provider
|
||||
|
||||
|
||||
def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
def setup_metrics(app: FastAPI, resource: Resource, db_engine: Engine) -> None:
|
||||
"""Attach OTel metrics middleware to *app* and initialise provider."""
|
||||
|
||||
metrics.set_meter_provider(_build_meter_provider(resource))
|
||||
@@ -124,32 +168,46 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
unit='ms',
|
||||
)
|
||||
|
||||
async def observe_active_users(
|
||||
options: metrics.CallbackOptions,
|
||||
) -> Sequence[metrics.Observation]:
|
||||
return [
|
||||
metrics.Observation(
|
||||
value=await Users.get_active_user_count(),
|
||||
)
|
||||
]
|
||||
# -- Observable gauge callbacks ----------------------------------------
|
||||
# These are called synchronously by the OTel SDK from a background
|
||||
# collection thread. They use the sync DB engine directly — no async
|
||||
# bridging required.
|
||||
|
||||
async def observe_total_registered_users(
|
||||
def observe_total_users(
|
||||
options: metrics.CallbackOptions,
|
||||
) -> Sequence[metrics.Observation]:
|
||||
# IMPORTANT: Use get_num_users() for efficient COUNT(*) query.
|
||||
# Do NOT use len(get_users()["users"]) - it loads ALL user records into memory,
|
||||
# causing connection pool exhaustion on high-latency databases (e.g., Aurora).
|
||||
return [
|
||||
metrics.Observation(
|
||||
value=await Users.get_num_users() or 0,
|
||||
)
|
||||
]
|
||||
) -> Iterable[metrics.Observation]:
|
||||
try:
|
||||
value = _count_total_users(db_engine)
|
||||
if value is not None:
|
||||
yield metrics.Observation(value=value)
|
||||
except Exception:
|
||||
logger.debug('Failed to observe total users', exc_info=True)
|
||||
|
||||
def observe_active_users(
|
||||
options: metrics.CallbackOptions,
|
||||
) -> Iterable[metrics.Observation]:
|
||||
try:
|
||||
value = _count_active_users(db_engine)
|
||||
if value is not None:
|
||||
yield metrics.Observation(value=value)
|
||||
except Exception:
|
||||
logger.debug('Failed to observe active users', exc_info=True)
|
||||
|
||||
def observe_users_active_today(
|
||||
options: metrics.CallbackOptions,
|
||||
) -> Iterable[metrics.Observation]:
|
||||
try:
|
||||
value = _count_users_active_today(db_engine)
|
||||
if value is not None:
|
||||
yield metrics.Observation(value=value)
|
||||
except Exception:
|
||||
logger.debug('Failed to observe users active today', exc_info=True)
|
||||
|
||||
meter.create_observable_gauge(
|
||||
name='webui.users.total',
|
||||
description='Total number of registered users',
|
||||
unit='users',
|
||||
callbacks=[observe_total_registered_users],
|
||||
callbacks=[observe_total_users],
|
||||
)
|
||||
|
||||
meter.create_observable_gauge(
|
||||
@@ -159,11 +217,6 @@ def setup_metrics(app: FastAPI, resource: Resource) -> None:
|
||||
callbacks=[observe_active_users],
|
||||
)
|
||||
|
||||
async def observe_users_active_today(
|
||||
options: metrics.CallbackOptions,
|
||||
) -> Sequence[metrics.Observation]:
|
||||
return [metrics.Observation(value=await Users.get_num_users_active_today())]
|
||||
|
||||
meter.create_observable_gauge(
|
||||
name='webui.users.active.today',
|
||||
description='Number of users active since midnight today',
|
||||
|
||||
@@ -55,4 +55,4 @@ def setup(app: FastAPI, db_engine: Engine):
|
||||
|
||||
# set up metrics only if enabled
|
||||
if ENABLE_OTEL_METRICS:
|
||||
setup_metrics(app, resource)
|
||||
setup_metrics(app, resource, db_engine)
|
||||
|
||||
@@ -22,12 +22,13 @@ aiocache
|
||||
aiofiles
|
||||
starlette-compress==1.7.0
|
||||
Brotli==1.2.0
|
||||
brotlicffi==1.2.0.1
|
||||
httpx[socks,http2,zstd,cli,brotli]==0.28.1
|
||||
starsessions[redis]==2.2.1
|
||||
|
||||
sqlalchemy==2.0.48
|
||||
aiosqlite==0.21.0
|
||||
asyncpg==0.30.0
|
||||
psycopg[binary]==3.2.9
|
||||
alembic==1.18.4
|
||||
peewee==3.19.0
|
||||
peewee-migrate==1.14.3
|
||||
|
||||
@@ -19,13 +19,14 @@ aiocache==0.12.3
|
||||
aiofiles==25.1.0
|
||||
starlette-compress==1.7.0
|
||||
Brotli==1.2.0
|
||||
brotlicffi==1.2.0.1
|
||||
httpx[socks,http2,zstd,cli,brotli]==0.28.1
|
||||
starsessions[redis]==2.2.1
|
||||
python-mimeparse==2.0.0
|
||||
|
||||
sqlalchemy==2.0.48
|
||||
sqlalchemy[asyncio]==2.0.48
|
||||
aiosqlite==0.21.0
|
||||
asyncpg==0.30.0
|
||||
psycopg[binary]==3.2.9
|
||||
alembic==1.18.4
|
||||
peewee==3.19.0
|
||||
peewee-migrate==1.14.3
|
||||
@@ -144,9 +145,6 @@ pytest-docker~=3.2.5
|
||||
## LDAP
|
||||
ldap3==2.9.1
|
||||
|
||||
## Firecrawl
|
||||
firecrawl-py==4.18.0
|
||||
|
||||
## Trace
|
||||
opentelemetry-api==1.40.0
|
||||
opentelemetry-sdk==1.40.0
|
||||
|
||||
@@ -24,7 +24,7 @@ IF NOT "%WEBUI_SECRET_KEY_FILE%" == "" (
|
||||
|
||||
IF "%PORT%"=="" SET PORT=8080
|
||||
IF "%HOST%"=="" SET HOST=0.0.0.0
|
||||
IF "%FORWARDED_ALLOW_IPS%"=="" SET "FORWARDED_ALLOW_IPS=*"
|
||||
IF "%FORWARDED_ALLOW_IPS%"=="" SET "FORWARDED_ALLOW_IPS='*'"
|
||||
SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
|
||||
SET "WEBUI_JWT_SECRET_KEY=%WEBUI_JWT_SECRET_KEY%"
|
||||
|
||||
@@ -47,5 +47,5 @@ IF "%WEBUI_SECRET_KEY% %WEBUI_JWT_SECRET_KEY%" == " " (
|
||||
:: Execute uvicorn
|
||||
SET "WEBUI_SECRET_KEY=%WEBUI_SECRET_KEY%"
|
||||
IF "%UVICORN_WORKERS%"=="" SET UVICORN_WORKERS=1
|
||||
uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips "%FORWARDED_ALLOW_IPS%" --workers %UVICORN_WORKERS% --ws auto
|
||||
uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips %FORWARDED_ALLOW_IPS% --workers %UVICORN_WORKERS% --ws auto
|
||||
:: For ssl user uvicorn open_webui.main:app --host "%HOST%" --port "%PORT%" --forwarded-allow-ips '*' --ssl-keyfile "key.pem" --ssl-certfile "cert.pem" --ws auto
|
||||
|
||||
Generated
+15
-15
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "open-webui",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@azure/msal-browser": "^4.5.0",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
@@ -2284,9 +2284,9 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@mermaid-js/parser": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.0.1.tgz",
|
||||
"integrity": "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz",
|
||||
"integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"langium": "^4.0.0"
|
||||
@@ -3582,9 +3582,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sveltejs/kit": {
|
||||
"version": "2.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.1.tgz",
|
||||
"integrity": "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw==",
|
||||
"version": "2.58.0",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.58.0.tgz",
|
||||
"integrity": "sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.0.0",
|
||||
@@ -5388,9 +5388,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
|
||||
"integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
|
||||
"version": "0.8.13",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz",
|
||||
"integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
@@ -10948,14 +10948,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "11.13.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.13.0.tgz",
|
||||
"integrity": "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==",
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.14.0.tgz",
|
||||
"integrity": "sha512-GSGloRsBs+JINmmhl0JDwjpuezCsHB4WGI4NASHxL3fHo3o/BRXTxhDLKnln8/Q0lRFRyDdEjmk1/d5Sn1Xz8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^7.1.1",
|
||||
"@iconify/utils": "^3.0.2",
|
||||
"@mermaid-js/parser": "^1.0.1",
|
||||
"@mermaid-js/parser": "^1.1.0",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@upsetjs/venn.js": "^2.0.0",
|
||||
"cytoscape": "^3.33.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open-webui",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run pyodide:fetch && vite dev --host",
|
||||
|
||||
+4
-4
@@ -26,14 +26,15 @@ dependencies = [
|
||||
"aiocache==0.12.3",
|
||||
"aiofiles==25.1.0",
|
||||
"starlette-compress==1.7.0",
|
||||
"Brotli==1.1.0",
|
||||
"Brotli==1.2.0",
|
||||
"brotlicffi==1.2.0.1",
|
||||
"httpx[socks,http2,zstd,cli,brotli]==0.28.1",
|
||||
"starsessions[redis]==2.2.1",
|
||||
"python-mimeparse==2.0.0",
|
||||
|
||||
"sqlalchemy==2.0.48",
|
||||
"sqlalchemy[asyncio]==2.0.48",
|
||||
"aiosqlite==0.21.0",
|
||||
"asyncpg==0.30.0",
|
||||
"psycopg[binary]==3.2.9",
|
||||
"alembic==1.18.4",
|
||||
"peewee==3.19.0",
|
||||
"peewee-migrate==1.14.3",
|
||||
@@ -166,7 +167,6 @@ all = [
|
||||
"oracledb==3.4.2",
|
||||
"colbert-ai==0.2.22",
|
||||
|
||||
"firecrawl-py==4.18.0",
|
||||
"azure-search-documents==11.6.0",
|
||||
"unstructured==0.18.31",
|
||||
]
|
||||
|
||||
@@ -22,7 +22,7 @@ const packages = [
|
||||
// static/pyodide/ so that the browser can install them offline via micropip.
|
||||
// Packages already provided by the Pyodide distribution (click, platformdirs,
|
||||
// typing_extensions, etc.) do NOT need to be listed here.
|
||||
const pypiPackages = ['black', 'pathspec', 'mypy_extensions'];
|
||||
const pypiPackages = ['black', 'pathspec', 'mypy_extensions', 'pytokens'];
|
||||
|
||||
import { loadPyodide } from 'pyodide';
|
||||
import { setGlobalDispatcher, ProxyAgent } from 'undici';
|
||||
|
||||
@@ -71,6 +71,16 @@
|
||||
metaThemeColorTag.setAttribute('content', '#171717');
|
||||
}
|
||||
|
||||
const preloadHref = document.documentElement.classList.contains('dark')
|
||||
? '/static/splash-dark.png'
|
||||
: '/static/splash.png';
|
||||
const preload = document.createElement('link');
|
||||
preload.rel = 'preload';
|
||||
preload.as = 'image';
|
||||
preload.href = preloadHref;
|
||||
preload.setAttribute('fetchpriority', 'high');
|
||||
document.head.appendChild(preload);
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
|
||||
if (localStorage.theme === 'system') {
|
||||
if (e.matches) {
|
||||
@@ -90,6 +100,8 @@
|
||||
logo.id = 'logo';
|
||||
logo.style =
|
||||
'position: absolute; width: auto; height: 6rem; top: 44%; left: 50%; transform: translateX(-50%); display:block;';
|
||||
logo.loading = 'eager';
|
||||
logo.fetchPriority = 'high';
|
||||
logo.src = isDarkMode ? '/static/splash-dark.png' : '/static/splash.png';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
@@ -139,6 +151,8 @@
|
||||
id="logo-her"
|
||||
style="width: auto; height: 13rem"
|
||||
src="/static/splash.png"
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
class="animate-pulse-fast"
|
||||
/>
|
||||
|
||||
|
||||
@@ -712,3 +712,33 @@ export const deleteAPIKey = async (token: string) => {
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export const deleteOAuthSession = async (token: string, provider: string) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(
|
||||
`${WEBUI_API_BASE_URL}/auths/oauth/sessions/${encodeURIComponent(provider)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
error = err.detail;
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -152,6 +152,9 @@ export const getBaseModels = async (token: string = '') => {
|
||||
export const createNewModel = async (token: string, model: object) => {
|
||||
let error = null;
|
||||
|
||||
const { id, base_model_id, name, meta, params, access_grants, is_active } = model as any;
|
||||
const payload = { id, base_model_id, name, meta, params, access_grants, is_active };
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -159,7 +162,7 @@ export const createNewModel = async (token: string, model: object) => {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify(model)
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
@@ -251,6 +254,9 @@ export const toggleModelById = async (token: string, id: string) => {
|
||||
export const updateModelById = async (token: string, id: string, model: object) => {
|
||||
let error = null;
|
||||
|
||||
const { base_model_id, name, meta, params, access_grants, is_active } = model as any;
|
||||
const payload = { id, base_model_id, name, meta, params, access_grants, is_active };
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/models/model/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -258,7 +264,7 @@ export const updateModelById = async (token: string, id: string, model: object)
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ ...model, id })
|
||||
body: JSON.stringify(payload)
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
|
||||
@@ -525,7 +525,7 @@
|
||||
TTS_MODEL = 'tts-1';
|
||||
} else if (e.target?.value === 'mistral') {
|
||||
TTS_VOICE = '';
|
||||
TTS_MODEL = 'mistral-tts-latest';
|
||||
TTS_MODEL = 'voxtral-mini-tts-2603';
|
||||
} else {
|
||||
TTS_VOICE = '';
|
||||
TTS_MODEL = '';
|
||||
|
||||
@@ -184,6 +184,13 @@
|
||||
toast.error($i18n.t('Mistral OCR API Key required.'));
|
||||
return;
|
||||
}
|
||||
if (
|
||||
RAGConfig.CONTENT_EXTRACTION_ENGINE === 'paddleocr_vl' &&
|
||||
RAGConfig.PADDLEOCR_VL_BASE_URL === ''
|
||||
) {
|
||||
toast.error($i18n.t('PaddleOCR-vl API URL required.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mineru' &&
|
||||
@@ -356,6 +363,7 @@
|
||||
<option value="datalab_marker">{$i18n.t('Datalab Marker API')}</option>
|
||||
<option value="document_intelligence">{$i18n.t('Document Intelligence')}</option>
|
||||
<option value="mistral_ocr">{$i18n.t('Mistral OCR')}</option>
|
||||
<option value="paddleocr_vl">{$i18n.t('PaddleOCR-vl')}</option>
|
||||
<option value="mineru">{$i18n.t('MinerU')}</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -657,6 +665,19 @@
|
||||
bind:value={RAGConfig.MISTRAL_OCR_API_KEY}
|
||||
/>
|
||||
</div>
|
||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'paddleocr_vl'}
|
||||
<div class="my-0.5 flex gap-2 pr-2">
|
||||
<input
|
||||
class="flex-1 w-full text-sm bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Enter PaddleOCR-vl API Base URL')}
|
||||
bind:value={RAGConfig.PADDLEOCR_VL_BASE_URL}
|
||||
/>
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('Enter PaddleOCR-vl API Token')}
|
||||
bind:value={RAGConfig.PADDLEOCR_VL_TOKEN}
|
||||
required={false}
|
||||
/>
|
||||
</div>
|
||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mineru'}
|
||||
<!-- API Mode Selection -->
|
||||
<div class="flex w-full mt-2">
|
||||
@@ -1349,6 +1370,14 @@
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
{#if RAGConfig.RAG_TEMPLATE && (RAGConfig.RAG_TEMPLATE.match(/\[context\]/g) || []).length + (RAGConfig.RAG_TEMPLATE.match(/\{\{CONTEXT\}\}/g) || []).length > 1}
|
||||
<div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
|
||||
{$i18n.t(
|
||||
'This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.'
|
||||
)}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
|
||||
import { getContext, tick, onDestroy } from 'svelte';
|
||||
import { getContext, tick } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
import { chatCompletion } from '$lib/apis/openai';
|
||||
|
||||
import ChatBubble from '$lib/components/icons/ChatBubble.svelte';
|
||||
import LightBulb from '$lib/components/icons/LightBulb.svelte';
|
||||
import Markdown from '../Messages/Markdown.svelte';
|
||||
import Skeleton from '../Messages/Skeleton.svelte';
|
||||
import { chatId, models, socket } from '$lib/stores';
|
||||
|
||||
export let id = '';
|
||||
export let messageId = '';
|
||||
|
||||
export let model = null;
|
||||
export let messages = [];
|
||||
export let actions = [];
|
||||
export let onAdd = (e) => {};
|
||||
export let onSetInputText = (text) => {};
|
||||
|
||||
let floatingInput = false;
|
||||
let selectedAction = null;
|
||||
@@ -29,11 +16,6 @@
|
||||
let selectedText = '';
|
||||
let floatingInputValue = '';
|
||||
|
||||
let content = '';
|
||||
let responseContent = null;
|
||||
let responseDone = false;
|
||||
let controller = null;
|
||||
|
||||
$: if (actions.length === 0) {
|
||||
actions = DEFAULT_ACTIONS;
|
||||
}
|
||||
@@ -54,25 +36,7 @@
|
||||
}
|
||||
];
|
||||
|
||||
const autoScroll = async () => {
|
||||
const responseContainer = document.getElementById('response-container');
|
||||
if (responseContainer) {
|
||||
// Scroll to bottom only if the scroll is at the bottom give 50px buffer
|
||||
if (
|
||||
responseContainer.scrollHeight - responseContainer.clientHeight <=
|
||||
responseContainer.scrollTop + 50
|
||||
) {
|
||||
responseContainer.scrollTop = responseContainer.scrollHeight;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const actionHandler = async (actionId) => {
|
||||
if (!model) {
|
||||
toast.error($i18n.t('Model not selected'));
|
||||
return;
|
||||
}
|
||||
|
||||
const actionHandler = (actionId) => {
|
||||
let selectedContent = selectedText
|
||||
.split('\n')
|
||||
.map((line) => `> ${line}`)
|
||||
@@ -80,27 +44,20 @@
|
||||
|
||||
let selectedAction = actions.find((action) => action.id === actionId);
|
||||
if (!selectedAction) {
|
||||
toast.error($i18n.t('Action not found'));
|
||||
return;
|
||||
}
|
||||
|
||||
let prompt = selectedAction?.prompt ?? '';
|
||||
let toolIds = [];
|
||||
|
||||
// Handle: {{variableId|tool:id="toolId"}} pattern
|
||||
// This regex captures variableId and toolId from {{variableId|tool:id="toolId"}}
|
||||
const varToolPattern = /\{\{(.*?)\|tool:id="([^"]+)"\}\}/g;
|
||||
prompt = prompt.replace(varToolPattern, (match, variableId, toolId) => {
|
||||
toolIds.push(toolId);
|
||||
return variableId; // Replace with just variableId
|
||||
});
|
||||
|
||||
// legacy {{TOOL:toolId}} pattern (for backward compatibility)
|
||||
let toolIdPattern = /\{\{TOOL:([^\}]+)\}\}/g;
|
||||
let match;
|
||||
while ((match = toolIdPattern.exec(prompt)) !== null) {
|
||||
toolIds.push(match[1]);
|
||||
}
|
||||
|
||||
// Remove all TOOL placeholders from the prompt
|
||||
prompt = prompt.replace(toolIdPattern, '');
|
||||
@@ -113,133 +70,17 @@
|
||||
prompt = prompt.replace('{{CONTENT}}', selectedText);
|
||||
prompt = prompt.replace('{{SELECTED_CONTENT}}', selectedContent);
|
||||
|
||||
content = prompt;
|
||||
responseContent = '';
|
||||
|
||||
let res;
|
||||
[res, controller] = await chatCompletion(localStorage.token, {
|
||||
model: model,
|
||||
model_item: $models.find((m) => m.id === model),
|
||||
session_id: $socket?.id,
|
||||
chat_id: $chatId,
|
||||
messages: [
|
||||
...messages,
|
||||
{
|
||||
role: 'user',
|
||||
content: content
|
||||
}
|
||||
].map((message) => ({
|
||||
role: message.role,
|
||||
content: message.content
|
||||
})),
|
||||
...(toolIds.length > 0
|
||||
? {
|
||||
tool_ids: toolIds
|
||||
// params: {
|
||||
// function_calling: 'native'
|
||||
// }
|
||||
}
|
||||
: {}),
|
||||
|
||||
stream: true // Enable streaming
|
||||
});
|
||||
|
||||
if (res && res.ok) {
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
const processStream = async () => {
|
||||
while (true) {
|
||||
// Read data chunks from the response stream
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Decode the received chunk
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
|
||||
// Process lines within the chunk
|
||||
const lines = chunk.split('\n').filter((line) => line.trim() !== '');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
if (line.startsWith('data: [DONE]')) {
|
||||
responseDone = true;
|
||||
|
||||
await tick();
|
||||
autoScroll();
|
||||
continue;
|
||||
} else {
|
||||
// Parse the JSON chunk
|
||||
try {
|
||||
const data = JSON.parse(line.slice(6));
|
||||
|
||||
// Append the `content` field from the "choices" object
|
||||
if (data.choices && data.choices[0]?.delta?.content) {
|
||||
responseContent += data.choices[0].delta.content;
|
||||
|
||||
autoScroll();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Process the stream in the background
|
||||
try {
|
||||
await processStream();
|
||||
} catch (e) {
|
||||
if (e.name !== 'AbortError') {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
toast.error($i18n.t('An error occurred while fetching the explanation'));
|
||||
}
|
||||
};
|
||||
|
||||
const addHandler = async () => {
|
||||
const messages = [
|
||||
{
|
||||
role: 'user',
|
||||
content: content
|
||||
},
|
||||
{
|
||||
role: 'assistant',
|
||||
content: responseContent
|
||||
}
|
||||
];
|
||||
|
||||
onAdd({
|
||||
modelId: model,
|
||||
parentId: messageId,
|
||||
messages: messages
|
||||
});
|
||||
// Prepopulate the main chat input instead of inline streaming
|
||||
onSetInputText(prompt);
|
||||
closeHandler();
|
||||
};
|
||||
|
||||
export const closeHandler = () => {
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
}
|
||||
|
||||
selectedAction = null;
|
||||
selectedText = '';
|
||||
responseContent = null;
|
||||
responseDone = false;
|
||||
floatingInput = false;
|
||||
floatingInputValue = '';
|
||||
};
|
||||
|
||||
onDestroy(() => {
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -247,120 +88,82 @@
|
||||
class="absolute rounded-lg mt-1 text-xs z-9999"
|
||||
style="display: none"
|
||||
>
|
||||
{#if responseContent === null}
|
||||
{#if !floatingInput}
|
||||
<div
|
||||
class="flex flex-row shrink-0 p-0.5 bg-white dark:bg-gray-850 dark:text-gray-100 text-medium rounded-xl shadow-xl border border-gray-100 dark:border-gray-800"
|
||||
>
|
||||
{#each actions as action}
|
||||
<button
|
||||
aria-label={action.label}
|
||||
class="px-1.5 py-[1px] hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl flex items-center gap-1 min-w-fit transition"
|
||||
on:click={async () => {
|
||||
selectedText = window.getSelection().toString();
|
||||
selectedAction = action;
|
||||
{#if !floatingInput}
|
||||
<div
|
||||
class="flex flex-row shrink-0 p-0.5 bg-white dark:bg-gray-850 dark:text-gray-100 text-medium rounded-xl shadow-xl border border-gray-100 dark:border-gray-800"
|
||||
>
|
||||
{#each actions as action}
|
||||
<button
|
||||
aria-label={action.label}
|
||||
class="px-1.5 py-[1px] hover:bg-gray-50 dark:hover:bg-gray-800 rounded-xl flex items-center gap-1 min-w-fit transition"
|
||||
on:click={async () => {
|
||||
selectedText = window.getSelection().toString();
|
||||
selectedAction = action;
|
||||
|
||||
if (action.prompt.includes('{{INPUT_CONTENT}}')) {
|
||||
floatingInput = true;
|
||||
floatingInputValue = '';
|
||||
if (action.prompt.includes('{{INPUT_CONTENT}}')) {
|
||||
floatingInput = true;
|
||||
floatingInputValue = '';
|
||||
|
||||
await tick();
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById('floating-message-input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
actionHandler(action.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if action.icon}
|
||||
<svelte:component this={action.icon} className="size-3 shrink-0" />
|
||||
{/if}
|
||||
<div class="shrink-0">{action.label}</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="py-1 flex dark:text-gray-100 bg-white dark:bg-gray-850 border border-gray-100 dark:border-gray-800 w-72 rounded-full shadow-xl"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="floating-message-input"
|
||||
class="ml-5 bg-transparent outline-hidden w-full flex-1 text-sm"
|
||||
placeholder={$i18n.t('Ask a question')}
|
||||
aria-label={$i18n.t('Ask a question')}
|
||||
bind:value={floatingInputValue}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
actionHandler(selectedAction?.id);
|
||||
await tick();
|
||||
setTimeout(() => {
|
||||
const input = document.getElementById('floating-message-input');
|
||||
if (input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 0);
|
||||
} else {
|
||||
actionHandler(action.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="ml-1 mr-1">
|
||||
<button
|
||||
aria-label={$i18n.t('Submit question')}
|
||||
class="{floatingInputValue !== ''
|
||||
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
|
||||
: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 m-0.5 self-center"
|
||||
on:click={() => {
|
||||
actionHandler(selectedAction?.id);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="size-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
>
|
||||
{#if action.icon}
|
||||
<svelte:component this={action.icon} className="size-3 shrink-0" />
|
||||
{/if}
|
||||
<div class="shrink-0">{action.label}</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div
|
||||
class="bg-white dark:bg-gray-850 dark:text-gray-100 rounded-3xl shadow-xl w-80 max-w-full border border-gray-100 dark:border-gray-800"
|
||||
class="py-1 flex dark:text-gray-100 bg-white dark:bg-gray-850 border border-gray-100 dark:border-gray-800 w-72 rounded-full shadow-xl"
|
||||
>
|
||||
<div
|
||||
class="bg-white dark:bg-gray-850 dark:text-gray-100 text-medium rounded-3xl px-3.5 pt-3 w-full"
|
||||
>
|
||||
<div class="font-medium">
|
||||
<Markdown id={`${id}-float-prompt`} {content} />
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="floating-message-input"
|
||||
class="ml-5 bg-transparent outline-hidden w-full flex-1 text-sm"
|
||||
placeholder={$i18n.t('Ask a question')}
|
||||
aria-label={$i18n.t('Ask a question')}
|
||||
bind:value={floatingInputValue}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
actionHandler(selectedAction?.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="bg-white dark:bg-gray-850 dark:text-gray-100 text-medium rounded-4xl w-full">
|
||||
<div
|
||||
class=" max-h-80 overflow-y-auto w-full markdown-prose-xs px-3.5 py-3"
|
||||
id="response-container"
|
||||
<div class="ml-1 mr-1">
|
||||
<button
|
||||
aria-label={$i18n.t('Submit question')}
|
||||
class="{floatingInputValue !== ''
|
||||
? 'bg-black text-white hover:bg-gray-900 dark:bg-white dark:text-black dark:hover:bg-gray-100 '
|
||||
: 'text-white bg-gray-200 dark:text-gray-900 dark:bg-gray-700 disabled'} transition rounded-full p-1.5 m-0.5 self-center"
|
||||
on:click={() => {
|
||||
actionHandler(selectedAction?.id);
|
||||
}}
|
||||
>
|
||||
{#if !responseContent || responseContent?.trim() === ''}
|
||||
<Skeleton size="sm" />
|
||||
{:else}
|
||||
<Markdown id={`${id}-float-response`} content={responseContent} />
|
||||
{/if}
|
||||
|
||||
{#if responseDone}
|
||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
|
||||
on:click={addHandler}
|
||||
>
|
||||
{$i18n.t('Add')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="size-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8 14a.75.75 0 0 1-.75-.75V4.56L4.03 7.78a.75.75 0 0 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1-1.06 1.06L8.75 4.56v8.69A.75.75 0 0 1 8 14Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
} from '$lib/stores';
|
||||
|
||||
import { getOAuthClientAuthorizationUrl } from '$lib/apis/configs';
|
||||
import { deleteOAuthSession } from '$lib/apis/auths';
|
||||
import { getTools } from '$lib/apis/tools';
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
import Knobs from '$lib/components/icons/Knobs.svelte';
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
@@ -27,6 +30,7 @@
|
||||
import Terminal from '$lib/components/icons/Terminal.svelte';
|
||||
import ChevronRight from '$lib/components/icons/ChevronRight.svelte';
|
||||
import ChevronLeft from '$lib/components/icons/ChevronLeft.svelte';
|
||||
import LinkSlash from '$lib/components/icons/LinkSlash.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@@ -375,6 +379,39 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if (tools[toolId]?.authenticated ?? true) && toolId.startsWith('server:mcp:')}
|
||||
<div class="shrink-0">
|
||||
<Tooltip content={$i18n.t('Disconnect OAuth')}>
|
||||
<button
|
||||
class="self-center w-fit text-sm text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition rounded-full"
|
||||
type="button"
|
||||
on:click={async (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const parts = toolId.split(':');
|
||||
const serverId = parts.at(-1) ?? toolId;
|
||||
const provider = `mcp:${serverId}`;
|
||||
|
||||
try {
|
||||
await deleteOAuthSession(localStorage.token, provider);
|
||||
toast.success($i18n.t('OAuth session disconnected'));
|
||||
|
||||
// Refresh tools to update authenticated state
|
||||
_tools.set(await getTools(localStorage.token));
|
||||
selectedToolIds = selectedToolIds.filter((id) => id !== toolId);
|
||||
await init();
|
||||
} catch (err) {
|
||||
toast.error(err ?? $i18n.t('Failed to disconnect'));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LinkSlash className="size-3.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if tools[toolId]?.has_user_valves && ($user?.role === 'admin' || ($user?.permissions?.chat?.valves ?? true))}
|
||||
<div class=" shrink-0">
|
||||
<Tooltip content={$i18n.t('Valves')}>
|
||||
|
||||
@@ -60,102 +60,7 @@
|
||||
export let messagesCount: number | null = 8;
|
||||
let messagesLoading = false;
|
||||
|
||||
// Off-screen message unloading. Heights are measured on scroll so spacers
|
||||
// always match real sizes — no scroll jumps, no feedback loops needed.
|
||||
const OVERSCAN = 3;
|
||||
const DEFAULT_HEIGHT = 150;
|
||||
let visibleStart = 0;
|
||||
let visibleEnd = 0;
|
||||
let messageHeights = new Map();
|
||||
let topSpacerHeight = 0;
|
||||
let bottomSpacerHeight = 0;
|
||||
let pendingCull = null;
|
||||
|
||||
// Helper: get height for a message (cached or default)
|
||||
const heightOf = (id) => messageHeights.get(id) ?? DEFAULT_HEIGHT;
|
||||
|
||||
/** Measure all currently rendered message elements and cache their heights */
|
||||
const measureMessageHeights = () => {
|
||||
const elements = document
|
||||
.getElementById('messages-container')
|
||||
?.querySelectorAll('[role="listitem"]');
|
||||
if (!elements) return;
|
||||
|
||||
messageHeights = new Map([
|
||||
...messageHeights,
|
||||
...Array.from(elements)
|
||||
.map((el, i) => [messages[visibleStart + i]?.id, el.getBoundingClientRect().height])
|
||||
.filter(([id]) => id != null)
|
||||
]);
|
||||
};
|
||||
|
||||
/** Compute visible range from current scroll position and apply */
|
||||
const updateVisibleRange = () => {
|
||||
const container = document.getElementById('messages-container');
|
||||
if (!container || messages.length === 0) return;
|
||||
|
||||
const st = container.scrollTop;
|
||||
const ch = container.clientHeight;
|
||||
|
||||
// Build prefix sums from measured heights
|
||||
const prefixSums = messages.reduce(
|
||||
(acc, m) => [...acc, acc[acc.length - 1] + heightOf(m.id)],
|
||||
[0]
|
||||
);
|
||||
|
||||
const firstVisible = Math.max(0, prefixSums.findIndex((h) => h > st) - 1);
|
||||
const lastVisible = prefixSums.findIndex((h) => h > st + ch);
|
||||
|
||||
// Only cull messages that have been measured (so spacer height is accurate)
|
||||
// findIndex returns -1 when all are measured → no limit on culling
|
||||
const firstUnmeasured = messages.findIndex((m) => !messageHeights.has(m.id));
|
||||
const cullLimit = firstUnmeasured === -1 ? messages.length : firstUnmeasured;
|
||||
|
||||
visibleStart = Math.max(0, Math.min(firstVisible - OVERSCAN, cullLimit));
|
||||
visibleEnd = Math.min(
|
||||
messages.length,
|
||||
(lastVisible === -1 ? messages.length : lastVisible) + OVERSCAN
|
||||
);
|
||||
topSpacerHeight = prefixSums[visibleStart] ?? 0;
|
||||
bottomSpacerHeight = (prefixSums[messages.length] ?? 0) - (prefixSums[visibleEnd] ?? 0);
|
||||
};
|
||||
|
||||
/** Scroll handler: measure every frame, cull via rAF (same throttle as pendingRebuild) */
|
||||
const handleContainerScroll = () => {
|
||||
measureMessageHeights();
|
||||
|
||||
// Don't cull during progressive loading
|
||||
if (messagesLoading) return;
|
||||
|
||||
if (!pendingCull) {
|
||||
pendingCull = requestAnimationFrame(() => {
|
||||
pendingCull = null;
|
||||
updateVisibleRange();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let scrollListenerAttached = false;
|
||||
|
||||
const attachScrollListener = () => {
|
||||
if (scrollListenerAttached) return;
|
||||
const container = document.getElementById('messages-container');
|
||||
if (!container) return;
|
||||
|
||||
container.addEventListener('scroll', handleContainerScroll, { passive: true });
|
||||
scrollListenerAttached = true;
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
attachScrollListener();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
const container = document.getElementById('messages-container');
|
||||
if (container && scrollListenerAttached) {
|
||||
container.removeEventListener('scroll', handleContainerScroll);
|
||||
}
|
||||
cancelAnimationFrame(pendingCull);
|
||||
cancelAnimationFrame(pendingRebuild);
|
||||
});
|
||||
|
||||
@@ -169,12 +74,6 @@
|
||||
|
||||
buildMessages();
|
||||
|
||||
// Show all messages during progressive loading (no culling)
|
||||
visibleStart = 0;
|
||||
visibleEnd = messages.length;
|
||||
topSpacerHeight = 0;
|
||||
bottomSpacerHeight = 0;
|
||||
|
||||
await tick();
|
||||
|
||||
messagesLoading = false;
|
||||
@@ -201,7 +100,6 @@
|
||||
}
|
||||
|
||||
messages = _messages.reverse();
|
||||
visibleEnd = messages.length;
|
||||
};
|
||||
|
||||
// Throttle message list rebuilds to once per animation frame during streaming.
|
||||
@@ -220,8 +118,6 @@
|
||||
cancelAnimationFrame(pendingRebuild);
|
||||
pendingRebuild = null;
|
||||
buildMessages();
|
||||
// No explicit culling needed — scrollToBottom will fire a scroll event,
|
||||
// which triggers handleContainerScroll → rAF → updateVisibleRange
|
||||
} else if (_messages) {
|
||||
// Content update (streaming) — throttle to once per frame
|
||||
if (!pendingRebuild) {
|
||||
@@ -570,13 +466,7 @@
|
||||
</Loader>
|
||||
{/if}
|
||||
<ul role="log" aria-live="polite" aria-relevant="additions" aria-atomic="false">
|
||||
<!-- Top spacer: sum of cached heights for messages above visible range -->
|
||||
{#if topSpacerHeight > 0}
|
||||
<div style="height: {topSpacerHeight}px" aria-hidden="true" />
|
||||
{/if}
|
||||
|
||||
{#each messages.slice(visibleStart, visibleEnd) as message, i (message.id)}
|
||||
{@const messageIdx = visibleStart + i}
|
||||
{#each messages as message, messageIdx (message.id)}
|
||||
<Message
|
||||
{chatId}
|
||||
bind:history
|
||||
@@ -605,11 +495,6 @@
|
||||
{topPadding}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<!-- Bottom spacer: sum of cached heights for messages below visible range -->
|
||||
{#if bottomSpacerHeight > 0}
|
||||
<div style="height: {bottomSpacerHeight}px" aria-hidden="true" />
|
||||
{/if}
|
||||
</ul>
|
||||
</section>
|
||||
<div class="pb-18" />
|
||||
|
||||
@@ -183,6 +183,14 @@
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{#if citations.length > 3}
|
||||
<div
|
||||
class="size-4 rounded-full shrink-0 border border-white dark:border-gray-850 bg-gray-100 dark:bg-gray-800 flex items-center justify-center text-[8px] font-semibold text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
>
|
||||
+{citations.length - Math.min(urlCitations.length, 3)}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
export let onSave = (e) => {};
|
||||
export let onSourceClick = (e) => {};
|
||||
export let onTaskClick = (e) => {};
|
||||
export let onAddMessages = (e) => {};
|
||||
export let onSetInputText = (text) => {};
|
||||
|
||||
let contentContainerElement;
|
||||
let floatingButtonsElement;
|
||||
@@ -140,20 +140,36 @@
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
if (floatingButtons) {
|
||||
contentContainerElement?.addEventListener('mouseup', updateButtonPosition);
|
||||
// Reactive listener attachment: re-attaches when floatingButtons
|
||||
// transitions from false → true (e.g. when message.done flips).
|
||||
let listenersAttached = false;
|
||||
|
||||
function attachListeners() {
|
||||
if (!listenersAttached && contentContainerElement) {
|
||||
contentContainerElement.addEventListener('mouseup', updateButtonPosition);
|
||||
document.addEventListener('mouseup', updateButtonPosition);
|
||||
document.addEventListener('keydown', keydownHandler);
|
||||
listenersAttached = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (floatingButtons) {
|
||||
function detachListeners() {
|
||||
if (listenersAttached) {
|
||||
contentContainerElement?.removeEventListener('mouseup', updateButtonPosition);
|
||||
document.removeEventListener('mouseup', updateButtonPosition);
|
||||
document.removeEventListener('keydown', keydownHandler);
|
||||
listenersAttached = false;
|
||||
}
|
||||
}
|
||||
|
||||
$: if (floatingButtons && contentContainerElement) {
|
||||
attachListeners();
|
||||
} else {
|
||||
detachListeners();
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
detachListeners();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -201,17 +217,9 @@
|
||||
<FloatingButtons
|
||||
bind:this={floatingButtonsElement}
|
||||
{id}
|
||||
{messageId}
|
||||
actions={$settings?.floatingActionButtons ?? []}
|
||||
model={(selectedModels ?? []).includes(model?.id)
|
||||
? model?.id
|
||||
: (selectedModels ?? []).length > 0
|
||||
? selectedModels.at(0)
|
||||
: (model?.id ?? null)}
|
||||
messages={createMessagesList(history, messageId)}
|
||||
onAdd={({ modelId, parentId, messages }) => {
|
||||
console.log(modelId, parentId, messages);
|
||||
onAddMessages({ modelId, parentId, messages });
|
||||
onSetInputText={(text) => {
|
||||
onSetInputText(text);
|
||||
closeFloatingButtons();
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
$: updateHandler(content);
|
||||
|
||||
// Throttle parsing to once per animation frame while streaming
|
||||
$: onDestroy(() => {
|
||||
onDestroy(() => {
|
||||
cancelAnimationFrame(pendingUpdate);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
role="listitem"
|
||||
class="flex flex-col justify-between px-5 mb-3 w-full {($settings?.widescreenMode ?? null)
|
||||
? 'max-w-full'
|
||||
: 'max-w-5xl'} mx-auto rounded-lg group"
|
||||
: 'max-w-5xl'} mx-auto rounded-lg group message-listitem"
|
||||
>
|
||||
{#if history.messages[messageId]}
|
||||
{#if history.messages[messageId].role === 'user'}
|
||||
@@ -128,3 +128,13 @@
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Browser-native virtualization: skip rendering of off-screen messages
|
||||
without destroying their component trees. Replaces the JS-based
|
||||
culling that caused catastrophic mount/destroy thrashing. */
|
||||
.message-listitem {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: auto 150px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -788,9 +788,6 @@
|
||||
<!-- unless message.error === true which is legacy error handling, where the error message is stored in message.content -->
|
||||
<ContentRenderer
|
||||
id={`${chatId}-${message.id}`}
|
||||
messageId={message.id}
|
||||
{history}
|
||||
{selectedModels}
|
||||
content={message.content}
|
||||
sources={message.sources}
|
||||
floatingButtons={message?.done &&
|
||||
@@ -814,8 +811,8 @@
|
||||
citationsElement?.showSourceModal(id);
|
||||
}
|
||||
}}
|
||||
onAddMessages={({ modelId, parentId, messages }) => {
|
||||
addMessages({ modelId, parentId, messages });
|
||||
onSetInputText={(text) => {
|
||||
setInputText(text);
|
||||
}}
|
||||
onSave={({ raw, oldContent, newContent }) => {
|
||||
history.messages[message.id].content = history.messages[
|
||||
|
||||
@@ -737,6 +737,17 @@
|
||||
StarterKit.configure({
|
||||
link: link,
|
||||
code: false, // Disabled in favor of FixedCode (see workaround above)
|
||||
// When rich text is on, ListKit + CodeBlockLowlight provide these.
|
||||
// Disable StarterKit's equivalents to avoid duplicate extension names.
|
||||
...(richText
|
||||
? {
|
||||
codeBlock: false,
|
||||
bulletList: false,
|
||||
orderedList: false,
|
||||
listItem: false,
|
||||
listKeymap: false
|
||||
}
|
||||
: {}),
|
||||
// When rich text is off, disable Strike from StarterKit so we can
|
||||
// re-add it below without its Mod-Shift-s shortcut (which conflicts
|
||||
// with the Toggle Sidebar shortcut). When rich text is on, the user
|
||||
|
||||
@@ -601,6 +601,8 @@
|
||||
src={`${WEBUI_API_BASE_URL}/models/model/profile/image?id=${model.id}&lang=${$i18n.language}`}
|
||||
alt="modelfile profile"
|
||||
class=" rounded-2xl size-12 object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
on:error={(e) => {
|
||||
e.target.src = '/favicon.png';
|
||||
}}
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "معلومات دقيقة",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "إجراء مطلوب لتخزين سجل الدردشة",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -163,7 +162,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "مساعد",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -583,6 +581,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "معطل",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "اكتشف نموذجا",
|
||||
"Discover a prompt": "اكتشاف موجه",
|
||||
@@ -772,6 +771,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -902,6 +903,7 @@
|
||||
"Failed to create API Key.": "فشل في إنشاء مفتاح API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1456,6 +1458,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "اكتوبر",
|
||||
"Off": "أغلاق",
|
||||
"Okay, Let's Go!": "حسنا دعنا نذهب!",
|
||||
@@ -1522,6 +1525,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2032,6 +2037,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
"Account Activation Pending": "انتظار تفعيل الحساب",
|
||||
"Accurate information": "معلومات دقيقة",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "إجراء مطلوب لتخزين سجل الدردشة",
|
||||
"Actions": "الإجراءات",
|
||||
"Activate": "تفعيل",
|
||||
@@ -163,7 +162,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "رائع",
|
||||
"an assistant": "مساعد",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "تم التحليل",
|
||||
"Analyzing...": "جارٍ التحليل...",
|
||||
@@ -583,6 +581,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "معطّل",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "اكتشف وظيفة",
|
||||
"Discover a model": "اكتشف نموذجا",
|
||||
"Discover a prompt": "اكتشاف موجه",
|
||||
@@ -772,6 +771,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "(e.g. 50) أدخل عدد الخطوات",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "أدخل مفتاح API لـ Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -902,6 +903,7 @@
|
||||
"Failed to create API Key.": "فشل في إنشاء مفتاح API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1456,6 +1458,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "معرّف OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "اكتوبر",
|
||||
"Off": "أغلاق",
|
||||
"Okay, Let's Go!": "حسنا دعنا نذهب!",
|
||||
@@ -1522,6 +1525,8 @@
|
||||
"Output format": "تنسيق الإخراج",
|
||||
"Output Format": "",
|
||||
"Overview": "نظرة عامة",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "صفحة",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2032,6 +2037,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "يحدد هذا الخيار الحد الأقصى لعدد الرموز التي يمكن للنموذج توليدها في الرد. زيادته تتيح للنموذج تقديم إجابات أطول، لكنها قد تزيد من احتمالية توليد محتوى غير مفيد أو غير ذي صلة.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "سيؤدي هذا الخيار إلى حذف جميع الملفات الحالية في المجموعة واستبدالها بالملفات التي تم تحميلها حديثًا.",
|
||||
"This response was generated by \"{{model}}\"": "تم توليد هذا الرد بواسطة \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "هذا سيقوم بالحذف",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "هذا سيحذف <strong>{{NAME}}</strong> و<strong>كل محتوياته</strong>.",
|
||||
"This will delete all models including custom models": "هذا سيحذف جميع النماذج بما في ذلك النماذج المخصصة",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Hesabın aktivləşdirilməsi gözlənilir",
|
||||
"Accurate information": "Dəqiq məlumat",
|
||||
"Action": "Fəaliyyət",
|
||||
"Action not found": "Fəaliyyət tapılmadı",
|
||||
"Action Required for Chat Log Storage": "Söhbət tarixçəsinin saxlanılması üçün hərəkət tələb olunur",
|
||||
"Actions": "Fəaliyyətlər",
|
||||
"Activate": "Aktivləşdir",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Bildiriş səsini həmişə çal",
|
||||
"Amazing": "Möhtəşəm",
|
||||
"an assistant": "bir köməkçi",
|
||||
"An error occurred while fetching the explanation": "İzahı gətirərkən xəta baş verdi",
|
||||
"Analytics": "Analitika",
|
||||
"Analyzed": "Analiz edildi",
|
||||
"Analyzing...": "Analiz edilir...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Şəkil çıxarılmasını söndür",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "PDF-dən şəkil çıxarılmasını söndürün. 'LLM istifadə et' aktivdirsə, şəkillərə avtomatik altyazı veriləcək. Standart olaraq 'Xeyr' (False) təyin edilib.",
|
||||
"Disabled": "Söndürülüb",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Funksiya kəşf edin",
|
||||
"Discover a model": "Model kəşf edin",
|
||||
"Discover a prompt": "Göstəriş kəşf edin",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Yeni şifrəni daxil edin",
|
||||
"Enter Number of Steps (e.g. 50)": "Addım sayını daxil edin (məs. 50)",
|
||||
"Enter Ollama Cloud API Key": "Ollama Cloud API açarını daxil edin",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Perplexity API açarını daxil edin",
|
||||
"Enter Perplexity Search API URL": "Perplexity axtarış API URL-ini daxil edin",
|
||||
"Enter Playwright Timeout": "Playwright vaxt aşımını daxil edin",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API açarı yaradılmadı.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Qeyd silinmədi",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Şəkil yüklənmədi",
|
||||
"Failed to extract content from the file: {{error}}": "Fayldan məzmun çıxarıla bilmədi: {{error}}",
|
||||
"Failed to extract content from the file.": "Fayldan məzmun çıxarıla bilmədi.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktyabr",
|
||||
"Off": "Bağlı",
|
||||
"Okay, Let's Go!": "Yaxşı, başlayaq!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Çıxış formatı",
|
||||
"Output Format": "Çıxış Formatı",
|
||||
"Overview": "İcmal",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "səhifə",
|
||||
"Page": "Səhifə",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Səhifə rejimi hər səhifə üçün bir sənəd yaradır. Tək rejim isə səhifə sərhədləri arasında daha yaxşı hissələrə ayırma (chunking) üçün bütün səhifələri bir sənəddə birləşdirir.",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Bu seçim modelin cavabında yarada biləcəyi maksimum token sayını təyin edir. Bu limiti artırmaq modelə daha uzun cavablar verməyə imkan verir, lakin faydasız və ya mövzuya aid olmayan məzmunun yaranma ehtimalını da artıra bilər.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Bu seçim kolleksiyadakı bütün mövcud faylları siləcək və onları yeni yüklənmiş fayllarla əvəz edəcək.",
|
||||
"This response was generated by \"{{model}}\"": "Bu cavab \"{{model}}\" tərəfindən yaradılmışdır",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Bu, siləcək:",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Bu, <strong>{{NAME}}</strong> adlı elementi və <strong>onun bütün məzmununu</strong> siləcək.",
|
||||
"This will delete all models including custom models": "Bu, fərdi modellər də daxil olmaqla bütün modelləri siləcək",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Активирането на акаунта е в процес на изчакване",
|
||||
"Accurate information": "Точна информация",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Изисква се действие за съхраняване на дневника на чата",
|
||||
"Actions": "Действия",
|
||||
"Activate": "Активиране",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "Невероятно",
|
||||
"an assistant": "асистент",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Анализирано",
|
||||
"Analyzing...": "Анализиране...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Деактивирано",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Открийте функция",
|
||||
"Discover a model": "Открийте модел",
|
||||
"Discover a prompt": "Откриване на промпт",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Въведете брой стъпки (напр. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Неуспешно създаване на API ключ.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID на OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Октомври",
|
||||
"Off": "Изкл.",
|
||||
"Okay, Let's Go!": "ОК, Нека започваме!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Изходен формат",
|
||||
"Output Format": "",
|
||||
"Overview": "Преглед",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "страница",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Тази опция ще изтрие всички съществуващи файлове в колекцията и ще ги замени с новокачени файлове.",
|
||||
"This response was generated by \"{{model}}\"": "Този отговор беше генериран от \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Това ще изтрие",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Това ще изтрие <strong>{{NAME}}</strong> и <strong>цялото му съдържание</strong>.",
|
||||
"This will delete all models including custom models": "Това ще изтрие всички модели, включително персонализираните модели",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "সঠিক তথ্য",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "চ্যাট লগ সংরক্ষণের জন্য পদক্ষেপ প্রয়োজন",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "একটা এসিস্ট্যান্ট",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "নিষ্ক্রিয়",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "একটি মডেল আবিষ্কার করুন",
|
||||
"Discover a prompt": "একটি প্রম্পট খুঁজে বের করুন",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "ধাপের সংখ্যা দিন (যেমন: 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API Key তৈরি করা যায়নি।",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "অক্টোবর",
|
||||
"Off": "বন্ধ",
|
||||
"Okay, Let's Go!": "ঠিক আছে, চলুন যাই!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"Account Activation Pending": "རྩིས་ཁྲ་སྒུལ་བསྐྱོད་སྒུག་བཞིན་པ།",
|
||||
"Accurate information": "གནས་ཚུལ་ཡང་དག",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "ཁ་བསྡམས་ཟིན་ཐོ་ཉར་ཚགས་ལ་བྱ་བ་དགོས།",
|
||||
"Actions": "བྱ་སྤྱོད།",
|
||||
"Activate": "སྒུལ་བསྐྱོད།",
|
||||
@@ -158,7 +157,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "ངོ་མཚར་ཆེན།",
|
||||
"an assistant": "ལག་རོགས་པ།",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "དབྱེ་ཞིབ་བྱས་པ།",
|
||||
"Analyzing...": "དབྱེ་ཞིབ་བྱེད་བཞིན་པ།...",
|
||||
@@ -578,6 +576,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "ནུས་མེད།",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "ལས་འགན་ཞིག་རྙེད་པ།",
|
||||
"Discover a model": "དཔེ་དབྱིབས་ཤིག་རྙེད་པ།",
|
||||
"Discover a prompt": "འགུལ་སློང་ཞིག་རྙེད་པ།",
|
||||
@@ -767,6 +766,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "གོམ་གྲངས་འཇུག་པ། (དཔེར་ན། ༥༠)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Perplexity API ལྡེ་མིག་འཇུག་པ།",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -897,6 +898,7 @@
|
||||
"Failed to create API Key.": "API ལྡེ་མིག་བཟོ་མ་ཐུབ།",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1451,6 +1453,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "ཟླ་བ་བཅུ་པ།",
|
||||
"Off": "ཁ་རྒྱག་པ།",
|
||||
"Okay, Let's Go!": "འགྲིག་སོང་། འགྲོ།",
|
||||
@@ -1517,6 +1520,8 @@
|
||||
"Output format": "ཐོན་འབྲས་ཀྱི་བཀོད་པ།",
|
||||
"Output Format": "",
|
||||
"Overview": "སྤྱི་མཐོང་།",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "ཤོག་ངོས།",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2017,6 +2022,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "འདེམས་ཀ་འདིས་དཔེ་དབྱིབས་ཀྱིས་དེའི་ལན་ནང་བཟོ་ཐུབ་པའི་ཊོཀ་ཀེན་གྱི་གྲངས་མང་ཤོས་འཇོག་པ། ཚད་བཀག་འདི་མང་དུ་བཏང་ན་དཔེ་དབྱིབས་ཀྱིས་ལན་རིང་བ་སྤྲོད་པར་གནང་བ་སྤྲོད། འོན་ཀྱང་དེས་ཕན་ཐོགས་མེད་པའམ་འབྲེལ་མེད་ཀྱི་ནང་དོན་བཟོ་བའི་ཆགས་ཚུལ་མང་དུ་གཏོང་སྲིད།",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "འདེམས་ཀ་འདིས་བསྡུ་གསོག་ནང་གི་ཡོད་པའི་ཡིག་ཆ་ཡོངས་རྫོགས་བསུབ་ནས་དེ་དག་གསར་དུ་སྤར་བའི་ཡིག་ཆས་ཚབ་བྱེད་ངེས།",
|
||||
"This response was generated by \"{{model}}\"": "ལན་འདི་ \"{{model}}\" ཡིས་བཟོས་པ།",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "འདིས་བསུབ་ངེས།",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "འདིས་ <strong>{{NAME}}</strong> དང་ <strong>དེའི་ནང་དོན་ཡོངས་རྫོགས་</strong> བསུབ་ངེས།",
|
||||
"This will delete all models including custom models": "འདིས་སྲོལ་བཟོས་དཔེ་དབྱིབས་ཚུད་པའི་དཔེ་དབྱིབས་ཡོངས་རྫོགས་བསུབ་ངེས།",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "Tačne informacije",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Potrebna je radnja za pohranu zapisnika razgovora",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "asistent",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "Analiziranje ... ",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Onemogućeno",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "Otkrijte model",
|
||||
"Discover a prompt": "Otkrijte prompt",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Unesite Novu Sifru",
|
||||
"Enter Number of Steps (e.g. 50)": "Unesite broj koraka (npr. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Neuspješno stvaranje API ključa.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktobar",
|
||||
"Off": "Isključeno",
|
||||
"Okay, Let's Go!": "U redu, idemo!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Activació del compte pendent",
|
||||
"Accurate information": "Informació precisa",
|
||||
"Action": "Acció",
|
||||
"Action not found": "Acció no trobada",
|
||||
"Action Required for Chat Log Storage": "Cal una acció per desar el registre del xat",
|
||||
"Actions": "Accions",
|
||||
"Activate": "Activar",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Reproduir sempre un so de notificació",
|
||||
"Amazing": "Al·lucinant",
|
||||
"an assistant": "un assistent",
|
||||
"An error occurred while fetching the explanation": "S'ha produït un error mentre s'obtenia l'explicació",
|
||||
"Analytics": "Analítica",
|
||||
"Analyzed": "Analitzat",
|
||||
"Analyzing...": "Analitzant...",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Deshabilitar l'extracció d'imatges",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Desactiva l'extracció d'imatges del PDF. Si Utilitza LLM està habilitat, les imatges es descriuran automàticament. Per defecte és Fals.",
|
||||
"Disabled": "Deshabilitat",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Descobrir una funció",
|
||||
"Discover a model": "Descobrir un model",
|
||||
"Discover a prompt": "Descobrir una indicació",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Introdueix un nova contrasenya",
|
||||
"Enter Number of Steps (e.g. 50)": "Introdueix el nombre de passos (p. ex. 50)",
|
||||
"Enter Ollama Cloud API Key": "Introdueix la clau API de Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Introdueix la clau API de Perplexity",
|
||||
"Enter Perplexity Search API URL": "Introduïu l'URL de l'API de cerca de Perplexity",
|
||||
"Enter Playwright Timeout": "Introdueix el temps d'espera de Playwright",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "No s'ha pogut crear la clau API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "No s'ha pogut eliminar la nota",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "No s'ha pogut descarregar la imatge",
|
||||
"Failed to extract content from the file: {{error}}": "No s'ha pogut extreure el contingut del fitxer: {{error}}",
|
||||
"Failed to extract content from the file.": "No s'ha pogut extreure el contingut del fitxer",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "OAuth 2.1 (Estàtic)",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Octubre",
|
||||
"Off": "Desactivat",
|
||||
"Okay, Let's Go!": "D'acord, som-hi!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Format de sortida",
|
||||
"Output Format": "Format de sortida",
|
||||
"Overview": "Vista general",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "pàgina",
|
||||
"Page": "Pàgina",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "El mode de pàgina crea un document per pàgina. El mode únic combina totes les pàgines en un sol document per a una millor segmentació entre els límits de les pàgines.",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Aquesta opció estableix el nombre màxim de tokens que el model pot generar en la seva resposta. Augmentar aquest límit permet que el model proporcioni respostes més llargues, però també pot augmentar la probabilitat que es generi contingut poc útil o irrellevant.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Aquesta opció eliminarà tots els fitxers existents de la col·lecció i els substituirà per fitxers recentment penjats.",
|
||||
"This response was generated by \"{{model}}\"": "Aquesta resposta l'ha generat el model \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Això eliminarà",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Això eliminarà <strong>{{NAME}}</strong> i <strong>tots els continguts</strong>.",
|
||||
"This will delete all models including custom models": "Això eliminarà tots els models incloent els personalitzats",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Gikinahanglan ang aksyon aron matipigan ang chat log",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "usa ka katabang",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Gipalong",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "",
|
||||
"Discover a prompt": "Pagkaplag usa ka prompt",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Pagsulod sa gidaghanon sa mga lakang (e.g. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "",
|
||||
"Off": "Napuo",
|
||||
"Okay, Let's Go!": "Okay, lakaw na!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
"Account Activation Pending": "Čeká se na aktivaci účtu",
|
||||
"Accurate information": "Přesné informace",
|
||||
"Action": "Akce",
|
||||
"Action not found": "Akce nenalezena",
|
||||
"Action Required for Chat Log Storage": "Je vyžadována akce pro uložení záznamu chatu",
|
||||
"Actions": "Akce",
|
||||
"Activate": "Aktivovat",
|
||||
@@ -161,7 +160,6 @@
|
||||
"Always Play Notification Sound": "Vždy přehrát zvuk oznámení",
|
||||
"Amazing": "Úžasné",
|
||||
"an assistant": "asistent",
|
||||
"An error occurred while fetching the explanation": "Při načítání vysvětlení došlo k chybě",
|
||||
"Analytics": "Analytika",
|
||||
"Analyzed": "Analyzováno",
|
||||
"Analyzing...": "Analyzuji...",
|
||||
@@ -581,6 +579,7 @@
|
||||
"Disable Image Extraction": "Zakázat extrakci obrázků",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Zakázat extrakci obrázků z PDF. Pokud je povoleno Použít LLM, obrázky budou automaticky opatřeny popisky. Výchozí hodnota je False.",
|
||||
"Disabled": "Zakázáno",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Objevit funkci",
|
||||
"Discover a model": "Objevit model",
|
||||
"Discover a prompt": "Objevit instrukci",
|
||||
@@ -770,6 +769,8 @@
|
||||
"Enter New Password": "Zadejte nové heslo",
|
||||
"Enter Number of Steps (e.g. 50)": "Zadejte počet kroků (např. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Zadejte API klíč pro Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "Zadejte časový limit pro Playwright",
|
||||
@@ -900,6 +901,7 @@
|
||||
"Failed to create API Key.": "Nepodařilo se vytvořit API klíč.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Nepodařilo se smazat poznámku",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "Nepodařilo se extrahovat obsah ze souboru: {{error}}",
|
||||
"Failed to extract content from the file.": "Nepodařilo se extrahovat obsah ze souboru.",
|
||||
@@ -1454,6 +1456,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Říjen",
|
||||
"Off": "Vypnuto",
|
||||
"Okay, Let's Go!": "Dobře, jdeme na to!",
|
||||
@@ -1520,6 +1523,8 @@
|
||||
"Output format": "Formát výstupu",
|
||||
"Output Format": "Formát výstupu",
|
||||
"Overview": "Přehled",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "stránka",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2026,6 +2031,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Tato možnost nastavuje maximální počet tokenů, které může model vygenerovat ve své odpovědi. Zvýšení tohoto limitu umožňuje modelu poskytovat delší odpovědi, ale může také zvýšit pravděpodobnost generování neužitečného nebo irelevantního obsahu.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Tato volba smaže všechny existující soubory v kolekci a nahradí je nově nahranými soubory.",
|
||||
"This response was generated by \"{{model}}\"": "Tato odpověď byla vygenerována modelem \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Tím se smaže",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tím se smaže <strong>{{NAME}}</strong> a <strong>veškerý jeho obsah</strong>.",
|
||||
"This will delete all models including custom models": "Tím se smažou všechny modely včetně vlastních modelů",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Aktivering af profil afventer",
|
||||
"Accurate information": "Profilinformation",
|
||||
"Action": "Handling",
|
||||
"Action not found": "Handling ikke fundet",
|
||||
"Action Required for Chat Log Storage": "Handling påkrævet for lagring af chatlog",
|
||||
"Actions": "Handlinger",
|
||||
"Activate": "Aktiver",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Afspil altid notifikationslyde",
|
||||
"Amazing": "Fantastisk",
|
||||
"an assistant": "en assistent",
|
||||
"An error occurred while fetching the explanation": "En fejl opstod under hentning af forklaringen",
|
||||
"Analytics": "Analytics",
|
||||
"Analyzed": "Analyseret",
|
||||
"Analyzing...": "Analyserer...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Deaktiver billedudtrækning",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Deaktiver billedudtrækning fra PDF'en. Hvis Use LLM er aktiveret, vil billeder automatisk få undertekster. Standard er False.",
|
||||
"Disabled": "Deaktiveret",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Find en funktion",
|
||||
"Discover a model": "Find en model",
|
||||
"Discover a prompt": "Find en prompt",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Indtast ny adgangskode",
|
||||
"Enter Number of Steps (e.g. 50)": "Indtast antal trin (f.eks. 50)",
|
||||
"Enter Ollama Cloud API Key": "Indtast Ollama Cloud API nøgle",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Indtast Perplexity API nøgle",
|
||||
"Enter Perplexity Search API URL": "Indtast Perplexity Search API URL",
|
||||
"Enter Playwright Timeout": "Indtast Playwright timeout",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Kunne ikke oprette API-nøgle.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Kunne ikke slette note",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "Kunne ikke udtrække indhold fra filen: {{error}}",
|
||||
"Failed to extract content from the file.": "Kunne ikke udtrække indhold fra filen.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth-ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktober",
|
||||
"Off": "Fra",
|
||||
"Okay, Let's Go!": "Okay, lad os komme i gang!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Outputformat",
|
||||
"Output Format": "Output format",
|
||||
"Overview": "Oversigt",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "side",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Denne indstilling sætter det maksimale antal tokens modellen kan generere i sit svar. At øge denne grænse tillader modellen at give længere svar, men det kan også øge sandsynligheden for at unyttigt eller irrelevant indhold genereres.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Denne indstilling sletter alle eksisterende filer i samlingen og erstatter dem med nyligt uploadede filer.",
|
||||
"This response was generated by \"{{model}}\"": "Dette svar blev genereret af \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Dette vil slette",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dette vil slette <strong>{{NAME}}</strong> og <strong>alt dens indhold</strong>.",
|
||||
"This will delete all models including custom models": "Dette vil slette alle modeller, inklusive brugerdefinerede modeller",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Kontoaktivierung ausstehend",
|
||||
"Accurate information": "Präzise Informationen",
|
||||
"Action": "Aktion",
|
||||
"Action not found": "Aktion nicht gefunden",
|
||||
"Action Required for Chat Log Storage": "Handlung erforderlich: Speicherung des Chat-Protokolls",
|
||||
"Actions": "Aktionen",
|
||||
"Activate": "Aktivieren",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Benachrichtigungston immer abspielen",
|
||||
"Amazing": "Fantastisch",
|
||||
"an assistant": "ein Assistent",
|
||||
"An error occurred while fetching the explanation": "Beim Abrufen der Erklärung ist ein Fehler aufgetreten",
|
||||
"Analytics": "Analyse",
|
||||
"Analyzed": "Analysiert",
|
||||
"Analyzing...": "Analysiere...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Bildextraktion deaktivieren",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Deaktiviert Bildextraktion aus PDFs. Wenn 'LLM verwenden' aktiv ist, werden Bilder automatisch beschriftet. Standard: False.",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Funktion entdecken",
|
||||
"Discover a model": "Modell entdecken",
|
||||
"Discover a prompt": "Prompt entdecken",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Neues Passwort eingeben",
|
||||
"Enter Number of Steps (e.g. 50)": "Anzahl der Schritte eingeben (z. B. 50)",
|
||||
"Enter Ollama Cloud API Key": "Ollama Cloud API-Schlüssel eingeben",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Perplexity API-Schlüssel eingeben",
|
||||
"Enter Perplexity Search API URL": "Perplexity Search API-URL eingeben",
|
||||
"Enter Playwright Timeout": "Playwright-Timeout eingeben",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API-Schlüssel konnte nicht erstellt werden.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Notiz konnte nicht gelöscht werden",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Bild konnte nicht heruntergeladen werden",
|
||||
"Failed to extract content from the file: {{error}}": "Inhaltsextraktion fehlgeschlagen: {{error}}",
|
||||
"Failed to extract content from the file.": "Inhaltsextraktion fehlgeschlagen.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "OAuth 2.1 (Statisch)",
|
||||
"OAuth ID": "OAuth-ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktober",
|
||||
"Off": "Aus",
|
||||
"Okay, Let's Go!": "Okay, los geht's!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Ausgabeformat",
|
||||
"Output Format": "Ausgabeformat",
|
||||
"Overview": "Übersicht",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "Seite",
|
||||
"Page": "Seite",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Der Seitenmodus erstellt ein Dokument pro Seite. Der Einzelmodus fasst alle Seiten zu einem Dokument zusammen, um besser über Seitenumbrüche hinweg zu chunking/segmentieren.",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Diese Option legt die maximale Anzahl von Token fest, die das Modell generieren darf. Ein höheres Limit ermöglicht längere Antworten, kann aber auch die Wahrscheinlichkeit für irrelevante Inhalte erhöhen.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Diese Option löscht alle vorhandenen Dateien in der Sammlung und ersetzt sie durch die neu hochgeladenen Dateien.",
|
||||
"This response was generated by \"{{model}}\"": "Diese Antwort wurde von \"{{model}}\" generiert",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Dies löscht",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dies löscht <strong>{{NAME}}</strong> und <strong>alle Inhalte</strong>.",
|
||||
"This will delete all models including custom models": "Dies löscht alle Modelle, einschließlich benutzerdefinierter Modelle",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Much action require for chat log storage",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "such assistant",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Disabled sad",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "",
|
||||
"Discover a prompt": "Discover a prompt",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Enter Number of Steps (e.g. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "",
|
||||
"Off": "Off",
|
||||
"Okay, Let's Go!": "Okay, Let's Go!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Ενεργοποίηση Λογαριασμού Εκκρεμεί",
|
||||
"Accurate information": "Ακριβείς πληροφορίες",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Απαιτείται ενέργεια για την αποθήκευση του αρχείου συνομιλίας",
|
||||
"Actions": "Ενέργειες",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Πάντα να αναπαράγετε ο ήχος ειδοποίησης",
|
||||
"Amazing": "Καταπληκτικό",
|
||||
"an assistant": "ένας βοηθός",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "Αναλυτικά",
|
||||
"Analyzed": "Αναλυμένα",
|
||||
"Analyzing...": "Ανάλυση...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Απενεργοποιημένο",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Ανακάλυψη λειτουργίας",
|
||||
"Discover a model": "Ανακάλυψη μοντέλου",
|
||||
"Discover a prompt": "Ανακάλυψη προτροπής",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Εισάγετε νέο κωδικό",
|
||||
"Enter Number of Steps (e.g. 50)": "Εισάγετε τον Αριθμό Βημάτων (π.χ. 50)",
|
||||
"Enter Ollama Cloud API Key": "Εισάγετε το Κλειδί API Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Εισάγετε το Κλειδί API Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "Εισάγετε το χρονικό όριο του Playwright",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Αποτυχία δημιουργίας Κλειδιού API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Αποτυχία διαγραφής σημειώσεως",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Οκτώβριος",
|
||||
"Off": "Ανενεργό",
|
||||
"Okay, Let's Go!": "Εντάξει, Πάμε!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Μορφή εξόδου",
|
||||
"Output Format": "Μορφή Εξόδου",
|
||||
"Overview": "Επισκόπηση",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "σελίδα",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Αυτή η επιλογή θα διαγράψει όλα τα υπάρχοντα αρχεία στη συλλογή και θα τα αντικαταστήσει με νέα ανεβασμένα αρχεία.",
|
||||
"This response was generated by \"{{model}}\"": "Αυτή η απάντηση δημιουργήθηκε από \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Αυτό θα διαγράψει",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Αυτό θα διαγράψει το <strong>{{NAME}}</strong> και <strong>όλο το περιεχόμενό του</strong>.",
|
||||
"This will delete all models including custom models": "Αυτό θα διαγράψει όλα τα μοντέλα, συμπεριλαμβανομένων των προσαρμοσμένων μοντέλων",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Analysed",
|
||||
"Analyzing...": "Analysing",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "",
|
||||
"Discover a prompt": "",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "",
|
||||
"Off": "",
|
||||
"Okay, Let's Go!": "",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "",
|
||||
"Discover a prompt": "",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "",
|
||||
"Off": "",
|
||||
"Okay, Let's Go!": "",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Activación de cuenta Pendiente",
|
||||
"Accurate information": "Información precisa",
|
||||
"Action": "Acción",
|
||||
"Action not found": "Acción no encontrada",
|
||||
"Action Required for Chat Log Storage": "Se requiere acción para almacenar el registro del chat",
|
||||
"Actions": "Acciones",
|
||||
"Activate": "Activar",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Reproducir Siempre Sonido de Notificación",
|
||||
"Amazing": "Emocionante",
|
||||
"an assistant": "un asistente",
|
||||
"An error occurred while fetching the explanation": "Se ha producido un error al obtener la explicación",
|
||||
"Analytics": "Analíticas",
|
||||
"Analyzed": "Analizado",
|
||||
"Analyzing...": "Analizando..",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Deshabilitar Extracción de Imágenes",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Desabilita la extracción de imágenes del pdf. Si está habilitado Usar LLM las imágenes se capturan automáticamente. Por defecto el valor es Falso (las imágenes se extraen).",
|
||||
"Disabled": "Deshabilitado",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Descubrir Funciónes",
|
||||
"Discover a model": "Descubrir Modelos",
|
||||
"Discover a prompt": "Descubrir Indicadores",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Ingresar Contraseña Nueva",
|
||||
"Enter Number of Steps (e.g. 50)": "Ingresar Número de Pasos (p.ej., 50)",
|
||||
"Enter Ollama Cloud API Key": "Ingresar Clave API de Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Ingresar Clave API de Perplexity",
|
||||
"Enter Perplexity Search API URL": "Ingresar URL API para la Búsqueda de Perplexity",
|
||||
"Enter Playwright Timeout": "Ingresar límite de tiempo de espera de Playwright",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Fallo al crear la Clave API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Fallo al eliminar nota",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Fallo al descargar imagen",
|
||||
"Failed to extract content from the file: {{error}}": "Fallo al extraer el contenido del archivo: {{error}}",
|
||||
"Failed to extract content from the file.": "Fallo al extraer el contenido del archivo.",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "OAuth 2.1 (Estático)",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Octubre",
|
||||
"Off": "Desactivado",
|
||||
"Okay, Let's Go!": "Vale, ¡Vamos!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Formato de salida",
|
||||
"Output Format": "Formato de Salida",
|
||||
"Overview": "Vista General",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "página",
|
||||
"Page": "Página",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "El modo Página crea un documento por página. El modo Individual combina todas las páginas en un solo documento para una mejor segmentación entre páginas.",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Esta opción establece el número máximo de tokens que el modelo puede generar en sus respuestas. Aumentar este límite permite al modelo proporcionar respuestas más largas, pero también puede aumentar la probabilidad de que se genere contenido inútil o irrelevante.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Esta opción eliminará todos los archivos existentes en la colección y los reemplazará con los nuevos archivos subidos.",
|
||||
"This response was generated by \"{{model}}\"": "Esta respuesta fue generada por \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Esto eliminará",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Esto eliminará <strong>{{NAME}}</strong> y <strong>todo su contenido</strong>.",
|
||||
"This will delete all models including custom models": "Esto eliminará todos los modelos, incluidos los modelos personalizados",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Konto aktiveerimine ootel",
|
||||
"Accurate information": "Täpne informatsioon",
|
||||
"Action": "Toiming",
|
||||
"Action not found": "Toimingut ei leitud",
|
||||
"Action Required for Chat Log Storage": "Vestluse logi salvestamiseks on vaja toimingut",
|
||||
"Actions": "Toimingud",
|
||||
"Activate": "Aktiveeri",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Esita teavitusheli alati",
|
||||
"Amazing": "Suurepärane",
|
||||
"an assistant": "assistent",
|
||||
"An error occurred while fetching the explanation": "Selgituse toomisel tekkis viga",
|
||||
"Analytics": "Analüütika",
|
||||
"Analyzed": "Analüüsitud",
|
||||
"Analyzing...": "Analüüsimine...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Keela piltide väljavõte",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Keela piltide eraldamine PDF-ist. Kui 'Kasuta LLM-i' on lubatud, lisatakse piltidele automaatselt pealdised. Vaikimisi välja lülitatud.",
|
||||
"Disabled": "Keelatud",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Avasta funktsioon",
|
||||
"Discover a model": "Avasta mudel",
|
||||
"Discover a prompt": "Avasta sisend",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Sisestage uus parool",
|
||||
"Enter Number of Steps (e.g. 50)": "Sisestage sammude arv (nt 50)",
|
||||
"Enter Ollama Cloud API Key": "Sisestage Ollama Cloud API võti",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Sisestage Perplexity API võti",
|
||||
"Enter Perplexity Search API URL": "Sisestage Perplexity Search API URL",
|
||||
"Enter Playwright Timeout": "Sisestage Playwright aegumine",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API võtme loomine ebaõnnestus.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Märkme kustutamine ebaõnnestus",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Pildi allalaadimine ebaõnnestus",
|
||||
"Failed to extract content from the file: {{error}}": "Failist sisu eraldamine ebaõnnestus: {{error}}",
|
||||
"Failed to extract content from the file.": "Failist sisu eraldamine ebaõnnestus.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktoober",
|
||||
"Off": "Väljas",
|
||||
"Okay, Let's Go!": "Hea küll, lähme!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Väljundformaat",
|
||||
"Output Format": "Väljundformaat",
|
||||
"Overview": "Ülevaade",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "leht",
|
||||
"Page": "Leht",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Lehe režiim loob ühe dokumendi lehe kohta. Üksikrežiim ühendab kõik lehed üheks dokumendiks parema tükeldamise jaoks üle lehepiiride.",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "See valik määrab maksimaalse tokenite arvu, mida mudel saab oma vastuses genereerida. Selle piirmäära suurendamine võimaldab mudelil anda pikemaid vastuseid, kuid võib suurendada ka ebavajaliku või ebaolulise sisu genereerimise tõenäosust.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "See valik kustutab kõik olemasolevad failid kogust ja asendab need äsja üleslaaditud failidega.",
|
||||
"This response was generated by \"{{model}}\"": "Selle vastuse genereeris \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "See kustutab",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "See kustutab <strong>{{NAME}}</strong> ja <strong>kogu selle sisu</strong>.",
|
||||
"This will delete all models including custom models": "See kustutab kõik mudelid, sealhulgas kohandatud mudelid",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Kontuaren Aktibazioa Zain",
|
||||
"Accurate information": "Informazio zehatza",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Txataren erregistroa gordetzeko ekintza behar da",
|
||||
"Actions": "Ekintzak",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "Harrigarria",
|
||||
"an assistant": "laguntzaile bat",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Desgaituta",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Aurkitu funtzio bat",
|
||||
"Discover a model": "Aurkitu eredu bat",
|
||||
"Discover a prompt": "Aurkitu prompt bat",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Sartu Urrats Kopurua (adib. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Huts egin du API Gakoa sortzean.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Urria",
|
||||
"Off": "Itzalita",
|
||||
"Okay, Let's Go!": "Ados, Goazen!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Irteera formatua",
|
||||
"Output Format": "",
|
||||
"Overview": "Ikuspegi orokorra",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "orria",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Aukera honek bilduman dauden fitxategi guztiak ezabatuko ditu eta berriki kargatutako fitxategiekin ordezkatuko ditu.",
|
||||
"This response was generated by \"{{model}}\"": "Erantzun hau \"{{model}}\" modeloak sortu du",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Honek ezabatuko du",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Honek <strong>{{NAME}}</strong> eta <strong>bere eduki guztiak</strong> ezabatuko ditu.",
|
||||
"This will delete all models including custom models": "Honek modelo guztiak ezabatuko ditu, modelo pertsonalizatuak barne",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "فعال\u200cسازی حساب در حال انتظار",
|
||||
"Accurate information": "اطلاعات دقیق",
|
||||
"Action": "عملیات",
|
||||
"Action not found": "عملیات یافت نشد",
|
||||
"Action Required for Chat Log Storage": "برای ذخیره گزارش گفت\u200cوگو اقدام لازم است",
|
||||
"Actions": "کنش\u200cها",
|
||||
"Activate": "فعال کردن",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "همیشه صدای اعلان پخش شود",
|
||||
"Amazing": "شگفت\u200cانگیز",
|
||||
"an assistant": "یک دستیار",
|
||||
"An error occurred while fetching the explanation": "هنگام واکشی توضیح خطایی رخ داد",
|
||||
"Analytics": "تحلیل و بررسی",
|
||||
"Analyzed": "تحلیل شده",
|
||||
"Analyzing...": "در حال تحلیل...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "غیرفعال کردن استخراج تصویر",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "غیرفعال کردن استخراج تصویر از PDF. اگر «استفاده از LLM» فعال باشد، تصاویر به\u200cطور خودکار زیرنویس خواهند شد. پیش\u200cفرض: False.",
|
||||
"Disabled": "غیرفعال",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "کشف یک تابع",
|
||||
"Discover a model": "کشف یک مدل",
|
||||
"Discover a prompt": "یک اعلان را کشف کنید",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "رمز عبور جدید را وارد کنید",
|
||||
"Enter Number of Steps (e.g. 50)": "تعداد گام\u200cها را وارد کنید (مثال: 50)",
|
||||
"Enter Ollama Cloud API Key": "کلید API ابری اُلاما را وارد کنید",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "کلید API پرپلکسیتی را وارد کنید",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "مهلت پلی\u200cرایت را وارد کنید",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "ایجاد کلید API با خطا مواجه شد.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "حذف یادداشت ناموفق بود",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "استخراج محتوا از فایل ناموفق بود: {{error}}",
|
||||
"Failed to extract content from the file.": "استخراج محتوا از فایل ناموفق بود.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "شناسه OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "اکتبر",
|
||||
"Off": "خاموش",
|
||||
"Okay, Let's Go!": "باشه، بزن بریم!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "قالب خروجی",
|
||||
"Output Format": "قالب خروجی",
|
||||
"Overview": "نمای کلی",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "صفحه",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "این گزینه حداکثر تعداد توکن\u200cهایی را که مدل می\u200cتواند در پاسخ خود تولید کند تنظیم می\u200cکند. افزایش این محدودیت به مدل اجازه می\u200cدهد پاسخ\u200cهای طولانی\u200cتری ارائه دهد، اما ممکن است احتمال تولید محتوای بی\u200cفایده یا نامربوط را نیز افزایش دهد.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "این گزینه تمام فایل\u200cهای موجود در مجموعه را حذف کرده و با فایل\u200cهای جدید آپلود شده جایگزین می\u200cکند.",
|
||||
"This response was generated by \"{{model}}\"": "این پاسخ توسط \"{{model}}\" تولید شده است",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "این حذف خواهد شد",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "این <strong>{{NAME}}</strong> و <strong>تمام محتویات آن</strong> را حذف خواهد کرد.",
|
||||
"This will delete all models including custom models": "این همه مدل\u200cها از جمله مدل\u200cهای سفارشی را حذف خواهد کرد",
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
"{{user}}'s Chats": "{{user}}:n keskustelut",
|
||||
"{{webUIName}} Backend Required": "{{webUIName}}-backend vaaditaan",
|
||||
"*Prompt node ID(s) are required for image generation": "Kuvan luomiseen vaaditaan kehote-solmun ID(t)",
|
||||
"1 hour before": "",
|
||||
"1 hour before": "1 tunti ennen",
|
||||
"1 Source": "1 lähde",
|
||||
"10 minutes before": "",
|
||||
"15 minutes before": "",
|
||||
"10 minutes before": "10 minuuttia ennen",
|
||||
"15 minutes before": "15 minuuttia ennen",
|
||||
"1m_time_ago": "",
|
||||
"30 minutes before": "",
|
||||
"5 minutes before": "",
|
||||
"30 minutes before": "30 minuuttia ennen",
|
||||
"5 minutes before": "5 minuuttia ennen",
|
||||
"A collaboration channel where people join as members": "Yhteistyökanava, johon ihmiset liittyvät jäseninä",
|
||||
"A discussion channel where access is controlled by groups and permissions": "Keskustelukanava, johon pääsyä rajoitetaan ryhmillä ja käyttöoikeuksilla",
|
||||
"A new version (v{{LATEST_VERSION}}) is now available.": "Uusi versio (v{{LATEST_VERSION}}) on nyt saatavilla.",
|
||||
@@ -52,13 +52,12 @@
|
||||
"Access Control": "Käyttöoikeuksien hallinta",
|
||||
"Access Grants": "Käyttöoikeudet",
|
||||
"Access List": "Pääsylista",
|
||||
"Access updated": "",
|
||||
"Access updated": "Käyttöoikeus päivitetty",
|
||||
"Accessible to all users": "Käytettävissä kaikille käyttäjille",
|
||||
"Account": "Tili",
|
||||
"Account Activation Pending": "Tilin aktivointi odottaa",
|
||||
"Accurate information": "Tarkkaa tietoa",
|
||||
"Action": "Toiminto",
|
||||
"Action not found": "Toimintoa ei löytynyt",
|
||||
"Action Required for Chat Log Storage": "Toiminto vaaditaan keskustelulokin tallentamiseksi",
|
||||
"Actions": "Toiminnot",
|
||||
"Activate": "Aktivoi",
|
||||
@@ -78,11 +77,11 @@
|
||||
"Add content here": "Lisää sisältöä tähän",
|
||||
"Add Custom Parameter": "Lisää mukautettu parametri",
|
||||
"Add Custom Prompt": "Lisää mukautettu kehote",
|
||||
"Add description": "",
|
||||
"Add description": "Lisää kuvaus",
|
||||
"Add Details": "Lisää yksityiskohtia",
|
||||
"Add Files": "Lisää tiedostoja",
|
||||
"Add Image": "Lisää kuva",
|
||||
"Add location": "",
|
||||
"Add location": "Lisää sijainti",
|
||||
"Add Member": "Lisää jäsen",
|
||||
"Add Members": "Lisää jäseniä",
|
||||
"Add Memory": "Lisää muistiin",
|
||||
@@ -99,7 +98,7 @@
|
||||
"Add webpage": "Lisää verkkosivu",
|
||||
"Add your Open Terminal URL and API key in Settings → Integrations.": "Lisää Open Terminal verkko-osoite ja API-avain Asetukset → Integraatiot",
|
||||
"Additional Config": "Lisäasetukset",
|
||||
"Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, '{\"key\": \"value\"}'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level": "",
|
||||
"Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, '{\"key\": \"value\"}'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level": "Lisäasetukset merkille. Tämä tulisi olla JSON-merkkijono, jossa on avain-arvo-pareja. Esimerkiksi '{\"key\": \"value\"}'. Tuetut avaimet sisältävät: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level.",
|
||||
"Additional feedback comments": "Lisäpalautteen kommentit",
|
||||
"Additional Parameters": "Lisäparametrit",
|
||||
"Adds filenames, titles, sections, and snippets into the BM25 text to improve lexical recall.": "Lisää tiedostonimiä, otsikoita, osioita ja katkelmia BM25-tekstiin leksikaalisen muistamisen parantamiseksi.",
|
||||
@@ -118,7 +117,7 @@
|
||||
"AI": "AI",
|
||||
"All": "Kaikki",
|
||||
"All chats have been unarchived.": "Kaikki keskustelut poistettu arkistosta.",
|
||||
"All day": "",
|
||||
"All day": "Koko päivä",
|
||||
"All models are now hidden": "Kaikki mallit ovat nyt piilotettu",
|
||||
"All models are now visible": "Kaikki mallit ovat nyt näkyvissä",
|
||||
"All models deleted successfully": "Kaikki mallit poistettu onnistuneesti",
|
||||
@@ -152,14 +151,13 @@
|
||||
"Allowed File Extensions": "Hyväksytyt tiedostomuodot",
|
||||
"Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.": "Hyväksyty tiedostomuodot. Erittele tiedostomuodot pilkulla. Jätä tyhjäksi kaikille tiedostomuodoille.",
|
||||
"Already have an account?": "Onko sinulla jo tili?",
|
||||
"Alternative to the top_p, and aims to ensure a balance of quality and variety. The parameter p represents the minimum probability for a token to be considered, relative to the probability of the most likely token. For example, with p=0.05 and the most likely token having a probability of 0.9, logits with a value less than 0.045 are filtered out.": "",
|
||||
"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.": "Vaihtoehto top_p:lle, ja sen tavoitteena on varmistaa laadun ja monimuotoisuuden tasapaino. Parametri p edustaa vähimmäistodennäköisyyttä, jonka on oltava tokenin huomioimiseksi suhteessa todennäköisimmän tokenin todennäköisyyteen. Esimerkiksi, kun p=0.05 ja todennäköisin tokenilla on todennäköisyys 0.9, logitit, joiden arvo on alle 0.045, suodatetaan pois.",
|
||||
"Always": "Aina",
|
||||
"Always Collapse Code Blocks": "Pienennä aina koodilohkot",
|
||||
"Always Expand Details": "Laajenna aina tiedot",
|
||||
"Always Play Notification Sound": "Toista aina ilmoitusääni",
|
||||
"Amazing": "Hämmästyttävä",
|
||||
"an assistant": "avustaja",
|
||||
"An error occurred while fetching the explanation": "Tapahtui virhe hakiessa selitystä",
|
||||
"Analytics": "Analytiikka",
|
||||
"Analyzed": "Analysoitu",
|
||||
"Analyzing...": "Analysoidaan..",
|
||||
@@ -191,7 +189,7 @@
|
||||
"Are you sure you want to archive all chats? This action cannot be undone.": "Haluatko varmasti arkistoida kaikki keskustelut? Tätä toimintoa ei voi peruuttaa.",
|
||||
"Are you sure you want to clear all memories? This action cannot be undone.": "Haluatko varmasti tyhjentää kaikki muistot? Tätä toimintoa ei voi peruuttaa.",
|
||||
"Are you sure you want to delete \"{{NAME}}\"?": "Haluatko varmasti poistaa \"{{NAME}}\"?",
|
||||
"Are you sure you want to delete **{{modelName}}**?": "",
|
||||
"Are you sure you want to delete **{{modelName}}**?": "Haluatko varmasti poistaa **{{modelName}}**?",
|
||||
"Are you sure you want to delete all chats? This action cannot be undone.": "Haluatko varmasti poistaa kaikki keskustelut? Tätä toimintoa ei voi peruuttaa.",
|
||||
"Are you sure you want to delete this channel?": "Haluatko varmasti poistaa tämän kanavan?",
|
||||
"Are you sure you want to delete this connection? This action cannot be undone.": "Haluatko varmasti poistaa yhteyden? Tätä toimintoa ei voi peruuttaa.",
|
||||
@@ -207,9 +205,9 @@
|
||||
"Ask a question": "Kysy kysymys",
|
||||
"Assistant": "Avustaja",
|
||||
"Async Embedding Processing": "Asynkroninen upotus prosessointi",
|
||||
"At time of event": "",
|
||||
"At time of event": "Tapahtumahetkellä",
|
||||
"Attach File From Knowledge": "Liitä tiedosto tietämyksestä",
|
||||
"Attach Files": "",
|
||||
"Attach Files": "Liitä tiedostoja",
|
||||
"Attach Knowledge": "Liitä tietoa",
|
||||
"Attach Notes": "Liitä muistiinpanoja",
|
||||
"Attach Webpage": "Liitä verkkosivu",
|
||||
@@ -232,13 +230,13 @@
|
||||
"AUTOMATIC1111 Base URL": "AUTOMATIC1111 verkko-osoite",
|
||||
"AUTOMATIC1111 Base URL is required.": "AUTOMATIC1111 verkko-osoite vaaditaan.",
|
||||
"Automatically inject system tools in native function calling mode (e.g., timestamps, memory, chat history, notes, etc.)": "Lisää järjestelmätyökaluja automaattisesti natiivissa toimintokutsutilassa (esim. aikaleimat, muisti, keskusteluhistoria, muistiinpanot jne.)",
|
||||
"Automation": "",
|
||||
"Automation created": "",
|
||||
"Automation Name": "",
|
||||
"Automation title": "",
|
||||
"Automation triggered": "",
|
||||
"Automation updated": "",
|
||||
"Automations": "",
|
||||
"Automation": "Automaatio",
|
||||
"Automation created": "Automaatio luotu",
|
||||
"Automation Name": "Automaation nimi",
|
||||
"Automation title": "Automaation otsikko",
|
||||
"Automation triggered": "Automaatio laukaistu",
|
||||
"Automation updated": "Automaatio päivitetty",
|
||||
"Automations": "Automaatiot",
|
||||
"Available list": "Käytettävissä oleva luettelo",
|
||||
"Available models": "Käytettävissä olevat mallit",
|
||||
"Available Tools": "Käytettävissä olevat työkalut",
|
||||
@@ -269,7 +267,7 @@
|
||||
"Boosting or penalizing specific tokens for constrained responses. Bias values will be clamped between -100 and 100 (inclusive). (Default: none)": "Tiettyjen tokeneiden tehostaminen tai rankaiseminen rajoitetuista vastauksista. Poikkeaman arvot rajoitetaan välille -100 ja 100 (mukaan lukien). (Oletus: ei mitään)",
|
||||
"Brave": "",
|
||||
"Brave Search API Key": "Brave Search API -avain",
|
||||
"Break down complex requests into trackable steps": "",
|
||||
"Break down complex requests into trackable steps": "Pilko monimutkaiset pyynnöt seurattaviin vaiheisiin",
|
||||
"Browse and query knowledge bases": "Selaa ja hae tietokannoista",
|
||||
"Builtin Tools": "Sisäänrakennetut työkalut",
|
||||
"Bullet List": "Luettelo",
|
||||
@@ -282,8 +280,8 @@
|
||||
"Bypass Web Loader": "Ohita verkkolataaja",
|
||||
"Cache Base Model List": "Malli luettelon välimuisti",
|
||||
"Calendar": "Kalenteri",
|
||||
"Calendar deleted": "",
|
||||
"Calendars": "",
|
||||
"Calendar deleted": "Kalenteri poistettu",
|
||||
"Calendars": "Kalenterit",
|
||||
"Call": "Puhelu",
|
||||
"Call feature is not supported when using Web STT engine": "Puhelutoimintoa ei tueta käytettäessä web-puheentunnistusmoottoria",
|
||||
"Camera": "Kamera",
|
||||
@@ -405,7 +403,7 @@
|
||||
"Concurrent Requests": "Samanaikaiset pyynnöt",
|
||||
"Config": "Määritykset",
|
||||
"Config imported successfully": "Määritysten tuonti onnistui",
|
||||
"Configuration": "",
|
||||
"Configuration": "Määritys",
|
||||
"Configure": "Määritä",
|
||||
"Confirm": "Vahvista",
|
||||
"Confirm Password": "Vahvista salasana",
|
||||
@@ -420,7 +418,7 @@
|
||||
"Connect to your own OpenAPI compatible external tool servers.": "Yhdistä omat ulkopuoliset OpenAPI yhteensopivat työkalu palvelimet.",
|
||||
"Connected ({{type}})": "Yhdistetty ({{type}})",
|
||||
"Connection failed": "Yhteys epäonnistui",
|
||||
"Connection lost. Reconnecting...": "",
|
||||
"Connection lost. Reconnecting...": "Yhteys katkaistu. Yhdistetään uudelleen...",
|
||||
"Connection successful": "Yhteys onnistui",
|
||||
"Connection Type": "Yhteystyyppi",
|
||||
"Connections": "Yhteydet",
|
||||
@@ -452,7 +450,7 @@
|
||||
"Copy Last Response": "Kopioi viimeisin vastaus",
|
||||
"Copy link": "Kopioi linkki",
|
||||
"Copy Link": "Kopioi linkki",
|
||||
"Copy Path": "",
|
||||
"Copy Path": "Kopioi polku",
|
||||
"Copy Prompt": "Kopioi kehote",
|
||||
"Copy Share Link": "Kopioi jakolinkki",
|
||||
"Copy to clipboard": "Kopioi leikepöydälle",
|
||||
@@ -468,7 +466,7 @@
|
||||
"Create a new note": "Luo uusi muistiinpano",
|
||||
"Create Account": "Luo tili",
|
||||
"Create Admin Account": "Luo ylläpitäjätili",
|
||||
"Create and manage scheduled automations": "",
|
||||
"Create and manage scheduled automations": "Luo ja hallinnoi aikataulutettuja automaatioita",
|
||||
"Create Channel": "Luo kanava",
|
||||
"Create Folder": "Luo kansio",
|
||||
"Create Image": "Luo kuva",
|
||||
@@ -478,7 +476,7 @@
|
||||
"Create new secret key": "Luo uusi salainen avain",
|
||||
"Create note": "Luo muistiinpano",
|
||||
"Create Note": "Luo muistiinpano",
|
||||
"Create scheduled prompts that run automatically on a recurring basis.": "",
|
||||
"Create scheduled prompts that run automatically on a recurring basis.": "Luo aikataulutettuja kehotteita, jotka suoritetaan automaattisesti toistuvasti.",
|
||||
"Create your first note by clicking on the plus button below.": "Luo ensimmäinen muistiinpanosi painamalla alla olevaa plus painiketta.",
|
||||
"Created at": "Luotu",
|
||||
"Created At": "Luotu",
|
||||
@@ -494,14 +492,14 @@
|
||||
"Custom Gender": "Muu sukupuoli",
|
||||
"Custom Parameter Name": "Mukautetun parametrin nimi",
|
||||
"Custom Parameter Value": "Mukautetun parametrin arvo",
|
||||
"Daily": "",
|
||||
"Daily": "Päivittäin",
|
||||
"Daily Messages": "Päivittäiset viestit",
|
||||
"Danger Zone": "Vaara-alue",
|
||||
"Dark": "Tumma",
|
||||
"Data Controls": "Datan hallinta",
|
||||
"Database": "Tietokanta",
|
||||
"Datalab Marker API": "Datalab Marker API",
|
||||
"Day": "",
|
||||
"Day": "Päivä",
|
||||
"DD/MM/YYYY": "DD/MM/YYYY",
|
||||
"DDGS Backend": "DDGS-taustajärjestelmä",
|
||||
"December": "joulukuu",
|
||||
@@ -532,12 +530,12 @@
|
||||
"Delete All": "Poista kaikki",
|
||||
"Delete All Chats": "Poista kaikki keskustelut",
|
||||
"Delete all contents inside this folder": "Poista kaikki sisällöt tästä kansiosta",
|
||||
"Delete automation?": "",
|
||||
"Delete calendar": "",
|
||||
"Delete Calendar": "",
|
||||
"Delete automation?": "Poista automaatio?",
|
||||
"Delete calendar": "Poista kalenteri",
|
||||
"Delete Calendar": "Poista kalenteri",
|
||||
"Delete Chat": "Poista keskustelu",
|
||||
"Delete chat?": "Haluatko varmasti poistaa tämän keskustelun?",
|
||||
"Delete Event": "",
|
||||
"Delete Event": "Poista tapahtuma?",
|
||||
"Delete File": "Poista tiedosto",
|
||||
"Delete folder?": "Haluatko varmasti poistaa tämän kansion?",
|
||||
"Delete function?": "Haluatko varmasti poistaa tämän toiminnon?",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Poista kuvien poiminta käytöstä",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Poista kuvien poiminta käytöstä PDF tiedostoista. Jos LLM on käytössä, kuvat tekstitetään automaattisesti. Oletuksena ei käytössä.",
|
||||
"Disabled": "Ei käytössä",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Löydä toiminto",
|
||||
"Discover a model": "Tutustu malliin",
|
||||
"Discover a prompt": "Löydä kehote",
|
||||
@@ -680,7 +679,7 @@
|
||||
"Embedding Concurrent Requests": "Samanaikaiset upotuspyynnöt",
|
||||
"Embedding Model": "Upotusmalli",
|
||||
"Embedding Model Engine": "Upotusmallin moottori",
|
||||
"Emojis": "",
|
||||
"Emojis": "Emojit",
|
||||
"Empty message": "Tyhjä viesti",
|
||||
"Enable All": "Ota kaikki käyttöön",
|
||||
"Enable API Keys": "Ota API-avaimet käyttöön",
|
||||
@@ -768,11 +767,13 @@
|
||||
"Enter New Password": "Kirjoita uusi salasana",
|
||||
"Enter Number of Steps (e.g. 50)": "Kirjoita askelten määrä (esim. 50)",
|
||||
"Enter Ollama Cloud API Key": "Kirjoita Ollama Cloud API avain",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Aseta Perplexity API-avain",
|
||||
"Enter Perplexity Search API URL": "Aseta Perplexity Search API verkko-osoite",
|
||||
"Enter Playwright Timeout": "Aseta Playwright aikakatkaisu",
|
||||
"Enter Playwright WebSocket URL": "Aseta Playwright WebSocket-aikakatkaisu",
|
||||
"Enter prompt here.": "",
|
||||
"Enter prompt here.": "Kirjoita kehote tähän.",
|
||||
"Enter proxy URL (e.g. https://user:password@host:port)": "Kirjoita välityspalvelimen verkko-osoite (esim. https://käyttäjä:salasana@host:portti)",
|
||||
"Enter reasoning effort": "Kirjoita päättelyn määrä",
|
||||
"Enter Score": "Kirjoita pistemäärä",
|
||||
@@ -797,7 +798,7 @@
|
||||
"Enter system prompt here": "Kirjoita järjestelmäkehote tähän",
|
||||
"Enter Tavily API Key": "Kirjoita Tavily API -avain",
|
||||
"Enter Tavily Extract Depth": "Kirjoita Tavily pominta syvyys",
|
||||
"Enter the prompt instructions for this automation...": "",
|
||||
"Enter the prompt instructions for this automation...": "Kirjoita kehotteen ohjeet tälle automaatiolle...",
|
||||
"Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.": "Kirjoita julkinen WebUI verkko-osoitteesi. Verkko-osoitetta käytetään osoitteiden luontiin ilmoituksissa.",
|
||||
"Enter the URL of the function to import": "Kirjoita tuotavan toiminnon verkko-osoite",
|
||||
"Enter the URL to import": "Kirjoita tuotavan verkko-osoite",
|
||||
@@ -831,23 +832,23 @@
|
||||
"Enter your webhook URL": "Kirjoita webhook osoitteesi",
|
||||
"Entra ID": "Entra ID",
|
||||
"Environment Variables": "Ympäristömuuttujat",
|
||||
"Ephemeral": "",
|
||||
"Ephemeral": "Tilapäinen",
|
||||
"Error": "Virhe",
|
||||
"ERROR": "VIRHE",
|
||||
"Error accessing directory": "Virhe hakemistoa avattaessa",
|
||||
"Error accessing Google Drive: {{error}}": "Virhe yhdistäessä Google Drive: {{error}}",
|
||||
"Error accessing media devices.": "Virhe medialaitteita käytettäessä.",
|
||||
"Error deleting model: {{error}}": "",
|
||||
"Error deleting model: {{error}}": "Virhe mallia poistaessa: {{error}}",
|
||||
"Error starting recording.": "Virhe nauhoitusta aloittaessa.",
|
||||
"Error unloading model: {{error}}": "Virhe mallia ladattaessa: {{error}}",
|
||||
"Error uploading file: {{error}}": "Virhe ladattaessa tiedostoa: {{error}}",
|
||||
"Error: A model with the ID '{{modelId}}' already exists. Please select a different ID to proceed.": "Virhe: Malli '{{modelId}}' on jo käytössä. Valitse toinen ID jatkaaksesi.",
|
||||
"Error: Model ID cannot be empty. Please enter a valid ID to proceed.": "Virhe: Mallin ID ei voi olla tyhjä. Kirjoita ID jatkaaksesi.",
|
||||
"Evaluations": "Arvioinnit",
|
||||
"Event created": "",
|
||||
"Event deleted": "",
|
||||
"Event title": "",
|
||||
"Event updated": "",
|
||||
"Event created": "Tapahtuma luotu",
|
||||
"Event deleted": "Tapahtuma poistettu",
|
||||
"Event title": "Tapahtuman otsikko",
|
||||
"Event updated": "Tapahtuma päivitetty",
|
||||
"Exa API Key": "Exa API -avain",
|
||||
"Example: (&(objectClass=inetOrgPerson)(uid=%s))": "Esimerkki: (&(objectClass=inetOrgPerson)(uid=%s))",
|
||||
"Example: ALL": "Esimerkki: KAIKKI",
|
||||
@@ -859,7 +860,7 @@
|
||||
"Execute code": "Suorita koodi",
|
||||
"Execute code for analysis": "Suorita koodi analysointia varten",
|
||||
"Executing **{{NAME}}**...": "Suoritetaan **{{NAME}}**...",
|
||||
"Execution Logs": "",
|
||||
"Execution Logs": "Suorituslokit",
|
||||
"Expand": "Laajenna",
|
||||
"Experimental": "Kokeellinen",
|
||||
"Explain": "Selitä",
|
||||
@@ -869,8 +870,8 @@
|
||||
"Export": "Vie",
|
||||
"Export All Archived Chats": "Vie kaikki arkistoidut keskustelut",
|
||||
"Export All Chats (All Users)": "Vie kaikki keskustelut (kaikki käyttäjät)",
|
||||
"Export as CSV": "",
|
||||
"Export as JSON": "",
|
||||
"Export as CSV": "Vie CSV-tiedostona",
|
||||
"Export as JSON": "Vie JSON:na",
|
||||
"Export chat (.json)": "Vie keskustelu (.json)",
|
||||
"Export Chats": "Vie keskustelut",
|
||||
"Export Config": "Vie asetukset",
|
||||
@@ -896,8 +897,9 @@
|
||||
"Failed to connect to {{URL}} terminal server": "Yhdistäminen {{URL}} päätepalvelimeen epäonnistui",
|
||||
"Failed to copy link": "Linkin kopiointi epäonnistui",
|
||||
"Failed to create API Key.": "API-avaimen luonti epäonnistui.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete calendar": "Kalenterin poistaminen epäonnistui",
|
||||
"Failed to delete note": "Muistiinpanon poistaminen epäonnistui",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Kuvan lataaminen epäonnistui",
|
||||
"Failed to extract content from the file: {{error}}": "Tiedoston sisällön pomiminen epäonnistui: {{error}}",
|
||||
"Failed to extract content from the file.": "Tiedoston sisällön pomiminen epäonnistui.",
|
||||
@@ -1068,7 +1070,7 @@
|
||||
"History": "Historia",
|
||||
"Home": "Koti",
|
||||
"Host": "Palvelin",
|
||||
"Hourly": "",
|
||||
"Hourly": "Tunneittain",
|
||||
"Hourly Messages": "Tuntikohtaiset viestit",
|
||||
"How can I help you today?": "Miten voin auttaa sinua tänään?",
|
||||
"How would you rate this response?": "Kuinka arvioisit tätä vastausta?",
|
||||
@@ -1128,7 +1130,7 @@
|
||||
"Insert Suggestion Prompt to Input": "Lisää kehote ehdotus syötteeseen",
|
||||
"Install from Github URL": "Asenna Github-URL:stä",
|
||||
"Instant Auto-Send After Voice Transcription": "Heti automaattinen lähetys äänitunnistuksen jälkeen",
|
||||
"Instructions": "",
|
||||
"Instructions": "Ohjeistukset",
|
||||
"Integration": "Integrointi",
|
||||
"Integrations": "Integraatiot",
|
||||
"Interface": "Käyttöliittymä",
|
||||
@@ -1188,7 +1190,7 @@
|
||||
"Last 90 days": "Viimeiset 90 päivää",
|
||||
"Last Active": "Viimeksi aktiivinen",
|
||||
"Last Modified": "Viimeksi muokattu",
|
||||
"Last ran": "",
|
||||
"Last ran": "Viimeksi suoritettu",
|
||||
"Last reply": "Viimeksi vastattu",
|
||||
"LDAP": "LDAP",
|
||||
"LDAP server updated": "LDAP-palvelin päivitetty",
|
||||
@@ -1218,7 +1220,7 @@
|
||||
"Limit concurrent search queries. 0 = unlimited (default). Set to 1 for sequential execution (recommended for APIs with strict rate limits like Brave free tier).": "Rajoita samanaikaisia hakukyselyitä. 0 = rajoittamaton (oletus). Aseta arvoon 1 peräkkäistä suoritusta varten (suositellaan API-rajapinnoille, joilla on tiukat nopeusrajoitukset, kuten Brave-ilmaistaso).",
|
||||
"Limits the number of concurrent embedding requests. Set to 0 for unlimited.": "Rajoittaa samanaikaisten upotuspyyntöjen määrää. Arvolla 0 ei rajoituksia.",
|
||||
"List": "Lista",
|
||||
"List calendars, search, create, update, and delete calendar events": "",
|
||||
"List calendars, search, create, update, and delete calendar events": "Listaa kalenterit, hae, luo, päivitä ja poista kalenteritapahtumia",
|
||||
"Listening...": "Kuuntelee...",
|
||||
"Live": "Live",
|
||||
"Llama.cpp": "Llama.cpp",
|
||||
@@ -1229,7 +1231,7 @@
|
||||
"local": "paikallinen",
|
||||
"Local": "Paikallinen",
|
||||
"Local Task Model": "Paikallinen työmalli",
|
||||
"Location": "",
|
||||
"Location": "Sijainti",
|
||||
"Location access not allowed": "Ei pääsyä sijaintitietoihin",
|
||||
"Lost": "Mennyt",
|
||||
"Low": "Matala",
|
||||
@@ -1297,15 +1299,15 @@
|
||||
"Model": "Malli",
|
||||
"Model '{{modelName}}' has been successfully downloaded.": "Malli '{{modelName}}' ladattiin onnistuneesti.",
|
||||
"Model '{{modelTag}}' is already in queue for downloading.": "Malli '{{modelTag}}' on jo jonossa ladattavaksi.",
|
||||
"Model {{modelId}} not found": "",
|
||||
"Model {{modelName}} deleted successfully": "",
|
||||
"Model {{modelId}} not found": "Malli {{modelId}} ei löytynyt",
|
||||
"Model {{modelName}} deleted successfully": "Malli {{modelName}} poistettu onnistuneesti",
|
||||
"Model {{modelName}} is not vision capable": "Malli {{modelName}} ei kykene näkökykyyn",
|
||||
"Model {{name}} is now {{status}}": "Malli {{name}} on nyt {{status}}",
|
||||
"Model {{name}} is now hidden": "Malli {{name}} on nyt piilotettu",
|
||||
"Model {{name}} is now visible": "Malli {{name}} on nyt näkyvissä",
|
||||
"Model accepts file inputs": "Malli hyväksyy tiedostosyötteet",
|
||||
"Model accepts image inputs": "Malli hyväksyy kuvasyötteitä",
|
||||
"Model can access Open Terminal for command execution and file management": "",
|
||||
"Model can access Open Terminal for command execution and file management": "Malli voi käyttää Open Terminal-toimintoa komentojen suorittamiseen ja tiedostojen hallintaan.",
|
||||
"Model can execute code and perform calculations": "Malli voi suorittaa koodia ja laskelmia",
|
||||
"Model can generate images based on text prompts": "Malli voi luoda kuvia tekstikehotteiden perusteella",
|
||||
"Model can search the web for information": "Malli voi hakea tietoa verkosta",
|
||||
@@ -1339,8 +1341,8 @@
|
||||
"Models Sharing": "Mallien jako",
|
||||
"Mojeek": "",
|
||||
"Mojeek Search API Key": "Mojeek Search API -avain",
|
||||
"Month": "",
|
||||
"Monthly": "",
|
||||
"Month": "Kuukausi",
|
||||
"Monthly": "Kuukausittain",
|
||||
"More": "Lisää",
|
||||
"More Concise": "Lyhyemmin",
|
||||
"More options": "Lisää vaihtoehtoja",
|
||||
@@ -1351,14 +1353,14 @@
|
||||
"Name": "Nimi",
|
||||
"Name and ID are required, please fill them out": "Nimi ja ID vaaditaan, täytä puuttuvat kentät",
|
||||
"Name your knowledge base": "Anna tietokannalle nimi",
|
||||
"Name, prompt, and model are required": "",
|
||||
"Name, prompt, and model are required": "Nimi, kehote ja malli ovat pakollisia",
|
||||
"Native": "Natiivi",
|
||||
"Never": "",
|
||||
"Never": "Ei koskaan",
|
||||
"New": "Uusi",
|
||||
"New Automation": "",
|
||||
"New Automation": "Uusi automaatio",
|
||||
"New Button": "Uusi painike",
|
||||
"New Chat": "Uusi keskustelu",
|
||||
"New Event": "",
|
||||
"New Event": "Uusi tapahtuma",
|
||||
"New File": "Uusi tiedosto",
|
||||
"New Folder": "Uusi kansio",
|
||||
"New Function": "Uusi toiminto",
|
||||
@@ -1375,11 +1377,11 @@
|
||||
"New Webhook": "Uusi Webhook",
|
||||
"new-channel": "uusi-kanava",
|
||||
"Next message": "Seuraava viesti",
|
||||
"Next run": "",
|
||||
"Next run": "Seuraava suoritus",
|
||||
"No access grants. Private to you.": "Ei käyttöoikeuksia. Yksityinen sinulle.",
|
||||
"No activity data": "Ei aktiivisuustietoja",
|
||||
"No authentication": "Ei todennusta",
|
||||
"No automations found": "",
|
||||
"No automations found": "Automaatioita ei löytynyt",
|
||||
"No chats found": "Keskuteluja ei löytynyt",
|
||||
"No chats found for this user.": "Käyttäjän keskusteluja ei löytynyt.",
|
||||
"No chats found.": "Keskusteluja ei löytynyt",
|
||||
@@ -1390,7 +1392,7 @@
|
||||
"No data": "Ei dataa",
|
||||
"No data found": "Dataa ei löytynyt",
|
||||
"No distance available": "Etäisyyttä ei saatavilla",
|
||||
"No execution logs available yet": "",
|
||||
"No execution logs available yet": "Suorituslokeja ei saatavilla",
|
||||
"No expiration can pose security risks.": "Vanhenemisen laittamatta jättäminen voi altistaa tietoturvariskeille.",
|
||||
"No feedback found": "Ei palautetta",
|
||||
"No file selected": "Tiedostoa ei ole valittu",
|
||||
@@ -1437,7 +1439,7 @@
|
||||
"Not factually correct": "Ei faktuaalisesti oikein",
|
||||
"Not helpful": "Ei hyödyllinen",
|
||||
"Not Registered": "Ei kirjautunut",
|
||||
"Not scheduled": "",
|
||||
"Not scheduled": "Ei aikataulutettu",
|
||||
"Note": "Muistiinpano",
|
||||
"Note deleted successfully": "Muistiinpano poistettiin onnistuneesti",
|
||||
"Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.": "Huomautus: Jos asetat vähimmäispistemäärän, haku palauttaa vain sellaiset asiakirjat, joiden pistemäärä on vähintään vähimmäismäärä.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "OAuth 2.1 (Staattinen)",
|
||||
"OAuth ID": "OAuth-tunnus",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "lokakuu",
|
||||
"Off": "Pois päältä",
|
||||
"Okay, Let's Go!": "Okei, mennään!",
|
||||
@@ -1462,7 +1465,7 @@
|
||||
"Ollama Cloud API Key": "Ollama Cloud API avain",
|
||||
"Ollama Version": "Ollama-versio",
|
||||
"On": "Päällä",
|
||||
"Once": "",
|
||||
"Once": "Kerran",
|
||||
"OneDrive": "OneDrive",
|
||||
"Only active when \"Paste Large Text as File\" setting is toggled on.": "Aktiivinen vain, kun \"Liitä suuri teksti tiedostona\" -asetus on käytössä.",
|
||||
"Only active when the chat input is in focus and an LLM is generating a response.": "Aktiivinen vain, kun tekstikenttä on kohdistettuna ja LLM luo vastausta.",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Tulosteen muoto",
|
||||
"Output Format": "Tulosteen muoto",
|
||||
"Overview": "Yleiskatsaus",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "sivu",
|
||||
"Page": "Sivu",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Sivutila luo yhden dokumentin sivua kohden. Yksittäistila yhdistää kaikki sivut yhdeksi dokumentiksi, mikä parantaa paloittelua sivurajojen yli.",
|
||||
@@ -1528,8 +1533,8 @@
|
||||
"Password": "Salasana",
|
||||
"Passwords do not match.": "Salasanat eivät täsmää",
|
||||
"Paste Large Text as File": "Liitä suuri teksti tiedostona",
|
||||
"Path copied": "",
|
||||
"Paused": "",
|
||||
"Path copied": "Polku kopioitu",
|
||||
"Paused": "Keskeytetty",
|
||||
"PDF document (.pdf)": "PDF-asiakirja (.pdf)",
|
||||
"PDF Extract Images (OCR)": "Poimi kuvat PDF:stä (OCR)",
|
||||
"PDF Loader Mode": "PDF latausmoodi",
|
||||
@@ -1548,7 +1553,7 @@
|
||||
"Persistent": "Pysyvä",
|
||||
"Personalization": "Personointi",
|
||||
"Pin": "Kiinnitä",
|
||||
"Pin to Sidebar": "",
|
||||
"Pin to Sidebar": "Kiinnitä sivupalkkiin",
|
||||
"Pinned": "Kiinnitetty",
|
||||
"Pinned Messages": "Kiinnitetyt viestit",
|
||||
"Pinned Models": "Kiinnitetyt mallit",
|
||||
@@ -1635,8 +1640,8 @@
|
||||
"Reason": "Päättely",
|
||||
"Reasoning Effort": "Päättelyn määrä",
|
||||
"Reasoning Tags": "Päättely tagit",
|
||||
"Recently Used": "",
|
||||
"Reconnected": "",
|
||||
"Recently Used": "Viimeeksi käytetty",
|
||||
"Reconnected": "Yhdistetty",
|
||||
"Record": "Nauhoita",
|
||||
"Record voice": "Nauhoita ääntä",
|
||||
"Redirecting you to Open WebUI Community": "Ohjataan sinut OpenWebUI-yhteisöön",
|
||||
@@ -1660,7 +1665,7 @@
|
||||
"Relevance": "Relevanssi",
|
||||
"Relevance Threshold": "Relevanssikynnys",
|
||||
"Remember Dismissal": "Muista sulkeminen",
|
||||
"Reminder": "",
|
||||
"Reminder": "Muistutus",
|
||||
"Remove": "Poista",
|
||||
"Remove {{MODELID}} from list.": "Poista {{MODELID}} listalta",
|
||||
"Remove action": "Poista toiminto",
|
||||
@@ -1673,7 +1678,7 @@
|
||||
"Renamed to {{name}}": "Nimetty uudelleen {{name}}",
|
||||
"Render Markdown in Previews": "Renderöi Markdown esikatseluissa",
|
||||
"Reorder Models": "Uudelleenjärjestä malleja",
|
||||
"Repeats": "",
|
||||
"Repeats": "Toistot",
|
||||
"Reply": "Vastaa",
|
||||
"Reply in Thread": "Vastaa ketjussa",
|
||||
"Reply to thread...": "Vastaa ketjussa...",
|
||||
@@ -1707,8 +1712,8 @@
|
||||
"RTL": "RTL",
|
||||
"Run": "Suorita",
|
||||
"Run All": "Suorita kaikki",
|
||||
"Run now": "",
|
||||
"Run Now": "",
|
||||
"Run now": "Suorita nyt",
|
||||
"Run Now": "Suorita nyt",
|
||||
"Running": "Käynnissä",
|
||||
"Running...": "Käynnissä...",
|
||||
"Runs embedding tasks concurrently to speed up processing. Turn off if rate limits become an issue.": "Suorittaa upotustehtäviä samanaikaisesti käsittelyn nopeuttamiseksi. Poista käytöstä, jos kutsurajoituksesta tulee ongelma.",
|
||||
@@ -1720,15 +1725,15 @@
|
||||
"Save Chat": "Tallenna keskustelu",
|
||||
"Saved": "Tallennettu",
|
||||
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "Keskustelulokien tallentaminen suoraan selaimen tallennustilaan ei ole enää tuettu. Lataa ja poista keskustelulokit napsauttamalla alla olevaa painiketta. Älä huoli, voit helposti tuoda keskustelulokit takaisin backendiin",
|
||||
"Schedule": "",
|
||||
"Scheduled time must be in the future": "",
|
||||
"Schedule": "Aikataulu",
|
||||
"Scheduled time must be in the future": "Aikataulutetun ajan on oltava tulevaisuudessa",
|
||||
"Scroll On Branch Change": "Vieritä haaran vaihtoon",
|
||||
"Search": "Haku",
|
||||
"Search a model": "Hae mallia",
|
||||
"Search all emojis": "Hae emojeista",
|
||||
"Search and manage user memories": "Hae ja hallinnoi käyttäjien muistoja",
|
||||
"Search and view user chat history": "Hae ja tarkastele käyttäjän keskusteluhistoriaa",
|
||||
"Search Automations": "",
|
||||
"Search Automations": "Etsi automaatioita",
|
||||
"Search Base": "Hakupohja",
|
||||
"Search channels and channel messages": "Hae kanavia ja kanavaviestejä",
|
||||
"Search Chats": "Hae keskusteluja",
|
||||
@@ -1798,7 +1803,7 @@
|
||||
"Select how to split message text for TTS requests": "Valitse, miten viestit jaetaan TTS-pyyntöjä varten",
|
||||
"Select Knowledge": "Valitse tietämys",
|
||||
"Select Method": "Valitse metodi",
|
||||
"Select model": "",
|
||||
"Select model": "Valitse malli",
|
||||
"Select only one model to call": "Valitse vain yksi malli kutsuttavaksi",
|
||||
"Select view": "Valitse näkymä",
|
||||
"Selected model: {{modelName}}": "Valittu malli: {{modelName}}",
|
||||
@@ -1907,12 +1912,12 @@
|
||||
"Start a new conversation": "Aloita uusi keskustelu",
|
||||
"Start of the channel": "Kanavan alku",
|
||||
"Start Tag": "Aloitus tagi",
|
||||
"Starting in {{count}} minutes_one": "",
|
||||
"Starting in {{count}} minutes_other": "",
|
||||
"Starting in 1 minute": "",
|
||||
"Starting in {{count}} minutes_one": "Aloitetaan {{count}} minutes_one",
|
||||
"Starting in {{count}} minutes_other": "Aloitetaan {{count}} minutes_other",
|
||||
"Starting in 1 minute": "Aloitetaan minuutin kuluttua",
|
||||
"Starting kernel...": "Käynnistetään kerneliä...",
|
||||
"Starting now": "",
|
||||
"State": "",
|
||||
"Starting now": "Aloitetaan nyt",
|
||||
"State": "Tila",
|
||||
"Status": "Tila",
|
||||
"Status cleared successfully": "Tila poistettu onnistuneesti",
|
||||
"Status updated successfully": "Tila päivitetty onnistuneesti",
|
||||
@@ -1923,7 +1928,7 @@
|
||||
"Stop Download": "Lopeta lataus",
|
||||
"Stop Generating": "Lopeta generointi",
|
||||
"Stop Sequence": "Lopetussekvenssi",
|
||||
"Storage": "",
|
||||
"Storage": "Käyttötila",
|
||||
"Stream Chat Response": "Striimaa keskusteluvastaus",
|
||||
"Stream Delta Chunk Size": "Striimin delta-lohkon koko",
|
||||
"Streamable HTTP": "Streamable HTTP",
|
||||
@@ -1964,10 +1969,10 @@
|
||||
"Talk to Model": "Puhu mallille",
|
||||
"Tap to interrupt": "Napauta keskeyttääksesi",
|
||||
"Task List": "Tehtävälista",
|
||||
"Task Management": "",
|
||||
"Task Management": "Tehtävien hallinta",
|
||||
"Task Model": "Työmalli",
|
||||
"Tasks": "Tehtävät",
|
||||
"tasks completed": "",
|
||||
"tasks completed": "Tehtävät suoritettu",
|
||||
"Tavily API Key": "Tavily API -avain",
|
||||
"Tavily Extract Depth": "Tavily poiminta syvyys",
|
||||
"Tell us more:": "Kerro lisää:",
|
||||
@@ -1999,7 +2004,7 @@
|
||||
"The score should be a value between 0.0 (0%) and 1.0 (100%).": "Pisteytyksen tulee olla arvo välillä 0,0 (0 %) ja 1,0 (100 %).",
|
||||
"The stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.": "Mallin striimin delta-lohkon koko. Lohkon koon kasvattaminen saa mallin vastaamaan kerralla suuremmilla tekstipaloilla.",
|
||||
"The temperature of the model. Increasing the temperature will make the model answer more creatively.": "Mallin lämpötila. Lisäämällä lämpötilaa mallin vastaukset ovat luovempia.",
|
||||
"The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "",
|
||||
"The Weight of BM25 Hybrid Search. 0 more semantic, 1 more lexical. Default 0.5": "BM25-hybridihaun painoarvo. 0 semanttista, 1 leksikaalista. Oletusarvo 0,5",
|
||||
"The width in pixels to compress images to. Leave empty for no compression.": "Leveys pikseleinä, johon kuvat pakataan. Jätä tyhjäksi, jos et halua pakkausta.",
|
||||
"Theme": "Teema",
|
||||
"There was an error syncing your stats. Please try again.": "Tilastojen synkronoinnissa tapahtui virhe. Yritä uudelleen.",
|
||||
@@ -2020,11 +2025,12 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Tämä vaihtoehto asettaa mallin vastauksessaan luomien tokenien enimmäismäärän. Tämän rajan nostaminen antaa mallille mahdollisuuden tarjota pidempiä vastauksia, mutta se voi myös lisätä hyödyttömän tai epäolennaisen sisällön luomisen todennäköisyyttä.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Tämä vaihtoehto poistaa kaikki kokoelman nykyiset tiedostot ja korvaa ne uusilla ladatuilla tiedostoilla.",
|
||||
"This response was generated by \"{{model}}\"": "Tämän vastauksen tuotti \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Tämä poistaa",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tämä poistaa <strong>{{NAME}}</strong> ja <strong>kaikki sen sisällöt</strong>.",
|
||||
"This will delete all models including custom models": "Tämä poistaa kaikki mallit mukaan lukien mukautetut mallit",
|
||||
"This will delete all models including custom models and cannot be undone.": "Tämä poistaa kaikki mallit, mukaan lukien mukautetut mallit, eikä sitä voi peruuttaa.",
|
||||
"This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "",
|
||||
"This will permanently delete the calendar \"{{name}}\" and all its events. This action cannot be undone.": "Tämä poistaa pysyvästi kalenterin \"{{name}}\" ja kaikki sen tapahtumat. Tätä toimintoa ei voi peruuttaa.",
|
||||
"This will reset the knowledge base and sync all files. Do you wish to continue?": "Tämä nollaa tietokannan ja synkronoi kaikki tiedostot. Haluatko jatkaa?",
|
||||
"Thorough explanation": "Perusteellinen selitys",
|
||||
"Thought": "Ajatus",
|
||||
@@ -2036,7 +2042,7 @@
|
||||
"Tika": "Tika",
|
||||
"Tika Server URL required.": "Tika palvelimen verkko-osoite vaaditaan.",
|
||||
"Tiktoken": "Tiktoken",
|
||||
"Time": "",
|
||||
"Time": "Aika",
|
||||
"Time & Calculation": "Aika ja laskenta",
|
||||
"Timeout": "Aikakatkaisu",
|
||||
"Title": "Otsikko",
|
||||
@@ -2044,7 +2050,7 @@
|
||||
"Title cannot be an empty string.": "Otsikko ei voi olla tyhjä merkkijono.",
|
||||
"Title Generation": "Otsikon luonti",
|
||||
"Title Generation Prompt": "Otsikon luontikehote",
|
||||
"Title is required": "",
|
||||
"Title is required": "Otsikko on pakollinen",
|
||||
"TLS": "TLS",
|
||||
"To access the available model names for downloading,": "Päästäksesi käsiksi ladattavissa oleviin mallinimiin,",
|
||||
"To access the GGUF models available for downloading,": "Päästäksesi käsiksi ladattavissa oleviin GGUF-malleihin,",
|
||||
@@ -2055,7 +2061,7 @@
|
||||
"To select toolkits here, add them to the \"Tools\" workspace first.": "Valitaksesi työkalusettejä tässä, lisää ne ensin \"Työkalut\"-työtilaan.",
|
||||
"Toast notifications for new updates": "Ilmoituspopuppien näyttäminen uusista päivityksistä",
|
||||
"Today": "Tänään",
|
||||
"Today at": "",
|
||||
"Today at": "Tänään",
|
||||
"Today at {{LOCALIZED_TIME}}": "Tänään {{LOCALIZED_TIME}}",
|
||||
"Toggle {{COUNT}} sources": "Näytä/piilota {{COUNT}} lähdettä",
|
||||
"Toggle 1 source": "Näytä/piilota 1 lähde",
|
||||
@@ -2111,7 +2117,7 @@
|
||||
"Unloads {{FROM_NOW}}": "Purkuja {{FROM_NOW}}",
|
||||
"Unlock mysteries": "Selvitä arvoituksia",
|
||||
"Unpin": "Irrota kiinnitys",
|
||||
"Unpin from Sidebar": "",
|
||||
"Unpin from Sidebar": "Irrota sivupalkista",
|
||||
"Unravel secrets": "Avaa salaisuuksia",
|
||||
"Unshare Chat": "Lopeta keskustelun jakaminen",
|
||||
"Unsupported file type.": "Ei tuettu tiedostotyyppi",
|
||||
@@ -2196,7 +2202,7 @@
|
||||
"Waiting for upload...": "Odottaa latausta...",
|
||||
"Warning": "Varoitus",
|
||||
"Warning:": "Varoitus:",
|
||||
"Warning: Enabling this will allow users to run scheduled prompts automatically.": "",
|
||||
"Warning: Enabling this will allow users to run scheduled prompts automatically.": "Varoitus: Tämän käyttöönotto sallii käyttäjien suorittaa aikataulutettuja kehotteita automaattisesti.",
|
||||
"Warning: Enabling this will allow users to upload arbitrary code on the server.": "Varoitus: Tämän käyttöönotto sallii käyttäjien ladata mielivaltaista koodia palvelimelle.",
|
||||
"Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.": "Varoitus: Jupyter käyttö voi mahdollistaa mielivaltaiseen koodin suorittamiseen, mikä voi aiheuttaa tietoturvariskejä - käytä äärimmäisen varoen.",
|
||||
"We_day_of_week": "",
|
||||
@@ -2216,15 +2222,15 @@
|
||||
"WebUI will make requests to \"{{url}}\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}\"",
|
||||
"WebUI will make requests to \"{{url}}/api/chat\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}/api/chat\"",
|
||||
"WebUI will make requests to \"{{url}}/chat/completions\"": "WebUI lähettää pyyntöjä osoitteeseen \"{{url}}/chat/completions\"",
|
||||
"Week": "",
|
||||
"Weekly": "",
|
||||
"Week": "Viikko",
|
||||
"Weekly": "Viikoittain",
|
||||
"What are you trying to achieve?": "Mitä yrität saavuttaa?",
|
||||
"What are you working on?": "Mitä olet työskentelemässä?",
|
||||
"What is NOT shared:": "Mitä EI jaeta:",
|
||||
"What is shared:": "Mitä jaetaan:",
|
||||
"What's New in": "Mitä uutta",
|
||||
"What's on your mind?": "Mitä ajattelet?",
|
||||
"When": "",
|
||||
"When": "Milloin",
|
||||
"When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.": "Kun käytössä, malli vastaa jokaiseen chatviestiin reaaliajassa, tuottaen vastauksen heti kun käyttäjä lähettää viestin. Tämä tila on hyödyllinen reaaliaikaisissa chat-sovelluksissa, mutta voi vaikuttaa suorituskykyyn hitaammilla laitteistoilla.",
|
||||
"wherever you are": "missä tahansa oletkin",
|
||||
"Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.": "Sivutetaanko tuloste. Jokainen sivu erotetaan toisistaan vaakasuoralla viivalla ja sivunumerolla. Oletusarvo ei käytössä.",
|
||||
@@ -2235,7 +2241,7 @@
|
||||
"Width": "Leveys",
|
||||
"Wikipedia": "",
|
||||
"Won": "Voitti",
|
||||
"Working Directory": "",
|
||||
"Working Directory": "Työhakemisto",
|
||||
"Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text.": "Toimii top-k:n kanssa. Korkeampi arvo (esim. 0.95) johtaa monipuolisempaan tekstiin, kun taas matalampi arvo (esim. 0.5) tuottaa kohdennetumpaa ja konservatiivisempaa teksti.",
|
||||
"Workspace": "Työtila",
|
||||
"Workspace Permissions": "Työtilan käyttöoikeudet",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Activation du compte en attente",
|
||||
"Accurate information": "Information exacte",
|
||||
"Action": "Action",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Action requise pour l’enregistrement du journal de discussion",
|
||||
"Actions": "Actions",
|
||||
"Activate": "Activer",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Toujours jouer la notification sonore",
|
||||
"Amazing": "Incroyable",
|
||||
"an assistant": "un assistant",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Analysé",
|
||||
"Analyzing...": "Analyse en cours",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Empecher l'extraction d'image",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Désactive l'extraction d'images du PDF. Si l'option Utiliser le LLM est activée, les images seront automatiquement légendées. La valeur par défaut est False.",
|
||||
"Disabled": "Désactivé",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Trouvez une fonction",
|
||||
"Discover a model": "Trouvez un modèle",
|
||||
"Discover a prompt": "Trouvez un prompt",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Entrez votre nouveau mots de passe",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (par ex. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Entrez la clé pour l'API de Perplixity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "Entrez le délai d'expiration Playwright",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Échec de la création de la clé API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Échec de la délétion de la note",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Octobre",
|
||||
"Off": "Désactivé",
|
||||
"Okay, Let's Go!": "D'accord, allons-y !",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Format de sortie",
|
||||
"Output Format": "Format de sortie",
|
||||
"Overview": "Aperçu",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "page",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Cette option définit le nombre maximal de Token que le modèle peut générer dans sa réponse. Une valeur plus élevée permet des réponses plus longues, mais peut aussi générer du contenu moins pertinent.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Cette option supprimera tous les fichiers existants dans la collection et les remplacera par les fichiers nouvellement téléchargés.",
|
||||
"This response was generated by \"{{model}}\"": "Cette réponse a été générée par \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Cela supprimera",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Cela supprimera <strong>{{NAME}}</strong> et <strong>tout son contenu</strong>.",
|
||||
"This will delete all models including custom models": "Cela supprimera tous les modèles, y compris les modèles personnalisés",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Activation du compte en attente",
|
||||
"Accurate information": "Information exacte",
|
||||
"Action": "Action",
|
||||
"Action not found": "Action non trouvée",
|
||||
"Action Required for Chat Log Storage": "Action requise pour l’enregistrement du journal de discussion",
|
||||
"Actions": "Actions",
|
||||
"Activate": "Activer",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Toujours jouer la notification sonore",
|
||||
"Amazing": "Incroyable",
|
||||
"an assistant": "un assistant",
|
||||
"An error occurred while fetching the explanation": "Une erreur s'est produite lors de la récupération de l'explication",
|
||||
"Analytics": "Analytique",
|
||||
"Analyzed": "Analysé",
|
||||
"Analyzing...": "Analyse en cours",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Empecher l'extraction d'image",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Désactive l'extraction d'images du PDF. Si l'option Utiliser le LLM est activée, les images seront automatiquement légendées. La valeur par défaut est False.",
|
||||
"Disabled": "Désactivé",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Trouvez une fonction",
|
||||
"Discover a model": "Trouvez un modèle",
|
||||
"Discover a prompt": "Trouvez un prompt",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Entrez votre nouveau mots de passe",
|
||||
"Enter Number of Steps (e.g. 50)": "Entrez le nombre d'étapes (par ex. 50)",
|
||||
"Enter Ollama Cloud API Key": "Entrez la clé API Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Entrez la clé API Perplexity",
|
||||
"Enter Perplexity Search API URL": "Entrez l'URL de l'API Perplexity",
|
||||
"Enter Playwright Timeout": "Entrez le délai d'expiration Playwright",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Échec de la création de la clé API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Échec de la délétion de la note",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Échec du téléchargement de l'image",
|
||||
"Failed to extract content from the file: {{error}}": "Échec de l'extraction du contenu du fichier : {{error}}",
|
||||
"Failed to extract content from the file.": "Échec de l'extraction du contenu du fichier",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Octobre",
|
||||
"Off": "Désactivé",
|
||||
"Okay, Let's Go!": "D'accord, allons-y !",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Format de sortie",
|
||||
"Output Format": "Format de sortie",
|
||||
"Overview": "Aperçu",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "page",
|
||||
"Page": "Page",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Le mode Page crée un document par page. Le mode Unique combine toutes les pages en un seul document pour une meilleure segmentation à travers les limites de page.",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Cette option définit le nombre maximal de tokens que le modèle peut générer dans sa réponse. Une valeur plus élevée permet des réponses plus longues, mais peut aussi générer du contenu moins pertinent.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Cette option supprimera tous les fichiers existants dans la collection et les remplacera par les fichiers nouvellement téléchargés.",
|
||||
"This response was generated by \"{{model}}\"": "Cette réponse a été générée par \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Cela supprimera",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Cela supprimera <strong>{{NAME}}</strong> et <strong>tout son contenu</strong>.",
|
||||
"This will delete all models including custom models": "Cela supprimera tous les modèles, y compris les modèles personnalisés",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Activación da conta pendente",
|
||||
"Accurate information": "Información precisa",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Requírese unha acción para gardar o rexistro do chat",
|
||||
"Actions": "Accións",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "Sorprendente",
|
||||
"an assistant": "un asistente",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Analizado",
|
||||
"Analyzing...": "Analizando..",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Desactivado",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Descubre unha función",
|
||||
"Discover a model": "Descubrir un modelo",
|
||||
"Discover a prompt": "Descubre un Prompt",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Ingrese o número de pasos (p.ej., 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Ingrese a chave API de Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Non pudo xerarse a chave API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Octubre",
|
||||
"Off": "Desactivado",
|
||||
"Okay, Let's Go!": "Bien, ¡Vamos!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Formato de saida",
|
||||
"Output Format": "",
|
||||
"Overview": "Vista xeral",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "Páxina",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Esta opción eliminará todos os arquivos existentes na colección y os reemplazará con novos arquivos subidos.",
|
||||
"This response was generated by \"{{model}}\"": "Esta resposta fue generada por \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Esto eliminará",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Esto eliminará <strong>{{NAME}}</strong> y <strong>todo su contido</strong>.",
|
||||
"This will delete all models including custom models": "Esto eliminará todos os modelos, incluidos os modelos personalizados",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "מידע מדויק",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "נדרשת פעולה לשמירת יומן הצ'אט",
|
||||
"Actions": "פעולה",
|
||||
"Activate": "",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "מדהים",
|
||||
"an assistant": "עוזר",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "מושבת",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "גלה מודל",
|
||||
"Discover a prompt": "גלה פקודה",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "הזן מספר שלבים (למשל 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "יצירת מפתח API נכשלה.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "אוקטובר",
|
||||
"Off": "כבוי",
|
||||
"Okay, Let's Go!": "בסדר, בואו נתחיל!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "עמוד",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "सटीक जानकारी",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "चैट लॉग सहेजने के लिए कार्रवाई आवश्यक है",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "एक सहायक",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "अक्षम",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "एक मॉडल की खोज करें",
|
||||
"Discover a prompt": "प्रॉम्प्ट खोजें",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "चरणों की संख्या दर्ज करें (उदा. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "एपीआई कुंजी बनाने में विफल.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "अक्टूबर",
|
||||
"Off": "बंद",
|
||||
"Okay, Let's Go!": "ठीक है, चलिए चलते हैं!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "Točne informacije",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Potrebna je radnja za pohranu zapisnika chata",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "asistent",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Onemogućeno",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "Otkrijte model",
|
||||
"Discover a prompt": "Otkrijte prompt",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Unesite broj koraka (npr. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Neuspješno stvaranje API ključa.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Listopad",
|
||||
"Off": "Isključeno",
|
||||
"Okay, Let's Go!": "U redu, idemo!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Fiók aktiválása folyamatban",
|
||||
"Accurate information": "Pontos információ",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Művelet szükséges a csevegési napló mentéséhez",
|
||||
"Actions": "Műveletek",
|
||||
"Activate": "Aktiválás",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "Csodálatos",
|
||||
"an assistant": "egy asszisztens",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Elemezve",
|
||||
"Analyzing...": "Elemzés...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Letiltva",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Funkció felfedezése",
|
||||
"Discover a model": "Modell felfedezése",
|
||||
"Discover a prompt": "Prompt felfedezése",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Add meg a lépések számát (pl. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Add meg a Perplexity API kulcsot",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Nem sikerült létrehozni az API kulcsot.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth azonosító",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Október",
|
||||
"Off": "Ki",
|
||||
"Okay, Let's Go!": "Rendben, kezdjük!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Kimeneti formátum",
|
||||
"Output Format": "",
|
||||
"Overview": "Áttekintés",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "oldal",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Ez az opció beállítja a modell által generálható tokenek maximális számát a válaszban. Ezen limit növelése hosszabb válaszokat tesz lehetővé, de növelheti a nem hasznos vagy irreleváns tartalom generálásának valószínűségét.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Ez az opció törli az összes meglévő fájlt a gyűjteményben és lecseréli őket az újonnan feltöltött fájlokkal.",
|
||||
"This response was generated by \"{{model}}\"": "Ezt a választ a \"{{model}}\" generálta",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Ez törölni fogja",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Ez törölni fogja a <strong>{{NAME}}</strong>-t és <strong>minden tartalmát</strong>.",
|
||||
"This will delete all models including custom models": "Ez törölni fogja az összes modellt, beleértve az egyéni modelleket is",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"Account Activation Pending": "Aktivasi Akun Tertunda",
|
||||
"Accurate information": "Informasi yang akurat",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Diperlukan tindakan untuk menyimpan log obrolan",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -158,7 +157,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "asisten",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -578,6 +576,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Dinonaktifkan",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Menemukan sebuah fungsi",
|
||||
"Discover a model": "Menemukan sebuah model",
|
||||
"Discover a prompt": "Temukan petunjuk",
|
||||
@@ -767,6 +766,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Masukkan Jumlah Langkah (mis. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -897,6 +898,7 @@
|
||||
"Failed to create API Key.": "Gagal membuat API Key.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1451,6 +1453,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktober",
|
||||
"Off": "Mati",
|
||||
"Okay, Let's Go!": "Oke, Ayo Kita Pergi!",
|
||||
@@ -1517,6 +1520,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2017,6 +2022,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Ini akan menghapus",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Gníomhachtaithe Cuntas",
|
||||
"Accurate information": "Faisnéis chruinn",
|
||||
"Action": "Gníomh",
|
||||
"Action not found": "Níor aimsíodh gníomh",
|
||||
"Action Required for Chat Log Storage": "Gníomh riachtanach chun logáil comhrá a shábháil",
|
||||
"Actions": "Gníomhartha",
|
||||
"Activate": "Gníomhachtaigh",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Seinn Fuaim Fógra i gCónaí",
|
||||
"Amazing": "Iontach",
|
||||
"an assistant": "cúntóir",
|
||||
"An error occurred while fetching the explanation": "Tharla earráid agus an míniú á fháil",
|
||||
"Analytics": "Anailísíocht",
|
||||
"Analyzed": "Anailísithe",
|
||||
"Analyzing...": "Ag déanamh anailíse...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Díchumasaigh Eastóscadh Íomhá",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Díchumasaigh eastóscadh íomhánna ón PDF. Má tá Úsáid LLM cumasaithe, cuirfear fotheidil leis na híomhánna go huathoibríoch. Is é Bréag an réamhshocrú.",
|
||||
"Disabled": "Díchumasaithe",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Faigh amach feidhm",
|
||||
"Discover a model": "Faigh amach samhail",
|
||||
"Discover a prompt": "Faigh amach treoir",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Cuir isteach Pasfhocal Nua",
|
||||
"Enter Number of Steps (e.g. 50)": "Iontráil Líon na gCéimeanna (m.sh. 50)",
|
||||
"Enter Ollama Cloud API Key": "Cuir isteach Eochair API Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Cuir isteach Eochair API Perplexity",
|
||||
"Enter Perplexity Search API URL": "Cuir isteach URL API Cuardaigh na Measctha",
|
||||
"Enter Playwright Timeout": "Iontráil Teorainn Ama na nDrámadóir",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Theip ar an eochair API a chruthú.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Theip ar an nóta a scriosadh",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Theip ar an íomhá a íoslódáil",
|
||||
"Failed to extract content from the file: {{error}}": "Theip ar an ábhar a bhaint as an gcomhad: {{error}}",
|
||||
"Failed to extract content from the file.": "Theip ar an ábhar a bhaint as an gcomhad.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "OAuth 2.1 (Statach)",
|
||||
"OAuth ID": "Aitheantas OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Deireadh Fómhair",
|
||||
"Off": "As",
|
||||
"Okay, Let's Go!": "Ceart go leor, Déanaimis Téigh!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Formáid aschuir",
|
||||
"Output Format": "Formáid Aschuir",
|
||||
"Overview": "Forbhreathnú",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "leathanach",
|
||||
"Page": "Leathanach",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Cruthaíonn mód leathanaigh doiciméad amháin in aghaidh an leathanaigh. Comhcheanglaíonn mód aonair na leathanaigh go léir in aon doiciméad amháin le haghaidh roinnt níos fearr trasna teorainneacha leathanaigh.",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Socraíonn an rogha seo an t-uaslíon comharthaí is féidir leis an tsamhail a ghiniúint ina fhreagra. Tríd an teorainn seo a mhéadú is féidir leis an tsamhail freagraí níos faide a sholáthar, ach d'fhéadfadh go méadódh sé an dóchúlacht go nginfear ábhar neamhchabhrach nó nach mbaineann le hábhar.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Scriosfaidh an rogha seo gach comhad atá sa bhailiúchán agus cuirfear comhaid nua-uaslódála ina n-ionad.",
|
||||
"This response was generated by \"{{model}}\"": "Gin an freagra seo ag \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Scriosfaidh sé seo",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Scriosfaidh sé seo <strong>{{NAME}}</strong> agus <strong>a bhfuil ann go léir</strong>.",
|
||||
"This will delete all models including custom models": "Scriosfaidh sé seo gach samhail lena n-áirítear samhlacha saincheaptha",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Account in attesa di attivazione",
|
||||
"Accurate information": "Informazioni accurate",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Azione richiesta per salvare il registro chat",
|
||||
"Actions": "Azioni",
|
||||
"Activate": "Attiva",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Riproduci sempre il suono di notifica",
|
||||
"Amazing": "Fantastico",
|
||||
"an assistant": "un assistente",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Analizzato",
|
||||
"Analyzing...": "Analisi in corso...",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Disattiva l'estrazione immagini",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Disattiva l'estrazione immagini dai PDF. Se LLM è attivo le immagini saranno didascalizzate. Predefinito a Falso.",
|
||||
"Disabled": "Disabilitato",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Scopri una funzione",
|
||||
"Discover a model": "Scopri un modello",
|
||||
"Discover a prompt": "Scopri un prompt",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Inserisci la Nuova Password",
|
||||
"Enter Number of Steps (e.g. 50)": "Inserisci Numero di Passaggi (ad esempio 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Inserisci Chiave API di Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "Inserisci Timeout di Playwright",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Impossibile creare Chiave API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Impossibile eliminare la nota",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Ottobre",
|
||||
"Off": "Disattivato",
|
||||
"Okay, Let's Go!": "Ok, andiamo!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Formato di output",
|
||||
"Output Format": "Formato output",
|
||||
"Overview": "Panoramica",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "pagina",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Questa opzione imposta il numero massimo di token che il modello può generare nella sua risposta. Aumentare questo limite consente al modello di fornire risposte più lunghe, ma potrebbe anche aumentare la probabilità che vengano generati contenuti non utili o irrilevanti.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Questa opzione eliminerà tutti i file esistenti nella collezione e li sostituirà con i file appena caricati.",
|
||||
"This response was generated by \"{{model}}\"": "Questa risposta è stata generata da \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Questa opzione eliminerà",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Questa opzione eliminerà <strong>{{NAME}}</strong> e <strong>tutti i suoi contenuti</strong>.",
|
||||
"This will delete all models including custom models": "Questa opzione eliminerà tutti i modelli, compresi i modelli personalizzati",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"Account Activation Pending": "アカウント承認待ち",
|
||||
"Accurate information": "情報が正確",
|
||||
"Action": "アクション",
|
||||
"Action not found": "アクションが見つかりません",
|
||||
"Action Required for Chat Log Storage": "チャットログの保存には操作が必要です",
|
||||
"Actions": "アクション",
|
||||
"Activate": "アクティブ化",
|
||||
@@ -158,7 +157,6 @@
|
||||
"Always Play Notification Sound": "常に通知音を再生",
|
||||
"Amazing": "素晴らしい",
|
||||
"an assistant": "アシスタント",
|
||||
"An error occurred while fetching the explanation": "説明の取得中にエラーが発生しました",
|
||||
"Analytics": "分析",
|
||||
"Analyzed": "分析済み",
|
||||
"Analyzing...": "分析中...",
|
||||
@@ -578,6 +576,7 @@
|
||||
"Disable Image Extraction": "画像の抽出を無効化",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "PDFからの画像の抽出を無効化します。LLMを使用 が有効の場合、画像は自動で説明文に変換されます。デフォルトで無効",
|
||||
"Disabled": "無効",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Functionを探す",
|
||||
"Discover a model": "モデルを探す",
|
||||
"Discover a prompt": "プロンプトを探す",
|
||||
@@ -767,6 +766,8 @@
|
||||
"Enter New Password": "新しいパスワードを入力",
|
||||
"Enter Number of Steps (e.g. 50)": "ステップ数を入力 (例: 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Perplexity APIキーを入力",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "Playwrightタイムアウトを入力",
|
||||
@@ -897,6 +898,7 @@
|
||||
"Failed to create API Key.": "APIキーの作成に失敗しました。",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "ノートの削除に失敗しました。",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "ファイルから中身の取得に失敗しました: {{error}}",
|
||||
"Failed to extract content from the file.": "ファイルから中身の取得に失敗しました。",
|
||||
@@ -1451,6 +1453,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "10月",
|
||||
"Off": "オフ",
|
||||
"Okay, Let's Go!": "OK、始めましょう!",
|
||||
@@ -1517,6 +1520,8 @@
|
||||
"Output format": "出力形式",
|
||||
"Output Format": "出力形式",
|
||||
"Overview": "概要",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "ページ",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2017,6 +2022,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "このオプションは、モデルが生成できるトークンの最大数を設定します。この制限を増加すると、モデルはより長い回答を生成できるようになりますが、不適切な内容や関連性の低い内容が生成される可能性も高まります。",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "このオプションを有効にすると、コレクション内の既存ファイルがすべて削除され、新たにアップロードしたファイルに置き換わります。",
|
||||
"This response was generated by \"{{model}}\"": "このレスポンスは\"{{model}}\"によって生成されました。",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "削除します",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "これは<strong>{{NAME}}</strong>とその<strong>すべての内容</strong>を削除します。",
|
||||
"This will delete all models including custom models": "これはカスタムモデルを含むすべてのモデルを削除します",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "დარჩენილი ანგარიშის აქტივაცია",
|
||||
"Accurate information": "სწორი ინფორმაცია",
|
||||
"Action": "ქმედება",
|
||||
"Action not found": "ქმედება აღმოჩენილი არაა",
|
||||
"Action Required for Chat Log Storage": "საჭიროა მოქმედება ჩატის ჟურნალის შესანახად",
|
||||
"Actions": "ქმედებები",
|
||||
"Activate": "აქტივაცია",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "გაფრთხილების ხმის ყოველთვის დაკვრა",
|
||||
"Amazing": "გადასარევია",
|
||||
"an assistant": "დამხმარე",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "ანალიტიკა",
|
||||
"Analyzed": "გაანაზლიებულია",
|
||||
"Analyzing...": "ანალიზი...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "გამორთული",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "აღმოაჩინეთ ფუნქცია",
|
||||
"Discover a model": "აღმოაჩინეთ მოდელი",
|
||||
"Discover a prompt": "აღმოაჩინეთ მოთხოვნა",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "შეიყვანეთ ახალი პაროლი",
|
||||
"Enter Number of Steps (e.g. 50)": "შეიყვანეთ ნაბიჯების რაოდენობა (მაგ. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API-ის გასაღების შექმნა ჩავარდა.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "შენიშვნის წაშლა ჩავარდა",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "ოქტომბერი",
|
||||
"Off": "გამორთ",
|
||||
"Okay, Let's Go!": "აბა, წავედით!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "გამოტანის ფორმატი",
|
||||
"Output Format": "გამოტანის ფორმატი",
|
||||
"Overview": "მიმოხილვა",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "პანელი",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "ეს წაშლის",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Armad n umiḍan deg uṛaǧu",
|
||||
"Accurate information": "Talɣut tusdidt",
|
||||
"Action": "Tigawt",
|
||||
"Action not found": "Tigawt ulac-itt",
|
||||
"Action Required for Chat Log Storage": "Isefk tigawt i usekles n uɣmis n udiwenni",
|
||||
"Actions": "Tigawin",
|
||||
"Activate": "Sermed",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "Rmed yal tikkelt alɣu s ṣṣut",
|
||||
"Amazing": "Igerrez",
|
||||
"an assistant": "d amallal",
|
||||
"An error occurred while fetching the explanation": "Teḍra-d tuccḍa lawan n tririt n usegzi",
|
||||
"Analytics": "Tasleḍt",
|
||||
"Analyzed": "Yettwasekyed",
|
||||
"Analyzing...": "La yettwasekyad…",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "Sens afsay n tugniwin",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Kkes-d asufeɣ n tugna seg PDF. Ma yella aseqdec n LLM yermed, tugniwin ad ttwakelsent s wudem awurman. Imezwura ɣer False.",
|
||||
"Disabled": "Yensa",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Af-d tasɣent",
|
||||
"Discover a model": "Snirem tamudemt",
|
||||
"Discover a prompt": "Snirem aneftaɣ",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "Sekcem-d awal n uɛeddi amaynut",
|
||||
"Enter Number of Steps (e.g. 50)": "Sekcem uṭṭun n yisurifen (amedya 50)",
|
||||
"Enter Ollama Cloud API Key": "Sekcem-d tasarut API n Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Sekcem-d tasarut API n Perplexity",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Ur yessaweḍ ara ad d-yesnulfu tasarut API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Ur yessaweḍ ara ad yekkes tazmilt",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "Ur yessaweḍ ara ad d-yekkes agbur seg ufaylu: {{error}}",
|
||||
"Failed to extract content from the file.": "Ur yessaweḍ ara ad d-yekkes agbur seg ufaylu-nni.",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "Asulay OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Tubeṛ",
|
||||
"Off": "Yensa",
|
||||
"Okay, Let's Go!": "Yerbaḥ, aha yya!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Amasal n tuffɣa",
|
||||
"Output Format": "Amasal n tuffɣa",
|
||||
"Overview": "Tamuɣli s umata",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "asebter",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "Tiririt-a teslal-itt-id \"{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Aya ad yekkes",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Aya ad yekkes <strong>{NAME}}</strong> akked <strong> akk ayen yellan deg-s</strong>.",
|
||||
"This will delete all models including custom models": "Aya ad yekkes akk timudmin yellan gar-asent timudmin n tannumi",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,6 @@
|
||||
"Account Activation Pending": "Laukiama paskyros patvirtinimo",
|
||||
"Accurate information": "Tiksli informacija",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Reikia veiksmo, kad būtų išsaugotas pokalbių žurnalas",
|
||||
"Actions": "Veiksmai",
|
||||
"Activate": "",
|
||||
@@ -161,7 +160,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "assistentas",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -581,6 +579,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Išjungta",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Atrasti funkciją",
|
||||
"Discover a model": "Atrasti modelį",
|
||||
"Discover a prompt": "Atrasti užklausas",
|
||||
@@ -770,6 +769,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Įveskite žingsnių kiekį (pvz. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -900,6 +901,7 @@
|
||||
"Failed to create API Key.": "Nepavyko sukurti API rakto",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1454,6 +1456,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "spalis",
|
||||
"Off": "Išjungta",
|
||||
"Okay, Let's Go!": "Gerai, važiuojam!",
|
||||
@@ -1520,6 +1523,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2026,6 +2031,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Tai ištrins",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
"Account Activation Pending": "Gaida konta aktivizēšanu",
|
||||
"Accurate information": "Precīza informācija",
|
||||
"Action": "Darbība",
|
||||
"Action not found": "Darbība nav atrasta",
|
||||
"Action Required for Chat Log Storage": "Nepieciešama darbība tērzēšanas žurnāla glabāšanai",
|
||||
"Actions": "Darbības",
|
||||
"Activate": "Aktivizēt",
|
||||
@@ -160,7 +159,6 @@
|
||||
"Always Play Notification Sound": "Vienmēr atskaņot paziņojuma skaņu",
|
||||
"Amazing": "Lieliski",
|
||||
"an assistant": "asistents",
|
||||
"An error occurred while fetching the explanation": "Iegūstot skaidrojumu, radās kļūda",
|
||||
"Analytics": "Analītika",
|
||||
"Analyzed": "Analizēts",
|
||||
"Analyzing...": "Analizē...",
|
||||
@@ -580,6 +578,7 @@
|
||||
"Disable Image Extraction": "Atspējot attēlu ekstrakciju",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Atspējot attēlu ekstrakciju no PDF. Ja ir iespējots Lietot LLM, attēliem automātiski tiks pievienoti paraksti. Noklusējums ir False.",
|
||||
"Disabled": "Atspējots",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Atklāt funkciju",
|
||||
"Discover a model": "Atklāt modeli",
|
||||
"Discover a prompt": "Atklāt uzvedni",
|
||||
@@ -769,6 +768,8 @@
|
||||
"Enter New Password": "Ievadiet jaunu paroli",
|
||||
"Enter Number of Steps (e.g. 50)": "Ievadiet soļu skaitu (piem., 50)",
|
||||
"Enter Ollama Cloud API Key": "Ievadiet Ollama Cloud API atslēgu",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Ievadiet Perplexity API atslēgu",
|
||||
"Enter Perplexity Search API URL": "Ievadiet Perplexity Search API URL",
|
||||
"Enter Playwright Timeout": "Ievadiet Playwright taimautu",
|
||||
@@ -899,6 +900,7 @@
|
||||
"Failed to create API Key.": "Neizdevās izveidot API atslēgu.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Neizdevās dzēst piezīmi",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "Neizdevās ekstrahēt saturu no faila: {{error}}",
|
||||
"Failed to extract content from the file.": "Neizdevās ekstrahēt saturu no faila.",
|
||||
@@ -1453,6 +1455,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktobris",
|
||||
"Off": "Izslēgts",
|
||||
"Okay, Let's Go!": "Labi, ejam!",
|
||||
@@ -1519,6 +1522,8 @@
|
||||
"Output format": "Izvades formāts",
|
||||
"Output Format": "Izvades formāts",
|
||||
"Overview": "Pārskats",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "lapa",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2023,6 +2028,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Šī opcija iestata maksimālo tokenu skaitu, ko modelis var ģenerēt savā atbildē. Šī ierobežojuma palielināšana ļauj modelim sniegt garākas atbildes, bet var arī palielināt nevēlama vai neatbilstoša satura ģenerēšanas iespējamību.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Šī opcija dzēsīs visus esošos failus kolekcijā un aizstās tos ar jaunaugšupielādētiem failiem.",
|
||||
"This response was generated by \"{{model}}\"": "Šo atbildi ģenerēja \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Tas dzēsīs",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Tas dzēsīs <strong>{{NAME}}</strong> un <strong>visu tā saturu</strong>.",
|
||||
"This will delete all models including custom models": "Tas dzēsīs visus modeļus, ieskaitot pielāgotos modeļus",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
"Account Activation Pending": "Pengaktifan Akaun belum selesai",
|
||||
"Accurate information": "Informasi tepat",
|
||||
"Action": "Tindakan",
|
||||
"Action not found": "Tindakan tidak ditemui",
|
||||
"Action Required for Chat Log Storage": "Tindakan diperlukan untuk menyimpan log sembang",
|
||||
"Actions": "Tindakan",
|
||||
"Activate": "Aktifkan",
|
||||
@@ -158,7 +157,6 @@
|
||||
"Always Play Notification Sound": "Sentiasa Mainkan Bunyi Pemberitahuan",
|
||||
"Amazing": "Hebat",
|
||||
"an assistant": "seorang pembantu",
|
||||
"An error occurred while fetching the explanation": "Ralat berlaku semasa mengambil penjelasan",
|
||||
"Analytics": "Analitik",
|
||||
"Analyzed": "Sudah dianalisis",
|
||||
"Analyzing...": "Menganalisis...",
|
||||
@@ -578,6 +576,7 @@
|
||||
"Disable Image Extraction": "Nyahlumpuhkan Pengekstrakan Imej",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Melumpuhkan pengekstrakan imej daripada PDF. Jika Gunakan LLM didayakan, imej akan diselia secara automatik. Lalai kepada Palsu.",
|
||||
"Disabled": "Dilumpuhkan",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Temui fungsi",
|
||||
"Discover a model": "Temui model",
|
||||
"Discover a prompt": "Temui arahan",
|
||||
@@ -767,6 +766,8 @@
|
||||
"Enter New Password": "Masukkan Kata Laluan Baharu",
|
||||
"Enter Number of Steps (e.g. 50)": "Masukkan Bilangan Langkah (cth 50)",
|
||||
"Enter Ollama Cloud API Key": "Masukkan Kunci API Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Masukkan Kunci API Perplexity",
|
||||
"Enter Perplexity Search API URL": "Masukkan URL API Pencarian Perplexity",
|
||||
"Enter Playwright Timeout": "Masukkan Masa Tamat Playwright",
|
||||
@@ -897,6 +898,7 @@
|
||||
"Failed to create API Key.": "Gagal mencipta kekunci API",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Gagal memadamkan nota",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "Gagal memuat turun imej",
|
||||
"Failed to extract content from the file: {{error}}": "Gagal mengekstrak kandungan daripada fail: {{error}}",
|
||||
"Failed to extract content from the file.": "Gagal mengekstrak kandungan daripada fail.",
|
||||
@@ -1451,6 +1453,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Oktober",
|
||||
"Off": "Mati",
|
||||
"Okay, Let's Go!": "Baiklah, Jom!",
|
||||
@@ -1517,6 +1520,8 @@
|
||||
"Output format": "Format output",
|
||||
"Output Format": "Format Output",
|
||||
"Overview": "Gambaran Keseluruhan",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "halaman",
|
||||
"Page": "Halaman",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "Mode Halaman membuat satu dokumen per halaman. Mode Tunggal menggabungkan semua halaman ke dalam satu dokumen untuk chunking yang lebih baik merentas sempadan halaman.",
|
||||
@@ -2017,6 +2022,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Pilihan ini menetapkan bilangan maksimum token yang boleh dijana oleh model dalam responsnya. Meningkatkan had ini membenarkan model memberikan jawapan yang lebih panjang, tetapi ia juga mungkin meningkatkan kemungkinan kandungan yang tidak berguna atau tidak relevan dijana.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Pilihan ini akan memadamkan semua fail sedia ada dalam koleksi dan menggantinya dengan fail yang baru dimuat naik.",
|
||||
"This response was generated by \"{{model}}\"": "Respons ini dijana oleh \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Ini akan memadam",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Ini akan memadam <strong>{{NAME}}</strong> dan <strong>semua kandungannya</strong>.",
|
||||
"This will delete all models including custom models": "Ini akan memadam semua model termasuk model tersuai",
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "Venter på kontoaktivering",
|
||||
"Accurate information": "Nøyaktig informasjon",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "Handling kreves for å lagre chatlogg",
|
||||
"Actions": "Handlinger",
|
||||
"Activate": "Aktiver",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "Flott",
|
||||
"an assistant": "en assistent",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "Analysert",
|
||||
"Analyzing...": "Analyserer...",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "Deaktivert",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Oppdag en funksjon",
|
||||
"Discover a model": "Oppdag en modell",
|
||||
"Discover a prompt": "Oppdag en ledetekst",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "Angi antall steg (f.eks. 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "Kan ikke opprette en API-nøkkel.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "OAuth-ID",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "oktober",
|
||||
"Off": "Av",
|
||||
"Okay, Let's Go!": "OK, kjør på!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "Format på utdata",
|
||||
"Output Format": "",
|
||||
"Overview": "Oversikt",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "side",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Dette alternativet sletter alle eksisterende filer i samlingen og erstatter dem med nyopplastede filer.",
|
||||
"This response was generated by \"{{model}}\"": "Dette svaret er generert av \"{{modell}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "Dette sletter",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "Dette sletter <strong>{{NAME}}</strong> og <strong>alt innholdet</strong>.",
|
||||
"This will delete all models including custom models": "Dette sletter alle modeller, inkludert tilpassede modeller",
|
||||
|
||||
+1233
-1227
File diff suppressed because it is too large
Load Diff
@@ -58,7 +58,6 @@
|
||||
"Account Activation Pending": "",
|
||||
"Accurate information": "ਸਹੀ ਜਾਣਕਾਰੀ",
|
||||
"Action": "",
|
||||
"Action not found": "",
|
||||
"Action Required for Chat Log Storage": "ਚੈਟ ਲਾਗ ਸੰਭਾਲਣ ਲਈ ਕਾਰਵਾਈ ਲੋੜੀਂਦੀ ਹੈ",
|
||||
"Actions": "",
|
||||
"Activate": "",
|
||||
@@ -159,7 +158,6 @@
|
||||
"Always Play Notification Sound": "",
|
||||
"Amazing": "",
|
||||
"an assistant": "ਇੱਕ ਸਹਾਇਕ",
|
||||
"An error occurred while fetching the explanation": "",
|
||||
"Analytics": "",
|
||||
"Analyzed": "",
|
||||
"Analyzing...": "",
|
||||
@@ -579,6 +577,7 @@
|
||||
"Disable Image Extraction": "",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "",
|
||||
"Disabled": "ਬੰਦ",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "",
|
||||
"Discover a model": "ਇੱਕ ਮਾਡਲ ਲੱਭੋ",
|
||||
"Discover a prompt": "ਇੱਕ ਪ੍ਰੰਪਟ ਖੋਜੋ",
|
||||
@@ -768,6 +767,8 @@
|
||||
"Enter New Password": "",
|
||||
"Enter Number of Steps (e.g. 50)": "ਕਦਮਾਂ ਦੀ ਗਿਣਤੀ ਦਰਜ ਕਰੋ (ਉਦਾਹਰਣ ਲਈ 50)",
|
||||
"Enter Ollama Cloud API Key": "",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "",
|
||||
"Enter Perplexity Search API URL": "",
|
||||
"Enter Playwright Timeout": "",
|
||||
@@ -898,6 +899,7 @@
|
||||
"Failed to create API Key.": "API ਕੁੰਜੀ ਬਣਾਉਣ ਵਿੱਚ ਅਸਫਲ।",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "",
|
||||
"Failed to extract content from the file.": "",
|
||||
@@ -1452,6 +1454,7 @@
|
||||
"OAuth 2.1": "",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "ਅਕਤੂਬਰ",
|
||||
"Off": "ਬੰਦ",
|
||||
"Okay, Let's Go!": "ਠੀਕ ਹੈ, ਚੱਲੋ ਚੱਲੀਏ!",
|
||||
@@ -1518,6 +1521,8 @@
|
||||
"Output format": "",
|
||||
"Output Format": "",
|
||||
"Overview": "",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2020,6 +2025,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "",
|
||||
"This response was generated by \"{{model}}\"": "",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "",
|
||||
"This will delete all models including custom models": "",
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
"Account Activation Pending": "Aktywacja konta w toku",
|
||||
"Accurate information": "Precyzyjne informacje",
|
||||
"Action": "Akcja",
|
||||
"Action not found": "Nie znaleziono akcji",
|
||||
"Action Required for Chat Log Storage": "Wymagane działanie, aby zapisać historię czatu",
|
||||
"Actions": "Akcje",
|
||||
"Activate": "Aktywuj",
|
||||
@@ -161,7 +160,6 @@
|
||||
"Always Play Notification Sound": "Zawsze odtwarzaj dźwięk powiadomienia",
|
||||
"Amazing": "Niesamowite",
|
||||
"an assistant": "asystent",
|
||||
"An error occurred while fetching the explanation": "Wystąpił błąd podczas pobierania wyjaśnienia",
|
||||
"Analytics": "Analityka",
|
||||
"Analyzed": "Przeanalizowano",
|
||||
"Analyzing...": "Analizowanie...",
|
||||
@@ -581,6 +579,7 @@
|
||||
"Disable Image Extraction": "Wyłącz ekstrakcję obrazów",
|
||||
"Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.": "Wyłącz wyciąganie obrazów z PDF. Jeśli używasz LLM, obrazy będą automatycznie opisywane. Domyślnie Wyłączone.",
|
||||
"Disabled": "Wyłączone",
|
||||
"Disconnect OAuth": "",
|
||||
"Discover a function": "Odkryj funkcję",
|
||||
"Discover a model": "Odkryj model",
|
||||
"Discover a prompt": "Odkryj prompt",
|
||||
@@ -770,6 +769,8 @@
|
||||
"Enter New Password": "Wprowadź nowe hasło",
|
||||
"Enter Number of Steps (e.g. 50)": "Wprowadź liczbę kroków (np. 50)",
|
||||
"Enter Ollama Cloud API Key": "Wprowadź klucz API Ollama Cloud",
|
||||
"Enter PaddleOCR-vl API Base URL": "",
|
||||
"Enter PaddleOCR-vl API Token": "",
|
||||
"Enter Perplexity API Key": "Wprowadź klucz API Perplexity",
|
||||
"Enter Perplexity Search API URL": "Wprowadź URL API Perplexity Search",
|
||||
"Enter Playwright Timeout": "Wprowadź timeout Playwright",
|
||||
@@ -900,6 +901,7 @@
|
||||
"Failed to create API Key.": "Nie udało się utworzyć klucza API.",
|
||||
"Failed to delete calendar": "",
|
||||
"Failed to delete note": "Nie udało się usunąć notatki",
|
||||
"Failed to disconnect": "",
|
||||
"Failed to download image": "",
|
||||
"Failed to extract content from the file: {{error}}": "Nie udało się wyodrębnić treści z pliku: {{error}}",
|
||||
"Failed to extract content from the file.": "Nie udało się wyodrębnić treści z pliku.",
|
||||
@@ -1454,6 +1456,7 @@
|
||||
"OAuth 2.1": "OAuth 2.1",
|
||||
"OAuth 2.1 (Static)": "",
|
||||
"OAuth ID": "ID OAuth",
|
||||
"OAuth session disconnected": "",
|
||||
"October": "Październik",
|
||||
"Off": "Wył.",
|
||||
"Okay, Let's Go!": "OK, Jedziemy!",
|
||||
@@ -1520,6 +1523,8 @@
|
||||
"Output format": "Format wyjściowy",
|
||||
"Output Format": "Format wyjściowy",
|
||||
"Overview": "Przegląd",
|
||||
"PaddleOCR-vl": "",
|
||||
"PaddleOCR-vl API URL required.": "",
|
||||
"page": "strona",
|
||||
"Page": "",
|
||||
"Page mode creates one document per page. Single mode combines all pages into one document for better chunking across page boundaries.": "",
|
||||
@@ -2026,6 +2031,7 @@
|
||||
"This option sets the maximum number of tokens the model can generate in its response. Increasing this limit allows the model to provide longer answers, but it may also increase the likelihood of unhelpful or irrelevant content being generated.": "Ustawia maksymalną liczbę tokenów w odpowiedzi. Zwiększenie pozwala na dłuższe odpowiedzi.",
|
||||
"This option will delete all existing files in the collection and replace them with newly uploaded files.": "Ta opcja usunie wszystkie pliki z kolekcji i zastąpi nowymi.",
|
||||
"This response was generated by \"{{model}}\"": "Odpowiedź wygenerowana przez \"{{model}}\"",
|
||||
"This template contains multiple context placeholders ([context] or {{CONTEXT}}). Context will be injected at each occurrence.": "",
|
||||
"This will delete": "To usunie",
|
||||
"This will delete <strong>{{NAME}}</strong> and <strong>all its contents</strong>.": "To usunie <strong>{{NAME}}</strong> i <strong>całą zawartość</strong>.",
|
||||
"This will delete all models including custom models": "To usunie wszystkie modele (w tym własne).",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user