Compare commits

...

28 Commits

Author SHA1 Message Date
Ruben Talstra
e34503edce 🔧 feat: Localize redirect message to OpenID provider in Login component 2025-03-10 12:55:46 +01:00
Ruben Talstra
14bd9f03fa 🔧 refactor: Update getLoginError to use TranslationKeys for improved type safety 2025-03-10 12:48:42 +01:00
Ruben Talstra
17b0f35f93 🔧 feat: Implement custom logout redirect handling and enhance OpenID auto-redirect logic 2025-03-10 11:52:36 +01:00
Ruben Talstra
a2f953460b Merge branch 'main' into feat/oidc-auto-redirect 2025-03-10 09:35:49 +01:00
github-actions[bot]
9db00edfc4 🌍 i18n: Update translation.json with latest translations (#6241)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-09 18:19:50 -04:00
sh4shii
a53638c481 🐛 fix: Await saveMessage in abortMiddleware to ensure proper execution (#6248) 2025-03-09 18:07:31 -04:00
Danny Avila
d6ab769b80 ⚠️ refactor: Use Error Content Part Instead Of Throwing Error for Agents (#6262) 2025-03-09 18:06:34 -04:00
Ruben Talstra
3e3dfe5bad 🔏 fix: Enhance Two-Factor Authentication (#6247)
* 🌟 feat: Implement Two-Factor Authentication (2FA) functionality

* fix: Two-Factor Authentication Logic and State Management

* 🌟 feat: Add LICENSE file and update package version to 0.0.2 with MIT license
2025-03-08 15:28:27 -05:00
Ruben Talstra
cc661c95ee 🔧 fix: MeiliSearch Field Error and Patch Incorrect Import by #6210 (#6245)
* 📦 refactor: Update MeiliSearch integration and improve schema handling

* Update indexSync.js

* 📦 refactor: Update Conversation model import path in indexSync.js

* 📦 refactor: Update import paths for Conversation and Message models in indexSync.js
2025-03-08 14:37:33 -05:00
github-actions[bot]
6ea88e09a2 🌍 i18n: Update translation.json with latest translations (#6240)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-08 11:18:04 -05:00
Danny Avila
a846e898a2 🐛 fix: Avatar Type Definitions in Agent/Assistant Schemas (#6235)
* fix: Simplify avatar type definition in agent and assistant schemas

* fix: Update regex to correctly match OpenAI model identifiers
2025-03-08 10:55:06 -05:00
Danny Avila
dc8d5dee6a 📦 chore: Patch axios to address CVE-2025-27152 (#6222)
* 📦 chore: remove `langchain` (no longer used)

* chore: patch `axios` to address CVE-2025-27152
2025-03-07 12:45:31 -05:00
github-actions[bot]
f04ae65a75 🌍 i18n: Update translation.json with latest translations (#6220)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-07 12:43:40 -05:00
Danny Avila
0a4da06fe1 📦 ci: Update npm authentication token for publishing in workflow 2025-03-07 12:20:56 -05:00
Danny Avila
932474c44e 📦 ci: Refactor workflow to combine build and publish steps with version check for @librechat/data-schemas 2025-03-07 12:18:03 -05:00
Danny Avila
a2b7812033 📦 ci: Update workflow to publish @librechat/data-schemas to NPM with manual trigger option 2025-03-07 12:12:30 -05:00
Danny Avila
88d2920b06 📦 ci: npm publish access to public for data-schemas 2025-03-07 12:10:32 -05:00
Ruben Talstra
c5e012abc0 🌍 i18n: Add Thai Language Support and Update Translations (#6219)
* 🌍 i18n: Add Thai Language Support and Update Translations

* 📝 docs: Update Locize Logo in README.md
2025-03-07 11:57:57 -05:00
Ruben Talstra
b51cd21b3c 📦 refactor: Move DB Models to @librechat/data-schemas (#6210)
* 🚀 feat: Introduce data schemas and refactor models to use @librechat/data-schemas

* 🚀 feat: Add installation step for Data Schemas Package in backend review workflow

* chore: Add `data-schemas` package to update/rebuild packages scripts

* chore: Update Dockerfile to include data-schemas package build process

* fix: add missing @rollup/plugin-typescript package

* chore: Add GitHub Actions workflow for publishing data-schemas package

---------

Co-authored-by: Danny Avila <danny@librechat.ai>
2025-03-07 11:55:44 -05:00
Danny Avila
4d04904af3 v0.7.7 (#6206)
* v0.7.7

* chore: Bump librechat-mcp version to 1.1.0

* action: update Unreleased changelog

* Update CHANGELOG.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ruben Talstra <RubenTalstra1211@outlook.com>
2025-03-06 14:33:33 -05:00
Danny Avila
8cb7f34f86 🚀 feat: Add Code API Proxy Support and Update MCP SDK (#6203)
* chore: bump mcp sdk

* feat: Add proxy support for file download and upload in Code Environment CRUD operations

* chore: remove unused files

* chore: change output format from CommonJS to ES module in server rollup config
2025-03-06 12:47:59 -05:00
Kaushik Iska
780fdf743a 🕒 feat: Add Configurable MCP Server Timeouts (#6199) 2025-03-06 12:02:43 -05:00
Danny Avila
c8f7588164 🪄 feat: Customize Sandpack bundlerURL for Artifacts (#6191) 2025-03-05 16:03:54 -05:00
Danny Avila
00b2d026c1 🚀 feat: Enhance Model Handling, Logging & xAI Agent Support (#6182)
* chore: update @librechat/agents to version 2.1.9

* feat: xAI standalone provider for agents

* chore: bump librechat-data-provider version to 0.7.6997

* fix: reorder import statements and enhance user listing output

* fix: Update Docker Compose commands to support v2 syntax with fallback

* 🔧 fix: drop `reasoning_effort` for o1-preview/mini models

* chore: requireLocalAuth logging

* fix: edge case artifact message editing logic to handle `new` conversation IDs

* fix: remove `temperature` from model options in OpenAIClient if o1-mini/preview

* fix: update type annotation for fetchPromisesMap to use Promise<string[]> instead of string[]

* feat: anthropic model fetching

* fix: update model name to use EModelEndpoint.openAI in fetchModels and fetchOpenAIModels

* fix: add error handling to modelController for loadModels

* fix: add error handling and logging for model fetching in loadDefaultModels

* ci: update getAnthropicModels tests to be asynchronous

* feat: add user ID to model options in OpenAI and custom endpoint initialization

---------

Co-authored-by: Andrei Berceanu <andreicberceanu@gmail.com>
Co-authored-by: KiGamji <maloyh44@gmail.com>
2025-03-05 12:04:26 -05:00
github-actions[bot]
287699331c 🌍 i18n: Update translation.json with latest translations (#6159)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-03 19:11:33 -05:00
Danny Avila
ceb0da874b 🧠 feat: Bedrock Anthropic Reasoning & Update Endpoint Handling (#6163)
* feat: Add thinking and thinkingBudget parameters for Bedrock Anthropic models

* chore: Update @librechat/agents to version 2.1.8

* refactor: change region order in params

* refactor: Add maxTokens parameter to conversation preset schema

* refactor: Update agent client to use bedrockInputSchema and improve error handling for model parameters

* refactor: streamline/optimize llmConfig initialization and saving for bedrock

* fix: ensure config titleModel is used for all endpoints

* refactor: enhance OpenAIClient and agent initialization to support endpoint checks for OpenRouter

* chore: bump @google/generative-ai
2025-03-03 19:09:22 -05:00
Danilo Pejakovic
bfc7179f16 Added Cooldown logic for OIDC auto redirect for failed login attempts 2025-02-27 10:58:52 +01:00
Danilo Pejakovic
caaadf2fdb added feature for oidc auto redirection 2025-02-26 15:39:55 +01:00
170 changed files with 6087 additions and 3506 deletions

View File

@@ -432,6 +432,9 @@ OPENID_NAME_CLAIM=
OPENID_BUTTON_LABEL=
OPENID_IMAGE_URL=
# Set to true to automatically redirect to the OpenID provider when a user visits the login page
# This will bypass the login form completely for users, only use this if OpenID is your only authentication method
OPENID_AUTO_REDIRECT=false
# LDAP
LDAP_URL=

View File

@@ -39,6 +39,9 @@ jobs:
- name: Install MCP Package
run: npm run build:mcp
- name: Install Data Schemas Package
run: npm run build:data-schemas
- name: Create empty auth.json file
run: |
mkdir -p api/data

58
.github/workflows/data-schemas.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Publish `@librechat/data-schemas` to NPM
on:
push:
branches:
- main
paths:
- 'packages/data-schemas/package.json'
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual trigger'
required: false
default: 'Manual publish requested'
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '18.x'
- name: Install dependencies
run: cd packages/data-schemas && npm ci
- name: Build
run: cd packages/data-schemas && npm run build
- name: Set up npm authentication
run: echo "//registry.npmjs.org/:_authToken=${{ secrets.PUBLISH_NPM_TOKEN }}" > ~/.npmrc
- name: Check version change
id: check
working-directory: packages/data-schemas
run: |
PACKAGE_VERSION=$(node -p "require('./package.json').version")
PUBLISHED_VERSION=$(npm view @librechat/data-schemas version 2>/dev/null || echo "0.0.0")
if [ "$PACKAGE_VERSION" = "$PUBLISHED_VERSION" ]; then
echo "No version change, skipping publish"
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "Version changed, proceeding with publish"
echo "skip=false" >> $GITHUB_OUTPUT
fi
- name: Pack package
if: steps.check.outputs.skip != 'true'
working-directory: packages/data-schemas
run: npm pack
- name: Publish
if: steps.check.outputs.skip != 'true'
working-directory: packages/data-schemas
run: npm publish *.tgz --access public

View File

@@ -84,11 +84,11 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
sign-commits: true
commit-message: "chore: update CHANGELOG for release ${GITHUB_REF##*/}"
commit-message: "chore: update CHANGELOG for release ${{ github.ref_name }}"
base: main
branch: "changelog/${GITHUB_REF##*/}"
branch: "changelog/${{ github.ref_name }}"
reviewers: danny-avila
title: "chore: update CHANGELOG for release ${GITHUB_REF##*/}"
title: "chore: update CHANGELOG for release ${{ github.ref_name }}"
body: |
**Description**:
- This PR updates the CHANGELOG.md by removing the "Unreleased" section and adding new release notes for release ${GITHUB_REF##*/} above previous releases.
- This PR updates the CHANGELOG.md by removing the "Unreleased" section and adding new release notes for release ${{ github.ref_name }} above previous releases.

16
CHANGELOG.md Normal file
View File

@@ -0,0 +1,16 @@
# Changelog
All notable changes to this project will be documented in this file.
## [Unreleased]
### ✨ New Features
- 🪄 feat: Agent Artifacts by **@danny-avila** in [#5804](https://github.com/danny-avila/LibreChat/pull/5804)
### ⚙️ Other Changes
- 🔄 chore: Enforce 18next Language Keys by **@rubentalstra** in [#5803](https://github.com/danny-avila/LibreChat/pull/5803)
- 🔃 refactor: Parent Message ID Handling on Error, Update Translations, Bump Agents by **@danny-avila** in [#5833](https://github.com/danny-avila/LibreChat/pull/5833)
---

View File

@@ -1,4 +1,4 @@
# v0.7.7-rc1
# v0.7.7
# Base node image
FROM node:20-alpine AS node

View File

@@ -1,5 +1,5 @@
# Dockerfile.multi
# v0.7.7-rc1
# v0.7.7
# Base for all builds
FROM node:20-alpine AS base-min
@@ -11,6 +11,7 @@ RUN npm config set fetch-retry-maxtimeout 600000 && \
COPY package*.json ./
COPY packages/data-provider/package*.json ./packages/data-provider/
COPY packages/mcp/package*.json ./packages/mcp/
COPY packages/data-schemas/package*.json ./packages/data-schemas/
COPY client/package*.json ./client/
COPY api/package*.json ./api/
@@ -32,6 +33,13 @@ COPY packages/mcp ./
COPY --from=data-provider-build /app/packages/data-provider/dist /app/packages/data-provider/dist
RUN npm run build
# Build data-schemas
FROM base AS data-schemas-build
WORKDIR /app/packages/data-schemas
COPY packages/data-schemas ./
COPY --from=data-provider-build /app/packages/data-provider/dist /app/packages/data-provider/dist
RUN npm run build
# Client build
FROM base AS client-build
WORKDIR /app/client
@@ -49,8 +57,9 @@ COPY api ./api
COPY config ./config
COPY --from=data-provider-build /app/packages/data-provider/dist ./packages/data-provider/dist
COPY --from=mcp-build /app/packages/mcp/dist ./packages/mcp/dist
COPY --from=data-schemas-build /app/packages/data-schemas/dist ./packages/data-schemas/dist
COPY --from=client-build /app/client/dist ./client/dist
WORKDIR /app/api
EXPOSE 3080
ENV HOST=0.0.0.0
CMD ["node", "server/index.js"]
CMD ["node", "server/index.js"]

View File

@@ -197,6 +197,6 @@ We thank [Locize](https://locize.com) for their translation management tools tha
<p align="center">
<a href="https://locize.com" target="_blank" rel="noopener noreferrer">
<img src="https://locize.com/img/locize_color.svg" alt="Locize Logo" height="50">
<img src="https://github.com/user-attachments/assets/d6b70894-6064-475e-bb65-92a9e23e0077" alt="Locize Logo" height="50">
</a>
</p>

View File

@@ -827,7 +827,8 @@ class GoogleClient extends BaseClient {
let reply = '';
const { abortController } = options;
const model = this.modelOptions.modelName ?? this.modelOptions.model ?? '';
const model =
this.options.titleModel ?? this.modelOptions.modelName ?? this.modelOptions.model ?? '';
const safetySettings = getSafetySettings(model);
if (!EXCLUDED_GENAI_MODELS.test(model) && !this.project_id) {
logger.debug('Identified titling model as GenAI version');

View File

@@ -112,7 +112,12 @@ class OpenAIClient extends BaseClient {
const { OPENAI_FORCE_PROMPT } = process.env ?? {};
const { reverseProxyUrl: reverseProxy } = this.options;
if (!this.useOpenRouter && reverseProxy && reverseProxy.includes(KnownEndpoints.openrouter)) {
if (
!this.useOpenRouter &&
((reverseProxy && reverseProxy.includes(KnownEndpoints.openrouter)) ||
(this.options.endpoint &&
this.options.endpoint.toLowerCase().includes(KnownEndpoints.openrouter)))
) {
this.useOpenRouter = true;
}
@@ -1302,8 +1307,12 @@ ${convo}
) {
delete modelOptions.stream;
delete modelOptions.stop;
} else if (!this.isOmni && modelOptions.reasoning_effort != null) {
} else if (
(!this.isOmni || /^o1-(mini|preview)/i.test(modelOptions.model)) &&
modelOptions.reasoning_effort != null
) {
delete modelOptions.reasoning_effort;
delete modelOptions.temperature;
}
let reasoningKey = 'reasoning_content';

View File

@@ -325,4 +325,37 @@ describe('formatAgentMessages', () => {
);
expect(result[0].content).not.toContain('Analyzing the problem...');
});
it('should exclude ERROR type content parts', () => {
const payload = [
{
role: 'assistant',
content: [
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello there' },
{
type: ContentTypes.ERROR,
[ContentTypes.ERROR]:
'An error occurred while processing the request: Something went wrong',
},
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Final answer' },
],
},
];
const result = formatAgentMessages(payload);
expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(AIMessage);
expect(result[0].content).toEqual([
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Hello there' },
{ type: ContentTypes.TEXT, [ContentTypes.TEXT]: 'Final answer' },
]);
// Make sure no error content exists in the result
const hasErrorContent = result[0].content.some(
(item) =>
item.type === ContentTypes.ERROR || JSON.stringify(item).includes('An error occurred'),
);
expect(hasErrorContent).toBe(false);
});
});

View File

@@ -211,6 +211,8 @@ const formatAgentMessages = (payload) => {
} else if (part.type === ContentTypes.THINK) {
hasReasoning = true;
continue;
} else if (part.type === ContentTypes.ERROR) {
continue;
} else {
currentContent.push(part);
}

View File

@@ -1,6 +1,6 @@
const { MeiliSearch } = require('meilisearch');
const Conversation = require('~/models/schema/convoSchema');
const Message = require('~/models/schema/messageSchema');
const { Conversation } = require('~/models/Conversation');
const { Message } = require('~/models/Message');
const { isEnabled } = require('~/server/utils');
const { logger } = require('~/config');

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const actionSchema = require('./schema/action');
const { actionSchema } = require('@librechat/data-schemas');
const Action = mongoose.model('action', actionSchema);

View File

@@ -9,7 +9,7 @@ const {
removeAgentFromAllProjects,
} = require('./Project');
const getLogStores = require('~/cache/getLogStores');
const agentSchema = require('./schema/agent');
const { agentSchema } = require('@librechat/data-schemas');
const Agent = mongoose.model('agent', agentSchema);

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const assistantSchema = require('./schema/assistant');
const { assistantSchema } = require('@librechat/data-schemas');
const Assistant = mongoose.model('assistant', assistantSchema);

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const balanceSchema = require('./schema/balance');
const { balanceSchema } = require('@librechat/data-schemas');
const { getMultiplier } = require('./tx');
const { logger } = require('~/config');

View File

@@ -1,5 +1,9 @@
const Banner = require('./schema/banner');
const mongoose = require('mongoose');
const logger = require('~/config/winston');
const { bannerSchema } = require('@librechat/data-schemas');
const Banner = mongoose.model('Banner', bannerSchema);
/**
* Retrieves the current active banner.
* @returns {Promise<Object|null>} The active banner object or null if no active banner is found.

View File

@@ -1,5 +1,4 @@
const { logger } = require('~/config');
// const { Categories } = require('./schema/categories');
const options = [
{

View File

@@ -1,7 +1,11 @@
const ConversationTag = require('./schema/conversationTagSchema');
const mongoose = require('mongoose');
const Conversation = require('./schema/convoSchema');
const logger = require('~/config/winston');
const { conversationTagSchema } = require('@librechat/data-schemas');
const ConversationTag = mongoose.model('ConversationTag', conversationTagSchema);
/**
* Retrieves all conversation tags for a user.
* @param {string} user - The user ID.

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const fileSchema = require('./schema/fileSchema');
const { fileSchema } = require('@librechat/data-schemas');
const File = mongoose.model('File', fileSchema);
@@ -7,7 +7,7 @@ const File = mongoose.model('File', fileSchema);
* Finds a file by its file_id with additional query options.
* @param {string} file_id - The unique identifier of the file.
* @param {object} options - Query options for filtering, projection, etc.
* @returns {Promise<MongoFile>} A promise that resolves to the file document or null.
* @returns {Promise<IMongoFile>} A promise that resolves to the file document or null.
*/
const findFileById = async (file_id, options = {}) => {
return await File.findOne({ file_id, ...options }).lean();
@@ -17,7 +17,7 @@ const findFileById = async (file_id, options = {}) => {
* Retrieves files matching a given filter, sorted by the most recently updated.
* @param {Object} filter - The filter criteria to apply.
* @param {Object} [_sortOptions] - Optional sort parameters.
* @returns {Promise<Array<MongoFile>>} A promise that resolves to an array of file documents.
* @returns {Promise<Array<IMongoFile>>} A promise that resolves to an array of file documents.
*/
const getFiles = async (filter, _sortOptions) => {
const sortOptions = { updatedAt: -1, ..._sortOptions };
@@ -26,9 +26,9 @@ const getFiles = async (filter, _sortOptions) => {
/**
* Creates a new file with a TTL of 1 hour.
* @param {MongoFile} data - The file data to be created, must contain file_id.
* @param {IMongoFile} data - The file data to be created, must contain file_id.
* @param {boolean} disableTTL - Whether to disable the TTL.
* @returns {Promise<MongoFile>} A promise that resolves to the created file document.
* @returns {Promise<IMongoFile>} A promise that resolves to the created file document.
*/
const createFile = async (data, disableTTL) => {
const fileData = {
@@ -48,8 +48,8 @@ const createFile = async (data, disableTTL) => {
/**
* Updates a file identified by file_id with new data and removes the TTL.
* @param {MongoFile} data - The data to update, must contain file_id.
* @returns {Promise<MongoFile>} A promise that resolves to the updated file document.
* @param {IMongoFile} data - The data to update, must contain file_id.
* @returns {Promise<IMongoFile>} A promise that resolves to the updated file document.
*/
const updateFile = async (data) => {
const { file_id, ...update } = data;
@@ -62,8 +62,8 @@ const updateFile = async (data) => {
/**
* Increments the usage of a file identified by file_id.
* @param {MongoFile} data - The data to update, must contain file_id and the increment value for usage.
* @returns {Promise<MongoFile>} A promise that resolves to the updated file document.
* @param {IMongoFile} data - The data to update, must contain file_id and the increment value for usage.
* @returns {Promise<IMongoFile>} A promise that resolves to the updated file document.
*/
const updateFileUsage = async (data) => {
const { file_id, inc = 1 } = data;
@@ -77,7 +77,7 @@ const updateFileUsage = async (data) => {
/**
* Deletes a file identified by file_id.
* @param {string} file_id - The unique identifier of the file to delete.
* @returns {Promise<MongoFile>} A promise that resolves to the deleted file document or null.
* @returns {Promise<IMongoFile>} A promise that resolves to the deleted file document or null.
*/
const deleteFile = async (file_id) => {
return await File.findOneAndDelete({ file_id }).lean();
@@ -86,7 +86,7 @@ const deleteFile = async (file_id) => {
/**
* Deletes a file identified by a filter.
* @param {object} filter - The filter criteria to apply.
* @returns {Promise<MongoFile>} A promise that resolves to the deleted file document or null.
* @returns {Promise<IMongoFile>} A promise that resolves to the deleted file document or null.
*/
const deleteFileByFilter = async (filter) => {
return await File.findOneAndDelete(filter).lean();

View File

@@ -1,4 +1,4 @@
const mongoose = require('mongoose');
const keySchema = require('./schema/key');
const { keySchema } = require('@librechat/data-schemas');
module.exports = mongoose.model('Key', keySchema);

View File

@@ -1,6 +1,6 @@
const { model } = require('mongoose');
const { GLOBAL_PROJECT_NAME } = require('librechat-data-provider').Constants;
const projectSchema = require('~/models/schema/projectSchema');
const { projectSchema } = require('@librechat/data-schemas');
const Project = model('Project', projectSchema);
@@ -9,7 +9,7 @@ const Project = model('Project', projectSchema);
*
* @param {string} projectId - The ID of the project to find and return as a plain object.
* @param {string|string[]} [fieldsToSelect] - The fields to include or exclude in the returned document.
* @returns {Promise<MongoProject>} A plain object representing the project document, or `null` if no project is found.
* @returns {Promise<IMongoProject>} A plain object representing the project document, or `null` if no project is found.
*/
const getProjectById = async function (projectId, fieldsToSelect = null) {
const query = Project.findById(projectId);
@@ -27,7 +27,7 @@ const getProjectById = async function (projectId, fieldsToSelect = null) {
*
* @param {string} projectName - The name of the project to find or create.
* @param {string|string[]} [fieldsToSelect] - The fields to include or exclude in the returned document.
* @returns {Promise<MongoProject>} A plain object representing the project document.
* @returns {Promise<IMongoProject>} A plain object representing the project document.
*/
const getProjectByName = async function (projectName, fieldsToSelect = null) {
const query = { name: projectName };
@@ -47,7 +47,7 @@ const getProjectByName = async function (projectName, fieldsToSelect = null) {
*
* @param {string} projectId - The ID of the project to update.
* @param {string[]} promptGroupIds - The array of prompt group IDs to add to the project.
* @returns {Promise<MongoProject>} The updated project document.
* @returns {Promise<IMongoProject>} The updated project document.
*/
const addGroupIdsToProject = async function (projectId, promptGroupIds) {
return await Project.findByIdAndUpdate(
@@ -62,7 +62,7 @@ const addGroupIdsToProject = async function (projectId, promptGroupIds) {
*
* @param {string} projectId - The ID of the project to update.
* @param {string[]} promptGroupIds - The array of prompt group IDs to remove from the project.
* @returns {Promise<MongoProject>} The updated project document.
* @returns {Promise<IMongoProject>} The updated project document.
*/
const removeGroupIdsFromProject = async function (projectId, promptGroupIds) {
return await Project.findByIdAndUpdate(
@@ -87,7 +87,7 @@ const removeGroupFromAllProjects = async (promptGroupId) => {
*
* @param {string} projectId - The ID of the project to update.
* @param {string[]} agentIds - The array of agent IDs to add to the project.
* @returns {Promise<MongoProject>} The updated project document.
* @returns {Promise<IMongoProject>} The updated project document.
*/
const addAgentIdsToProject = async function (projectId, agentIds) {
return await Project.findByIdAndUpdate(
@@ -102,7 +102,7 @@ const addAgentIdsToProject = async function (projectId, agentIds) {
*
* @param {string} projectId - The ID of the project to update.
* @param {string[]} agentIds - The array of agent IDs to remove from the project.
* @returns {Promise<MongoProject>} The updated project document.
* @returns {Promise<IMongoProject>} The updated project document.
*/
const removeAgentIdsFromProject = async function (projectId, agentIds) {
return await Project.findByIdAndUpdate(

View File

@@ -1,3 +1,4 @@
const mongoose = require('mongoose');
const { ObjectId } = require('mongodb');
const { SystemRoles, SystemCategories, Constants } = require('librechat-data-provider');
const {
@@ -6,10 +7,13 @@ const {
removeGroupIdsFromProject,
removeGroupFromAllProjects,
} = require('./Project');
const { Prompt, PromptGroup } = require('./schema/promptSchema');
const { promptGroupSchema, promptSchema } = require('@librechat/data-schemas');
const { escapeRegExp } = require('~/server/utils');
const { logger } = require('~/config');
const PromptGroup = mongoose.model('PromptGroup', promptGroupSchema);
const Prompt = mongoose.model('Prompt', promptSchema);
/**
* Create a pipeline for the aggregation to get prompt groups
* @param {Object} query

View File

@@ -1,3 +1,4 @@
const mongoose = require('mongoose');
const {
CacheKeys,
SystemRoles,
@@ -12,9 +13,11 @@ const {
temporaryChatPermissionsSchema,
} = require('librechat-data-provider');
const getLogStores = require('~/cache/getLogStores');
const Role = require('~/models/schema/roleSchema');
const { roleSchema } = require('@librechat/data-schemas');
const { logger } = require('~/config');
const Role = mongoose.model('Role', roleSchema);
/**
* Retrieve a role by name and convert the found role document to a plain object.
* If the role with the given name doesn't exist and the name is a system defined role, create it and return the lean version.
@@ -168,6 +171,7 @@ const initializeRoles = async function () {
}
};
module.exports = {
Role,
getRoleByName,
initializeRoles,
updateRoleByName,

View File

@@ -8,7 +8,7 @@ const {
} = require('librechat-data-provider');
const { updateAccessPermissions, initializeRoles } = require('~/models/Role');
const getLogStores = require('~/cache/getLogStores');
const Role = require('~/models/schema/roleSchema');
const { Role } = require('~/models/Role');
// Mock the cache
jest.mock('~/cache/getLogStores', () => {

View File

@@ -1,7 +1,7 @@
const mongoose = require('mongoose');
const signPayload = require('~/server/services/signPayload');
const { hashToken } = require('~/server/utils/crypto');
const sessionSchema = require('./schema/session');
const { sessionSchema } = require('@librechat/data-schemas');
const { logger } = require('~/config');
const Session = mongoose.model('Session', sessionSchema);

View File

@@ -1,7 +1,9 @@
const mongoose = require('mongoose');
const { nanoid } = require('nanoid');
const { Constants } = require('librechat-data-provider');
const { Conversation } = require('~/models/Conversation');
const SharedLink = require('./schema/shareSchema');
const { shareSchema } = require('@librechat/data-schemas');
const SharedLink = mongoose.model('SharedLink', shareSchema);
const { getMessages } = require('./Message');
const logger = require('~/config/winston');

View File

@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { encryptV2 } = require('~/server/utils/crypto');
const tokenSchema = require('./schema/tokenSchema');
const { tokenSchema } = require('@librechat/data-schemas');
const { logger } = require('~/config');
/**

View File

@@ -1,9 +1,11 @@
const ToolCall = require('./schema/toolCallSchema');
const mongoose = require('mongoose');
const { toolCallSchema } = require('@librechat/data-schemas');
const ToolCall = mongoose.model('ToolCall', toolCallSchema);
/**
* Create a new tool call
* @param {ToolCallData} toolCallData - The tool call data
* @returns {Promise<ToolCallData>} The created tool call document
* @param {IToolCallData} toolCallData - The tool call data
* @returns {Promise<IToolCallData>} The created tool call document
*/
async function createToolCall(toolCallData) {
try {
@@ -16,7 +18,7 @@ async function createToolCall(toolCallData) {
/**
* Get a tool call by ID
* @param {string} id - The tool call document ID
* @returns {Promise<ToolCallData|null>} The tool call document or null if not found
* @returns {Promise<IToolCallData|null>} The tool call document or null if not found
*/
async function getToolCallById(id) {
try {
@@ -44,7 +46,7 @@ async function getToolCallsByMessage(messageId, userId) {
* Get tool calls by conversation ID and user
* @param {string} conversationId - The conversation ID
* @param {string} userId - The user's ObjectId
* @returns {Promise<ToolCallData[]>} Array of tool call documents
* @returns {Promise<IToolCallData[]>} Array of tool call documents
*/
async function getToolCallsByConvo(conversationId, userId) {
try {
@@ -57,8 +59,8 @@ async function getToolCallsByConvo(conversationId, userId) {
/**
* Update a tool call
* @param {string} id - The tool call document ID
* @param {Partial<ToolCallData>} updateData - The data to update
* @returns {Promise<ToolCallData|null>} The updated tool call document or null if not found
* @param {Partial<IToolCallData>} updateData - The data to update
* @returns {Promise<IToolCallData|null>} The updated tool call document or null if not found
*/
async function updateToolCall(id, updateData) {
try {

View File

@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const { isEnabled } = require('~/server/utils/handleText');
const transactionSchema = require('./schema/transaction');
const { transactionSchema } = require('@librechat/data-schemas');
const { getMultiplier, getCacheMultiplier } = require('./tx');
const { logger } = require('~/config');
const Balance = require('./Balance');

View File

@@ -1,5 +1,5 @@
const mongoose = require('mongoose');
const userSchema = require('~/models/schema/userSchema');
const { userSchema } = require('@librechat/data-schemas');
const User = mongoose.model('User', userSchema);

View File

@@ -4,9 +4,28 @@ const { MeiliSearch } = require('meilisearch');
const { cleanUpPrimaryKeyValue } = require('~/lib/utils/misc');
const logger = require('~/config/meiliLogger');
// Environment flags
/**
* Flag to indicate if search is enabled based on environment variables.
* @type {boolean}
*/
const searchEnabled = process.env.SEARCH && process.env.SEARCH.toLowerCase() === 'true';
/**
* Flag to indicate if MeiliSearch is enabled based on required environment variables.
* @type {boolean}
*/
const meiliEnabled = process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY && searchEnabled;
/**
* Validates the required options for configuring the mongoMeili plugin.
*
* @param {Object} options - The configuration options.
* @param {string} options.host - The MeiliSearch host.
* @param {string} options.apiKey - The MeiliSearch API key.
* @param {string} options.indexName - The name of the index.
* @throws {Error} Throws an error if any required option is missing.
*/
const validateOptions = function (options) {
const requiredKeys = ['host', 'apiKey', 'indexName'];
requiredKeys.forEach((key) => {
@@ -16,53 +35,64 @@ const validateOptions = function (options) {
});
};
// const createMeiliMongooseModel = function ({ index, indexName, client, attributesToIndex }) {
/**
* Factory function to create a MeiliMongooseModel class which extends a Mongoose model.
* This class contains static and instance methods to synchronize and manage the MeiliSearch index
* corresponding to the MongoDB collection.
*
* @param {Object} config - Configuration object.
* @param {Object} config.index - The MeiliSearch index object.
* @param {Array<string>} config.attributesToIndex - List of attributes to index.
* @returns {Function} A class definition that will be loaded into the Mongoose schema.
*/
const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
// The primary key is assumed to be the first attribute in the attributesToIndex array.
const primaryKey = attributesToIndex[0];
// MeiliMongooseModel is of type Mongoose.Model
class MeiliMongooseModel {
/**
* `syncWithMeili`: synchronizes the data between a MongoDB collection and a MeiliSearch index,
* only triggered if there's ever a discrepancy determined by `api\lib\db\indexSync.js`.
* Synchronizes the data between the MongoDB collection and the MeiliSearch index.
*
* 1. Fetches all documents from the MongoDB collection and the MeiliSearch index.
* 2. Compares the documents from both sources.
* 3. If a document exists in MeiliSearch but not in MongoDB, it's deleted from MeiliSearch.
* 4. If a document exists in MongoDB but not in MeiliSearch, it's added to MeiliSearch.
* 5. If a document exists in both but has different `text` or `title` fields (depending on the `primaryKey`), it's updated in MeiliSearch.
* 6. After all operations, it updates the `_meiliIndex` field in MongoDB to indicate whether the document is indexed in MeiliSearch.
* The synchronization process involves:
* 1. Fetching all documents from the MongoDB collection and MeiliSearch index.
* 2. Comparing documents from both sources.
* 3. Deleting documents from MeiliSearch that no longer exist in MongoDB.
* 4. Adding documents to MeiliSearch that exist in MongoDB but not in the index.
* 5. Updating documents in MeiliSearch if key fields (such as `text` or `title`) differ.
* 6. Updating the `_meiliIndex` field in MongoDB to indicate the indexing status.
*
* Note: This strategy does not use batch operations for Meilisearch as the `index.addDocuments` will discard
* the entire batch if there's an error with one document, and will not throw an error if there's an issue.
* Also, `index.getDocuments` needs an exact limit on the amount of documents to return, so we build the map in batches.
* Note: The function processes documents in batches because MeiliSearch's
* `index.getDocuments` requires an exact limit and `index.addDocuments` does not handle
* partial failures in a batch.
*
* @returns {Promise} A promise that resolves when the synchronization is complete.
*
* @throws {Error} Throws an error if there's an issue with adding a document to MeiliSearch.
* @returns {Promise<void>} Resolves when the synchronization is complete.
*/
static async syncWithMeili() {
try {
let moreDocuments = true;
// Retrieve all MongoDB documents from the collection as plain JavaScript objects.
const mongoDocuments = await this.find().lean();
const format = (doc) => _.pick(doc, attributesToIndex);
// Prepare for comparison
// Helper function to format a document by selecting only the attributes to index
// and omitting keys starting with '$'.
const format = (doc) =>
_.omitBy(_.pick(doc, attributesToIndex), (v, k) => k.startsWith('$'));
// Build a map of MongoDB documents for quick lookup based on the primary key.
const mongoMap = new Map(mongoDocuments.map((doc) => [doc[primaryKey], format(doc)]));
const indexMap = new Map();
let offset = 0;
const batchSize = 1000;
// Fetch documents from the MeiliSearch index in batches.
while (moreDocuments) {
const batch = await index.getDocuments({ limit: batchSize, offset });
if (batch.results.length === 0) {
moreDocuments = false;
}
for (const doc of batch.results) {
indexMap.set(doc[primaryKey], format(doc));
}
offset += batchSize;
}
@@ -70,13 +100,12 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
const updateOps = [];
// Iterate over Meili index documents
// Process documents present in the MeiliSearch index.
for (const [id, doc] of indexMap) {
const update = {};
update[primaryKey] = id;
if (mongoMap.has(id)) {
// Case: Update
// If document also exists in MongoDB, would be update case
// If document exists in MongoDB, check for discrepancies in key fields.
if (
(doc.text && doc.text !== mongoMap.get(id).text) ||
(doc.title && doc.title !== mongoMap.get(id).title)
@@ -92,8 +121,7 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
await index.addDocuments([doc]);
}
} else {
// Case: Delete
// If document does not exist in MongoDB, its a delete case from meili index
// If the document does not exist in MongoDB, delete it from MeiliSearch.
await index.deleteDocument(id);
updateOps.push({
updateOne: { filter: update, update: { $set: { _meiliIndex: false } } },
@@ -101,24 +129,25 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
}
}
// Iterate over MongoDB documents
// Process documents present in MongoDB.
for (const [id, doc] of mongoMap) {
const update = {};
update[primaryKey] = id;
// Case: Insert
// If document does not exist in Meili Index, Its an insert case
// If the document is missing in the Meili index, add it.
if (!indexMap.has(id)) {
await index.addDocuments([doc]);
updateOps.push({
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
});
} else if (doc._meiliIndex === false) {
// If the document exists but is marked as not indexed, update the flag.
updateOps.push({
updateOne: { filter: update, update: { $set: { _meiliIndex: true } } },
});
}
}
// Execute bulk update operations in MongoDB to update the _meiliIndex flags.
if (updateOps.length > 0) {
await this.collection.bulkWrite(updateOps);
logger.debug(
@@ -132,34 +161,47 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
}
}
// Set one or more settings of the meili index
/**
* Updates settings for the MeiliSearch index.
*
* @param {Object} settings - The settings to update on the MeiliSearch index.
* @returns {Promise<Object>} Promise resolving to the update result.
*/
static async setMeiliIndexSettings(settings) {
return await index.updateSettings(settings);
}
// Search the index
/**
* Searches the MeiliSearch index and optionally populates the results with data from MongoDB.
*
* @param {string} q - The search query.
* @param {Object} params - Additional search parameters for MeiliSearch.
* @param {boolean} populate - Whether to populate search hits with full MongoDB documents.
* @returns {Promise<Object>} The search results with populated hits if requested.
*/
static async meiliSearch(q, params, populate) {
const data = await index.search(q, params);
// Populate hits with content from mongodb
if (populate) {
// Find objects into mongodb matching `objectID` from Meili search
// Build a query using the primary key values from the search hits.
const query = {};
// query[primaryKey] = { $in: _.map(data.hits, primaryKey) };
query[primaryKey] = _.map(data.hits, (hit) => cleanUpPrimaryKeyValue(hit[primaryKey]));
// logger.debug('query', query);
const hitsFromMongoose = await this.find(
query,
_.reduce(
this.schema.obj,
function (results, value, key) {
return { ...results, [key]: 1 };
},
{ _id: 1, __v: 1 },
),
).lean();
// Add additional data from mongodb into Meili search hits
// Build a projection object, including only keys that do not start with '$'.
const projection = Object.keys(this.schema.obj).reduce(
(results, key) => {
if (!key.startsWith('$')) {
results[key] = 1;
}
return results;
},
{ _id: 1, __v: 1 },
);
// Retrieve the full documents from MongoDB.
const hitsFromMongoose = await this.find(query, projection).lean();
// Merge the MongoDB documents with the search hits.
const populatedHits = data.hits.map(function (hit) {
const query = {};
query[primaryKey] = hit[primaryKey];
@@ -176,10 +218,21 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
return data;
}
/**
* Preprocesses the current document for indexing.
*
* This method:
* - Picks only the defined attributes to index.
* - Omits any keys starting with '$'.
* - Replaces pipe characters ('|') in `conversationId` with '--'.
* - Extracts and concatenates text from an array of content items.
*
* @returns {Object} The preprocessed object ready for indexing.
*/
preprocessObjectForIndex() {
const object = _.pick(this.toJSON(), attributesToIndex);
// NOTE: MeiliSearch does not allow | in primary key, so we replace it with - for Bing convoIds
// object.conversationId = object.conversationId.replace(/\|/g, '-');
const object = _.omitBy(_.pick(this.toJSON(), attributesToIndex), (v, k) =>
k.startsWith('$'),
);
if (object.conversationId && object.conversationId.includes('|')) {
object.conversationId = object.conversationId.replace(/\|/g, '--');
}
@@ -195,32 +248,53 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
return object;
}
// Push new document to Meili
/**
* Adds the current document to the MeiliSearch index.
*
* The method preprocesses the document, adds it to MeiliSearch, and then updates
* the MongoDB document's `_meiliIndex` flag to true.
*
* @returns {Promise<void>}
*/
async addObjectToMeili() {
const object = this.preprocessObjectForIndex();
try {
// logger.debug('Adding document to Meili', object);
await index.addDocuments([object]);
} catch (error) {
// logger.debug('Error adding document to Meili');
// logger.error(error);
// Error handling can be enhanced as needed.
logger.error('[addObjectToMeili] Error adding document to Meili', error);
}
await this.collection.updateMany({ _id: this._id }, { $set: { _meiliIndex: true } });
}
// Update an existing document in Meili
/**
* Updates the current document in the MeiliSearch index.
*
* @returns {Promise<void>}
*/
async updateObjectToMeili() {
const object = _.pick(this.toJSON(), attributesToIndex);
const object = _.omitBy(_.pick(this.toJSON(), attributesToIndex), (v, k) =>
k.startsWith('$'),
);
await index.updateDocuments([object]);
}
// Delete a document from Meili
/**
* Deletes the current document from the MeiliSearch index.
*
* @returns {Promise<void>}
*/
async deleteObjectFromMeili() {
await index.deleteDocument(this._id);
}
// * schema.post('save')
/**
* Post-save hook to synchronize the document with MeiliSearch.
*
* If the document is already indexed (i.e. `_meiliIndex` is true), it updates it;
* otherwise, it adds the document to the index.
*/
postSaveHook() {
if (this._meiliIndex) {
this.updateObjectToMeili();
@@ -229,14 +303,24 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
}
}
// * schema.post('update')
/**
* Post-update hook to update the document in MeiliSearch.
*
* This hook is triggered after a document update, ensuring that changes are
* propagated to the MeiliSearch index if the document is indexed.
*/
postUpdateHook() {
if (this._meiliIndex) {
this.updateObjectToMeili();
}
}
// * schema.post('remove')
/**
* Post-remove hook to delete the document from MeiliSearch.
*
* This hook is triggered after a document is removed, ensuring that the document
* is also removed from the MeiliSearch index if it was previously indexed.
*/
postRemoveHook() {
if (this._meiliIndex) {
this.deleteObjectFromMeili();
@@ -247,11 +331,27 @@ const createMeiliMongooseModel = function ({ index, attributesToIndex }) {
return MeiliMongooseModel;
};
/**
* Mongoose plugin to synchronize MongoDB collections with a MeiliSearch index.
*
* This plugin:
* - Validates the provided options.
* - Adds a `_meiliIndex` field to the schema to track indexing status.
* - Sets up a MeiliSearch client and creates an index if it doesn't already exist.
* - Loads class methods for syncing, searching, and managing documents in MeiliSearch.
* - Registers Mongoose hooks (post-save, post-update, post-remove, etc.) to maintain index consistency.
*
* @param {mongoose.Schema} schema - The Mongoose schema to which the plugin is applied.
* @param {Object} options - Configuration options.
* @param {string} options.host - The MeiliSearch host.
* @param {string} options.apiKey - The MeiliSearch API key.
* @param {string} options.indexName - The name of the MeiliSearch index.
* @param {string} options.primaryKey - The primary key field for indexing.
*/
module.exports = function mongoMeili(schema, options) {
// Vaidate Options for mongoMeili
validateOptions(options);
// Add meiliIndex to schema
// Add _meiliIndex field to the schema to track if a document has been indexed in MeiliSearch.
schema.add({
_meiliIndex: {
type: Boolean,
@@ -263,69 +363,77 @@ module.exports = function mongoMeili(schema, options) {
const { host, apiKey, indexName, primaryKey } = options;
// Setup MeiliSearch Client
// Setup the MeiliSearch client.
const client = new MeiliSearch({ host, apiKey });
// Asynchronously create the index
// Create the index asynchronously if it doesn't exist.
client.createIndex(indexName, { primaryKey });
// Setup the index to search for this schema
// Setup the MeiliSearch index for this schema.
const index = client.index(indexName);
// Collect attributes from the schema that should be indexed.
const attributesToIndex = [
..._.reduce(
schema.obj,
function (results, value, key) {
return value.meiliIndex ? [...results, key] : results;
// }, []), '_id'];
},
[],
),
];
// Load the class methods into the schema.
schema.loadClass(createMeiliMongooseModel({ index, indexName, client, attributesToIndex }));
// Register hooks
// Register Mongoose hooks to synchronize with MeiliSearch.
// Post-save: synchronize after a document is saved.
schema.post('save', function (doc) {
doc.postSaveHook();
});
// Post-update: synchronize after a document is updated.
schema.post('update', function (doc) {
doc.postUpdateHook();
});
// Post-remove: synchronize after a document is removed.
schema.post('remove', function (doc) {
doc.postRemoveHook();
});
// Pre-deleteMany hook: remove corresponding documents from MeiliSearch when multiple documents are deleted.
schema.pre('deleteMany', async function (next) {
if (!meiliEnabled) {
next();
return next();
}
try {
// Check if the schema has a "messages" field to determine if it's a conversation schema.
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messages')) {
const convoIndex = client.index('convos');
const deletedConvos = await mongoose.model('Conversation').find(this._conditions).lean();
let promises = [];
for (const convo of deletedConvos) {
promises.push(convoIndex.deleteDocument(convo.conversationId));
}
const promises = deletedConvos.map((convo) =>
convoIndex.deleteDocument(convo.conversationId),
);
await Promise.all(promises);
}
// Check if the schema has a "messageId" field to determine if it's a message schema.
if (Object.prototype.hasOwnProperty.call(schema.obj, 'messageId')) {
const messageIndex = client.index('messages');
const deletedMessages = await mongoose.model('Message').find(this._conditions).lean();
let promises = [];
for (const message of deletedMessages) {
promises.push(messageIndex.deleteDocument(message.messageId));
}
const promises = deletedMessages.map((message) =>
messageIndex.deleteDocument(message.messageId),
);
await Promise.all(promises);
}
return next();
} catch (error) {
if (meiliEnabled) {
logger.error(
'[MeiliMongooseModel.deleteMany] There was an issue deleting conversation indexes upon deletion, next startup may be slow due to syncing',
'[MeiliMongooseModel.deleteMany] There was an issue deleting conversation indexes upon deletion. Next startup may be slow due to syncing.',
error,
);
}
@@ -333,17 +441,19 @@ module.exports = function mongoMeili(schema, options) {
}
});
// Post-findOneAndUpdate hook: update MeiliSearch index after a document is updated via findOneAndUpdate.
schema.post('findOneAndUpdate', async function (doc) {
if (!meiliEnabled) {
return;
}
// If the document is unfinished, do not update the index.
if (doc.unfinished) {
return;
}
let meiliDoc;
// Doc is a Conversation
// For conversation documents, try to fetch the document from the "convos" index.
if (doc.messages) {
try {
meiliDoc = await client.index('convos').getDocument(doc.conversationId);
@@ -356,10 +466,12 @@ module.exports = function mongoMeili(schema, options) {
}
}
// If the MeiliSearch document exists and the title is unchanged, do nothing.
if (meiliDoc && meiliDoc.title === doc.title) {
return;
}
// Otherwise, trigger a post-save hook to synchronize the document.
doc.postSaveHook();
});
};

View File

@@ -1,60 +0,0 @@
const mongoose = require('mongoose');
const { Schema } = mongoose;
const AuthSchema = new Schema(
{
authorization_type: String,
custom_auth_header: String,
type: {
type: String,
enum: ['service_http', 'oauth', 'none'],
},
authorization_content_type: String,
authorization_url: String,
client_url: String,
scope: String,
token_exchange_method: {
type: String,
enum: ['default_post', 'basic_auth_header', null],
},
},
{ _id: false },
);
const actionSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true,
required: true,
},
action_id: {
type: String,
index: true,
required: true,
},
type: {
type: String,
default: 'action_prototype',
},
settings: Schema.Types.Mixed,
agent_id: String,
assistant_id: String,
metadata: {
api_key: String, // private, encrypted
auth: AuthSchema,
domain: {
type: String,
required: true,
},
// json_schema: Schema.Types.Mixed,
privacy_policy_url: String,
raw_spec: String,
oauth_client_id: String, // private, encrypted
oauth_client_secret: String, // private, encrypted
},
});
// }, { minimize: false }); // Prevent removal of empty objects
module.exports = actionSchema;

View File

@@ -1,17 +0,0 @@
const mongoose = require('mongoose');
const balanceSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true,
required: true,
},
// 1000 tokenCredits = 1 mill ($0.001 USD)
tokenCredits: {
type: Number,
default: 0,
},
});
module.exports = balanceSchema;

View File

@@ -1,19 +0,0 @@
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const categoriesSchema = new Schema({
label: {
type: String,
required: true,
unique: true,
},
value: {
type: String,
required: true,
unique: true,
},
});
const categories = mongoose.model('categories', categoriesSchema);
module.exports = { Categories: categories };

View File

@@ -1,32 +0,0 @@
const mongoose = require('mongoose');
const conversationTagSchema = mongoose.Schema(
{
tag: {
type: String,
index: true,
},
user: {
type: String,
index: true,
},
description: {
type: String,
index: true,
},
count: {
type: Number,
default: 0,
},
position: {
type: Number,
default: 0,
index: true,
},
},
{ timestamps: true },
);
conversationTagSchema.index({ tag: 1, user: 1 }, { unique: true });
module.exports = mongoose.model('ConversationTag', conversationTagSchema);

View File

@@ -1,46 +1,7 @@
const mongoose = require('mongoose');
const mongoMeili = require('../plugins/mongoMeili');
const { conversationPreset } = require('./defaults');
const convoSchema = mongoose.Schema(
{
conversationId: {
type: String,
unique: true,
required: true,
index: true,
meiliIndex: true,
},
title: {
type: String,
default: 'New Chat',
meiliIndex: true,
},
user: {
type: String,
index: true,
},
messages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Message' }],
agentOptions: {
type: mongoose.Schema.Types.Mixed,
},
...conversationPreset,
agent_id: {
type: String,
},
tags: {
type: [String],
default: [],
meiliIndex: true,
},
files: {
type: [String],
},
expiredAt: {
type: Date,
},
},
{ timestamps: true },
);
const { convoSchema } = require('@librechat/data-schemas');
if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
convoSchema.plugin(mongoMeili, {
@@ -52,10 +13,6 @@ if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
});
}
convoSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
convoSchema.index({ createdAt: 1, updatedAt: 1 });
convoSchema.index({ conversationId: 1, user: 1 }, { unique: true });
const Conversation = mongoose.models.Conversation || mongoose.model('Conversation', convoSchema);
module.exports = Conversation;

View File

@@ -1,111 +0,0 @@
const { FileSources } = require('librechat-data-provider');
const mongoose = require('mongoose');
/**
* @typedef {Object} MongoFile
* @property {ObjectId} [_id] - MongoDB Document ID
* @property {number} [__v] - MongoDB Version Key
* @property {ObjectId} user - User ID
* @property {string} [conversationId] - Optional conversation ID
* @property {string} file_id - File identifier
* @property {string} [temp_file_id] - Temporary File identifier
* @property {number} bytes - Size of the file in bytes
* @property {string} filename - Name of the file
* @property {string} filepath - Location of the file
* @property {'file'} object - Type of object, always 'file'
* @property {string} type - Type of file
* @property {number} [usage=0] - Number of uses of the file
* @property {string} [context] - Context of the file origin
* @property {boolean} [embedded=false] - Whether or not the file is embedded in vector db
* @property {string} [model] - The model to identify the group region of the file (for Azure OpenAI hosting)
* @property {string} [source] - The source of the file (e.g., from FileSources)
* @property {number} [width] - Optional width of the file
* @property {number} [height] - Optional height of the file
* @property {Object} [metadata] - Metadata related to the file
* @property {string} [metadata.fileIdentifier] - Unique identifier for the file in metadata
* @property {Date} [expiresAt] - Optional expiration date of the file
* @property {Date} [createdAt] - Date when the file was created
* @property {Date} [updatedAt] - Date when the file was updated
*/
/** @type {MongooseSchema<MongoFile>} */
const fileSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
index: true,
required: true,
},
conversationId: {
type: String,
ref: 'Conversation',
index: true,
},
file_id: {
type: String,
// required: true,
index: true,
},
temp_file_id: {
type: String,
// required: true,
},
bytes: {
type: Number,
required: true,
},
filename: {
type: String,
required: true,
},
filepath: {
type: String,
required: true,
},
object: {
type: String,
required: true,
default: 'file',
},
embedded: {
type: Boolean,
},
type: {
type: String,
required: true,
},
context: {
type: String,
// required: true,
},
usage: {
type: Number,
required: true,
default: 0,
},
source: {
type: String,
default: FileSources.local,
},
model: {
type: String,
},
width: Number,
height: Number,
metadata: {
fileIdentifier: String,
},
expiresAt: {
type: Date,
expires: 3600, // 1 hour in seconds
},
},
{
timestamps: true,
},
);
fileSchema.index({ createdAt: 1, updatedAt: 1 });
module.exports = fileSchema;

View File

@@ -1,145 +1,6 @@
const mongoose = require('mongoose');
const mongoMeili = require('~/models/plugins/mongoMeili');
const messageSchema = mongoose.Schema(
{
messageId: {
type: String,
unique: true,
required: true,
index: true,
meiliIndex: true,
},
conversationId: {
type: String,
index: true,
required: true,
meiliIndex: true,
},
user: {
type: String,
index: true,
required: true,
default: null,
},
model: {
type: String,
default: null,
},
endpoint: {
type: String,
},
conversationSignature: {
type: String,
},
clientId: {
type: String,
},
invocationId: {
type: Number,
},
parentMessageId: {
type: String,
},
tokenCount: {
type: Number,
},
summaryTokenCount: {
type: Number,
},
sender: {
type: String,
meiliIndex: true,
},
text: {
type: String,
meiliIndex: true,
},
summary: {
type: String,
},
isCreatedByUser: {
type: Boolean,
required: true,
default: false,
},
unfinished: {
type: Boolean,
default: false,
},
error: {
type: Boolean,
default: false,
},
finish_reason: {
type: String,
},
_meiliIndex: {
type: Boolean,
required: false,
select: false,
default: false,
},
files: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
plugin: {
type: {
latest: {
type: String,
required: false,
},
inputs: {
type: [mongoose.Schema.Types.Mixed],
required: false,
default: undefined,
},
outputs: {
type: String,
required: false,
},
},
default: undefined,
},
plugins: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
content: {
type: [{ type: mongoose.Schema.Types.Mixed }],
default: undefined,
meiliIndex: true,
},
thread_id: {
type: String,
},
/* frontend components */
iconURL: {
type: String,
},
attachments: { type: [{ type: mongoose.Schema.Types.Mixed }], default: undefined },
/*
attachments: {
type: [
{
file_id: String,
filename: String,
filepath: String,
expiresAt: Date,
width: Number,
height: Number,
type: String,
conversationId: String,
messageId: {
type: String,
required: true,
},
toolCallId: String,
},
],
default: undefined,
},
*/
expiredAt: {
type: Date,
},
},
{ timestamps: true },
);
const { messageSchema } = require('@librechat/data-schemas');
if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
messageSchema.plugin(mongoMeili, {
@@ -149,11 +10,7 @@ if (process.env.MEILI_HOST && process.env.MEILI_MASTER_KEY) {
primaryKey: 'messageId',
});
}
messageSchema.index({ expiredAt: 1 }, { expireAfterSeconds: 0 });
messageSchema.index({ createdAt: 1 });
messageSchema.index({ messageId: 1, user: 1 }, { unique: true });
/** @type {mongoose.Model<TMessage>} */
const Message = mongoose.models.Message || mongoose.model('Message', messageSchema);
module.exports = Message;

View File

@@ -1,25 +1,5 @@
const mongoose = require('mongoose');
const pluginAuthSchema = mongoose.Schema(
{
authField: {
type: String,
required: true,
},
value: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
pluginKey: {
type: String,
},
},
{ timestamps: true },
);
const { pluginAuthSchema } = require('@librechat/data-schemas');
const PluginAuth = mongoose.models.Plugin || mongoose.model('PluginAuth', pluginAuthSchema);

View File

@@ -1,36 +1,5 @@
const mongoose = require('mongoose');
const { conversationPreset } = require('./defaults');
const presetSchema = mongoose.Schema(
{
presetId: {
type: String,
unique: true,
required: true,
index: true,
},
title: {
type: String,
default: 'New Chat',
meiliIndex: true,
},
user: {
type: String,
default: null,
},
defaultPreset: {
type: Boolean,
},
order: {
type: Number,
},
...conversationPreset,
agentOptions: {
type: mongoose.Schema.Types.Mixed,
default: null,
},
},
{ timestamps: true },
);
const { presetSchema } = require('@librechat/data-schemas');
const Preset = mongoose.models.Preset || mongoose.model('Preset', presetSchema);

View File

@@ -1,35 +0,0 @@
const { Schema } = require('mongoose');
/**
* @typedef {Object} MongoProject
* @property {ObjectId} [_id] - MongoDB Document ID
* @property {string} name - The name of the project
* @property {ObjectId[]} promptGroupIds - Array of PromptGroup IDs associated with the project
* @property {Date} [createdAt] - Date when the project was created (added by timestamps)
* @property {Date} [updatedAt] - Date when the project was last updated (added by timestamps)
*/
const projectSchema = new Schema(
{
name: {
type: String,
required: true,
index: true,
},
promptGroupIds: {
type: [Schema.Types.ObjectId],
ref: 'PromptGroup',
default: [],
},
agentIds: {
type: [String],
ref: 'Agent',
default: [],
},
},
{
timestamps: true,
},
);
module.exports = projectSchema;

View File

@@ -1,118 +0,0 @@
const mongoose = require('mongoose');
const { Constants } = require('librechat-data-provider');
const Schema = mongoose.Schema;
/**
* @typedef {Object} MongoPromptGroup
* @property {ObjectId} [_id] - MongoDB Document ID
* @property {string} name - The name of the prompt group
* @property {ObjectId} author - The author of the prompt group
* @property {ObjectId} [projectId=null] - The project ID of the prompt group
* @property {ObjectId} [productionId=null] - The project ID of the prompt group
* @property {string} authorName - The name of the author of the prompt group
* @property {number} [numberOfGenerations=0] - Number of generations the prompt group has
* @property {string} [oneliner=''] - Oneliner description of the prompt group
* @property {string} [category=''] - Category of the prompt group
* @property {string} [command] - Command for the prompt group
* @property {Date} [createdAt] - Date when the prompt group was created (added by timestamps)
* @property {Date} [updatedAt] - Date when the prompt group was last updated (added by timestamps)
*/
const promptGroupSchema = new Schema(
{
name: {
type: String,
required: true,
index: true,
},
numberOfGenerations: {
type: Number,
default: 0,
},
oneliner: {
type: String,
default: '',
},
category: {
type: String,
default: '',
index: true,
},
projectIds: {
type: [Schema.Types.ObjectId],
ref: 'Project',
index: true,
},
productionId: {
type: Schema.Types.ObjectId,
ref: 'Prompt',
required: true,
index: true,
},
author: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
index: true,
},
authorName: {
type: String,
required: true,
},
command: {
type: String,
index: true,
validate: {
validator: function (v) {
return v === undefined || v === null || v === '' || /^[a-z0-9-]+$/.test(v);
},
message: (props) =>
`${props.value} is not a valid command. Only lowercase alphanumeric characters and highfins (') are allowed.`,
},
maxlength: [
Constants.COMMANDS_MAX_LENGTH,
`Command cannot be longer than ${Constants.COMMANDS_MAX_LENGTH} characters`,
],
},
},
{
timestamps: true,
},
);
const PromptGroup = mongoose.model('PromptGroup', promptGroupSchema);
const promptSchema = new Schema(
{
groupId: {
type: Schema.Types.ObjectId,
ref: 'PromptGroup',
required: true,
index: true,
},
author: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true,
},
prompt: {
type: String,
required: true,
},
type: {
type: String,
enum: ['text', 'chat'],
required: true,
},
},
{
timestamps: true,
},
);
const Prompt = mongoose.model('Prompt', promptSchema);
promptSchema.index({ createdAt: 1, updatedAt: 1 });
promptGroupSchema.index({ createdAt: 1, updatedAt: 1 });
module.exports = { Prompt, PromptGroup };

View File

@@ -1,20 +0,0 @@
const mongoose = require('mongoose');
const sessionSchema = mongoose.Schema({
refreshTokenHash: {
type: String,
required: true,
},
expiration: {
type: Date,
required: true,
expires: 0,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
});
module.exports = sessionSchema;

View File

@@ -1,54 +0,0 @@
const mongoose = require('mongoose');
/**
* @typedef {Object} ToolCallData
* @property {string} conversationId - The ID of the conversation
* @property {string} messageId - The ID of the message
* @property {string} toolId - The ID of the tool
* @property {string | ObjectId} user - The user's ObjectId
* @property {unknown} [result] - Optional result data
* @property {TAttachment[]} [attachments] - Optional attachments data
* @property {number} [blockIndex] - Optional code block index
* @property {number} [partIndex] - Optional part index
*/
/** @type {MongooseSchema<ToolCallData>} */
const toolCallSchema = mongoose.Schema(
{
conversationId: {
type: String,
required: true,
},
messageId: {
type: String,
required: true,
},
toolId: {
type: String,
required: true,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
result: {
type: mongoose.Schema.Types.Mixed,
},
attachments: {
type: mongoose.Schema.Types.Mixed,
},
blockIndex: {
type: Number,
},
partIndex: {
type: Number,
},
},
{ timestamps: true },
);
toolCallSchema.index({ messageId: 1, user: 1 });
toolCallSchema.index({ conversationId: 1, user: 1 });
module.exports = mongoose.model('ToolCall', toolCallSchema);

View File

@@ -1,151 +0,0 @@
const mongoose = require('mongoose');
const { SystemRoles } = require('librechat-data-provider');
/**
* @typedef {Object} MongoSession
* @property {string} [refreshToken] - The refresh token
*/
/**
* @typedef {Object} MongoUser
* @property {ObjectId} [_id] - MongoDB Document ID
* @property {string} [name] - The user's name
* @property {string} [username] - The user's username, in lowercase
* @property {string} email - The user's email address
* @property {boolean} emailVerified - Whether the user's email is verified
* @property {string} [password] - The user's password, trimmed with 8-128 characters
* @property {string} [avatar] - The URL of the user's avatar
* @property {string} provider - The provider of the user's account (e.g., 'local', 'google')
* @property {string} [role='USER'] - The role of the user
* @property {string} [googleId] - Optional Google ID for the user
* @property {string} [facebookId] - Optional Facebook ID for the user
* @property {string} [openidId] - Optional OpenID ID for the user
* @property {string} [ldapId] - Optional LDAP ID for the user
* @property {string} [githubId] - Optional GitHub ID for the user
* @property {string} [discordId] - Optional Discord ID for the user
* @property {string} [appleId] - Optional Apple ID for the user
* @property {Array} [plugins=[]] - List of plugins used by the user
* @property {Array.<MongoSession>} [refreshToken] - List of sessions with refresh tokens
* @property {Date} [expiresAt] - Optional expiration date of the file
* @property {Date} [createdAt] - Date when the user was created (added by timestamps)
* @property {Date} [updatedAt] - Date when the user was last updated (added by timestamps)
*/
/** @type {MongooseSchema<MongoSession>} */
const Session = mongoose.Schema({
refreshToken: {
type: String,
default: '',
},
});
const backupCodeSchema = mongoose.Schema({
codeHash: { type: String, required: true },
used: { type: Boolean, default: false },
usedAt: { type: Date, default: null },
});
/** @type {MongooseSchema<MongoUser>} */
const userSchema = mongoose.Schema(
{
name: {
type: String,
},
username: {
type: String,
lowercase: true,
default: '',
},
email: {
type: String,
required: [true, 'can\'t be blank'],
lowercase: true,
unique: true,
match: [/\S+@\S+\.\S+/, 'is invalid'],
index: true,
},
emailVerified: {
type: Boolean,
required: true,
default: false,
},
password: {
type: String,
trim: true,
minlength: 8,
maxlength: 128,
},
avatar: {
type: String,
required: false,
},
provider: {
type: String,
required: true,
default: 'local',
},
role: {
type: String,
default: SystemRoles.USER,
},
googleId: {
type: String,
unique: true,
sparse: true,
},
facebookId: {
type: String,
unique: true,
sparse: true,
},
openidId: {
type: String,
unique: true,
sparse: true,
},
ldapId: {
type: String,
unique: true,
sparse: true,
},
githubId: {
type: String,
unique: true,
sparse: true,
},
discordId: {
type: String,
unique: true,
sparse: true,
},
appleId: {
type: String,
unique: true,
sparse: true,
},
plugins: {
type: Array,
},
totpSecret: {
type: String,
},
backupCodes: {
type: [backupCodeSchema],
},
refreshToken: {
type: [Session],
},
expiresAt: {
type: Date,
expires: 604800, // 7 days in seconds
},
termsAccepted: {
type: Boolean,
default: false,
},
},
{ timestamps: true },
);
module.exports = userSchema;

View File

@@ -1,6 +1,6 @@
{
"name": "@librechat/backend",
"version": "v0.7.7-rc1",
"version": "v0.7.7",
"description": "",
"scripts": {
"start": "echo 'please run this from the root directory'",
@@ -36,18 +36,19 @@
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
"@azure/search-documents": "^12.0.0",
"@google/generative-ai": "^0.21.0",
"@google/generative-ai": "^0.23.0",
"@googleapis/youtube": "^20.0.0",
"@keyv/mongo": "^2.1.8",
"@keyv/redis": "^2.8.1",
"@langchain/community": "^0.3.14",
"@langchain/community": "^0.3.34",
"@langchain/core": "^0.3.40",
"@langchain/google-genai": "^0.1.9",
"@langchain/google-vertexai": "^0.2.0",
"@langchain/textsplitters": "^0.1.0",
"@librechat/agents": "^2.1.7",
"@librechat/agents": "^2.2.0",
"@librechat/data-schemas": "*",
"@waylaidwanderer/fetch-event-source": "^3.0.1",
"axios": "1.7.8",
"axios": "^1.8.2",
"bcryptjs": "^2.4.3",
"cohere-ai": "^7.9.1",
"compression": "^1.7.4",
@@ -74,7 +75,6 @@
"keyv": "^4.5.4",
"keyv-file": "^0.2.0",
"klona": "^2.0.6",
"langchain": "^0.2.19",
"librechat-data-provider": "*",
"librechat-mcp": "*",
"lodash": "^4.17.21",

View File

@@ -1,6 +1,7 @@
const { CacheKeys } = require('librechat-data-provider');
const { loadDefaultModels, loadConfigModels } = require('~/server/services/Config');
const { getLogStores } = require('~/cache');
const { logger } = require('~/config');
/**
* @param {ServerRequest} req
@@ -36,8 +37,13 @@ async function loadModels(req) {
}
async function modelController(req, res) {
const modelConfig = await loadModels(req);
res.send(modelConfig);
try {
const modelConfig = await loadModels(req);
res.send(modelConfig);
} catch (error) {
logger.error('Error fetching models:', error);
res.status(500).send({ error: error.message });
}
}
module.exports = { modelController, loadModels, getModelsConfig };

View File

@@ -11,17 +11,19 @@ const { encryptV2 } = require('~/server/utils/crypto');
const enable2FAController = async (req, res) => {
const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, '');
try {
const userId = req.user.id;
const secret = generateTOTPSecret();
const { plainCodes, codeObjects } = await generateBackupCodes();
const encryptedSecret = await encryptV2(secret);
const user = await updateUser(userId, { totpSecret: encryptedSecret, backupCodes: codeObjects });
// Set twoFactorEnabled to false until the user confirms 2FA.
const user = await updateUser(userId, {
totpSecret: encryptedSecret,
backupCodes: codeObjects,
twoFactorEnabled: false,
});
const otpauthUrl = `otpauth://totp/${safeAppTitle}:${user.email}?secret=${secret}&issuer=${safeAppTitle}`;
res.status(200).json({
otpauthUrl,
backupCodes: plainCodes,
@@ -37,6 +39,7 @@ const verify2FAController = async (req, res) => {
const userId = req.user.id;
const { token, backupCode } = req.body;
const user = await getUserById(userId);
// Ensure that 2FA is enabled for this user.
if (!user || !user.totpSecret) {
return res.status(400).json({ message: '2FA not initiated' });
}
@@ -52,7 +55,6 @@ const verify2FAController = async (req, res) => {
return res.status(200).json();
}
}
return res.status(400).json({ message: 'Invalid token.' });
} catch (err) {
logger.error('[verify2FAController]', err);
@@ -74,6 +76,8 @@ const confirm2FAController = async (req, res) => {
const secret = await getTOTPSecret(user.totpSecret);
if (await verifyTOTP(secret, token)) {
// Upon successful verification, enable 2FA.
await updateUser(userId, { twoFactorEnabled: true });
return res.status(200).json();
}
@@ -87,7 +91,7 @@ const confirm2FAController = async (req, res) => {
const disable2FAController = async (req, res) => {
try {
const userId = req.user.id;
await updateUser(userId, { totpSecret: null, backupCodes: [] });
await updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false });
res.status(200).json();
} catch (err) {
logger.error('[disable2FAController]', err);

View File

@@ -17,7 +17,7 @@ const {
KnownEndpoints,
anthropicSchema,
isAgentsEndpoint,
bedrockOutputParser,
bedrockInputSchema,
removeNullishValues,
} = require('librechat-data-provider');
const {
@@ -30,6 +30,7 @@ const {
const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens');
const { getBufferString, HumanMessage } = require('@langchain/core/messages');
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
const { getCustomEndpointConfig } = require('~/server/services/Config');
const Tokenizer = require('~/server/services/Tokenizer');
const BaseClient = require('~/app/clients/BaseClient');
const { createRun } = require('./run');
@@ -39,10 +40,10 @@ const { logger } = require('~/config');
/** @typedef {import('@langchain/core/runnables').RunnableConfig} RunnableConfig */
const providerParsers = {
[EModelEndpoint.openAI]: openAISchema,
[EModelEndpoint.azureOpenAI]: openAISchema,
[EModelEndpoint.anthropic]: anthropicSchema,
[EModelEndpoint.bedrock]: bedrockOutputParser,
[EModelEndpoint.openAI]: openAISchema.parse,
[EModelEndpoint.azureOpenAI]: openAISchema.parse,
[EModelEndpoint.anthropic]: anthropicSchema.parse,
[EModelEndpoint.bedrock]: bedrockInputSchema.parse,
};
const legacyContentEndpoints = new Set([KnownEndpoints.groq, KnownEndpoints.deepseek]);
@@ -187,7 +188,14 @@ class AgentClient extends BaseClient {
: {};
if (parseOptions) {
runOptions = parseOptions(this.options.agent.model_parameters);
try {
runOptions = parseOptions(this.options.agent.model_parameters);
} catch (error) {
logger.error(
'[api/server/controllers/agents/client.js #getSaveOptions] Error parsing options',
error,
);
}
}
return removeNullishValues(
@@ -804,7 +812,10 @@ class AgentClient extends BaseClient {
'[api/server/controllers/agents/client.js #sendCompletion] Unhandled error type',
err,
);
throw err;
this.contentParts.push({
type: ContentTypes.ERROR,
[ContentTypes.ERROR]: `An error occurred while processing the request${err?.message ? `: ${err.message}` : ''}`,
});
}
}
}
@@ -824,13 +835,16 @@ class AgentClient extends BaseClient {
const clientOptions = {
maxTokens: 75,
};
const providerConfig = this.options.req.app.locals[this.options.agent.provider];
let endpointConfig = this.options.req.app.locals[this.options.agent.endpoint];
if (!endpointConfig) {
endpointConfig = await getCustomEndpointConfig(this.options.agent.endpoint);
}
if (
providerConfig &&
providerConfig.titleModel &&
providerConfig.titleModel !== Constants.CURRENT_MODEL
endpointConfig &&
endpointConfig.titleModel &&
endpointConfig.titleModel !== Constants.CURRENT_MODEL
) {
clientOptions.model = providerConfig.titleModel;
clientOptions.model = endpointConfig.titleModel;
}
try {
const titleResult = await this.run.generateTitle({

View File

@@ -45,7 +45,10 @@ async function createRun({
/** @type {'reasoning_content' | 'reasoning'} */
let reasoningKey;
if (llmConfig.configuration?.baseURL?.includes(KnownEndpoints.openrouter)) {
if (
llmConfig.configuration?.baseURL?.includes(KnownEndpoints.openrouter) ||
(agent.endpoint && agent.endpoint.toLowerCase().includes(KnownEndpoints.openrouter))
) {
reasoningKey = 'reasoning';
}
if (/o1(?!-(?:mini|preview)).*$/.test(llmConfig.model)) {

View File

@@ -8,7 +8,7 @@ const loginController = async (req, res) => {
return res.status(400).json({ message: 'Invalid credentials' });
}
if (req.user.backupCodes != null && req.user.backupCodes.length > 0) {
if (req.user.twoFactorEnabled) {
const tempToken = generate2FATempToken(req.user._id);
return res.status(200).json({ twoFAPending: true, tempToken });
}

View File

@@ -1,5 +1,9 @@
const jwt = require('jsonwebtoken');
const { verifyTOTP, verifyBackupCode, getTOTPSecret } = require('~/server/services/twoFactorService');
const {
verifyTOTP,
verifyBackupCode,
getTOTPSecret,
} = require('~/server/services/twoFactorService');
const { setAuthTokens } = require('~/server/services/AuthService');
const { getUserById } = require('~/models/userMethods');
const { logger } = require('~/config');
@@ -19,12 +23,12 @@ const verify2FA = async (req, res) => {
}
const user = await getUserById(payload.userId);
// Ensure that the user exists and has backup codes (i.e. 2FA enabled)
if (!user || !(user.backupCodes && user.backupCodes.length > 0)) {
// Ensure that the user exists and has 2FA enabled
if (!user || !user.twoFactorEnabled) {
return res.status(400).json({ message: '2FA is not enabled for this user' });
}
// Use the new getTOTPSecret function to retrieve (and decrypt if necessary) the TOTP secret.
// Retrieve (and decrypt if necessary) the TOTP secret.
const secret = await getTOTPSecret(user.totpSecret);
let verified = false;
@@ -39,9 +43,7 @@ const verify2FA = async (req, res) => {
}
// Prepare user data for response.
// If the user is a plain object (from lean queries), we create a shallow copy.
const userData = user.toObject ? user.toObject() : { ...user };
// Remove sensitive fields.
delete userData.password;
delete userData.__v;
delete userData.totpSecret;

View File

@@ -120,7 +120,7 @@ const createAbortController = (req, res, getAbortData, getReqData) => {
{ promptTokens, completionTokens },
);
saveMessage(
await saveMessage(
req,
{ ...responseMessage, user },
{ context: 'api/server/middleware/abortMiddleware.js' },

View File

@@ -1,32 +1,18 @@
const passport = require('passport');
const DebugControl = require('../../utils/debug.js');
function log({ title, parameters }) {
DebugControl.log.functionName(title);
if (parameters) {
DebugControl.log.parameters(parameters);
}
}
const { logger } = require('~/config');
const requireLocalAuth = (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) {
log({
title: '(requireLocalAuth) Error at passport.authenticate',
parameters: [{ name: 'error', value: err }],
});
logger.error('[requireLocalAuth] Error at passport.authenticate:', err);
return next(err);
}
if (!user) {
log({
title: '(requireLocalAuth) Error: No user',
});
logger.debug('[requireLocalAuth] Error: No user');
return res.status(404).send(info);
}
if (info && info.message) {
log({
title: '(requireLocalAuth) Error: ' + info.message,
});
logger.debug('[requireLocalAuth] Error: ' + info.message);
return res.status(422).send({ message: info.message });
}
req.user = user;

View File

@@ -18,6 +18,7 @@ afterEach(() => {
delete process.env.OPENID_ISSUER;
delete process.env.OPENID_SESSION_SECRET;
delete process.env.OPENID_BUTTON_LABEL;
delete process.env.OPENID_AUTO_REDIRECT;
delete process.env.OPENID_AUTH_URL;
delete process.env.GITHUB_CLIENT_ID;
delete process.env.GITHUB_CLIENT_SECRET;

View File

@@ -47,10 +47,10 @@ router.get('/', async function (req, res) {
githubLoginEnabled: !!process.env.GITHUB_CLIENT_ID && !!process.env.GITHUB_CLIENT_SECRET,
googleLoginEnabled: !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET,
appleLoginEnabled:
!!process.env.APPLE_CLIENT_ID &&
!!process.env.APPLE_TEAM_ID &&
!!process.env.APPLE_KEY_ID &&
!!process.env.APPLE_PRIVATE_KEY_PATH,
!!process.env.APPLE_CLIENT_ID &&
!!process.env.APPLE_TEAM_ID &&
!!process.env.APPLE_KEY_ID &&
!!process.env.APPLE_PRIVATE_KEY_PATH,
openidLoginEnabled:
!!process.env.OPENID_CLIENT_ID &&
!!process.env.OPENID_CLIENT_SECRET &&
@@ -58,6 +58,7 @@ router.get('/', async function (req, res) {
!!process.env.OPENID_SESSION_SECRET,
openidLabel: process.env.OPENID_BUTTON_LABEL || 'Continue with OpenID',
openidImageUrl: process.env.OPENID_IMAGE_URL,
openidAutoRedirect: isEnabled(process.env.OPENID_AUTO_REDIRECT),
serverDomain: process.env.DOMAIN_SERVER || 'http://localhost:3080',
emailLoginEnabled,
registrationEnabled: !ldap?.enabled && isEnabled(process.env.ALLOW_REGISTRATION),
@@ -80,6 +81,7 @@ router.get('/', async function (req, res) {
publicSharedLinksEnabled,
analyticsGtmId: process.env.ANALYTICS_GTM_ID,
instanceProjectId: instanceProject._id.toString(),
bundlerURL: process.env.SANDPACK_BUNDLER_URL,
};
if (ldap) {

View File

@@ -31,7 +31,9 @@ const oauthHandler = async (req, res) => {
router.get('/error', (req, res) => {
// A single error message is pushed by passport when authentication fails.
logger.error('Error in OAuth authentication:', { message: req.session.messages.pop() });
res.redirect(`${domains.client}/login`);
// Redirect to login page with auth_failed parameter to prevent infinite redirect loops
res.redirect(`${domains.client}/login?redirect=false`);
});
/**

View File

@@ -47,7 +47,7 @@ async function loadConfigModels(req) {
);
/**
* @type {Record<string, string[]>}
* @type {Record<string, Promise<string[]>>}
* Map for promises keyed by unique combination of baseURL and apiKey */
const fetchPromisesMap = {};
/**
@@ -102,7 +102,7 @@ async function loadConfigModels(req) {
for (const name of associatedNames) {
const endpoint = endpointsMap[name];
modelsConfig[name] = !modelData?.length ? endpoint.models.default ?? [] : modelData;
modelsConfig[name] = !modelData?.length ? (endpoint.models.default ?? []) : modelData;
}
}

View File

@@ -5,8 +5,8 @@ const {
getGoogleModels,
getBedrockModels,
getAnthropicModels,
getChatGPTBrowserModels,
} = require('~/server/services/ModelService');
const { logger } = require('~/config');
/**
* Loads the default models for the application.
@@ -15,31 +15,68 @@ const {
* @param {Express.Request} req - The Express request object.
*/
async function loadDefaultModels(req) {
const google = getGoogleModels();
const openAI = await getOpenAIModels({ user: req.user.id });
const anthropic = getAnthropicModels();
const chatGPTBrowser = getChatGPTBrowserModels();
const azureOpenAI = await getOpenAIModels({ user: req.user.id, azure: true });
const gptPlugins = await getOpenAIModels({
user: req.user.id,
azure: useAzurePlugins,
plugins: true,
});
const assistants = await getOpenAIModels({ assistants: true });
const azureAssistants = await getOpenAIModels({ azureAssistants: true });
try {
const [
openAI,
anthropic,
azureOpenAI,
gptPlugins,
assistants,
azureAssistants,
google,
bedrock,
] = await Promise.all([
getOpenAIModels({ user: req.user.id }).catch((error) => {
logger.error('Error fetching OpenAI models:', error);
return [];
}),
getAnthropicModels({ user: req.user.id }).catch((error) => {
logger.error('Error fetching Anthropic models:', error);
return [];
}),
getOpenAIModels({ user: req.user.id, azure: true }).catch((error) => {
logger.error('Error fetching Azure OpenAI models:', error);
return [];
}),
getOpenAIModels({ user: req.user.id, azure: useAzurePlugins, plugins: true }).catch(
(error) => {
logger.error('Error fetching Plugin models:', error);
return [];
},
),
getOpenAIModels({ assistants: true }).catch((error) => {
logger.error('Error fetching OpenAI Assistants API models:', error);
return [];
}),
getOpenAIModels({ azureAssistants: true }).catch((error) => {
logger.error('Error fetching Azure OpenAI Assistants API models:', error);
return [];
}),
Promise.resolve(getGoogleModels()).catch((error) => {
logger.error('Error getting Google models:', error);
return [];
}),
Promise.resolve(getBedrockModels()).catch((error) => {
logger.error('Error getting Bedrock models:', error);
return [];
}),
]);
return {
[EModelEndpoint.openAI]: openAI,
[EModelEndpoint.agents]: openAI,
[EModelEndpoint.google]: google,
[EModelEndpoint.anthropic]: anthropic,
[EModelEndpoint.gptPlugins]: gptPlugins,
[EModelEndpoint.azureOpenAI]: azureOpenAI,
[EModelEndpoint.chatGPTBrowser]: chatGPTBrowser,
[EModelEndpoint.assistants]: assistants,
[EModelEndpoint.azureAssistants]: azureAssistants,
[EModelEndpoint.bedrock]: getBedrockModels(),
};
return {
[EModelEndpoint.openAI]: openAI,
[EModelEndpoint.agents]: openAI,
[EModelEndpoint.google]: google,
[EModelEndpoint.anthropic]: anthropic,
[EModelEndpoint.gptPlugins]: gptPlugins,
[EModelEndpoint.azureOpenAI]: azureOpenAI,
[EModelEndpoint.assistants]: assistants,
[EModelEndpoint.azureAssistants]: azureAssistants,
[EModelEndpoint.bedrock]: bedrock,
};
} catch (error) {
logger.error('Error fetching default models:', error);
throw new Error(`Failed to load default models: ${error.message}`);
}
}
module.exports = loadDefaultModels;

View File

@@ -22,6 +22,7 @@ const { getAgent } = require('~/models/Agent');
const { logger } = require('~/config');
const providerConfigMap = {
[Providers.XAI]: initCustom,
[Providers.OLLAMA]: initCustom,
[Providers.DEEPSEEK]: initCustom,
[Providers.OPENROUTER]: initCustom,
@@ -101,6 +102,7 @@ const initializeAgentOptions = async ({
});
const provider = agent.provider;
agent.endpoint = provider;
let getOptions = providerConfigMap[provider];
if (!getOptions && providerConfigMap[provider.toLowerCase()] != null) {
agent.provider = provider.toLowerCase();
@@ -112,9 +114,7 @@ const initializeAgentOptions = async ({
}
getOptions = initCustom;
agent.provider = Providers.OPENAI;
agent.endpoint = provider.toLowerCase();
}
const model_parameters = Object.assign(
{},
agent.model_parameters ?? { model: agent.model },

View File

@@ -27,6 +27,7 @@ const initializeClient = async ({ req, res, endpointOption, overrideModel, optio
if (anthropicConfig) {
clientOptions.streamRate = anthropicConfig.streamRate;
clientOptions.titleModel = anthropicConfig.titleModel;
}
/** @type {undefined | TBaseEndpoint} */

View File

@@ -1,6 +1,5 @@
const { removeNullishValues, bedrockInputParser } = require('librechat-data-provider');
const { removeNullishValues } = require('librechat-data-provider');
const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts');
const { logger } = require('~/config');
const buildOptions = (endpoint, parsedBody) => {
const {
@@ -15,12 +14,6 @@ const buildOptions = (endpoint, parsedBody) => {
artifacts,
...model_parameters
} = parsedBody;
let parsedParams = model_parameters;
try {
parsedParams = bedrockInputParser.parse(model_parameters);
} catch (error) {
logger.warn('Failed to parse bedrock input', error);
}
const endpointOption = removeNullishValues({
endpoint,
name,
@@ -31,7 +24,7 @@ const buildOptions = (endpoint, parsedBody) => {
spec,
promptPrefix,
maxContextTokens,
model_parameters: parsedParams,
model_parameters,
});
if (typeof artifacts === 'string') {

View File

@@ -1,14 +1,16 @@
const { HttpsProxyAgent } = require('https-proxy-agent');
const {
EModelEndpoint,
Constants,
AuthType,
Constants,
EModelEndpoint,
bedrockInputParser,
bedrockOutputParser,
removeNullishValues,
} = require('librechat-data-provider');
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
const { sleep } = require('~/server/utils');
const getOptions = async ({ req, endpointOption }) => {
const getOptions = async ({ req, overrideModel, endpointOption }) => {
const {
BEDROCK_AWS_SECRET_ACCESS_KEY,
BEDROCK_AWS_ACCESS_KEY_ID,
@@ -62,39 +64,44 @@ const getOptions = async ({ req, endpointOption }) => {
/** @type {BedrockClientOptions} */
const requestOptions = {
model: endpointOption.model,
model: overrideModel ?? endpointOption.model,
region: BEDROCK_AWS_DEFAULT_REGION,
streaming: true,
streamUsage: true,
callbacks: [
{
handleLLMNewToken: async () => {
if (!streamRate) {
return;
}
await sleep(streamRate);
},
},
],
};
if (credentials) {
requestOptions.credentials = credentials;
}
if (BEDROCK_REVERSE_PROXY) {
requestOptions.endpointHost = BEDROCK_REVERSE_PROXY;
}
const configOptions = {};
if (PROXY) {
/** NOTE: NOT SUPPORTED BY BEDROCK */
configOptions.httpAgent = new HttpsProxyAgent(PROXY);
}
const llmConfig = bedrockOutputParser(
bedrockInputParser.parse(
removeNullishValues(Object.assign(requestOptions, endpointOption.model_parameters)),
),
);
if (credentials) {
llmConfig.credentials = credentials;
}
if (BEDROCK_REVERSE_PROXY) {
llmConfig.endpointHost = BEDROCK_REVERSE_PROXY;
}
llmConfig.callbacks = [
{
handleLLMNewToken: async () => {
if (!streamRate) {
return;
}
await sleep(streamRate);
},
},
];
return {
/** @type {BedrockClientOptions} */
llmConfig: removeNullishValues(Object.assign(requestOptions, endpointOption.model_parameters)),
llmConfig,
configOptions,
};
};

View File

@@ -141,7 +141,8 @@ const initializeClient = async ({ req, res, endpointOption, optionsOnly, overrid
},
clientOptions,
);
const options = getLLMConfig(apiKey, clientOptions);
clientOptions.modelOptions.user = req.user.id;
const options = getLLMConfig(apiKey, clientOptions, endpoint);
if (!customOptions.streamRate) {
return options;
}

View File

@@ -5,12 +5,7 @@ const { isEnabled } = require('~/server/utils');
const { GoogleClient } = require('~/app');
const initializeClient = async ({ req, res, endpointOption, overrideModel, optionsOnly }) => {
const {
GOOGLE_KEY,
GOOGLE_REVERSE_PROXY,
GOOGLE_AUTH_HEADER,
PROXY,
} = process.env;
const { GOOGLE_KEY, GOOGLE_REVERSE_PROXY, GOOGLE_AUTH_HEADER, PROXY } = process.env;
const isUserProvided = GOOGLE_KEY === 'user_provided';
const { key: expiresAt } = req.body;
@@ -43,6 +38,7 @@ const initializeClient = async ({ req, res, endpointOption, overrideModel, optio
if (googleConfig) {
clientOptions.streamRate = googleConfig.streamRate;
clientOptions.titleModel = googleConfig.titleModel;
}
if (allConfig) {

View File

@@ -113,6 +113,7 @@ const initializeClient = async ({
if (!isAzureOpenAI && openAIConfig) {
clientOptions.streamRate = openAIConfig.streamRate;
clientOptions.titleModel = openAIConfig.titleModel;
}
/** @type {undefined | TBaseEndpoint} */
@@ -140,6 +141,7 @@ const initializeClient = async ({
},
clientOptions,
);
clientOptions.modelOptions.user = req.user.id;
const options = getLLMConfig(apiKey, clientOptions);
if (!clientOptions.streamRate) {
return options;

View File

@@ -9,6 +9,7 @@ const { isEnabled } = require('~/server/utils');
* @param {Object} options - Additional options for configuring the LLM.
* @param {Object} [options.modelOptions] - Model-specific options.
* @param {string} [options.modelOptions.model] - The name of the model to use.
* @param {string} [options.modelOptions.user] - The user ID
* @param {number} [options.modelOptions.temperature] - Controls randomness in output generation (0-2).
* @param {number} [options.modelOptions.top_p] - Controls diversity via nucleus sampling (0-1).
* @param {number} [options.modelOptions.frequency_penalty] - Reduces repetition of token sequences (-2 to 2).
@@ -23,9 +24,10 @@ const { isEnabled } = require('~/server/utils');
* @param {boolean} [options.streaming] - Whether to use streaming mode.
* @param {Object} [options.addParams] - Additional parameters to add to the model options.
* @param {string[]} [options.dropParams] - Parameters to remove from the model options.
* @param {string|null} [endpoint=null] - The endpoint name
* @returns {Object} Configuration options for creating an LLM instance.
*/
function getLLMConfig(apiKey, options = {}) {
function getLLMConfig(apiKey, options = {}, endpoint = null) {
const {
modelOptions = {},
reverseProxyUrl,
@@ -58,7 +60,10 @@ function getLLMConfig(apiKey, options = {}) {
let useOpenRouter;
/** @type {OpenAIClientOptions['configuration']} */
const configOptions = {};
if (reverseProxyUrl && reverseProxyUrl.includes(KnownEndpoints.openrouter)) {
if (
(reverseProxyUrl && reverseProxyUrl.includes(KnownEndpoints.openrouter)) ||
(endpoint && endpoint.toLowerCase().includes(KnownEndpoints.openrouter))
) {
useOpenRouter = true;
llmConfig.include_reasoning = true;
configOptions.baseURL = reverseProxyUrl;

View File

@@ -1,4 +1,3 @@
// Code Files
const axios = require('axios');
const FormData = require('form-data');
const { getCodeBaseURL } = require('@librechat/agents');
@@ -16,7 +15,8 @@ const MAX_FILE_SIZE = 150 * 1024 * 1024;
async function getCodeOutputDownloadStream(fileIdentifier, apiKey) {
try {
const baseURL = getCodeBaseURL();
const response = await axios({
/** @type {import('axios').AxiosRequestConfig} */
const options = {
method: 'get',
url: `${baseURL}/download/${fileIdentifier}`,
responseType: 'stream',
@@ -25,10 +25,22 @@ async function getCodeOutputDownloadStream(fileIdentifier, apiKey) {
'X-API-Key': apiKey,
},
timeout: 15000,
});
};
if (process.env.PROXY) {
options.proxy = {
host: process.env.PROXY,
protocol: process.env.PROXY.startsWith('https') ? 'https' : 'http',
};
}
const response = await axios(options);
return response;
} catch (error) {
logAxiosError({
message: `Error downloading code environment file stream: ${error.message}`,
error,
});
throw new Error(`Error downloading file: ${error.message}`);
}
}
@@ -54,7 +66,8 @@ async function uploadCodeEnvFile({ req, stream, filename, apiKey, entity_id = ''
form.append('file', stream, filename);
const baseURL = getCodeBaseURL();
const response = await axios.post(`${baseURL}/upload`, form, {
/** @type {import('axios').AxiosRequestConfig} */
const options = {
headers: {
...form.getHeaders(),
'Content-Type': 'multipart/form-data',
@@ -64,7 +77,16 @@ async function uploadCodeEnvFile({ req, stream, filename, apiKey, entity_id = ''
},
maxContentLength: MAX_FILE_SIZE,
maxBodyLength: MAX_FILE_SIZE,
});
};
if (process.env.PROXY) {
options.proxy = {
host: process.env.PROXY,
protocol: process.env.PROXY.startsWith('https') ? 'https' : 'http',
};
}
const response = await axios.post(`${baseURL}/upload`, form, options);
/** @type {{ message: string; session_id: string; files: Array<{ fileId: string; filename: string }> }} */
const result = response.data;

View File

@@ -4,7 +4,9 @@ const { HttpsProxyAgent } = require('https-proxy-agent');
const { EModelEndpoint, defaultModels, CacheKeys } = require('librechat-data-provider');
const { inputSchema, logAxiosError, extractBaseURL, processModelData } = require('~/utils');
const { OllamaClient } = require('~/app/clients/OllamaClient');
const { isUserProvided } = require('~/server/utils');
const getLogStores = require('~/cache/getLogStores');
const { logger } = require('~/config');
/**
* Splits a string by commas and trims each resulting value.
@@ -42,7 +44,7 @@ const fetchModels = async ({
user,
apiKey,
baseURL,
name = 'OpenAI',
name = EModelEndpoint.openAI,
azure = false,
userIdQuery = false,
createTokenConfig = true,
@@ -64,12 +66,19 @@ const fetchModels = async ({
try {
const options = {
headers: {
Authorization: `Bearer ${apiKey}`,
},
headers: {},
timeout: 5000,
};
if (name === EModelEndpoint.anthropic) {
options.headers = {
'x-api-key': apiKey,
'anthropic-version': process.env.ANTHROPIC_VERSION || '2023-06-01',
};
} else {
options.headers.Authorization = `Bearer ${apiKey}`;
}
if (process.env.PROXY) {
options.httpsAgent = new HttpsProxyAgent(process.env.PROXY);
}
@@ -148,7 +157,7 @@ const fetchOpenAIModels = async (opts, _models = []) => {
baseURL,
azure: opts.azure,
user: opts.user,
name: baseURL,
name: EModelEndpoint.openAI,
});
}
@@ -157,7 +166,7 @@ const fetchOpenAIModels = async (opts, _models = []) => {
}
if (baseURL === openaiBaseURL) {
const regex = /(text-davinci-003|gpt-|o\d+-)/;
const regex = /(text-davinci-003|gpt-|o\d+)/;
const excludeRegex = /audio|realtime/;
models = models.filter((model) => regex.test(model) && !excludeRegex.test(model));
const instructModels = models.filter((model) => model.includes('instruct'));
@@ -231,13 +240,71 @@ const getChatGPTBrowserModels = () => {
return models;
};
const getAnthropicModels = () => {
/**
* Fetches models from the Anthropic API.
* @async
* @function
* @param {object} opts - The options for fetching the models.
* @param {string} opts.user - The user ID to send to the API.
* @param {string[]} [_models=[]] - The models to use as a fallback.
*/
const fetchAnthropicModels = async (opts, _models = []) => {
let models = _models.slice() ?? [];
let apiKey = process.env.ANTHROPIC_API_KEY;
const anthropicBaseURL = 'https://api.anthropic.com/v1';
let baseURL = anthropicBaseURL;
let reverseProxyUrl = process.env.ANTHROPIC_REVERSE_PROXY;
if (reverseProxyUrl) {
baseURL = extractBaseURL(reverseProxyUrl);
}
if (!apiKey) {
return models;
}
const modelsCache = getLogStores(CacheKeys.MODEL_QUERIES);
const cachedModels = await modelsCache.get(baseURL);
if (cachedModels) {
return cachedModels;
}
if (baseURL) {
models = await fetchModels({
apiKey,
baseURL,
user: opts.user,
name: EModelEndpoint.anthropic,
tokenKey: EModelEndpoint.anthropic,
});
}
if (models.length === 0) {
return _models;
}
await modelsCache.set(baseURL, models);
return models;
};
const getAnthropicModels = async (opts = {}) => {
let models = defaultModels[EModelEndpoint.anthropic];
if (process.env.ANTHROPIC_MODELS) {
models = splitAndTrim(process.env.ANTHROPIC_MODELS);
return models;
}
return models;
if (isUserProvided(process.env.ANTHROPIC_API_KEY)) {
return models;
}
try {
return await fetchAnthropicModels(opts, models);
} catch (error) {
logger.error('Error fetching Anthropic models:', error);
return models;
}
};
const getGoogleModels = () => {

View File

@@ -352,15 +352,15 @@ describe('splitAndTrim', () => {
});
describe('getAnthropicModels', () => {
it('returns default models when ANTHROPIC_MODELS is not set', () => {
it('returns default models when ANTHROPIC_MODELS is not set', async () => {
delete process.env.ANTHROPIC_MODELS;
const models = getAnthropicModels();
const models = await getAnthropicModels();
expect(models).toEqual(defaultModels[EModelEndpoint.anthropic]);
});
it('returns models from ANTHROPIC_MODELS when set', () => {
it('returns models from ANTHROPIC_MODELS when set', async () => {
process.env.ANTHROPIC_MODELS = 'claude-1, claude-2 ';
const models = getAnthropicModels();
const models = await getAnthropicModels();
expect(models).toEqual(['claude-1', 'claude-2']);
});
});

View File

@@ -766,36 +766,6 @@
* @memberof typedefs
*/
/**
* @exports MongoFile
* @typedef {import('~/models/schema/fileSchema.js').MongoFile} MongoFile
* @memberof typedefs
*/
/**
* @exports ToolCallData
* @typedef {import('~/models/schema/toolCallSchema.js').ToolCallData} ToolCallData
* @memberof typedefs
*/
/**
* @exports MongoUser
* @typedef {import('~/models/schema/userSchema.js').MongoUser} MongoUser
* @memberof typedefs
*/
/**
* @exports MongoProject
* @typedef {import('~/models/schema/projectSchema.js').MongoProject} MongoProject
* @memberof typedefs
*/
/**
* @exports MongoPromptGroup
* @typedef {import('~/models/schema/promptSchema.js').MongoPromptGroup} MongoPromptGroup
* @memberof typedefs
*/
/**
* @exports uploadImageBuffer
* @typedef {import('~/server/services/Files/process').uploadImageBuffer} uploadImageBuffer

View File

@@ -1,56 +0,0 @@
const levels = {
NONE: 0,
LOW: 1,
MEDIUM: 2,
HIGH: 3,
};
let level = levels.HIGH;
module.exports = {
levels,
setLevel: (l) => (level = l),
log: {
parameters: (parameters) => {
if (levels.HIGH > level) {
return;
}
console.group();
parameters.forEach((p) => console.log(`${p.name}:`, p.value));
console.groupEnd();
},
functionName: (name) => {
if (levels.MEDIUM > level) {
return;
}
console.log(`\nEXECUTING: ${name}\n`);
},
flow: (flow) => {
if (levels.LOW > level) {
return;
}
console.log(`\n\n\nBEGIN FLOW: ${flow}\n\n\n`);
},
variable: ({ name, value }) => {
if (levels.HIGH > level) {
return;
}
console.group();
console.group();
console.log(`VARIABLE ${name}:`, value);
console.groupEnd();
console.groupEnd();
},
request: () => (req, res, next) => {
if (levels.HIGH > level) {
return next();
}
console.log('Hit URL', req.url, 'with following:');
console.group();
console.log('Query:', req.query);
console.log('Body:', req.body);
console.groupEnd();
return next();
},
},
};

View File

@@ -1,6 +1,6 @@
{
"name": "@librechat/frontend",
"version": "v0.7.7-rc1",
"version": "v0.7.7",
"description": "",
"type": "module",
"scripts": {

View File

@@ -399,7 +399,7 @@ export type TAuthContext = {
isAuthenticated: boolean;
error: string | undefined;
login: (data: t.TLoginUser) => void;
logout: () => void;
logout: (redirect?: string) => void;
setError: React.Dispatch<React.SetStateAction<string | undefined>>;
roles?: Record<string, t.TRole | null | undefined>;
};

View File

@@ -8,8 +8,8 @@ import {
import { SandpackProviderProps } from '@codesandbox/sandpack-react/unstyled';
import type { CodeEditorRef } from '@codesandbox/sandpack-react';
import type { ArtifactFiles, Artifact } from '~/common';
import { useEditArtifact, useGetStartupConfig } from '~/data-provider';
import { sharedFiles, sharedOptions } from '~/utils/artifacts';
import { useEditArtifact } from '~/data-provider';
import { useEditorContext } from '~/Providers';
const createDebouncedMutation = (
@@ -124,6 +124,17 @@ export const ArtifactCodeEditor = memo(function ({
sharedProps: Partial<SandpackProviderProps>;
editorRef: React.MutableRefObject<CodeEditorRef>;
}) {
const { data: config } = useGetStartupConfig();
const options: typeof sharedOptions = useMemo(() => {
if (!config) {
return sharedOptions;
}
return {
...sharedOptions,
bundlerURL: config.bundlerURL,
};
}, [config]);
if (Object.keys(files).length === 0) {
return null;
}
@@ -135,7 +146,7 @@ export const ArtifactCodeEditor = memo(function ({
...files,
...sharedFiles,
}}
options={{ ...sharedOptions }}
options={options}
{...sharedProps}
template={template}
>

View File

@@ -7,6 +7,7 @@ import {
import type { SandpackPreviewRef } from '@codesandbox/sandpack-react/unstyled';
import type { ArtifactFiles } from '~/common';
import { sharedFiles, sharedOptions } from '~/utils/artifacts';
import { useGetStartupConfig } from '~/data-provider';
import { useEditorContext } from '~/Providers';
export const ArtifactPreview = memo(function ({
@@ -23,6 +24,8 @@ export const ArtifactPreview = memo(function ({
previewRef: React.MutableRefObject<SandpackPreviewRef>;
}) {
const { currentCode } = useEditorContext();
const { data: config } = useGetStartupConfig();
const artifactFiles = useMemo(() => {
if (Object.keys(files).length === 0) {
return files;
@@ -38,6 +41,17 @@ export const ArtifactPreview = memo(function ({
},
};
}, [currentCode, files, fileKey]);
const options: typeof sharedOptions = useMemo(() => {
if (!config) {
return sharedOptions;
}
return {
...sharedOptions,
bundlerURL: config.bundlerURL,
};
}, [config]);
if (Object.keys(artifactFiles).length === 0) {
return null;
}
@@ -48,7 +62,7 @@ export const ArtifactPreview = memo(function ({
...artifactFiles,
...sharedFiles,
}}
options={{ ...sharedOptions }}
options={options}
{...sharedProps}
template={template}
>

View File

@@ -1,16 +1,78 @@
import { useOutletContext } from 'react-router-dom';
import { useOutletContext, useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { useAuthContext } from '~/hooks/AuthContext';
import type { TLoginLayoutContext } from '~/common';
import { ErrorMessage } from '~/components/Auth/ErrorMessage';
import { getLoginError } from '~/utils';
import { useLocalize } from '~/hooks';
import LoginForm from './LoginForm';
import SocialButton from '~/components/Auth/SocialButton';
import { OpenIDIcon } from '~/components';
function Login() {
const localize = useLocalize();
const { error, setError, login } = useAuthContext();
const { startupConfig } = useOutletContext<TLoginLayoutContext>();
const [searchParams, setSearchParams] = useSearchParams();
// Determine if auto-redirect should be disabled based on the URL parameter
const disableAutoRedirect = searchParams.get('redirect') === 'false';
// Persist the disable flag locally so that once detected, auto-redirect stays disabled.
const [isAutoRedirectDisabled, setIsAutoRedirectDisabled] = useState(disableAutoRedirect);
// Once the disable flag is detected, update local state and remove the parameter from the URL.
useEffect(() => {
if (disableAutoRedirect) {
setIsAutoRedirectDisabled(true);
const newParams = new URLSearchParams(searchParams);
newParams.delete('redirect');
setSearchParams(newParams, { replace: true });
}
}, [disableAutoRedirect, searchParams, setSearchParams]);
// Determine whether we should auto-redirect to OpenID.
const shouldAutoRedirect =
startupConfig?.openidLoginEnabled &&
startupConfig?.openidAutoRedirect &&
startupConfig?.serverDomain &&
!isAutoRedirectDisabled;
useEffect(() => {
if (shouldAutoRedirect) {
console.log('Auto-redirecting to OpenID provider...');
window.location.href = `${startupConfig.serverDomain}/oauth/openid`;
}
}, [shouldAutoRedirect, startupConfig]);
// Render fallback UI if auto-redirect is active.
if (shouldAutoRedirect) {
return (
<div className="flex min-h-screen flex-col items-center justify-center p-4">
<p className="text-lg font-semibold">
{localize('com_ui_redirecting_to_provider', { 0: startupConfig.openidLabel })}
</p>
<div className="mt-4">
<SocialButton
key="openid"
enabled={startupConfig.openidLoginEnabled}
serverDomain={startupConfig.serverDomain}
oauthPath="openid"
Icon={() =>
startupConfig.openidImageUrl ? (
<img src={startupConfig.openidImageUrl} alt="OpenID Logo" className="h-5 w-5" />
) : (
<OpenIDIcon />
)
}
label={startupConfig.openidLabel}
id="openid"
/>
</div>
</div>
);
}
return (
<>
{error != null && <ErrorMessage>{localize(getLoginError(error))}</ErrorMessage>}

View File

@@ -32,7 +32,12 @@ const Part = memo(({ part, isSubmitting, attachments, showCursor, isCreatedByUse
}
if (part.type === ContentTypes.ERROR) {
return <ErrorMessage text={part[ContentTypes.TEXT].value} className="my-2" />;
return (
<ErrorMessage
text={part[ContentTypes.ERROR] ?? part[ContentTypes.TEXT]?.value}
className="my-2"
/>
);
} else if (part.type === ContentTypes.TEXT) {
const text = typeof part.text === 'string' ? part.text : part.text.value;

View File

@@ -22,7 +22,7 @@ function Account() {
<div className="pb-3">
<EnableTwoFactorItem />
</div>
{Array.isArray(user.user?.backupCodes) && user.user?.backupCodes.length > 0 && (
{user?.user?.twoFactorEnabled && (
<div className="pb-3">
<BackupCodesItem />
</div>

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { motion } from 'framer-motion';
import { LockIcon, UnlockIcon } from 'lucide-react';
// import { motion } from 'framer-motion';
// import { LockIcon, UnlockIcon } from 'lucide-react';
import { Label, Button } from '~/components';
import { useLocalize } from '~/hooks';

View File

@@ -37,7 +37,7 @@ const TwoFactorAuthentication: React.FC = () => {
const [backupCodes, setBackupCodes] = useState<string[]>([]);
const [isDialogOpen, setDialogOpen] = useState<boolean>(false);
const [verificationToken, setVerificationToken] = useState<string>('');
const [phase, setPhase] = useState<Phase>(Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 ? 'disable' : 'setup');
const [phase, setPhase] = useState<Phase>(user?.twoFactorEnabled ? 'disable' : 'setup');
const { mutate: confirm2FAMutate } = useConfirmTwoFactorMutation();
const { mutate: enable2FAMutate, isLoading: isGenerating } = useEnableTwoFactorMutation();
@@ -56,7 +56,7 @@ const TwoFactorAuthentication: React.FC = () => {
const currentStep = steps.indexOf(phasesLabel[phase]);
const resetState = useCallback(() => {
if (Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 && otpauthUrl) {
if (user?.twoFactorEnabled && otpauthUrl) {
disable2FAMutate(undefined, {
onError: () =>
showToast({ message: localize('com_ui_2fa_disable_error'), status: 'error' }),
@@ -68,7 +68,7 @@ const TwoFactorAuthentication: React.FC = () => {
setBackupCodes([]);
setVerificationToken('');
setDisableToken('');
setPhase(Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 ? 'disable' : 'setup');
setPhase(user?.twoFactorEnabled ? 'disable' : 'setup');
setDownloaded(false);
}, [user, otpauthUrl, disable2FAMutate, localize, showToast]);
@@ -136,6 +136,7 @@ const TwoFactorAuthentication: React.FC = () => {
used: false,
usedAt: null,
})),
twoFactorEnabled: true,
}) as TUser,
);
}, [setUser, localize, showToast, backupCodes]);
@@ -171,6 +172,7 @@ const TwoFactorAuthentication: React.FC = () => {
...prev,
totpSecret: '',
backupCodes: [],
twoFactorEnabled: false,
}) as TUser,
);
setPhase('setup');
@@ -183,7 +185,7 @@ const TwoFactorAuthentication: React.FC = () => {
onError: () => showToast({ message: localize('com_ui_2fa_invalid'), status: 'error' }),
});
},
[disableToken, verify2FAMutate, disable2FAMutate, showToast, localize, setUser],
[verify2FAMutate, disable2FAMutate, showToast, localize, setUser],
);
return (
@@ -197,7 +199,7 @@ const TwoFactorAuthentication: React.FC = () => {
}}
>
<DisableTwoFactorToggle
enabled={Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0}
enabled={!!user?.twoFactorEnabled}
onChange={() => setDialogOpen(true)}
disabled={isVerifying || isDisabling || isGenerating}
/>
@@ -215,9 +217,11 @@ const TwoFactorAuthentication: React.FC = () => {
<OGDialogHeader>
<OGDialogTitle className="mb-2 flex items-center gap-3 text-2xl font-bold">
<SmartphoneIcon className="h-6 w-6 text-primary" />
{Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 ? localize('com_ui_2fa_disable') : localize('com_ui_2fa_setup')}
{user?.twoFactorEnabled
? localize('com_ui_2fa_disable')
: localize('com_ui_2fa_setup')}
</OGDialogTitle>
{Array.isArray(user?.backupCodes) && user?.backupCodes.length > 0 && phase !== 'disable' && (
{user?.twoFactorEnabled && phase !== 'disable' && (
<div className="mt-4 space-y-3">
<Progress
value={(steps.indexOf(phasesLabel[phase]) / (steps.length - 1)) * 100}

View File

@@ -74,7 +74,7 @@ export const DisablePhase: React.FC<DisablePhaseProps> = ({ onDisable, isDisabli
disabled={isDisabling || token.length !== (useBackup ? 8 : 6)}
className="w-full rounded-xl px-6 py-3 transition-all disabled:opacity-50"
>
{isDisabling === true && <Spinner className="mr-2" />}
{isDisabling && <Spinner className="mr-2" />}
{isDisabling ? localize('com_ui_disabling') : localize('com_ui_2fa_disable')}
</Button>
<button

View File

@@ -18,7 +18,7 @@ interface SetupPhaseProps {
onGenerate: () => void;
}
export const SetupPhase: React.FC<SetupPhaseProps> = ({ isGenerating, onGenerate, onNext }) => {
export const SetupPhase: React.FC<SetupPhaseProps> = ({ isGenerating, onGenerate }) => {
const localize = useLocalize();
return (

View File

@@ -68,6 +68,7 @@ export const LangSelector = ({
{ value: 'sv-SE', label: localize('com_nav_lang_swedish') },
{ value: 'ko-KR', label: localize('com_nav_lang_korean') },
{ value: 'vi-VN', label: localize('com_nav_lang_vietnamese') },
{ value: 'th-TH', label: localize('com_nav_lang_thai') },
{ value: 'tr-TR', label: localize('com_nav_lang_turkish') },
{ value: 'nl-NL', label: localize('com_nav_lang_dutch') },
{ value: 'id-ID', label: localize('com_nav_lang_indonesia') },

View File

@@ -553,8 +553,10 @@ const bedrockAnthropic: SettingsConfiguration = [
bedrock.topP,
bedrock.topK,
baseDefinitions.stop,
bedrock.region,
librechat.resendFiles,
bedrock.region,
anthropic.thinking,
anthropic.thinkingBudget,
];
const bedrockMistral: SettingsConfiguration = [
@@ -564,8 +566,8 @@ const bedrockMistral: SettingsConfiguration = [
bedrock.maxTokens,
mistral.temperature,
mistral.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
const bedrockCohere: SettingsConfiguration = [
@@ -575,8 +577,8 @@ const bedrockCohere: SettingsConfiguration = [
bedrock.maxTokens,
cohere.temperature,
cohere.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
const bedrockGeneral: SettingsConfiguration = [
@@ -585,8 +587,8 @@ const bedrockGeneral: SettingsConfiguration = [
librechat.maxContextTokens,
meta.temperature,
meta.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
const bedrockAnthropicCol1: SettingsConfiguration = [
@@ -602,8 +604,10 @@ const bedrockAnthropicCol2: SettingsConfiguration = [
bedrock.temperature,
bedrock.topP,
bedrock.topK,
bedrock.region,
librechat.resendFiles,
bedrock.region,
anthropic.thinking,
anthropic.thinkingBudget,
];
const bedrockMistralCol1: SettingsConfiguration = [
@@ -617,8 +621,8 @@ const bedrockMistralCol2: SettingsConfiguration = [
bedrock.maxTokens,
mistral.temperature,
mistral.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
const bedrockCohereCol1: SettingsConfiguration = [
@@ -632,8 +636,8 @@ const bedrockCohereCol2: SettingsConfiguration = [
bedrock.maxTokens,
cohere.temperature,
cohere.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
const bedrockGeneralCol1: SettingsConfiguration = [
@@ -646,8 +650,8 @@ const bedrockGeneralCol2: SettingsConfiguration = [
librechat.maxContextTokens,
meta.temperature,
meta.topP,
bedrock.region,
librechat.resendFiles,
bedrock.region,
];
export const settings: Record<string, SettingsConfiguration | undefined> = {

View File

@@ -1,7 +1,7 @@
import { dataService, QueryKeys } from 'librechat-data-provider';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import type * as t from 'librechat-data-provider';
import { dataService, QueryKeys, Constants } from 'librechat-data-provider';
import type { UseMutationResult } from '@tanstack/react-query';
import type * as t from 'librechat-data-provider';
export const useEditArtifact = (
_options?: t.EditArtifactOptions,
@@ -11,33 +11,47 @@ export const useEditArtifact = (
return useMutation({
mutationFn: (variables: t.TEditArtifactRequest) => dataService.editArtifact(variables),
onSuccess: (data, vars, context) => {
queryClient.setQueryData<t.TMessage[]>([QueryKeys.messages, data.conversationId], (prev) => {
if (!prev) {
return prev;
let targetNotFound = true;
const setMessageData = (conversationId?: string | null) => {
if (!conversationId) {
return;
}
const newArray = [...prev];
let targetIndex: number | undefined;
for (let i = newArray.length - 1; i >= 0; i--) {
if (newArray[i].messageId === vars.messageId) {
targetIndex = i;
break;
queryClient.setQueryData<t.TMessage[]>([QueryKeys.messages, conversationId], (prev) => {
if (!prev) {
return prev;
}
}
if (targetIndex == null) {
return prev;
}
const newArray = [...prev];
let targetIndex: number | undefined;
newArray[targetIndex] = {
...newArray[targetIndex],
content: data.content,
text: data.text,
};
for (let i = newArray.length - 1; i >= 0; i--) {
if (newArray[i].messageId === vars.messageId) {
targetIndex = i;
targetNotFound = false;
break;
}
}
return newArray;
});
if (targetIndex == null) {
return prev;
}
newArray[targetIndex] = {
...newArray[targetIndex],
content: data.content,
text: data.text,
};
return newArray;
});
};
setMessageData(data.conversationId);
if (targetNotFound) {
console.warn(
'Edited Artifact Message not found in cache, trying `new` as `conversationId`',
);
setMessageData(Constants.NEW_CONVO);
}
onSuccess?.(data, vars, context);
},

View File

@@ -6,6 +6,7 @@ import {
useContext,
useCallback,
createContext,
useRef,
} from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilState } from 'recoil';
@@ -35,6 +36,8 @@ const AuthContextProvider = ({
const [token, setToken] = useState<string | undefined>(undefined);
const [error, setError] = useState<string | undefined>(undefined);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
const logoutRedirectRef = useRef<string | undefined>(undefined);
const { data: userRole = null } = useGetRole(SystemRoles.USER, {
enabled: !!(isAuthenticated && (user?.role ?? '')),
});
@@ -52,16 +55,17 @@ const AuthContextProvider = ({
//@ts-ignore - ok for token to be undefined initially
setTokenHeader(token);
setIsAuthenticated(isAuthenticated);
if (redirect == null) {
// Use a custom redirect if set
const finalRedirect = logoutRedirectRef.current || redirect;
// Clear the stored redirect
logoutRedirectRef.current = undefined;
if (finalRedirect == null) {
return;
}
if (redirect.startsWith('http://') || redirect.startsWith('https://')) {
// For external links, use window.location
window.location.href = redirect;
// Or if you want to open in a new tab:
// window.open(redirect, '_blank');
if (finalRedirect.startsWith('http://') || finalRedirect.startsWith('https://')) {
window.location.href = finalRedirect;
} else {
navigate(redirect, { replace: true });
navigate(finalRedirect, { replace: true });
}
},
[navigate, setUser],
@@ -106,7 +110,16 @@ const AuthContextProvider = ({
});
const refreshToken = useRefreshTokenMutation();
const logout = useCallback(() => logoutUser.mutate(undefined), [logoutUser]);
const logout = useCallback(
(redirect?: string) => {
if (redirect) {
logoutRedirectRef.current = redirect;
}
logoutUser.mutate(undefined);
},
[logoutUser],
);
const userQuery = useGetUserQuery({ enabled: !!(token ?? '') });
const login = (data: t.TLoginUser) => {

View File

@@ -337,6 +337,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -348,6 +349,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",

View File

@@ -0,0 +1,744 @@
{
"com_a11y_ai_composing": "AI stále tvoří odpověď.",
"com_a11y_end": "AI dokončila svou odpověď.",
"com_a11y_start": "AI začala tvořit odpověď.",
"com_agents_allow_editing": "Povolit ostatním uživatelům upravovat vašeho agenta",
"com_agents_by_librechat": "od LibreChat",
"com_agents_code_interpreter": "Při povolení může váš agent využívat LibreChat Code Interpreter API ke spuštění generovaného kódu, včetně zpracování souborů, bezpečně. Vyžaduje platný API klíč.",
"com_agents_code_interpreter_title": "API pro interpretaci kódu",
"com_agents_create_error": "Při vytváření agenta došlo k chybě.",
"com_agents_description_placeholder": "Volitelné: Popište zde svého agenta",
"com_agents_enable_file_search": "Povolit vyhledávání souborů",
"com_agents_file_search_disabled": "Než nahrajete soubory pro vyhledávání, musíte vytvořit agenta.",
"com_agents_file_search_info": "Při povolení bude agent informován o přesných názvech souborů uvedených níže, což mu umožní získat relevantní kontext z těchto souborů.",
"com_agents_instructions_placeholder": "Systémové instrukce, které agent používá",
"com_agents_missing_provider_model": "Před vytvořením agenta vyberte poskytovatele a model.",
"com_agents_name_placeholder": "Volitelné: Název agenta",
"com_agents_no_access": "Nemáte oprávnění upravovat tohoto agenta.",
"com_agents_not_available": "Agent není k dispozici",
"com_agents_search_name": "Hledat agenty podle jména",
"com_agents_update_error": "Při aktualizaci agenta došlo k chybě.",
"com_assistants_action_attempt": "Asistent se chce spojit s {{0}}",
"com_assistants_actions": "Akce",
"com_assistants_actions_disabled": "Než přidáte akce, musíte vytvořit asistenta.",
"com_assistants_actions_info": "Umožněte asistentovi získávat informace nebo provádět akce přes API",
"com_assistants_add_actions": "Přidat akce",
"com_assistants_add_tools": "Přidat nástroje",
"com_assistants_allow_sites_you_trust": "Povolte pouze weby, kterým důvěřujete.",
"com_assistants_append_date": "Připojit aktuální datum a čas",
"com_assistants_append_date_tooltip": "Při povolení se k systémovým instrukcím asistenta připojí aktuální datum a čas klienta.",
"com_assistants_attempt_info": "Asistent chce odeslat následující:",
"com_assistants_available_actions": "Dostupné akce",
"com_assistants_capabilities": "Schopnosti",
"com_assistants_code_interpreter": "Interpret kódu",
"com_assistants_code_interpreter_files": "Soubory níže jsou určeny pouze pro interpret kódu:",
"com_assistants_code_interpreter_info": "Interpret kódu umožňuje asistentovi psát a spouštět kód. Tento nástroj dokáže zpracovat soubory s různými daty a formáty a generovat soubory, například grafy.",
"com_assistants_completed_action": "Spojil se s {{0}}",
"com_assistants_completed_function": "Spustil {{0}}",
"com_assistants_conversation_starters": "Témata konverzace",
"com_assistants_conversation_starters_placeholder": "Zadejte téma konverzace",
"com_assistants_create_error": "Při vytváření asistenta došlo k chybě.",
"com_assistants_create_success": "Úspěšně vytvořeno",
"com_assistants_delete_actions_error": "Při mazání akce došlo k chybě.",
"com_assistants_delete_actions_success": "Akce byla úspěšně odstraněna z asistenta",
"com_assistants_description_placeholder": "Volitelné: Popište zde svého asistenta",
"com_assistants_domain_info": "Asistent poslal tyto informace: {{0}}",
"com_assistants_file_search": "Vyhledávání souborů",
"com_assistants_file_search_info": "Vyhledávání souborů umožňuje asistentovi pracovat se znalostmi z nahraných souborů. Jakmile je soubor nahrán, asistent automaticky rozhodne, kdy získat jeho obsah na základě požadavků uživatelů. Připojování vektorových úložišť pro vyhledávání není zatím podporováno.",
"com_auth_already_have_account": "Už máte účet?",
"com_auth_apple_login": "Přihlásit se přes Apple",
"com_auth_back_to_login": "Zpět k přihlášení",
"com_auth_click": "Klikněte",
"com_auth_click_here": "Klikněte zde",
"com_auth_continue": "Pokračovat",
"com_auth_create_account": "Vytvořit účet",
"com_auth_discord_login": "Pokračovat přes Discord",
"com_auth_email": "E-mail",
"com_auth_email_address": "E-mailová adresa",
"com_auth_email_verification_invalid": "Neplatné ověření e-mailu",
"com_auth_email_verification_redirecting": "Přesměrování za {{0}} sekund...",
"com_auth_email_verification_resend_prompt": "Nedostali jste e-mail?",
"com_auth_email_verification_success": "E-mail byl úspěšně ověřen",
"com_auth_email_verifying_ellipsis": "Ověřování...",
"com_auth_error_create": "Při registraci účtu došlo k chybě. Zkuste to prosím znovu.",
"com_auth_error_invalid_reset_token": "Tento resetovací token hesla již není platný.",
"com_auth_error_login": "Nelze se přihlásit s poskytnutými údaji. Zkontrolujte své přihlašovací údaje a zkuste to znovu.",
"com_auth_error_login_ban": "Váš účet byl dočasně zablokován kvůli porušení našich pravidel.",
"com_auth_error_login_rl": "Příliš mnoho pokusů o přihlášení v krátkém čase. Zkuste to prosím později.",
"com_auth_error_login_server": "Došlo k interní chybě serveru. Počkejte několik okamžiků a zkuste to znovu.",
"com_auth_error_login_unverified": "Váš účet nebyl ověřen. Zkontrolujte svůj e-mail a najděte ověřovací odkaz.",
"com_auth_facebook_login": "Pokračovat přes Facebook",
"com_auth_full_name": "Celé jméno",
"com_auth_github_login": "Pokračovat přes Github",
"com_auth_google_login": "Pokračovat přes Google",
"com_auth_here": "ZDE",
"com_auth_login": "Přihlášení",
"com_auth_login_with_new_password": "Nyní se můžete přihlásit s novým heslem.",
"com_auth_name_max_length": "Jméno musí mít méně než 80 znaků",
"com_auth_name_min_length": "Jméno musí mít alespoň 3 znaky",
"com_auth_name_required": "Jméno je povinné",
"com_auth_no_account": "Nemáte účet?",
"com_auth_password": "Heslo",
"com_auth_password_confirm": "Potvrdit heslo",
"com_auth_password_forgot": "Zapomněli jste heslo?",
"com_auth_password_max_length": "Heslo musí mít méně než 128 znaků",
"com_auth_password_min_length": "Heslo musí mít alespoň 8 znaků",
"com_auth_password_not_match": "Hesla se neshodují",
"com_auth_password_required": "Heslo je povinné",
"com_auth_registration_success_generic": "Zkontrolujte svůj e-mail pro ověření vaší e-mailové adresy.",
"com_auth_registration_success_insecure": "Registrace byla úspěšná.",
"com_auth_reset_password": "Obnovit heslo",
"com_auth_reset_password_if_email_exists": "Pokud účet s touto e-mailovou adresou existuje, byl odeslán e-mail s instrukcemi pro resetování hesla. Nezapomeňte zkontrolovat složku spamu.",
"com_auth_reset_password_link_sent": "E-mail odeslán",
"com_auth_reset_password_success": "Obnova hesla úspěšná",
"com_auth_sign_in": "Přihlásit se",
"com_auth_sign_up": "Registrovat se",
"com_auth_submit_registration": "Odeslat registraci",
"com_auth_to_reset_your_password": "pro obnovení hesla.",
"com_auth_to_try_again": "zkusit znovu.",
"com_auth_two_factor": "Zkontrolujte vaši preferovanou aplikaci pro jednorázové heslo a zadejte kód",
"com_auth_username": "Uživatelské jméno (volitelné)",
"com_auth_username_max_length": "Uživatelské jméno musí mít méně než 20 znaků",
"com_auth_username_min_length": "Uživatelské jméno musí mít alespoň 2 znaky",
"com_auth_verify_your_identity": "Ověřte svou identitu",
"com_auth_welcome_back": "Vítejte zpět",
"com_click_to_download": "(klikněte zde pro stažení)",
"com_download_expired": "(platnost stažení vypršela)",
"com_download_expires": "(klikněte zde pro stažení - platnost vyprší {{0}})",
"com_endpoint": "Koncový bod",
"com_endpoint_agent": "Agent",
"com_endpoint_agent_model": "Model agenta (Doporučeno: GPT-3.5)",
"com_endpoint_agent_placeholder": "Vyberte prosím agenta",
"com_endpoint_ai": "AI",
"com_endpoint_anthropic_maxoutputtokens": "Maximální počet tokenů, které lze v odpovědi vygenerovat. Zadejte nižší hodnotu pro kratší odpovědi a vyšší hodnotu pro delší odpovědi. Poznámka: modely mohou skončit před dosažením tohoto maxima.",
"com_endpoint_anthropic_prompt_cache": "Ukládání výzev umožňuje znovu použít rozsáhlý kontext nebo instrukce napříč API voláními, čímž se snižují náklady a latence.",
"com_endpoint_anthropic_temp": "Hodnoty od 0 do 1. Použijte hodnotu blíže k 0 pro analytické / výběrové úlohy a blíže k 1 pro kreativní a generativní úkoly.",
"com_endpoint_anthropic_thinking": "Povoluje interní uvažování u podporovaných modelů Claude (3.7 Sonnet). Poznámka: vyžaduje nastavení \"Thinking Budget\" na nižší hodnotu než \"Max Output Tokens\".",
"com_endpoint_anthropic_thinking_budget": "Určuje maximální počet tokenů, které může Claude použít pro svůj interní proces uvažování.",
"com_endpoint_assistant": "Asistent",
"com_endpoint_assistant_model": "Model asistenta",
"com_endpoint_completion": "Dokončení",
"com_endpoint_completion_model": "Model dokončení (Doporučeno: GPT-4)",
"com_endpoint_config_key_name": "Klíč",
"com_endpoint_config_key_never_expires": "Váš klíč nikdy nevyprší",
"com_endpoint_config_placeholder": "Nastavte svůj klíč v nabídce záhlaví pro chat.",
"com_endpoint_config_value": "Zadejte hodnotu pro",
"com_endpoint_context": "Kontext",
"com_endpoint_context_info": "Maximální počet tokenů, které lze použít pro kontext. Použijte toto pro kontrolu počtu tokenů odeslaných na požadavek. Pokud není uvedeno, použije se výchozí nastavení systému podle velikosti kontextu známých modelů. Nastavení vyšších hodnot může vést k chybám a/nebo vyšším nákladům na tokeny.",
"com_endpoint_context_tokens": "Maximální počet kontextových tokenů",
"com_endpoint_custom_name": "Vlastní název",
"com_endpoint_default": "výchozí",
"com_endpoint_default_blank": "výchozí: prázdné",
"com_endpoint_default_empty": "výchozí: prázdné",
"com_endpoint_default_with_num": "výchozí: {{0}}",
"com_endpoint_examples": "Předvolby",
"com_endpoint_export": "Exportovat",
"com_endpoint_export_share": "Exportovat/Sdílet",
"com_endpoint_frequency_penalty": "Postih za časté opakování",
"com_endpoint_func_hover": "Povolit použití pluginů jako funkcí OpenAI",
"com_endpoint_google_custom_name_placeholder": "Nastavit vlastní název pro Google",
"com_endpoint_google_maxoutputtokens": "Maximální počet tokenů, které lze v odpovědi vygenerovat. Nižší hodnota pro kratší odpovědi, vyšší hodnota pro delší odpovědi. Poznámka: modely mohou skončit před dosažením tohoto maxima.",
"com_endpoint_google_temp": "Vyšší hodnoty = větší náhodnost, nižší hodnoty = soustředěnější a deterministický výstup. Doporučujeme upravit buď toto, nebo Top P, ale ne obojí.",
"com_endpoint_google_topk": "Top-k mění způsob, jakým model vybírá tokeny pro výstup. Top-k 1 znamená, že vybraný token je nejpravděpodobnější ze všech v modelové slovní zásobě (také nazývané chamtivé dekódování), zatímco top-k 3 znamená, že další token je vybrán ze tří nejpravděpodobnějších (s použitím teploty).",
"com_endpoint_google_topp": "Top-p mění způsob, jakým model vybírá tokeny pro výstup. Tokeny jsou vybírány od nejpravděpodobnějších, dokud součet jejich pravděpodobností nedosáhne hodnoty top-p.",
"com_endpoint_instructions_assistants": "Přepsat instrukce",
"com_endpoint_instructions_assistants_placeholder": "Přepíše instrukce asistenta. Užitečné pro úpravu chování v jednotlivých bězích.",
"com_endpoint_max_output_tokens": "Maximální počet výstupních tokenů",
"com_endpoint_message": "Zpráva",
"com_endpoint_message_new": "Zpráva {{0}}",
"com_endpoint_message_not_appendable": "Upravte svou zprávu nebo ji znovu vygenerujte.",
"com_endpoint_my_preset": "Moje předvolba",
"com_endpoint_no_presets": "Žádné předvolby zatím nejsou, použijte tlačítko nastavení k vytvoření jedné",
"com_endpoint_open_menu": "Otevřít nabídku",
"com_endpoint_openai_custom_name_placeholder": "Nastavit vlastní název pro AI",
"com_endpoint_openai_temp": "Vyšší hodnoty = větší náhodnost, nižší hodnoty = soustředěnější a deterministický výstup.",
"com_endpoint_openai_topp": "Alternativa k vzorkování s teplotou, tzv. nucleus sampling, kde model zvažuje výsledky tokenů s nejvyšší pravděpodobností. Například hodnota 0.1 znamená, že se berou v úvahu pouze tokeny tvořící 10 % nejvyšší pravděpodobnosti.",
"com_endpoint_output": "Výstup",
"com_endpoint_plug_image_detail": "Detail obrazu",
"com_endpoint_plug_resend_files": "Znovu odeslat soubory",
"com_endpoint_prompt_cache": "Použít cache výzev",
"com_endpoint_prompt_prefix": "Vlastní instrukce",
"com_endpoint_prompt_prefix_assistants": "Další instrukce",
"com_endpoint_reasoning_effort": "Úroveň úsilí při uvažování",
"com_endpoint_save_as_preset": "Uložit jako předvolbu",
"com_endpoint_search": "Hledat koncový bod podle názvu",
"com_endpoint_stop": "Zastavit sekvence",
"com_endpoint_temperature": "Teplota",
"com_endpoint_thinking": "Přemýšlení",
"com_endpoint_thinking_budget": "Rozpočet na přemýšlení",
"com_endpoint_top_k": "Top K",
"com_endpoint_top_p": "Top P",
"com_endpoint_use_active_assistant": "Použít aktivního asistenta",
"com_error_expired_user_key": "Poskytnutý klíč pro {{0}} vypršel v {{1}}. Zadejte nový klíč a zkuste to znovu.",
"com_error_files_dupe": "Byl zjištěn duplicitní soubor.",
"com_error_files_empty": "Prázdné soubory nejsou povoleny.",
"com_error_files_process": "Při zpracování souboru došlo k chybě.",
"com_error_files_unsupported_capability": "Nejsou povoleny žádné funkce podporující tento typ souboru.",
"com_error_files_upload": "Při nahrávání souboru došlo k chybě.",
"com_error_files_upload_canceled": "Požadavek na nahrání souboru byl zrušen. Poznámka: nahrávání souboru může stále probíhat a bude nutné jej ručně smazat.",
"com_error_files_validation": "Při ověřování souboru došlo k chybě.",
"com_error_input_length": "Počet tokenů v poslední zprávě je příliš dlouhý a přesahuje limit tokenů ({{0}}). Zkraťte svou zprávu, upravte maximální velikost kontextu v parametrech konverzace nebo rozdělte konverzaci.",
"com_error_invalid_user_key": "Zadaný klíč je neplatný. Zadejte platný klíč a zkuste to znovu.",
"com_error_moderation": "Zdá se, že obsah vaší zprávy byl označen naším moderovacím systémem, protože neodpovídá našim komunitním zásadám. Nemůžeme v této záležitosti pokračovat. Pokud máte jiné otázky nebo témata, která chcete prozkoumat, upravte svou zprávu nebo vytvořte novou konverzaci.",
"com_error_no_base_url": "Nebyla nalezena základní URL. Zadejte ji a zkuste to znovu.",
"com_error_no_user_key": "Nebyl nalezen žádný klíč. Zadejte klíč a zkuste to znovu.",
"com_files_filter": "Filtrovat soubory...",
"com_files_no_results": "Žádné výsledky.",
"com_files_number_selected": "Vybráno {{0}} z {{1}} položek",
"com_files_table": "něco sem musí přijít. bylo prázdné",
"com_generated_files": "Vygenerované soubory:",
"com_hide_examples": "Skrýt příklady",
"com_nav_2fa": "Dvoufaktorové ověřování (2FA)",
"com_nav_account_settings": "Nastavení účtu",
"com_nav_always_make_prod": "Vždy nastavovat nové verze jako produkční",
"com_nav_archive_created_at": "Datum archivace",
"com_nav_archive_name": "Název",
"com_nav_archived_chats": "Archivované chaty",
"com_nav_archived_chats_empty": "Nemáte žádné archivované konverzace.",
"com_nav_at_command": "@-Příkaz",
"com_nav_at_command_description": "Přepínání příkazů \"@\" pro přepínání koncových bodů, modelů, předvoleb atd.",
"com_nav_audio_play_error": "Chyba při přehrávání zvuku: {{0}}",
"com_nav_audio_process_error": "Chyba při zpracování zvuku: {{0}}",
"com_nav_auto_scroll": "Automaticky rolovat na nejnovější zprávu po otevření chatu",
"com_nav_auto_send_prompts": "Automatické odesílání výzev",
"com_nav_auto_send_text": "Automatické odesílání textu",
"com_nav_auto_send_text_disabled": "nastavte -1 pro deaktivaci",
"com_nav_auto_transcribe_audio": "Automaticky přepisovat zvuk",
"com_nav_automatic_playback": "Automatické přehrávání poslední zprávy",
"com_nav_balance": "Zůstatek",
"com_nav_browser": "Prohlížeč",
"com_nav_buffer_append_error": "Problém s přenosem zvuku. Přehrávání může být přerušeno.",
"com_nav_change_picture": "Změnit obrázek",
"com_nav_chat_commands": "Příkazy chatu",
"com_nav_chat_commands_info": "Tyto příkazy se aktivují zadáním specifických znaků na začátku vaší zprávy. Každý příkaz je spuštěn svým určeným prefixem. Můžete je deaktivovat, pokud tyto znaky často používáte na začátku zpráv.",
"com_nav_chat_direction": "Směr chatu",
"com_nav_clear_all_chats": "Vymazat všechny chaty",
"com_nav_clear_cache_confirm_message": "Opravdu chcete vymazat mezipaměť?",
"com_nav_clear_conversation": "Vymazat konverzace",
"com_nav_clear_conversation_confirm_message": "Opravdu chcete vymazat všechny konverzace? Tuto akci nelze vrátit zpět.",
"com_nav_close_sidebar": "Zavřít boční panel",
"com_nav_commands": "Příkazy",
"com_nav_confirm_clear": "Potvrdit vymazání",
"com_nav_conversation_mode": "Režim konverzace",
"com_nav_convo_menu_options": "Možnosti menu konverzace",
"com_nav_db_sensitivity": "Citlivost decibelů",
"com_nav_delete_account": "Smazat účet",
"com_nav_delete_account_button": "Trvale smazat můj účet",
"com_nav_delete_account_confirm": "Smazat účet - jste si jisti?",
"com_nav_delete_account_email_placeholder": "Zadejte e-mail vašeho účtu",
"com_nav_delete_cache_storage": "Smazat úložiště mezipaměti TTS",
"com_nav_delete_data_info": "Všechna vaše data budou smazána.",
"com_nav_delete_warning": "VAROVÁNÍ: Tato akce trvale smaže váš účet.",
"com_nav_edge": "Edge",
"com_nav_enable_cache_tts": "Povolit mezipaměť TTS",
"com_nav_enable_cloud_browser_voice": "Používat cloudové hlasy",
"com_nav_enabled": "Povoleno",
"com_nav_engine": "Engine",
"com_nav_enter_to_send": "Stiskněte Enter pro odeslání zprávy",
"com_nav_export": "Exportovat",
"com_nav_export_all_message_branches": "Exportovat všechny větve zpráv",
"com_nav_export_conversation": "Exportovat konverzaci",
"com_nav_export_filename": "Název souboru",
"com_nav_export_filename_placeholder": "Zadejte název souboru",
"com_nav_export_include_endpoint_options": "Zahrnout možnosti koncového bodu",
"com_nav_export_recursive": "Rekurzivní",
"com_nav_export_recursive_or_sequential": "Rekurzivní nebo sekvenční?",
"com_nav_export_type": "Typ",
"com_nav_external": "Externí",
"com_nav_font_size": "Velikost písma zprávy",
"com_nav_font_size_base": "Střední",
"com_nav_font_size_lg": "Velká",
"com_nav_font_size_sm": "Malá",
"com_nav_font_size_xl": "Extra velká",
"com_nav_font_size_xs": "Extra malá",
"com_nav_help_faq": "Nápověda a FAQ",
"com_nav_hide_panel": "Skrýt pravý panel",
"com_nav_info_code_artifacts": "Povoluje zobrazování experimentálních kódových artefaktů vedle chatu",
"com_nav_info_code_artifacts_agent": "Povoluje použití kódových artefaktů pro tohoto agenta. Ve výchozím nastavení jsou přidány další instrukce specifické pro použití artefaktů, pokud není povolen režim \"Vlastní výzva\".",
"com_nav_info_custom_prompt_mode": "Při povolení nebude zahrnuta výchozí systémová výzva pro artefakty. Všechny instrukce pro generování artefaktů musí být v tomto režimu poskytnuty ručně.",
"com_nav_info_enter_to_send": "Při povolení odešle stisk `ENTER` zprávu. Při deaktivaci přidá Enter nový řádek a zprávu odešlete stiskem `CTRL + ENTER` / `⌘ + ENTER`.",
"com_nav_info_fork_change_default": "`Viditelné zprávy pouze` zahrnuje pouze přímou cestu k vybrané zprávě. `Zahrnout související větve` přidá větve podél cesty. `Zahrnout vše od/do` zahrnuje všechny propojené zprávy a větve.",
"com_nav_info_fork_split_target_setting": "Při povolení začne větvení od cílové zprávy až po nejnovější zprávu v konverzaci podle zvoleného chování.",
"com_nav_info_include_shadcnui": "Při povolení budou zahrnuty instrukce pro použití komponent shadcn/ui.",
"com_nav_info_latex_parsing": "Při povolení bude LaTeX kód v zprávách vykreslen jako matematické rovnice.",
"com_nav_info_save_draft": "Při povolení se text a přílohy, které zadáte do chatu, automaticky ukládají jako koncepty.",
"com_nav_info_show_thinking": "Při povolení se automaticky zobrazí rozbalovací nabídky uvažování AI.",
"com_nav_info_user_name_display": "Při povolení se nad každou vaší zprávou zobrazí uživatelské jméno.",
"com_nav_lang_arabic": "العربية",
"com_nav_lang_auto": "Automatické rozpoznání",
"com_nav_lang_brazilian_portuguese": "Português Brasileiro",
"com_nav_lang_chinese": "中文",
"com_nav_lang_dutch": "Nederlands",
"com_nav_lang_english": "English",
"com_nav_lang_french": "Français ",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_italian": "Italiano",
"com_nav_lang_japanese": "日本語",
"com_nav_lang_korean": "한국어",
"com_nav_lang_polish": "Polski",
"com_nav_lang_portuguese": "Português",
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",
"com_nav_language": "Jazyk",
"com_nav_latex_parsing": "Zpracování LaTeXu v zprávách",
"com_nav_log_out": "Odhlásit se",
"com_nav_maximize_chat_space": "Maximalizovat prostor chatu",
"com_nav_modular_chat": "Povolit přepínání koncových bodů během konverzace",
"com_nav_my_files": "Moje soubory",
"com_nav_no_search_results": "Nebyl nalezen žádný výsledek",
"com_nav_not_supported": "Nepodporováno",
"com_nav_open_sidebar": "Otevřít boční panel",
"com_nav_playback_rate": "Rychlost přehrávání zvuku",
"com_nav_plugin_auth_error": "Při ověřování pluginu došlo k chybě.",
"com_nav_plugin_install": "Instalovat",
"com_nav_plugin_search": "Hledat pluginy",
"com_nav_plugin_store": "Obchod s pluginy",
"com_nav_plugin_uninstall": "Odinstalovat",
"com_nav_plus_command": "+-Příkaz",
"com_nav_plus_command_description": "Přepnutí příkazu \"+\" pro přidání nastavení více odpovědí",
"com_nav_profile_picture": "Profilový obrázek",
"com_nav_save_drafts": "Ukládat koncepty lokálně",
"com_nav_scroll_button": "Tlačítko pro posun na konec",
"com_nav_search_placeholder": "Hledat zprávy",
"com_nav_send_message": "Odeslat zprávu",
"com_nav_setting_account": "Účet",
"com_nav_setting_beta": "Beta funkce",
"com_nav_setting_chat": "Chat",
"com_nav_setting_data": "Ovládání dat",
"com_nav_setting_general": "Obecné",
"com_nav_setting_speech": "Hlas",
"com_nav_settings": "Nastavení",
"com_nav_shared_links": "Sdílené odkazy",
"com_nav_show_code": "Vždy zobrazit kód při použití interpretace kódu",
"com_nav_show_thinking": "Otevřít uvažovací nabídky ve výchozím nastavení",
"com_nav_slash_command": "/-Příkaz",
"com_nav_slash_command_description": "Přepnutí příkazu \"/\" pro výběr výzvy pomocí klávesnice",
"com_nav_source_buffer_error": "Chyba při nastavení přehrávání zvuku. Obnovte stránku.",
"com_nav_speech_cancel_error": "Nelze zastavit přehrávání zvuku. Možná budete muset stránku obnovit.",
"com_nav_speech_to_text": "Převod řeči na text",
"com_nav_stop_generating": "Zastavit generování",
"com_nav_text_to_speech": "Převod textu na řeč",
"com_nav_theme": "Motiv",
"com_nav_theme_dark": "Tmavý",
"com_nav_theme_light": "Světlý",
"com_nav_theme_system": "Systémový",
"com_nav_tool_dialog": "Nástroje asistenta",
"com_nav_tool_dialog_agents": "Nástroje agenta",
"com_nav_tool_dialog_description": "Asistent musí být uložen, aby výběr nástrojů přetrval.",
"com_nav_tool_remove": "Odstranit",
"com_nav_tool_search": "Hledat nástroje",
"com_nav_tts_init_error": "Nepodařilo se inicializovat převod textu na řeč: {{0}}",
"com_nav_tts_unsupported_error": "Převod textu na řeč pro vybraný engine není v tomto prohlížeči podporován.",
"com_nav_user": "UŽIVATEL",
"com_nav_user_msg_markdown": "Zobrazit uživatelské zprávy ve formátu Markdown",
"com_nav_user_name_display": "Zobrazit uživatelské jméno ve zprávách",
"com_nav_voice_select": "Hlas",
"com_nav_voices_fetch_error": "Nepodařilo se načíst možnosti hlasu. Zkontrolujte připojení k internetu.",
"com_nav_welcome_agent": "Vyberte agenta",
"com_nav_welcome_assistant": "Vyberte asistenta",
"com_nav_welcome_message": "Jak vám mohu dnes pomoci?",
"com_show_agent_settings": "Zobrazit nastavení agenta",
"com_show_completion_settings": "Zobrazit nastavení dokončení",
"com_show_examples": "Zobrazit příklady",
"com_sidepanel_agent_builder": "Tvůrce agentů",
"com_sidepanel_assistant_builder": "Tvůrce asistentů",
"com_sidepanel_attach_files": "Připojit soubory",
"com_sidepanel_conversation_tags": "Záložky",
"com_sidepanel_hide_panel": "Skrýt panel",
"com_sidepanel_manage_files": "Správa souborů",
"com_sidepanel_parameters": "Parametry",
"com_sidepanel_select_agent": "Vybrat agenta",
"com_sidepanel_select_assistant": "Vybrat asistenta",
"com_ui_2fa_account_security": "Dvoufaktorové ověřování přidává další vrstvu zabezpečení vašeho účtu",
"com_ui_2fa_disable": "Zakázat 2FA",
"com_ui_2fa_disable_error": "Při deaktivaci dvoufaktorového ověřování došlo k chybě",
"com_ui_2fa_disabled": "2FA bylo deaktivováno",
"com_ui_2fa_enable": "Povolit 2FA",
"com_ui_2fa_enabled": "2FA bylo povoleno",
"com_ui_2fa_generate_error": "Při generování nastavení 2FA došlo k chybě",
"com_ui_2fa_invalid": "Neplatný kód dvoufaktorového ověřování",
"com_ui_2fa_setup": "Nastavit 2FA",
"com_ui_2fa_verified": "Dvoufaktorové ověřování úspěšně ověřeno",
"com_ui_accept": "Přijímám",
"com_ui_add": "Přidat",
"com_ui_add_model_preset": "Přidat model nebo předvolbu pro další odpověď",
"com_ui_add_multi_conversation": "Přidat více konverzací",
"com_ui_admin": "Administrátor",
"com_ui_admin_access_warning": "Zakázání přístupu správce k této funkci může způsobit problémy v uživatelském rozhraní.",
"com_ui_admin_settings": "Nastavení správce",
"com_ui_advanced": "Pokročilé",
"com_ui_agent": "Agent",
"com_ui_agent_delete_error": "Při mazání agenta došlo k chybě",
"com_ui_agent_deleted": "Agent byl úspěšně smazán",
"com_ui_agent_duplicate_error": "Při duplikaci agenta došlo k chybě",
"com_ui_agent_duplicated": "Agent byl úspěšně duplikován",
"com_ui_agents": "Agenti",
"com_ui_agents_allow_create": "Povolit vytváření agentů",
"com_ui_agents_allow_share_global": "Povolit sdílení agentů všem uživatelům",
"com_ui_agents_allow_use": "Povolit používání agentů",
"com_ui_all": "vše",
"com_ui_all_proper": "Vše",
"com_ui_analyzing": "Analýza",
"com_ui_analyzing_finished": "Analýza dokončena",
"com_ui_api_key": "API klíč",
"com_ui_archive": "Archivovat",
"com_ui_archive_error": "Nepodařilo se archivovat konverzaci",
"com_ui_artifact_click": "Klikněte pro otevření",
"com_ui_artifacts": "Artefakty",
"com_ui_artifacts_toggle": "Přepnout uživatelské rozhraní artefaktů",
"com_ui_artifacts_toggle_agent": "Povolit artefakty",
"com_ui_ascending": "Vzestupně",
"com_ui_assistant": "Asistent",
"com_ui_assistant_delete_error": "Při mazání asistenta došlo k chybě",
"com_ui_assistant_deleted": "Asistent byl úspěšně smazán",
"com_ui_assistants": "Asistenti",
"com_ui_assistants_output": "Výstup asistentů",
"com_ui_attach_error": "Nelze připojit soubor. Vytvořte nebo vyberte konverzaci.",
"com_ui_attach_error_openai": "Nelze připojit soubory asistenta k jiným koncovým bodům",
"com_ui_attach_error_size": "Překročena velikost souboru pro koncový bod:",
"com_ui_attach_error_type": "Nepodporovaný typ souboru pro koncový bod:",
"com_ui_attach_warn_endpoint": "Nepodporované soubory mohou být ignorovány",
"com_ui_attachment": "Příloha",
"com_ui_auth_type": "Typ ověření",
"com_ui_auth_url": "Autorizační URL",
"com_ui_authentication": "Ověření",
"com_ui_authentication_type": "Typ ověření",
"com_ui_avatar": "Avatar",
"com_ui_azure": "Azure",
"com_ui_back_to_chat": "Zpět do chatu",
"com_ui_back_to_prompts": "Zpět na výzvy",
"com_ui_backup_codes": "Záložní kódy",
"com_ui_backup_codes_regenerate_error": "Při generování záložních kódů došlo k chybě",
"com_ui_backup_codes_regenerated": "Záložní kódy byly úspěšně vygenerovány",
"com_ui_basic": "Základní",
"com_ui_basic_auth_header": "Základní autorizační hlavička",
"com_ui_bearer": "Bearer",
"com_ui_bookmark_delete_confirm": "Opravdu chcete smazat tuto záložku?",
"com_ui_bookmarks": "Záložky",
"com_ui_bookmarks_add": "Přidat záložky",
"com_ui_bookmarks_add_to_conversation": "Přidat do aktuální konverzace",
"com_ui_bookmarks_count": "Počet",
"com_ui_bookmarks_create_error": "Při vytváření záložky došlo k chybě",
"com_ui_bookmarks_create_exists": "Tato záložka již existuje",
"com_ui_bookmarks_create_success": "Záložka byla úspěšně vytvořena",
"com_ui_bookmarks_delete": "Smazat záložku",
"com_ui_bookmarks_delete_error": "Při mazání záložky došlo k chybě",
"com_ui_bookmarks_delete_success": "Záložka byla úspěšně smazána",
"com_ui_bookmarks_description": "Popis",
"com_ui_bookmarks_edit": "Upravit záložku",
"com_ui_bookmarks_filter": "Filtrovat záložky...",
"com_ui_bookmarks_new": "Nová záložka",
"com_ui_bookmarks_title": "Název",
"com_ui_bookmarks_update_error": "Při aktualizaci záložky došlo k chybě",
"com_ui_bookmarks_update_success": "Záložka byla úspěšně aktualizována",
"com_ui_bulk_delete_error": "Nepodařilo se smazat sdílené odkazy",
"com_ui_callback_url": "Callback URL",
"com_ui_cancel": "Zrušit",
"com_ui_chat": "Chat",
"com_ui_chat_history": "Historie chatu",
"com_ui_clear": "Vymazat",
"com_ui_clear_all": "Vymazat vše",
"com_ui_client_id": "ID klienta",
"com_ui_client_secret": "Tajný klíč klienta",
"com_ui_close": "Zavřít",
"com_ui_close_menu": "Zavřít nabídku",
"com_ui_code": "Kód",
"com_ui_collapse_chat": "Sbalit chat",
"com_ui_command_placeholder": "Volitelné: Zadejte příkaz pro výzvu, jinak se použije název",
"com_ui_command_usage_placeholder": "Vybrat výzvu podle příkazu nebo názvu",
"com_ui_complete_setup": "Dokončit nastavení",
"com_ui_confirm_action": "Potvrdit akci",
"com_ui_confirm_admin_use_change": "Změna tohoto nastavení zablokuje přístup správcům, včetně vás. Opravdu chcete pokračovat?",
"com_ui_confirm_change": "Potvrdit změnu",
"com_ui_context": "Kontext",
"com_ui_continue": "Pokračovat",
"com_ui_controls": "Ovládání",
"com_ui_copied": "Zkopírováno!",
"com_ui_copied_to_clipboard": "Zkopírováno do schránky",
"com_ui_copy_code": "Kopírovat kód",
"com_ui_copy_link": "Kopírovat odkaz",
"com_ui_copy_to_clipboard": "Kopírovat do schránky",
"com_ui_create": "Vytvořit",
"com_ui_create_link": "Vytvořit odkaz",
"com_ui_create_prompt": "Vytvořit výzvu",
"com_ui_currently_production": "Aktuálně ve výrobě",
"com_ui_custom": "Vlastní",
"com_ui_custom_header_name": "Vlastní název hlavičky",
"com_ui_custom_prompt_mode": "Režim vlastní výzvy",
"com_ui_dashboard": "Dashboard",
"com_ui_date": "Datum",
"com_ui_date_april": "Duben",
"com_ui_date_august": "Srpen",
"com_ui_date_december": "Prosinec",
"com_ui_date_february": "Únor",
"com_ui_date_january": "Leden",
"com_ui_date_july": "Červenec",
"com_ui_date_june": "Červen",
"com_ui_date_march": "Březen",
"com_ui_date_may": "Květen",
"com_ui_date_november": "Listopad",
"com_ui_date_october": "Říjen",
"com_ui_date_previous_30_days": "Předchozích 30 dní",
"com_ui_date_previous_7_days": "Předchozích 7 dní",
"com_ui_date_september": "Září",
"com_ui_date_today": "Dnes",
"com_ui_date_yesterday": "Včera",
"com_ui_decline": "Nepřijímám",
"com_ui_default_post_request": "Výchozí (POST request)",
"com_ui_delete": "Smazat",
"com_ui_delete_action": "Smazat akci",
"com_ui_delete_action_confirm": "Opravdu chcete tuto akci smazat?",
"com_ui_delete_agent_confirm": "Opravdu chcete tohoto agenta smazat?",
"com_ui_delete_assistant_confirm": "Opravdu chcete tohoto asistenta smazat? Tuto akci nelze vrátit zpět.",
"com_ui_delete_confirm": "Tímto smažete",
"com_ui_delete_confirm_prompt_version_var": "Tímto smažete vybranou verzi pro \"{{0}}.\" Pokud neexistují žádné další verze, výzva bude smazána.",
"com_ui_delete_conversation": "Smazat chat?",
"com_ui_delete_prompt": "Smazat výzvu?",
"com_ui_delete_shared_link": "Smazat sdílený odkaz?",
"com_ui_delete_tool": "Smazat nástroj",
"com_ui_delete_tool_confirm": "Opravdu chcete tento nástroj smazat?",
"com_ui_descending": "Sestupně",
"com_ui_description": "Popis",
"com_ui_description_placeholder": "Volitelné: Zadejte popis pro zobrazení výzvy",
"com_ui_disabling": "Deaktivace...",
"com_ui_download": "Stáhnout",
"com_ui_download_artifact": "Stáhnout artefakt",
"com_ui_download_backup": "Stáhnout záložní kódy",
"com_ui_download_backup_tooltip": "Před pokračováním si stáhněte záložní kódy. Budete je potřebovat k opětovnému přístupu v případě ztráty autentizačního zařízení.",
"com_ui_download_error": "Chyba při stahování souboru. Soubor mohl být smazán.",
"com_ui_drag_drop": "něco sem musí přijít. bylo prázdné",
"com_ui_dropdown_variables": "Proměnné rozevírací nabídky:",
"com_ui_dropdown_variables_info": "Vytvořte vlastní rozevírací nabídky pro vaše výzvy: `{{variable_name:option1|option2|option3}}`",
"com_ui_duplicate": "Duplikovat",
"com_ui_duplication_error": "Při duplikaci konverzace došlo k chybě",
"com_ui_duplication_processing": "Duplikuji konverzaci...",
"com_ui_duplication_success": "Konverzace úspěšně duplikována",
"com_ui_edit": "Upravit",
"com_ui_empty_category": "-",
"com_ui_endpoint": "Koncový bod",
"com_ui_endpoint_menu": "Nabídka LLM koncových bodů",
"com_ui_endpoints_available": "Dostupné koncové body",
"com_ui_enter": "Enter",
"com_ui_enter_api_key": "Zadejte API klíč",
"com_ui_enter_openapi_schema": "Zadejte svůj OpenAPI schéma zde",
"com_ui_enter_var": "Zadejte {{0}}",
"com_ui_error": "Chyba",
"com_ui_error_connection": "Chyba při připojení k serveru, zkuste obnovit stránku.",
"com_ui_error_save_admin_settings": "Při ukládání nastavení správce došlo k chybě.",
"com_ui_examples": "Příklady",
"com_ui_export_convo_modal": "Exportovat konverzaci",
"com_ui_field_required": "Toto pole je povinné",
"com_ui_filter_prompts": "Filtrovat výzvy",
"com_ui_filter_prompts_name": "Filtrovat výzvy podle názvu",
"com_ui_finance": "Finance",
"com_ui_fork": "Rozdělit",
"com_ui_fork_all_target": "Zahrnout vše od/do",
"com_ui_fork_branches": "Zahrnout související větve",
"com_ui_fork_change_default": "Výchozí možnost rozdělení",
"com_ui_fork_default": "Použít výchozí možnost rozdělení",
"com_ui_fork_error": "Při rozdělování konverzace došlo k chybě",
"com_ui_fork_from_message": "Vyberte možnost rozdělení",
"com_ui_fork_info_1": "Použijte toto nastavení pro rozdělení zpráv podle požadovaného chování.",
"com_ui_fork_info_2": "\"Rozdělení\" znamená vytvoření nové konverzace začínající/končící u určitých zpráv v aktuální konverzaci, čímž se vytvoří kopie dle vybraných možností.",
"com_ui_fork_info_3": "\"Cílová zpráva\" označuje buď zprávu, ze které bylo okno otevřeno, nebo pokud zaškrtnete \"{{0}}\", nejnovější zprávu v konverzaci.",
"com_ui_fork_info_branches": "Tato možnost rozděluje viditelné zprávy spolu se souvisejícími větvemi; jinými slovy, přímou cestu k cílové zprávě včetně větví na této cestě.",
"com_ui_fork_info_remember": "Zaškrtnutím si zapamatujete vybrané možnosti pro budoucí použití, což urychlí rozdělování konverzací.",
"com_ui_fork_info_start": "Pokud zaškrtnuto, rozdělení začne od této zprávy až po nejnovější zprávu v konverzaci dle zvoleného chování.",
"com_ui_fork_info_target": "Tato možnost rozděluje všechny zprávy vedoucí k cílové zprávě, včetně sousedních; jinými slovy, zahrnuje všechny větve zpráv.",
"com_ui_fork_info_visible": "Tato možnost rozděluje pouze viditelné zprávy; jinými slovy, přímou cestu k cílové zprávě bez větví.",
"com_ui_fork_processing": "Rozděluji konverzaci...",
"com_ui_fork_remember": "Zapamatovat",
"com_ui_fork_remember_checked": "Vaše volba bude zapamatována. Můžete ji kdykoli změnit v nastavení.",
"com_ui_fork_split_target": "Začít rozdělení zde",
"com_ui_fork_split_target_setting": "Výchozí rozdělení od cílové zprávy",
"com_ui_fork_success": "Konverzace úspěšně rozdělena",
"com_ui_fork_visible": "Pouze viditelné zprávy",
"com_ui_generate_backup": "Generovat záložní kódy",
"com_ui_generate_qrcode": "Generovat QR kód",
"com_ui_generating": "Generuji...",
"com_ui_global_group": "něco sem musí přijít. bylo prázdné",
"com_ui_go_back": "Zpět",
"com_ui_go_to_conversation": "Přejít na konverzaci",
"com_ui_happy_birthday": "Mám 1. narozeniny!",
"com_ui_hide_qr": "Skrýt QR kód",
"com_ui_host": "Hostitel",
"com_ui_idea": "Nápady",
"com_ui_image_gen": "Generování obrázků",
"com_ui_import": "Importovat",
"com_ui_import_conversation_error": "Při importu konverzací došlo k chybě",
"com_ui_import_conversation_file_type_error": "Nepodporovaný typ souboru pro import",
"com_ui_import_conversation_info": "Importovat konverzace ze souboru JSON",
"com_ui_import_conversation_success": "Konverzace úspěšně importovány",
"com_ui_include_shadcnui": "Zahrnout instrukce pro shadcn/ui",
"com_ui_input": "Vstup",
"com_ui_instructions": "Instrukce",
"com_ui_latest_footer": "AICon se může plést. Vždy kontrolujte důležité informace.",
"com_ui_latest_production_version": "Nejnovější produkční verze",
"com_ui_latest_version": "Nejnovější verze",
"com_ui_librechat_code_api_key": "Získejte svůj API klíč pro LibreChat Code Interpreter",
"com_ui_librechat_code_api_subtitle": "Bezpečné. Vícejazyčné. Vstupní/Výstupní soubory.",
"com_ui_librechat_code_api_title": "Spustit AI kód",
"com_ui_llm_menu": "Nabídka LLM",
"com_ui_llms_available": "Dostupné LLM modely",
"com_ui_loading": "Načítání...",
"com_ui_locked": "Zamčeno",
"com_ui_logo": "Logo {{0}}",
"com_ui_manage": "Spravovat",
"com_ui_max_tags": "Maximální povolený počet je {{0}}, používám nejnovější hodnoty.",
"com_ui_mention": "Zmiňte koncový bod, asistenta nebo předvolbu pro rychlé přepnutí",
"com_ui_min_tags": "Nelze odebrat další hodnoty, minimální počet je {{0}}.",
"com_ui_misc": "Různé",
"com_ui_model": "Model",
"com_ui_model_parameters": "Parametry modelu",
"com_ui_more_info": "Více informací",
"com_ui_my_prompts": "Moje výzvy",
"com_ui_name": "Název",
"com_ui_new": "Nový",
"com_ui_new_chat": "Nový chat",
"com_ui_next": "Další",
"com_ui_no": "Ne",
"com_ui_no_backup_codes": "Nejsou k dispozici žádné záložní kódy. Vygenerujte nové.",
"com_ui_no_bookmarks": "Zdá se, že zatím nemáte žádné záložky. Klikněte na chat a přidejte novou.",
"com_ui_no_category": "Žádná kategorie",
"com_ui_no_changes": "Žádné změny k aktualizaci",
"com_ui_no_data": "něco sem musí přijít. bylo prázdné",
"com_ui_no_terms_content": "Žádný obsah podmínek a pravidel k zobrazení",
"com_ui_no_valid_items": "něco sem musí přijít. bylo prázdné",
"com_ui_none": "Žádné",
"com_ui_none_selected": "Nic nevybráno",
"com_ui_not_used": "Nepoužito",
"com_ui_nothing_found": "Nic nenalezeno",
"com_ui_oauth": "OAuth",
"com_ui_of": "z",
"com_ui_off": "Vypnuto",
"com_ui_on": "Zapnuto",
"com_ui_openai": "OpenAI",
"com_ui_page": "Stránka",
"com_ui_prev": "Předchozí",
"com_ui_preview": "Náhled",
"com_ui_privacy_policy": "Zásady ochrany osobních údajů",
"com_ui_privacy_policy_url": "URL zásad ochrany osobních údajů",
"com_ui_prompt": "Výzva",
"com_ui_prompt_already_shared_to_all": "Tato výzva je již sdílena se všemi uživateli",
"com_ui_prompt_name": "Název výzvy",
"com_ui_prompt_name_required": "Název výzvy je povinný",
"com_ui_prompt_preview_not_shared": "Autor neumožnil spolupráci na této výzvě.",
"com_ui_prompt_text": "Text",
"com_ui_prompt_text_required": "Text je povinný",
"com_ui_prompt_update_error": "Při aktualizaci výzvy došlo k chybě",
"com_ui_prompts": "Výzvy",
"com_ui_prompts_allow_create": "Povolit vytváření výzev",
"com_ui_prompts_allow_share_global": "Povolit sdílení výzev všem uživatelům",
"com_ui_prompts_allow_use": "Povolit používání výzev",
"com_ui_provider": "Poskytovatel",
"com_ui_read_aloud": "Přečíst nahlas",
"com_ui_refresh_link": "Obnovit odkaz",
"com_ui_regenerate": "Znovu vygenerovat",
"com_ui_regenerate_backup": "Znovu vygenerovat záložní kódy",
"com_ui_regenerating": "Generuji znovu...",
"com_ui_region": "Oblast",
"com_ui_rename": "Přejmenovat",
"com_ui_rename_prompt": "Přejmenovat výzvu",
"com_ui_requires_auth": "Vyžaduje ověření",
"com_ui_reset_var": "Obnovit {{0}}",
"com_ui_result": "Výsledek",
"com_ui_revoke": "Odvolat",
"com_ui_revoke_info": "Odvolat všechna uživatelem poskytnutá pověření",
"com_ui_revoke_key_confirm": "Opravdu chcete odvolat tento klíč?",
"com_ui_revoke_key_endpoint": "Odvolat klíč pro {{0}}",
"com_ui_revoke_keys": "Odvolat klíče",
"com_ui_revoke_keys_confirm": "Opravdu chcete odvolat všechny klíče?",
"com_ui_role_select": "Role",
"com_ui_roleplay": "Roleplay",
"com_ui_run_code": "Spustit kód",
"com_ui_run_code_error": "Při spouštění kódu došlo k chybě",
"com_ui_save": "Uložit",
"com_ui_save_submit": "Uložit a odeslat",
"com_ui_saved": "Uloženo!",
"com_ui_schema": "Schéma",
"com_ui_scope": "Rozsah",
"com_ui_search": "Hledat",
"com_ui_secret_key": "Tajný klíč",
"com_ui_select": "Vybrat",
"com_ui_select_file": "Vyberte soubor",
"com_ui_select_model": "Vyberte model",
"com_ui_select_provider": "Vyberte poskytovatele",
"com_ui_select_provider_first": "Nejprve vyberte poskytovatele",
"com_ui_select_region": "Vyberte oblast",
"com_ui_select_search_model": "Hledat model podle názvu",
"com_ui_select_search_plugin": "Hledat plugin podle názvu",
"com_ui_select_search_provider": "Hledat poskytovatele podle názvu",
"com_ui_select_search_region": "Hledat oblast podle názvu",
"com_ui_share": "Sdílet",
"com_ui_share_create_message": "Vaše jméno a zprávy, které přidáte po sdílení, zůstanou soukromé.",
"com_ui_share_delete_error": "Při mazání sdíleného odkazu došlo k chybě",
"com_ui_share_error": "Při sdílení odkazu na chat došlo k chybě",
"com_ui_share_form_description": "něco sem musí přijít. bylo prázdné",
"com_ui_share_link_to_chat": "Sdílet odkaz na chat",
"com_ui_share_to_all_users": "Sdílet se všemi uživateli",
"com_ui_share_update_message": "Vaše jméno, vlastní instrukce a zprávy přidané po sdílení zůstanou soukromé.",
"com_ui_share_var": "Sdílet {{0}}",
"com_ui_shared_link_bulk_delete_success": "Sdílené odkazy byly úspěšně smazány",
"com_ui_shared_link_delete_success": "Sdílený odkaz byl úspěšně smazán",
"com_ui_shared_link_not_found": "Sdílený odkaz nebyl nalezen",
"com_ui_shared_prompts": "Sdílené výzvy",
"com_ui_shop": "Nakupování",
"com_ui_show": "Zobrazit",
"com_ui_show_all": "Zobrazit vše",
"com_ui_show_qr": "Zobrazit QR kód",
"com_ui_sign_in_to_domain": "Přihlásit se do {{0}}",
"com_ui_simple": "Jednoduché",
"com_ui_size": "Velikost",
"com_ui_special_variables": "Speciální proměnné:",
"com_ui_special_variables_info": "Použijte `{{current_date}}` pro aktuální datum a `{{current_user}}` pro vaše uživatelské jméno.",
"com_ui_speech_while_submitting": "Nelze odeslat hlasový vstup, zatímco se generuje odpověď",
"com_ui_stop": "Zastavit",
"com_ui_storage": "Úložiště",
"com_ui_submit": "Odeslat",
"com_ui_teach_or_explain": "Učení",
"com_ui_temporary_chat": "Dočasný chat",
"com_ui_terms_and_conditions": "Obchodní podmínky",
"com_ui_terms_of_service": "Podmínky služby",
"com_ui_thinking": "Přemýšlení...",
"com_ui_thoughts": "Myšlenky",
"com_ui_token_exchange_method": "Metoda výměny tokenů",
"com_ui_token_url": "URL tokenu",
"com_ui_tools": "Nástroje",
"com_ui_travel": "Cestování",
"com_ui_unarchive": "Obnovit archiv",
"com_ui_unarchive_error": "Nepodařilo se obnovit archivovanou konverzaci",
"com_ui_unknown": "Neznámé",
"com_ui_update": "Aktualizovat",
"com_ui_upload": "Nahrát",
"com_ui_upload_code_files": "Nahrát soubory pro interpret kódu",
"com_ui_upload_delay": "Nahrávání \"{{0}}\" trvá déle než obvykle. Počkejte, než bude soubor indexován.",
"com_ui_upload_error": "Při nahrávání souboru došlo k chybě",
"com_ui_upload_file_search": "Nahrát pro vyhledávání v souborech",
"com_ui_upload_files": "Nahrát soubory",
"com_ui_upload_image": "Nahrát obrázek",
"com_ui_upload_image_input": "Nahrát obrázek",
"com_ui_upload_invalid": "Neplatný soubor pro nahrání. Musí to být obrázek nepřesahující limit.",
"com_ui_upload_invalid_var": "Neplatný soubor pro nahrání. Musí to být obrázek nepřesahující {{0}} MB",
"com_ui_upload_success": "Soubor byl úspěšně nahrán",
"com_ui_upload_type": "Vyberte typ nahrávání",
"com_ui_use_2fa_code": "Použít kód 2FA",
"com_ui_use_backup_code": "Použít záložní kód",
"com_ui_use_micrphone": "Použít mikrofon",
"com_ui_use_prompt": "Použít výzvu",
"com_ui_used": "Použito",
"com_ui_variables": "Proměnné",
"com_ui_variables_info": "Použijte dvojité složené závorky k vytvoření proměnných, např. `{{příklad proměnné}}`, které lze vyplnit při použití výzvy.",
"com_ui_verify": "Ověřit",
"com_ui_version_var": "Verze {{0}}",
"com_ui_versions": "Verze",
"com_ui_view_source": "Zobrazit zdrojový chat",
"com_ui_write": "Psát",
"com_ui_yes": "Ano",
"com_ui_zoom": "Přiblížit",
"com_user_message": "Vy",
"com_warning_resubmit_unsupported": "Opětovné odeslání AI zprávy není pro tento koncový bod podporováno."
}

View File

@@ -139,6 +139,8 @@
"com_endpoint_anthropic_maxoutputtokens": "Maximale Anzahl von Token, die in der Antwort erzeugt werden können. Gib einen niedrigeren Wert für kürzere Antworten und einen höheren Wert für längere Antworten an. Hinweis: Die Modelle können auch vor Erreichen dieses Maximums stoppen.",
"com_endpoint_anthropic_prompt_cache": "Prompt-Caching ermöglicht die Wiederverwendung von umfangreichen Kontexten oder Anweisungen über mehrere API-Aufrufe hinweg, wodurch Kosten und Latenzzeiten reduziert werden",
"com_endpoint_anthropic_temp": "Reicht von 0 bis 1. Verwende Temperaturen näher an 0 für analytische / Multiple-Choice-Aufgaben und näher an 1 für kreative und generative Aufgaben. Wir empfehlen, entweder dies oder Top P zu ändern, aber nicht beides.",
"com_endpoint_anthropic_thinking": "Aktiviert internes logisches Denken für unterstützte Claude-Modelle (3.7 Sonnet). Hinweis: Erfordert, dass \"Denkbudget\" festgelegt und niedriger als \"Max. Ausgabe-Token\" ist",
"com_endpoint_anthropic_thinking_budget": "Bestimmt die maximale Anzahl an Token, die Claude für seinen internen Denkprozess verwenden darf. Ein höheres Budget kann die Antwortqualität verbessern, indem es eine gründlichere Analyse bei komplexen Problemen ermöglicht. Claude nutzt jedoch möglicherweise nicht das gesamte zugewiesene Budget, insbesondere bei Werten über 32.000. Diese Einstellung muss niedriger sein als \"Max. Ausgabe-Token\".",
"com_endpoint_anthropic_topk": "Top-k ändert, wie das Modell Token für die Ausgabe auswählt. Ein Top-k von 1 bedeutet, dass das ausgewählte Token das wahrscheinlichste unter allen Token im Vokabular des Modells ist (auch \"Greedy Decoding\" genannt), während ein Top-k von 3 bedeutet, dass das nächste Token aus den 3 wahrscheinlichsten Token ausgewählt wird (unter Verwendung der Temperatur).",
"com_endpoint_anthropic_topp": "Top-p ändert, wie das Modell Token für die Ausgabe auswählt. Token werden von den wahrscheinlichsten K (siehe topK-Parameter) bis zu den am wenigsten wahrscheinlichen ausgewählt, bis die Summe ihrer Wahrscheinlichkeiten dem Top-p-Wert entspricht.",
"com_endpoint_assistant": "Assistent",
@@ -189,7 +191,7 @@
"com_endpoint_instructions_assistants_placeholder": "Überschreibt die Anweisungen des Assistenten. Dies ist nützlich, um das Verhalten auf Basis einzelner Ausführungen zu modifizieren.",
"com_endpoint_max_output_tokens": "Max. Antwort-Token",
"com_endpoint_message": "Nachricht an",
"com_endpoint_message_new": "Nachricht {{0}}",
"com_endpoint_message_new": "Nachricht an {{0}}",
"com_endpoint_message_not_appendable": "Bearbeite deine Nachricht oder generiere neu.",
"com_endpoint_my_preset": "Meine Voreinstellung",
"com_endpoint_no_presets": "Noch keine Voreinstellungen, verwende die KI-Einstellungsschaltfläche, um eine zu erstellen",
@@ -243,6 +245,8 @@
"com_endpoint_stop": "Stop-Sequenzen",
"com_endpoint_stop_placeholder": "Trenne Stoppwörter durch Drücken der `Enter`-Taste",
"com_endpoint_temperature": "Temperatur",
"com_endpoint_thinking": "Denken",
"com_endpoint_thinking_budget": "Denkbudget",
"com_endpoint_top_k": "Top K",
"com_endpoint_top_p": "Top P",
"com_endpoint_use_active_assistant": "Aktiven Assistenten verwenden",
@@ -349,6 +353,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -360,6 +365,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",
@@ -434,7 +440,7 @@
"com_sidepanel_parameters": "KI-Einstellungen",
"com_sidepanel_select_agent": "Wähle einen Agenten",
"com_sidepanel_select_assistant": "Assistenten auswählen",
"com_ui_2fa_account_security": "Die Zwei-Faktor-Authentifizierung bietet deinem Konto eine zusätzliche Sicherheitsebene.",
"com_ui_2fa_account_security": "Die Zwei-Faktor-Authentifizierung bietet Ihrem Konto eine zusätzliche Sicherheitsebene.",
"com_ui_2fa_disable": "2FA deaktivieren",
"com_ui_2fa_disable_error": "Beim Deaktivieren der Zwei-Faktor-Authentifizierung ist ein Fehler aufgetreten.",
"com_ui_2fa_disabled": "2FA wurde deaktiviert.",
@@ -525,6 +531,7 @@
"com_ui_chat_history": "Chatverlauf",
"com_ui_clear": "Löschen",
"com_ui_clear_all": "Auswahl löschen",
"com_ui_client_id": "Client-ID",
"com_ui_client_secret": "Client Secret",
"com_ui_close": "Schließen",
"com_ui_close_menu": "Menü schließen",
@@ -590,8 +597,9 @@
"com_ui_download": "Herunterladen",
"com_ui_download_artifact": "Artefakt herunterladen",
"com_ui_download_backup": "Backup-Codes herunterladen",
"com_ui_download_backup_tooltip": "Bevor du fortfährst, lade bitte deine Backup-Codes herunter. Du benötigst sie, um den Zugang wiederherzustellen, falls du dein Authentifizierungsgerät verlierst.",
"com_ui_download_backup_tooltip": "Bevor Sie fortfahren, laden Sie bitte Ihre Backup-Codes herunter. Sie benötigen sie, um den Zugang wiederherzustellen, falls Sie Ihr Authentifizierungsgerät verlieren.",
"com_ui_download_error": "Fehler beim Herunterladen der Datei. Die Datei wurde möglicherweise gelöscht.",
"com_ui_drag_drop": "Ziehen und Ablegen",
"com_ui_dropdown_variables": "Dropdown-Variablen:",
"com_ui_dropdown_variables_info": "Erstellen Sie benutzerdefinierte Dropdown-Menüs für Ihre Eingabeaufforderungen: `{{variable_name:option1|option2|option3}}`",
"com_ui_duplicate": "Duplizieren",
@@ -599,6 +607,7 @@
"com_ui_duplication_processing": "Konversation wird dupliziert...",
"com_ui_duplication_success": "Unterhaltung erfolgreich dupliziert",
"com_ui_edit": "Bearbeiten",
"com_ui_empty_category": "-",
"com_ui_endpoint": "Endpunkt",
"com_ui_endpoint_menu": "LLM-Endpunkt-Menü",
"com_ui_endpoints_available": "Verfügbare Endpunkte",
@@ -677,6 +686,7 @@
"com_ui_more_info": "Mehr Infos",
"com_ui_my_prompts": "Meine Prompts",
"com_ui_name": "Name",
"com_ui_new": "Neu",
"com_ui_new_chat": "Neuer Chat",
"com_ui_next": "Weiter",
"com_ui_no": "Nein",

View File

@@ -101,6 +101,7 @@
"com_auth_google_login": "Continue with Google",
"com_auth_here": "HERE",
"com_auth_login": "Login",
"com_ui_redirecting_to_provider": "Redirecting to {{0}}, please wait...",
"com_auth_login_with_new_password": "You may now login with your new password.",
"com_auth_name_max_length": "Name must be less than 80 characters",
"com_auth_name_min_length": "Name must be at least 3 characters",
@@ -369,6 +370,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",
@@ -834,4 +836,4 @@
"com_ui_zoom": "Zoom",
"com_user_message": "You",
"com_warning_resubmit_unsupported": "Resubmitting the AI message is not supported for this endpoint."
}
}

View File

@@ -1,4 +1,6 @@
{
"chat_direction_left_to_right": "algo debería ir aquí pero está vacío",
"chat_direction_right_to_left": "algo debería ir aquí pero está vacío",
"com_a11y_ai_composing": "La IA está componiendo la respuesta",
"com_a11y_end": "La IA ha finalizado su respuesta",
"com_a11y_start": "La IA ha comenzado su respuesta",
@@ -18,13 +20,16 @@
"com_agents_not_available": "Agente no disponible",
"com_agents_search_name": "Buscar agentes por nombre",
"com_agents_update_error": "Hubo un error al actualizar su agente.",
"com_assistants_action_attempt": "El asistente quiere hablar con {{0}}",
"com_assistants_actions": "Acciones",
"com_assistants_actions_disabled": "Necesita crear un asistente antes de añadir acciones.",
"com_assistants_actions_info": "Permita que su Asistente recupere información o realice acciones a través de API's",
"com_assistants_add_actions": "Añadir Acciones",
"com_assistants_add_tools": "Añadir Herramientas",
"com_assistants_allow_sites_you_trust": "Solo permite sitios en los que confíes.",
"com_assistants_append_date": "Añadir Fecha y Hora Actual",
"com_assistants_append_date_tooltip": "Cuando está habilitado, la fecha y hora actual del cliente se adjuntarán a las instrucciones del sistema del asistente.",
"com_assistants_attempt_info": "El asistente quiere enviar lo siguiente:",
"com_assistants_available_actions": "Acciones Disponibles",
"com_assistants_capabilities": "Capacidades",
"com_assistants_code_interpreter": "Intérprete de Código",
@@ -59,6 +64,7 @@
"com_assistants_update_error": "Hubo un error al actualizar su asistente.",
"com_assistants_update_success": "Actualizado con éxito",
"com_auth_already_have_account": "¿Ya tiene una cuenta?",
"com_auth_apple_login": "Inicia con Apple",
"com_auth_back_to_login": "Volver al inicio de sesión",
"com_auth_click": "Haga clic",
"com_auth_click_here": "Haz clic aquí",
@@ -117,9 +123,11 @@
"com_auth_submit_registration": "Enviar registro",
"com_auth_to_reset_your_password": "para restablecer su contraseña.",
"com_auth_to_try_again": "para intentar de nuevo.",
"com_auth_two_factor": "Revisa tu aplicación preferida de OTP para obtener el código",
"com_auth_username": "Nombre de usuario (opcional)",
"com_auth_username_max_length": "El nombre de usuario debe tener menos de 20 caracteres",
"com_auth_username_min_length": "El nombre de usuario debe tener al menos 2 caracteres",
"com_auth_verify_your_identity": "Verifica Tu Identidad",
"com_auth_welcome_back": "Bienvenido de nuevo",
"com_click_to_download": "(haga clic aquí para descargar)",
"com_download_expired": "Descarga expirada",
@@ -337,6 +345,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -348,6 +357,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",
@@ -420,6 +430,8 @@
"com_sidepanel_parameters": "Parámetros",
"com_sidepanel_select_agent": "Seleccione un Agente",
"com_sidepanel_select_assistant": "Seleccionar un Asistente",
"com_ui_2fa_enable": "Activa 2FA",
"com_ui_2fa_enabled": "2FA ha sido activada",
"com_ui_accept": "Acepto",
"com_ui_add": "Agregar",
"com_ui_add_model_preset": "Agregar un modelo o configuración preestablecida para una respuesta adicional",

View File

@@ -87,6 +87,7 @@
"com_auth_email_verification_redirecting": "Suunatakse ümber {{0}} sekundi pärast...",
"com_auth_email_verification_resend_prompt": "Kas sa ei saanud e-kirja?",
"com_auth_email_verification_success": "E-post kinnitatud",
"com_auth_email_verifying_ellipsis": "Kontrollimine...",
"com_auth_error_create": "Konto registreerimisel tekkis viga. Proovige uuesti.",
"com_auth_error_invalid_reset_token": "See parooli lähtestamise tunnus pole enam kehtiv.",
"com_auth_error_login": "Sisselogimine esitatud teabega ei õnnestu. Palun kontrolli oma andmeid ja proovi uuesti.",
@@ -123,9 +124,11 @@
"com_auth_submit_registration": "Saada registreerimine",
"com_auth_to_reset_your_password": "parooli lähtestamiseks.",
"com_auth_to_try_again": "uuesti proovimiseks.",
"com_auth_two_factor": "Kontrolli oma eelistatud ühekordse parooli rakendust koodi saamiseks",
"com_auth_username": "Kasutajanimi (valikuline)",
"com_auth_username_max_length": "Kasutajanimi peab olema vähem kui 20 tähemärki",
"com_auth_username_min_length": "Kasutajanimi peab olema vähemalt 2 tähemärki",
"com_auth_verify_your_identity": "Kontrolli",
"com_auth_welcome_back": "Teretulemast tagasi",
"com_click_to_download": "(vajuta siia, et alla laadida)",
"com_download_expired": "(allalaadimine aegunud)",
@@ -265,6 +268,7 @@
"com_files_table": "Failide tabel",
"com_generated_files": "Genereeritud failid:",
"com_hide_examples": "Peida näited",
"com_nav_2fa": "Kaheastmeline autentimine (2FA)",
"com_nav_account_settings": "Konto seaded",
"com_nav_always_make_prod": "Tee uued versioonid alati toodangusse",
"com_nav_archive_created_at": "Arhiveerimise kuupäev",
@@ -349,6 +353,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -360,6 +365,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",
@@ -434,6 +440,16 @@
"com_sidepanel_parameters": "Parameetrid",
"com_sidepanel_select_agent": "Vali agent",
"com_sidepanel_select_assistant": "Vali assistent",
"com_ui_2fa_account_security": "Kaheastmeline autentimine lisab teie kontole täiendava turvalisuse kihi",
"com_ui_2fa_disable": "Lülita 2FA välja",
"com_ui_2fa_disable_error": "Tekkis viga kaheastmelise autentimise väljalülitamisel",
"com_ui_2fa_disabled": "2FA on välja lülitatud",
"com_ui_2fa_enable": "Aktiveeri 2FA",
"com_ui_2fa_enabled": "2FA on aktiveeritud",
"com_ui_2fa_generate_error": "Kaheastmelise autentimise seadete genereerimisel tekkis viga",
"com_ui_2fa_invalid": "Vale kaheastmeline autentimise kood",
"com_ui_2fa_setup": "Seadista 2FA",
"com_ui_2fa_verified": "Kaheastmeline autentimine õnnestus",
"com_ui_accept": "Nõustun",
"com_ui_add": "Lisa",
"com_ui_add_model_preset": "Lisa mudel või eelseadistus täiendava vastuse jaoks",
@@ -484,6 +500,9 @@
"com_ui_azure": "Azure",
"com_ui_back_to_chat": "Tagasi vestlusesse",
"com_ui_back_to_prompts": "Tagasi sisendite juurde",
"com_ui_backup_codes": "Varukoodid",
"com_ui_backup_codes_regenerate_error": "Varukoodide loomisel tekkis viga",
"com_ui_backup_codes_regenerated": "Varukoodide loomine oli edukas",
"com_ui_basic": "Põhiline",
"com_ui_basic_auth_header": "Põhiline autentimise päis",
"com_ui_bearer": "Bearer",
@@ -520,6 +539,7 @@
"com_ui_collapse_chat": "Ahenda vestlus",
"com_ui_command_placeholder": "Valikuline: sisesta sisendi jaoks käsk või kasutatakse nime",
"com_ui_command_usage_placeholder": "Vali sisend käsu või nime järgi",
"com_ui_complete_setup": "Valmis",
"com_ui_confirm_action": "Kinnita tegevus",
"com_ui_confirm_admin_use_change": "Selle seadistuse muutmine blokeerib juurdepääsu administraatoritele, sealhulgas sinule endale. Oled sa kindel, et sa soovid jätkata?",
"com_ui_confirm_change": "Kinnita muudatus",
@@ -573,8 +593,11 @@
"com_ui_descending": "Desc",
"com_ui_description": "Kirjeldus",
"com_ui_description_placeholder": "Valikuline: sisesta sisendi jaoks kuvatav kirjeldus",
"com_ui_disabling": "Välja lülitamine...",
"com_ui_download": "Laadi alla",
"com_ui_download_artifact": "Laadi artefakt alla",
"com_ui_download_backup": "Laadi alla varukoodid",
"com_ui_download_backup_tooltip": "Enne jätkamist laadi alla oma varukoodid. Vajad neid ligipääsu taastamiseks, kui peaksid oma autentimisseadme kaotama.",
"com_ui_download_error": "Viga faili allalaadimisel. Fail võib olla kustutatud.",
"com_ui_drag_drop": "Lohistage",
"com_ui_dropdown_variables": "Rippmenüü muutujad:",
@@ -623,6 +646,9 @@
"com_ui_fork_split_target_setting": "Alusta vaikimisi sihtsõnumist hargnemist",
"com_ui_fork_success": "Vestluse hargnemine õnnestus",
"com_ui_fork_visible": "Ainult nähtavad sõnumid",
"com_ui_generate_backup": "Loo varukoodid",
"com_ui_generate_qrcode": "Loo QR-kood",
"com_ui_generating": "Loomine...",
"com_ui_global_group": "Ülene grupp",
"com_ui_go_back": "Mine tagasi",
"com_ui_go_to_conversation": "Mine vestlusesse",
@@ -631,6 +657,7 @@
"com_ui_host": "Host",
"com_ui_idea": "Ideed",
"com_ui_image_gen": "Pildi genereerimine",
"com_ui_import": "Impordi",
"com_ui_import_conversation_error": "Vestluste importimisel tekkis viga",
"com_ui_import_conversation_file_type_error": "Toetamatu imporditüüp",
"com_ui_import_conversation_info": "Impordi vestlused JSON-failist",
@@ -663,6 +690,7 @@
"com_ui_new_chat": "Uus vestlus",
"com_ui_next": "Järgmine",
"com_ui_no": "Ei",
"com_ui_no_backup_codes": "Varukoodid puuduvad. Palun loo uued",
"com_ui_no_bookmarks": "Tundub, et sul pole veel järjehoidjaid. Klõpsa vestlusele ja lisa uus",
"com_ui_no_category": "Kategooriat pole",
"com_ui_no_changes": "Uuendamiseks pole muudatusi",
@@ -671,6 +699,7 @@
"com_ui_no_valid_items": "Sobivad üksused puuduvad!",
"com_ui_none": "Puudub",
"com_ui_none_selected": "Ühtegi pole valitud",
"com_ui_not_used": "Kasutamata",
"com_ui_nothing_found": "Midagi ei leitud",
"com_ui_oauth": "OAuth",
"com_ui_of": "kohta",
@@ -698,6 +727,8 @@
"com_ui_read_aloud": "Loe valjusti",
"com_ui_refresh_link": "Värskenda linki",
"com_ui_regenerate": "Genereeri uuesti",
"com_ui_regenerate_backup": "Loo varukoodid uuesti",
"com_ui_regenerating": "Uuesti loomine...",
"com_ui_region": "Piirkond",
"com_ui_rename": "Nimeta ümber",
"com_ui_rename_prompt": "Nimeta sisend ümber",
@@ -720,6 +751,7 @@
"com_ui_schema": "Skeem",
"com_ui_scope": "Ulatus",
"com_ui_search": "Otsi",
"com_ui_secret_key": "Salavõti",
"com_ui_select": "Vali",
"com_ui_select_file": "Vali fail",
"com_ui_select_model": "Vali mudel",
@@ -744,6 +776,7 @@
"com_ui_shared_link_not_found": "Jagatud linki ei leitud",
"com_ui_shared_prompts": "Jagatud sisendid",
"com_ui_shop": "Ostlemine",
"com_ui_show": "Kuva",
"com_ui_show_all": "Näita kõiki",
"com_ui_show_qr": "Näita QR-koodi",
"com_ui_sign_in_to_domain": "Logi sisse {{0}}",
@@ -781,10 +814,14 @@
"com_ui_upload_invalid_var": "Fail on üleslaadimiseks vigane. Peab olema pilt, mis ei ületa {{0}} MB",
"com_ui_upload_success": "Faili üleslaadimine õnnestus",
"com_ui_upload_type": "Vali üleslaadimise tüüp",
"com_ui_use_2fa_code": "Kasuta hoopis 2FA koodi",
"com_ui_use_backup_code": "Kasuta hoopis varukoodi",
"com_ui_use_micrphone": "Kasuta mikrofoni",
"com_ui_use_prompt": "Kasuta sisendit",
"com_ui_used": "Kasutatud",
"com_ui_variables": "Muutujad",
"com_ui_variables_info": "Kasuta oma tekstis topelt sulgusid, et luua muutujaid, nt `{{näidismuutuja}}`, et hiljem sisendi kasutamisel täita.",
"com_ui_verify": "Kontrolli",
"com_ui_version_var": "Versioon {{0}}",
"com_ui_versions": "Versioonid",
"com_ui_view_source": "Vaata algset vestlust",

View File

@@ -274,6 +274,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -285,6 +286,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",

View File

@@ -341,6 +341,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -352,6 +353,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",

View File

@@ -20,7 +20,7 @@
"com_agents_not_available": "הסוכן לא זמין",
"com_agents_search_name": "חפש סוכן לפי שם",
"com_agents_update_error": "אירעה שגיאה בעדכון הסוכן שלך.",
"com_assistants_action_attempt": "הסייען מעוניין לתקשר עם {{0}}",
"com_assistants_action_attempt": "הסוכן מעוניין לתקשר עם {{0}}",
"com_assistants_actions": "פעולות",
"com_assistants_actions_disabled": "עליך ליצור סייען לפני הוספת פעולות.",
"com_assistants_actions_info": "אפשר לסייען לאחזר מידע או לבצע פעולות באמצעות API",
@@ -37,7 +37,7 @@
"com_assistants_code_interpreter_info": "מתורגמן קוד מאפשר לסייען לכתוב ולהריץ קוד. כלי זה יכול לעבד קבצים עם נתונים ועיצוב מגוונים, וליצור קבצים כגון גרפים.",
"com_assistants_completed_action": "תקשר עם {{0}}",
"com_assistants_completed_function": "מריץ {{0}}",
"com_assistants_conversation_starters": "התחלת שיחות",
"com_assistants_conversation_starters": "התחלות שיחה",
"com_assistants_conversation_starters_placeholder": "הכנס פתיח לשיחה",
"com_assistants_create_error": "אירעה שגיאה ביצירת הסייען שלך.",
"com_assistants_create_success": "נוצר בהצלחה",
@@ -87,6 +87,7 @@
"com_auth_email_verification_redirecting": "מפנה מחדש בעוד {{0}} שניות...",
"com_auth_email_verification_resend_prompt": "לא קיבלת את הדוא\"ל?",
"com_auth_email_verification_success": "הדוא\"ל אומת בהצלחה",
"com_auth_email_verifying_ellipsis": "מאמת...",
"com_auth_error_create": "אירעה שגיאה בניסיון לרשום את החשבון שלך. בבקשה נסה שוב.",
"com_auth_error_invalid_reset_token": "אסימון איפוס הסיסמה הזה אינו תקף עוד.",
"com_auth_error_login": "לא ניתן להתחבר עם המידע שסופק. אנא בדוק את האישורים שלך ונסה שוב.",
@@ -123,9 +124,11 @@
"com_auth_submit_registration": "שלח רישום",
"com_auth_to_reset_your_password": "כדי לאפס את הסיסמה שלך.",
"com_auth_to_try_again": "כדי לנסות שוב.",
"com_auth_two_factor": "בדוק את יישום הסיסמה החד-פעמית שלך לקבלת קוד",
"com_auth_username": "שם משתמש (אופציונלי)",
"com_auth_username_max_length": "שם המשתמש חייב להיות פחות מ-20 תווים",
"com_auth_username_min_length": "שם משתמש חייב להיות לפחות 2 תווים",
"com_auth_verify_your_identity": "אמת את הזהות שלך",
"com_auth_welcome_back": "ברוכים הבאים",
"com_click_to_download": "(לחץ כאן להורדה)",
"com_download_expired": "(פג תוקף ההורדה)",
@@ -137,6 +140,8 @@
"com_endpoint_anthropic_maxoutputtokens": "מספר האסימונים המרבי שניתן להפיק בתגובה. ציין ערך נמוך יותר עבור תגובות קצרות יותר וערך גבוה יותר עבור תגובות ארוכות יותר.",
"com_endpoint_anthropic_prompt_cache": "שמירת מטמון מהירה מאפשרת שימוש חוזר בהקשר גדול או בהוראות בקריאות API, תוך הפחתת העלויות וההשהייה",
"com_endpoint_anthropic_temp": "נע בין 0 ל-1. השתמש בטמפ' הקרובה יותר ל-0 עבור בחירה אנליטית / מרובה, וקרוב יותר ל-1 עבור משימות יצירתיות ויצירתיות. אנו ממליצים לשנות את זה או את Top P אבל לא את שניהם.",
"com_endpoint_anthropic_thinking": "מאפשר חשיבה פנימית עבור דגמי Claude נתמכים (3.7 Sonnet). הערה: דורש שההגדרה של 'תקציב חשיבה' תהיה נמוכה מ'מקסימום טוקנים לפלט'",
"com_endpoint_anthropic_thinking_budget": "קובע את מספר הטוקנים המקסימלי שקלוד רשאי להשתמש בו עבור תהליך החשיבה הפנימי. תקציב גבוה יותר עשוי לשפר את איכות התשובה על ידי מתן אפשרות לניתוח מעמיק יותר של בעיות מורכבות, אם כי קלוד לא בהכרח ישתמש בכל התקציב שהוקצה, במיוחד בטווחים שמעל 32K. הגדרה זו חייבת להיות נמוכה מ'מקסימום טוקנים לפלט'.",
"com_endpoint_anthropic_topk": "Top-k משנה את האופן שבו המודל בוחר אסימונים לפלט. Top-k של 1 פירושו שהאסימון שנבחר הוא הסביר ביותר מבין כל האסימונים באוצר המילים של הדגם (נקרא גם פענוח חמדן), בעוד ש-top-k של 3 פירושו שהאסימון הבא נבחר מבין 3 הכי הרבה. אסימונים סבירים (באמצעות טמפרטורה).",
"com_endpoint_anthropic_topp": "Top-p משנה את האופן שבו המודל בוחר אסימונים לפלט. אסימונים נבחרים מבין רוב K (ראה פרמטר topK) הסביר לפחות עד שסכום ההסתברויות שלהם שווה לערך העליון-p.",
"com_endpoint_assistant": "סייען",
@@ -242,6 +247,8 @@
"com_endpoint_stop": "רצף לעצירה",
"com_endpoint_stop_placeholder": "הפרד ערכים על ידי לחיצה על 'Enter'",
"com_endpoint_temperature": "טמפרטורה",
"com_endpoint_thinking": "חשיבה",
"com_endpoint_thinking_budget": "תקציב חשיבה",
"com_endpoint_top_k": "Top K",
"com_endpoint_top_p": "Top P",
"com_endpoint_use_active_assistant": "השתמש ב-סייען פעיל",
@@ -264,6 +271,7 @@
"com_files_table": "השדה חייב להכיל תוכן, הוא אינו יכול להישאר ריק",
"com_generated_files": "קבצים שנוצרו:",
"com_hide_examples": "הסתר דוגמאות",
"com_nav_2fa": "אימות דו-שלבי (2FA)",
"com_nav_account_settings": "הגדרות חשבון",
"com_nav_always_make_prod": "ייצר תמיד גרסאות חדשות",
"com_nav_archive_created_at": "תאריך ייצור",
@@ -272,10 +280,10 @@
"com_nav_archived_chats_empty": "אין שיחות מארכיון.",
"com_nav_at_command": "@-פקודה",
"com_nav_at_command_description": "הפקודה \"@\" משמשת כמנגנון הפעלה/החלפה של נקודות קצה, מודלים, הגדרות קבועות מראש וכו'.",
"com_nav_audio_play_error": "שגיאה בהפעלת האודיו: {{0}}",
"com_nav_audio_play_error": "שגיאה בהפעלת אודיו: {{0}}",
"com_nav_audio_process_error": "שגיאה בעיבוד האודיו: {{0}}",
"com_nav_auto_scroll": "Auto-s גלול אל הכי חדש בפתיחה",
"com_nav_auto_send_prompts": "הנחיות (פרומפטים) לשליחה אוטומטית",
"com_nav_auto_send_prompts": "שליחת הנחיות (פרומפטים) אוטומטית",
"com_nav_auto_send_text": "טקסט לשליחה אוטומטית",
"com_nav_auto_send_text_disabled": "הגדר -1 כדי להשבית",
"com_nav_auto_transcribe_audio": "תמלול אוטומטי של אודיו",
@@ -294,10 +302,10 @@
"com_nav_close_sidebar": "סגור סרגל צד",
"com_nav_commands": "פקודות",
"com_nav_confirm_clear": "אשר ניקוי",
"com_nav_conversation_mode": "ביקורות בהמתנה",
"com_nav_conversation_mode": "מצב שיחה",
"com_nav_convo_menu_options": "אפשרויות מצב שיחה",
"com_nav_db_sensitivity": "רגישות דציבלים",
"com_nav_delete_account": "מחק חשבון",
"com_nav_delete_account": "מחיקת החשבון",
"com_nav_delete_account_button": "מחק את החשבון שלי לצמיתות",
"com_nav_delete_account_confirm": "מחק חשבון - אתה בטוח?",
"com_nav_delete_account_email_placeholder": "אנא הזן את כתובת הדוא\"ל של החשבון שלך",
@@ -321,13 +329,15 @@
"com_nav_export_type": "סוג",
"com_nav_external": "חיצוני",
"com_nav_font_size": "גודל גופן",
"com_nav_font_size_base": "בינוני",
"com_nav_font_size_base": "מדיום",
"com_nav_font_size_lg": "גדול",
"com_nav_font_size_sm": "קטן",
"com_nav_font_size_xl": "גדול מאוד",
"com_nav_font_size_xs": "קטן מאוד",
"com_nav_font_size_xs": "קט מאוד",
"com_nav_help_faq": "עזרה ושאלות נפוצות",
"com_nav_hide_panel": "הסתר לוח הצד הימני ביותר",
"com_nav_info_code_artifacts": "אפשר הצגה של רכיבי תצוגת קוד ניסיוניים לצד הצ'אט",
"com_nav_info_code_artifacts_agent": "אפשר שימוש ברכיבי תצוגת קוד עבור סוכן זה כברירת מחדל, מתווספות הוראות נוספות ספציפיות לשימוש ברכיבי התצוגה אלא אם \"מצב הנחיה מותאם אישית\" מופעל.",
"com_nav_info_custom_prompt_mode": "כאשר אפשרות זו מופעלת, הנחיית ברירת המחדל של מערכת רכיבי תצוגה לא תיכלל. כל ההוראות ליצירת רכיבי תצוגה יהיו חייבות להינתן באופן ידני במצב זה.",
"com_nav_info_enter_to_send": "כאשר מופעל, לחיצה על \"ENTER\" תשלח את ההודעה שלך, כאשר מושבת לחיצה על \"Enter\" תוסיף שורה חדשה, ותצטרך ללחוץ על \"CTRL + ENTER\" כדי לשלוח את ההודעה.",
"com_nav_info_fork_change_default": "'הודעות ישירות בלבד' כולל רק את הנתיב הישיר להודעה שנבחרה. 'כלול הסתעפויות קשורות' מוסיף את כל ההסתעפויות הקשורות לאורך הנתיב. 'כלול הכל עד כאן/מכאן' כולל את כל ההודעות וההסתעפויות המחוברות.",
@@ -338,7 +348,7 @@
"com_nav_info_show_thinking": "כאשר אפשרות זו מופעלת, תיבות תצוגה שמציגות את תהליך החשיבה של הבינה המלאכותית יופיעו פתוחות כברירת מחדל, כך שתוכל לראות את תהליך הניתוח בזמן אמת. כאשר האפשרות מושבתת, תיבות הבחירה יישארו סגורות כברירת מחדל, מה שיוצר ממשק נקי וזורם יותר.",
"com_nav_info_user_name_display": "כאשר אפשרות זו מופעלת, שם המשתמש של השולח יוצג מעל כל הודעה שאתה שולח. כאשר האפשרות מושבתת, יוצג רק הכיתוב \"אתה\" מעל ההודעות שלך.",
"com_nav_lang_arabic": "ערבית (العربية)",
"com_nav_lang_auto": "זיהוי אוטומטי",
"com_nav_lang_auto": "זיהוי באופן אוטומטי",
"com_nav_lang_brazilian_portuguese": "פורטוגזית ברזילאית (Português Brasileiro)",
"com_nav_lang_chinese": "סינית (中文)",
"com_nav_lang_dutch": "הולנדית (Nederlands)",
@@ -346,6 +356,7 @@
"com_nav_lang_estonian": "אסטונית (Eesti keel)",
"com_nav_lang_finnish": "פינית (Suomi)",
"com_nav_lang_french": "צרפתית (Français)",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "גרמנית (Deutsch)",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "אינדונזית (Indonesia)",
@@ -357,6 +368,7 @@
"com_nav_lang_russian": "רוסית (Русский)",
"com_nav_lang_spanish": "ספרדית (Español)",
"com_nav_lang_swedish": "שוודית (Svenska)",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "סינית מסורתית (繁體中文)",
"com_nav_lang_turkish": "טורקית (Türkçe)",
"com_nav_lang_vietnamese": "וייטנאמית (Tiếng Việt)",
@@ -370,13 +382,13 @@
"com_nav_no_search_results": "לא נמצאו תוצאות בחיפוש",
"com_nav_not_supported": "לא נתמך",
"com_nav_open_sidebar": "פתח סרגל צד",
"com_nav_playback_rate": "קצב השמעת אודיו",
"com_nav_playback_rate": "קצב השמעת האודיו",
"com_nav_plugin_auth_error": "אירעה שגיאה בניסיון לאמת את הפלאגין הזה. בבקשה נסה שוב.",
"com_nav_plugin_install": "התקן",
"com_nav_plugin_search": "תוספי חיפוש",
"com_nav_plugin_store": "חנות פלאגין",
"com_nav_plugin_uninstall": "הסר התקנה",
"com_nav_plus_command": "פקודת+-",
"com_nav_plus_command": "פקודות+-",
"com_nav_plus_command_description": "הפעל או בטל את הפקודה '+' כדי להוסיף הגדרת תגובות מרובות",
"com_nav_profile_picture": "תמונת פרופיל",
"com_nav_save_drafts": "שמיר את האפצה באותו מחשב",
@@ -431,6 +443,16 @@
"com_sidepanel_parameters": "פרמטרים",
"com_sidepanel_select_agent": "בחר סוכן",
"com_sidepanel_select_assistant": "בחר סייען",
"com_ui_2fa_account_security": "אימות דו-שלבי מוסיף שכבת אבטחה נוספת לחשבון שלך",
"com_ui_2fa_disable": "השבת אימות דו-שלבי (2FA)",
"com_ui_2fa_disable_error": "התרחשה שגיאה בעת ביטול האימות הדו-שלבי",
"com_ui_2fa_disabled": "האימות הדו-שלבי הושבת (2FA)",
"com_ui_2fa_enable": "אפשר אימות דו-שלבי (2FA)",
"com_ui_2fa_enabled": "האימות הדו-שלבי (2FA) הופעל",
"com_ui_2fa_generate_error": "תרחשה שגיאה בעת יצירת הגדרות האימות הדו-שלבי (2FA)",
"com_ui_2fa_invalid": "קוד האימות הדו-שלבי שגוי",
"com_ui_2fa_setup": "הגדר אימות דו-שלבי (2FA)",
"com_ui_2fa_verified": "האימות הדו-שלבי אומת בהצלחה",
"com_ui_accept": "אני מקבל",
"com_ui_add": "הוסף",
"com_ui_add_model_preset": "הוספת מודל או הגדרה קבועה לתגובה נוספת",
@@ -461,10 +483,11 @@
"com_ui_artifacts": "רכיבי תצוגה",
"com_ui_artifacts_toggle": "הפעל/כבה רכיבי תצוגה",
"com_ui_artifacts_toggle_agent": "אפשר רכיבי תצוגה",
"com_ui_ascending": "סדר עולה",
"com_ui_assistant": "סייען",
"com_ui_assistant_delete_error": "אירעה שגיאה בעת מחיקת הסייען",
"com_ui_assistant_deleted": "הסייען נמחק בהצלחה",
"com_ui_assistants": "סייענים",
"com_ui_assistants": "סייען",
"com_ui_assistants_output": "פלט סייענים",
"com_ui_attach_error": "לא ניתן לצרף קובץ. צור או בחר שיחה, או נסה לרענן את הדף.",
"com_ui_attach_error_openai": "לא ניתן לצרף את קבצי הסייען לנקודות קצה אחרות",
@@ -473,7 +496,7 @@
"com_ui_attach_warn_endpoint": "עשוי להתעלם מקבצים שאינם של הסייען שאין להם כלי תואם",
"com_ui_attachment": "קובץ מצורף",
"com_ui_auth_type": "סוג אישור",
"com_ui_auth_url": "כתובת URL לאימות",
"com_ui_auth_url": "כתובת URL לאימות הרשאה",
"com_ui_authentication": "אימות",
"com_ui_authentication_type": "סוג אימות",
"com_ui_avatar": "אווטאר",
@@ -573,7 +596,7 @@
"com_ui_description": "תיאור",
"com_ui_description_placeholder": "אופציונלי: הזן תיאור שיוצג עבור ההנחיה (פרומפט)",
"com_ui_disabling": "מבטל הפעלה...",
"com_ui_download": "הורדה",
"com_ui_download": "הורדות",
"com_ui_download_artifact": "רכיב תצוגת הורדות",
"com_ui_download_backup": "הורד קודי גיבוי",
"com_ui_download_backup_tooltip": "לפני שתמשיך, הורד את קודי הגיבוי שלך. תזדקק להם כדי לשחזר גישה במקרה שתאבד את מכשיר האימות שלך",
@@ -610,50 +633,126 @@
"com_ui_fork_default": "השתמש בהגדרות הסתעפויות ברירת מחדל",
"com_ui_fork_error": "אירעה שגיאה בעת פיצול השיחה",
"com_ui_fork_from_message": "בחר הגדרת הסתעפויות",
"com_ui_fork_info_1": "השתמש בהגדרה זו כדי ליצור הסתעפות של הודעות עם ההתנהגות הרצויה.",
"com_ui_fork_info_2": "\"הסתעפות\" מתייחסת ליצירת שיחה חדשה המתחילה/מסתיימת מהודעות ספציפיות בשיחה הנוכחית, תוך יצירת העתק בהתאם לאפשרויות שנבחרו.",
"com_ui_fork_info_3": "\"הודעת היעד\" מתייחסת להודעה שממנה נפתחה חלונית זו, או, אם סימנת \"{{0}}\", להודעה האחרונה בשיחה.",
"com_ui_fork_info_branches": "אפשרות זו מפצלת את ההודעות הגלויות, יחד עם ההסתעפויות הקשורות; במילים אחרות, המסלול הישיר להודעת היעד, כולל את ההסתעפויות לאורך המסלול.",
"com_ui_fork_info_remember": "סמן כדי לזכור את האפשרויות שבחרת לשימושים הבאים, כך שתוכל ליצור הסתעפויות בשיחות מהר יותר לפי העדפתך.",
"com_ui_fork_info_start": "כאשר מסומן, ההסתעפות תחל מההודעה זו ותימשך עד להודעה האחרונה בשיחה, על פי ההתנהגות שנבחרה לעיל.",
"com_ui_fork_info_target": "אפשרות זו תיצור הסתעפות שתכלול את כל ההודעות המובילות להודעת היעד, כולל ההודעות הסמוכות; במילים אחרות, כל ההסתעפויות של ההודעות יכללו, בין אם הם גלויות או לא, ובין אם הם נמצאות באותו מסלול או לא.",
"com_ui_fork_info_visible": "אפשרות זו תיצור הסתעפות רק של ההודעות הגלויות; במילים אחרות, רק את המסלול הישיר להודעת היעד, ללא הסתעפויות נוספות.",
"com_ui_fork_processing": "יוצר הסתעפות בשיחה...",
"com_ui_fork_remember": "זכור",
"com_ui_fork_remember_checked": "הבחירה שלך תישמר אחרי השימוש. תוכל לשנות זאת בכל זמן בהגדרות.",
"com_ui_fork_split_target": "התחל הסתעפות כאן",
"com_ui_fork_split_target_setting": "התחל הסתעפות מהודעת היעד כברירת מחדל",
"com_ui_fork_success": "יצירת ההסתעפות בשיחה הסתיימה בהצלחה",
"com_ui_fork_visible": "הודעות גלויות בלבד",
"com_ui_generate_backup": "צור קודי גיבוי",
"com_ui_generate_qrcode": "צור קוד QR",
"com_ui_generating": "יוצר...",
"com_ui_global_group": "שדה זה לא יכול להישאר ריק",
"com_ui_go_back": "חזור",
"com_ui_go_to_conversation": "חזור לצ'אט",
"com_ui_happy_birthday": "זה יום ההולדת הראשון שלי!",
"com_ui_hide_qr": "הסתר קוד QR",
"com_ui_host": "מארח",
"com_ui_idea": "רעיונות",
"com_ui_image_gen": "מחולל תמונות",
"com_ui_import": "ייבוא",
"com_ui_import_conversation_error": "אירעה שגיאה בעת ייבוא השיחות שלך",
"com_ui_import_conversation_file_type_error": "סוג ייבוא לא נתמך",
"com_ui_import_conversation_info": "ייבא שיחות מקובץ JSON",
"com_ui_import_conversation_success": "השיחות יובאו בהצלחה",
"com_ui_include_shadcnui": "יש לכלול הוראות לשימוש ברכיבי ממשק המשתמש של shadcn/ui",
"com_ui_include_shadcnui_agent": "יש לכלול הוראות שימוש ב-shadcn/ui",
"com_ui_input": "קלט",
"com_ui_instructions": "הוראות",
"com_ui_latest_footer": "גישה לכל הבינות המלאכותיות (AI) לכולם",
"com_ui_latest_production_version": "גרסת הפיתוח העדכנית ביותר",
"com_ui_latest_version": "גרסה אחרונה",
"com_ui_librechat_code_api_key": "קבל את מפתח ה-API של מפענח הקוד LibreChat",
"com_ui_librechat_code_api_subtitle": "אבטחה ללא פשרות. תמיכה במגוון שפות תכנות. יכולת עבודה מלאה עם קבצים.",
"com_ui_librechat_code_api_title": "הרץ קוד AI",
"com_ui_llm_menu": "תפריט מודל שפה גדול (LLM)",
"com_ui_llms_available": "מודל שפה גדול (LLM)",
"com_ui_loading": "טוען...",
"com_ui_locked": "נעול",
"com_ui_logo": "\"לוגו {{0}}\"",
"com_ui_manage": "נהל",
"com_ui_max_tags": "המספר המקסימלי המותר על פי הערכים העדכניים הוא {{0}}.",
"com_ui_mention": "ציין נקודת קצה, סייען, או הנחייה (פרופמט) כדי לעבור אליה במהירות",
"com_ui_min_tags": "לא ניתן למחוק ערכים נוספים, יש צורך במינימום {{0}} ערכים.",
"com_ui_misc": "כללי",
"com_ui_model": "דגם",
"com_ui_model_parameters": "הגדרות המודל",
"com_ui_more_info": "מידע נוסף",
"com_ui_my_prompts": "ההנחיות (פרומפטים) שלי",
"com_ui_name": "שם",
"com_ui_new_chat": "שיחה חדשה",
"com_ui_next": "הבא",
"com_ui_no": "לא",
"com_ui_no_backup_codes": "אין קודי גיבוי זמינים. אנא צור קודים חדשים",
"com_ui_no_bookmarks": "עדיין אין לך סימניות. בחר שיחה והוסף סימניה חדשה",
"com_ui_no_category": "אין קטגוריה",
"com_ui_no_changes": "אין שינויים לעדכן",
"com_ui_no_data": "השדה חייב להכיל תוכן, הוא לא יכול להישאר ריק",
"com_ui_no_terms_content": "אין תוכן תנאים והגבלות להצגה",
"com_ui_no_valid_items": "השדה חייב להכיל תוכן, הוא לא יכול להישאר ריק",
"com_ui_none": "אף אחד",
"com_ui_none_selected": "לא ",
"com_ui_not_used": "לא בשימוש",
"com_ui_nothing_found": "לא נמצא",
"com_ui_oauth": "פרוטוקול אימות פתוח (OAuth)",
"com_ui_of": "של",
"com_ui_off": "של",
"com_ui_on": "פעיל",
"com_ui_page": "עמוד",
"com_ui_prev": "הקודם",
"com_ui_preview": "תצוגה מקדימה",
"com_ui_privacy_policy": "מדיניות פרטיות",
"com_ui_privacy_policy_url": "קישור למדיניות הפרטיות",
"com_ui_prompt": "הנחיה (פרומפט)",
"com_ui_prompt_already_shared_to_all": "ההנחיה הזו כבר משותפת עם כל המשתמשים",
"com_ui_prompt_name": "שם הנחיה (פרומפט)",
"com_ui_prompt_name_required": "נדרש שם הנחיה (פרומפט)",
"com_ui_prompt_preview_not_shared": "היוצר לא אפשר שיתוף פעולה להנחיה זו",
"com_ui_prompt_text": "טקסט",
"com_ui_prompt_text_required": "נדרש טקסט",
"com_ui_prompt_update_error": "אירעה שגיאה בעדכון ההנחיה (פרומפט)",
"com_ui_prompts": "הנחיות (פרומפטים)",
"com_ui_prompts_allow_create": "אפשר יצירת הנחיות",
"com_ui_prompts_allow_share_global": "אפשר שיתוף הנחיות (פרומפטים) עם כל המשתמשים",
"com_ui_prompts_allow_use": "אפשר שימוש בהנחיות (פרומפטים)",
"com_ui_provider": "ספק",
"com_ui_read_aloud": "הקראה",
"com_ui_refresh_link": "רענון קישור",
"com_ui_regenerate": "לחדש",
"com_ui_regenerate_backup": "צור קודי גיבוי מחדש",
"com_ui_regenerating": "יוצר מחדש...",
"com_ui_region": "איזור",
"com_ui_rename": "שנה שם",
"com_ui_rename_prompt": "שנה שם הנחיה (פרומפט)",
"com_ui_requires_auth": "נדרש אימות",
"com_ui_reset_var": "איפוס {{0}}",
"com_ui_result": "תוצאה",
"com_ui_revoke": "בטל",
"com_ui_revoke_info": "בטל את כל האישורים שסופקו על ידי המשתמש",
"com_ui_revoke_key_confirm": "האם אתה בטוח שברצונך לבטל את המפתח הזה?",
"com_ui_revoke_key_endpoint": "ביטול מפתח עבור {{0}}",
"com_ui_revoke_keys": "ביטול מפתחות",
"com_ui_revoke_keys_confirm": "האם אתה בטוח שברצונך לבטל את כל המפתחות?",
"com_ui_role_select": "תפקיד",
"com_ui_roleplay": "משחק תפקידים",
"com_ui_run_code": "הרץ קו",
"com_ui_run_code_error": "אירעה שגיאה בהרצת הקוד",
"com_ui_save": "שמור",
"com_ui_save_submit": "שמור ושלח",
"com_ui_saved": "שמור!",
"com_ui_schema": "סכמה",
"com_ui_scope": "תחום",
"com_ui_search": "חיפוש",
"com_ui_secret_key": "מפתח סודי",
"com_ui_select": "בחר",
"com_ui_select_file": "בחר קובץ",
"com_ui_select_model": "בחר מודל",
@@ -668,6 +767,7 @@
"com_ui_share_create_message": "שמך וכל הודעה שתוסיף לאחר השיתוף יישארו פרטיים.",
"com_ui_share_delete_error": "אירעה שגיאה בעת מחיקת הקישור המשותף.",
"com_ui_share_error": "אירעה שגיאה בעת שיתוף קישור הצ'אט",
"com_ui_share_form_description": "השדה חייב להכיל תוכן, הוא אינו יכול להישאר ריק",
"com_ui_share_link_to_chat": "שתף קישור בצ'אט",
"com_ui_share_to_all_users": "שתף עם כל המשתמשים",
"com_ui_share_update_message": "השם שלך, ההוראות המותאמות אישית וכל ההודעות שתוסיף לאחר השיתוף יישארו פרטיים.",
@@ -677,12 +777,14 @@
"com_ui_shared_link_not_found": "הקישור המשותף לא נמצא",
"com_ui_shared_prompts": "הנחיות (פרומפטים) משותפות",
"com_ui_shop": "קניות",
"com_ui_show": "הצג",
"com_ui_show_all": "הראה הכל",
"com_ui_show_qr": "הראה קוד QR",
"com_ui_sign_in_to_domain": "היכנס אל {{0}}",
"com_ui_simple": "פשוט",
"com_ui_size": "סוג",
"com_ui_special_variables": "משתנים מיוחדים:",
"com_ui_special_variables_info": "השתמש ב-`{{current_date}}` עבור התאריך הנוכחי, וב-`{{current_user}}` עבור שם החשבון שלך.",
"com_ui_speech_while_submitting": "לא ניתן לשלוח אודיו בזמן שנוצרת תגובה",
"com_ui_stop": "עצור",
"com_ui_storage": "אחסון",
@@ -696,12 +798,16 @@
"com_ui_token_exchange_method": "שיטת החלפת טוקנים",
"com_ui_token_url": "קישור URL לטוקן",
"com_ui_tools": "כלים",
"com_ui_travel": "מסע",
"com_ui_unarchive": "לארכיון",
"com_ui_unarchive_error": "אירעה שגיאה בארכיון השיחה",
"com_ui_unknown": "לא ידוע",
"com_ui_update": "עדכון",
"com_ui_upload": "העלה",
"com_ui_upload_code_files": "העלאה עבור מפענח הקוד",
"com_ui_upload_delay": "העלאת \"{{0}}\" לוקחת יותר זמן מהצפוי. אנא המתן בזמן שהקובץ מסיים את האינדוקס לאחזור.",
"com_ui_upload_error": "אירעה שגיאה בהעלאת הקובץ שלך",
"com_ui_upload_file_search": "העלאה לחיפוש בקבצים",
"com_ui_upload_files": "העלה קבצים",
"com_ui_upload_image": "העלה תמונה",
"com_ui_upload_image_input": "העלה תמונה",
@@ -709,9 +815,14 @@
"com_ui_upload_invalid_var": "אין אפשרות להעלות את הקובץ. התמונה צריכה להיות בגודל של עד {{0}} MB",
"com_ui_upload_success": "הקובץ הועלה בהצלחה",
"com_ui_upload_type": "בחר סוג העלאה",
"com_ui_use_2fa_code": "השתמש בקוד אימות דו-שלבי (2FA) במקום",
"com_ui_use_backup_code": "השתמש בקוד גיבוי במקום",
"com_ui_use_micrphone": "שימוש במיקורפון",
"com_ui_use_prompt": "השתמש בהנחיה (פרומפט)",
"com_ui_used": "נוצל",
"com_ui_variables": "משתנים",
"com_ui_variables_info": "השתמש בסוגריים מסולסלות כפולות בטקסט שלך ליצירת משתנים, לדוגמא `{{example variable}}`, כדי למלא אותם מאוחר יותר בשימוש בהנחיה.",
"com_ui_verify": "אמת",
"com_ui_version_var": "גרסה {{0}}",
"com_ui_versions": "גרסה",
"com_ui_view_source": "הצג צ'אט מקורי",

View File

@@ -18,8 +18,9 @@ import translationJa from './ja/translation.json';
import translationKa from './ka/translation.json';
import translationSv from './sv/translation.json';
import translationKo from './ko/translation.json';
import translationVi from './vi/translation.json';
import translationTh from './th/translation.json';
import translationTr from './tr/translation.json';
import translationVi from './vi/translation.json';
import translationNl from './nl/translation.json';
import translationId from './id/translation.json';
import translationHe from './he/translation.json';
@@ -47,8 +48,9 @@ export const resources = {
ka: { translation: translationKa },
sv: { translation: translationSv },
ko: { translation: translationKo },
vi: { translation: translationVi },
th: { translation: translationTh },
tr: { translation: translationTr },
vi: { translation: translationVi },
nl: { translation: translationNl },
id: { translation: translationId },
he: { translation: translationHe },
@@ -62,7 +64,7 @@ i18n
fallbackLng: {
'zh-TW': ['zh-Hant', 'en'],
'zh-HK': ['zh-Hant', 'en'],
'zh': ['zh-Hans', 'en'],
zh: ['zh-Hans', 'en'],
default: ['en'],
},
fallbackNS: 'translation',
@@ -73,4 +75,4 @@ i18n
interpolation: { escapeValue: false },
});
export default i18n;
export default i18n;

View File

@@ -170,6 +170,7 @@
"com_nav_lang_estonian": "Eesti keel",
"com_nav_lang_finnish": "Suomi",
"com_nav_lang_french": "Français ",
"com_nav_lang_georgian": "ქართული",
"com_nav_lang_german": "Deutsch",
"com_nav_lang_hebrew": "עברית",
"com_nav_lang_indonesia": "Indonesia",
@@ -181,6 +182,7 @@
"com_nav_lang_russian": "Русский",
"com_nav_lang_spanish": "Español",
"com_nav_lang_swedish": "Svenska",
"com_nav_lang_thai": "ไทย",
"com_nav_lang_traditional_chinese": "繁體中文",
"com_nav_lang_turkish": "Türkçe",
"com_nav_lang_vietnamese": "Tiếng Việt",

Some files were not shown because too many files have changed in this diff Show More