Compare commits
8 Commits
dev-exampl
...
feat/compo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75010369c | ||
|
|
dc9d219f3e | ||
|
|
b0c5db6756 | ||
|
|
ff722366e9 | ||
|
|
4f06c159be | ||
|
|
e042e1500f | ||
|
|
0503f0f903 | ||
|
|
e4adfe771b |
@@ -142,10 +142,10 @@ GOOGLE_KEY=user_provided
|
||||
# GOOGLE_AUTH_HEADER=true
|
||||
|
||||
# Gemini API (AI Studio)
|
||||
# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite-preview-06-17,gemini-2.0-flash,gemini-2.0-flash-lite
|
||||
# GOOGLE_MODELS=gemini-2.5-pro-preview-05-06,gemini-2.5-flash-preview-04-17,gemini-2.0-flash-001,gemini-2.0-flash-exp,gemini-2.0-flash-lite-001,gemini-1.5-pro-002,gemini-1.5-flash-002
|
||||
|
||||
# Vertex AI
|
||||
# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite-preview-06-17,gemini-2.0-flash-001,gemini-2.0-flash-lite-001
|
||||
# GOOGLE_MODELS=gemini-2.5-pro-preview-05-06,gemini-2.5-flash-preview-04-17,gemini-2.0-flash-001,gemini-2.0-flash-exp,gemini-2.0-flash-lite-001,gemini-1.5-pro-002,gemini-1.5-flash-002
|
||||
|
||||
# GOOGLE_TITLE_MODEL=gemini-2.0-flash-lite-001
|
||||
|
||||
@@ -657,4 +657,4 @@ OPENWEATHER_API_KEY=
|
||||
# Reranker (Required)
|
||||
# JINA_API_KEY=your_jina_api_key
|
||||
# or
|
||||
# COHERE_API_KEY=your_cohere_api_key
|
||||
# COHERE_API_KEY=your_cohere_api_key
|
||||
@@ -1,7 +1,7 @@
|
||||
const { google } = require('googleapis');
|
||||
const { Tokenizer } = require('@librechat/api');
|
||||
const { concat } = require('@langchain/core/utils/stream');
|
||||
const { ChatVertexAI } = require('@langchain/google-vertexai');
|
||||
const { Tokenizer, getSafetySettings } = require('@librechat/api');
|
||||
const { ChatGoogleGenerativeAI } = require('@langchain/google-genai');
|
||||
const { GoogleGenerativeAI: GenAI } = require('@google/generative-ai');
|
||||
const { HumanMessage, SystemMessage } = require('@langchain/core/messages');
|
||||
@@ -12,13 +12,13 @@ const {
|
||||
endpointSettings,
|
||||
parseTextParts,
|
||||
EModelEndpoint,
|
||||
googleSettings,
|
||||
ContentTypes,
|
||||
VisionModes,
|
||||
ErrorTypes,
|
||||
Constants,
|
||||
AuthKeys,
|
||||
} = require('librechat-data-provider');
|
||||
const { getSafetySettings } = require('~/server/services/Endpoints/google/llm');
|
||||
const { encodeAndFormat } = require('~/server/services/Files/images');
|
||||
const { spendTokens } = require('~/models/spendTokens');
|
||||
const { getModelMaxTokens } = require('~/utils');
|
||||
@@ -166,16 +166,6 @@ class GoogleClient extends BaseClient {
|
||||
);
|
||||
}
|
||||
|
||||
// Add thinking configuration
|
||||
this.modelOptions.thinkingConfig = {
|
||||
thinkingBudget:
|
||||
(this.modelOptions.thinking ?? googleSettings.thinking.default)
|
||||
? this.modelOptions.thinkingBudget
|
||||
: 0,
|
||||
};
|
||||
delete this.modelOptions.thinking;
|
||||
delete this.modelOptions.thinkingBudget;
|
||||
|
||||
this.sender =
|
||||
this.options.sender ??
|
||||
getResponseSender({
|
||||
|
||||
3
api/cache/banViolation.js
vendored
3
api/cache/banViolation.js
vendored
@@ -1,8 +1,7 @@
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { isEnabled, math } = require('@librechat/api');
|
||||
const { ViolationTypes } = require('librechat-data-provider');
|
||||
const { isEnabled, math, removePorts } = require('~/server/utils');
|
||||
const { deleteAllUserSessions } = require('~/models');
|
||||
const { removePorts } = require('~/server/utils');
|
||||
const getLogStores = require('./getLogStores');
|
||||
|
||||
const { BAN_VIOLATIONS, BAN_INTERVAL } = process.env ?? {};
|
||||
|
||||
2
api/cache/getLogStores.js
vendored
2
api/cache/getLogStores.js
vendored
@@ -1,7 +1,7 @@
|
||||
const { Keyv } = require('keyv');
|
||||
const { isEnabled, math } = require('@librechat/api');
|
||||
const { CacheKeys, ViolationTypes, Time } = require('librechat-data-provider');
|
||||
const { logFile, violationFile } = require('./keyvFiles');
|
||||
const { isEnabled, math } = require('~/server/utils');
|
||||
const keyvRedis = require('./keyvRedis');
|
||||
const keyvMongo = require('./keyvMongo');
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { createTempChatExpirationDate } = require('@librechat/api');
|
||||
const getCustomConfig = require('~/server/services/Config/loadCustomConfig');
|
||||
const { getMessages, deleteMessages } = require('./Message');
|
||||
const { Conversation } = require('~/db/models');
|
||||
|
||||
@@ -100,15 +98,10 @@ module.exports = {
|
||||
update.conversationId = newConversationId;
|
||||
}
|
||||
|
||||
if (req?.body?.isTemporary) {
|
||||
try {
|
||||
const customConfig = await getCustomConfig();
|
||||
update.expiredAt = createTempChatExpirationDate(customConfig);
|
||||
} catch (err) {
|
||||
logger.error('Error creating temporary chat expiration date:', err);
|
||||
logger.info(`---\`saveConvo\` context: ${metadata?.context}`);
|
||||
update.expiredAt = null;
|
||||
}
|
||||
if (req.body.isTemporary) {
|
||||
const expiredAt = new Date();
|
||||
expiredAt.setDate(expiredAt.getDate() + 30);
|
||||
update.expiredAt = expiredAt;
|
||||
} else {
|
||||
update.expiredAt = null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
const { z } = require('zod');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { createTempChatExpirationDate } = require('@librechat/api');
|
||||
const getCustomConfig = require('~/server/services/Config/loadCustomConfig');
|
||||
const { Message } = require('~/db/models');
|
||||
|
||||
const idSchema = z.string().uuid();
|
||||
@@ -56,14 +54,9 @@ async function saveMessage(req, params, metadata) {
|
||||
};
|
||||
|
||||
if (req?.body?.isTemporary) {
|
||||
try {
|
||||
const customConfig = await getCustomConfig();
|
||||
update.expiredAt = createTempChatExpirationDate(customConfig);
|
||||
} catch (err) {
|
||||
logger.error('Error creating temporary chat expiration date:', err);
|
||||
logger.info(`---\`saveMessage\` context: ${metadata?.context}`);
|
||||
update.expiredAt = null;
|
||||
}
|
||||
const expiredAt = new Date();
|
||||
expiredAt.setDate(expiredAt.getDate() + 30);
|
||||
update.expiredAt = expiredAt;
|
||||
} else {
|
||||
update.expiredAt = null;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"@langchain/google-genai": "^0.2.13",
|
||||
"@langchain/google-vertexai": "^0.2.13",
|
||||
"@langchain/textsplitters": "^0.1.0",
|
||||
"@librechat/agents": "^2.4.46",
|
||||
"@librechat/agents": "^2.4.42",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@node-saml/passport-saml": "^5.0.0",
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
const cookies = require('cookie');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const openIdClient = require('openid-client');
|
||||
const { isEnabled } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
requestPasswordReset,
|
||||
setOpenIDAuthTokens,
|
||||
registerUser,
|
||||
resetPassword,
|
||||
setAuthTokens,
|
||||
registerUser,
|
||||
requestPasswordReset,
|
||||
setOpenIDAuthTokens,
|
||||
} = require('~/server/services/AuthService');
|
||||
const { findUser, getUserById, deleteAllUserSessions, findSession } = require('~/models');
|
||||
const { getOpenIdConfig } = require('~/strategies');
|
||||
const { isEnabled } = require('~/server/utils');
|
||||
|
||||
const registrationController = async (req, res) => {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { getResponseSender } = require('librechat-data-provider');
|
||||
const {
|
||||
handleAbortError,
|
||||
@@ -12,8 +10,9 @@ const {
|
||||
clientRegistry,
|
||||
requestDataMap,
|
||||
} = require('~/server/cleanup');
|
||||
const { createOnProgress } = require('~/server/utils');
|
||||
const { sendMessage, createOnProgress } = require('~/server/utils');
|
||||
const { saveMessage } = require('~/models');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const EditController = async (req, res, next, initializeClient) => {
|
||||
let {
|
||||
@@ -199,7 +198,7 @@ const EditController = async (req, res, next, initializeClient) => {
|
||||
const finalUserMessage = reqDataContext.userMessage;
|
||||
const finalResponseMessage = { ...response };
|
||||
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
final: true,
|
||||
conversation,
|
||||
title: conversation.title,
|
||||
|
||||
@@ -44,17 +44,6 @@ const BaseClient = require('~/app/clients/BaseClient');
|
||||
const { loadAgent } = require('~/models/Agent');
|
||||
const { getMCPManager } = require('~/config');
|
||||
|
||||
const omitTitleOptions = new Set([
|
||||
'stream',
|
||||
'thinking',
|
||||
'streaming',
|
||||
'clientOptions',
|
||||
'thinkingConfig',
|
||||
'thinkingBudget',
|
||||
'includeThoughts',
|
||||
'maxOutputTokens',
|
||||
]);
|
||||
|
||||
/**
|
||||
* @param {ServerRequest} req
|
||||
* @param {Agent} agent
|
||||
@@ -1049,16 +1038,6 @@ class AgentClient extends BaseClient {
|
||||
delete clientOptions.maxTokens;
|
||||
}
|
||||
|
||||
clientOptions = Object.assign(
|
||||
Object.fromEntries(
|
||||
Object.entries(clientOptions).filter(([key]) => !omitTitleOptions.has(key)),
|
||||
),
|
||||
);
|
||||
|
||||
if (provider === Providers.GOOGLE) {
|
||||
clientOptions.json = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const titleResult = await this.run.generateTitle({
|
||||
provider,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// errorHandler.js
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { logger } = require('~/config');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { CacheKeys, ViolationTypes } = require('librechat-data-provider');
|
||||
const { sendResponse } = require('~/server/middleware/error');
|
||||
const { recordUsage } = require('~/server/services/Threads');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { sendResponse } = require('~/server/utils');
|
||||
|
||||
/**
|
||||
* @typedef {Object} ErrorHandlerContext
|
||||
@@ -75,7 +75,7 @@ const createErrorHandler = ({ req, res, getContext, originPath = '/assistants/ch
|
||||
} else if (/Files.*are invalid/.test(error.message)) {
|
||||
const errorMessage = `Files are invalid, or may not have uploaded yet.${
|
||||
endpoint === 'azureAssistants'
|
||||
? " If using Azure OpenAI, files are only available in the region of the assistant's model at the time of upload."
|
||||
? ' If using Azure OpenAI, files are only available in the region of the assistant\'s model at the time of upload.'
|
||||
: ''
|
||||
}`;
|
||||
return sendResponse(req, res, messageData, errorMessage);
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { Constants } = require('librechat-data-provider');
|
||||
const {
|
||||
handleAbortError,
|
||||
@@ -7,7 +5,9 @@ const {
|
||||
cleanupAbortController,
|
||||
} = require('~/server/middleware');
|
||||
const { disposeClient, clientRegistry, requestDataMap } = require('~/server/cleanup');
|
||||
const { sendMessage } = require('~/server/utils');
|
||||
const { saveMessage } = require('~/models');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
||||
let {
|
||||
@@ -206,7 +206,7 @@ const AgentController = async (req, res, next, initializeClient, addTitle) => {
|
||||
// Create a new response object with minimal copies
|
||||
const finalResponse = { ...response };
|
||||
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
final: true,
|
||||
conversation,
|
||||
title: conversation.title,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const { v4 } = require('uuid');
|
||||
const { sleep } = require('@librechat/agents');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
Time,
|
||||
Constants,
|
||||
@@ -22,20 +19,20 @@ const {
|
||||
addThreadMetadata,
|
||||
saveAssistantMessage,
|
||||
} = require('~/server/services/Threads');
|
||||
const { sendResponse, sendMessage, sleep, countTokens } = require('~/server/utils');
|
||||
const { runAssistant, createOnTextProgress } = require('~/server/services/AssistantService');
|
||||
const validateAuthor = require('~/server/middleware/assistants/validateAuthor');
|
||||
const { formatMessage, createVisionPrompt } = require('~/app/clients/prompts');
|
||||
const { createRun, StreamRunManager } = require('~/server/services/Runs');
|
||||
const { addTitle } = require('~/server/services/Endpoints/assistants');
|
||||
const { createRunBody } = require('~/server/services/createRunBody');
|
||||
const { sendResponse } = require('~/server/middleware/error');
|
||||
const { getTransactions } = require('~/models/Transaction');
|
||||
const { checkBalance } = require('~/models/balanceMethods');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { countTokens } = require('~/server/utils');
|
||||
const { getModelMaxTokens } = require('~/utils');
|
||||
const { getOpenAIClient } = require('./helpers');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* @route POST /
|
||||
@@ -474,7 +471,7 @@ const chatV1 = async (req, res) => {
|
||||
await Promise.all(promises);
|
||||
|
||||
const sendInitialResponse = () => {
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
sync: true,
|
||||
conversationId,
|
||||
// messages: previousMessages,
|
||||
@@ -590,7 +587,7 @@ const chatV1 = async (req, res) => {
|
||||
iconURL: endpointOption.iconURL,
|
||||
};
|
||||
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
final: true,
|
||||
conversation,
|
||||
requestMessage: {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const { v4 } = require('uuid');
|
||||
const { sleep } = require('@librechat/agents');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
Time,
|
||||
Constants,
|
||||
@@ -25,14 +22,15 @@ const { createErrorHandler } = require('~/server/controllers/assistants/errors')
|
||||
const validateAuthor = require('~/server/middleware/assistants/validateAuthor');
|
||||
const { createRun, StreamRunManager } = require('~/server/services/Runs');
|
||||
const { addTitle } = require('~/server/services/Endpoints/assistants');
|
||||
const { sendMessage, sleep, countTokens } = require('~/server/utils');
|
||||
const { createRunBody } = require('~/server/services/createRunBody');
|
||||
const { getTransactions } = require('~/models/Transaction');
|
||||
const { checkBalance } = require('~/models/balanceMethods');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { countTokens } = require('~/server/utils');
|
||||
const { getModelMaxTokens } = require('~/utils');
|
||||
const { getOpenAIClient } = require('./helpers');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* @route POST /
|
||||
@@ -311,7 +309,7 @@ const chatV2 = async (req, res) => {
|
||||
await Promise.all(promises);
|
||||
|
||||
const sendInitialResponse = () => {
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
sync: true,
|
||||
conversationId,
|
||||
// messages: previousMessages,
|
||||
@@ -434,7 +432,7 @@ const chatV2 = async (req, res) => {
|
||||
iconURL: endpointOption.iconURL,
|
||||
};
|
||||
|
||||
sendEvent(res, {
|
||||
sendMessage(res, {
|
||||
final: true,
|
||||
conversation,
|
||||
requestMessage: {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// errorHandler.js
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { CacheKeys, ViolationTypes, ContentTypes } = require('librechat-data-provider');
|
||||
const { recordUsage, checkMessageGaps } = require('~/server/services/Threads');
|
||||
const { sendResponse } = require('~/server/middleware/error');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const { sendResponse } = require('~/server/utils');
|
||||
const { logger } = require('~/config');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { CacheKeys, ViolationTypes, ContentTypes } = require('librechat-data-provider');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const { recordUsage, checkMessageGaps } = require('~/server/services/Threads');
|
||||
|
||||
/**
|
||||
* @typedef {Object} ErrorHandlerContext
|
||||
@@ -78,7 +78,7 @@ const createErrorHandler = ({ req, res, getContext, originPath = '/assistants/ch
|
||||
} else if (/Files.*are invalid/.test(error.message)) {
|
||||
const errorMessage = `Files are invalid, or may not have uploaded yet.${
|
||||
endpoint === 'azureAssistants'
|
||||
? " If using Azure OpenAI, files are only available in the region of the assistant's model at the time of upload."
|
||||
? ' If using Azure OpenAI, files are only available in the region of the assistant\'s model at the time of upload.'
|
||||
: ''
|
||||
}`;
|
||||
return sendResponse(req, res, messageData, errorMessage);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { countTokens, isEnabled, sendEvent } = require('@librechat/api');
|
||||
// abortMiddleware.js
|
||||
const { isAssistantsEndpoint, ErrorTypes } = require('librechat-data-provider');
|
||||
const { sendMessage, sendError, countTokens, isEnabled } = require('~/server/utils');
|
||||
const { truncateText, smartTruncateText } = require('~/app/clients/prompts');
|
||||
const clearPendingReq = require('~/cache/clearPendingReq');
|
||||
const { sendError } = require('~/server/middleware/error');
|
||||
const { spendTokens } = require('~/models/spendTokens');
|
||||
const abortControllers = require('./abortControllers');
|
||||
const { saveMessage, getConvo } = require('~/models');
|
||||
const { abortRun } = require('./abortRun');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const abortDataMap = new WeakMap();
|
||||
|
||||
@@ -101,7 +101,7 @@ async function abortMessage(req, res) {
|
||||
cleanupAbortController(abortKey);
|
||||
|
||||
if (res.headersSent && finalEvent) {
|
||||
return sendEvent(res, finalEvent);
|
||||
return sendMessage(res, finalEvent);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
@@ -174,7 +174,7 @@ const createAbortController = (req, res, getAbortData, getReqData) => {
|
||||
* @param {string} responseMessageId
|
||||
*/
|
||||
const onStart = (userMessage, responseMessageId) => {
|
||||
sendEvent(res, { message: userMessage, created: true });
|
||||
sendMessage(res, { message: userMessage, created: true });
|
||||
|
||||
const abortKey = userMessage?.conversationId ?? req.user.id;
|
||||
getReqData({ abortKey });
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { CacheKeys, RunStatus, isUUID } = require('librechat-data-provider');
|
||||
const { initializeClient } = require('~/server/services/Endpoints/assistants');
|
||||
const { checkMessageGaps, recordUsage } = require('~/server/services/Threads');
|
||||
const { deleteMessages } = require('~/models/Message');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const { sendMessage } = require('~/server/utils');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
const three_minutes = 1000 * 60 * 3;
|
||||
|
||||
@@ -34,7 +34,7 @@ async function abortRun(req, res) {
|
||||
const [thread_id, run_id] = runValues.split(':');
|
||||
|
||||
if (!run_id) {
|
||||
logger.warn("[abortRun] Couldn't find run for cancel request", { thread_id });
|
||||
logger.warn('[abortRun] Couldn\'t find run for cancel request', { thread_id });
|
||||
return res.status(204).send({ message: 'Run not found' });
|
||||
} else if (run_id === 'cancelled') {
|
||||
logger.warn('[abortRun] Run already cancelled', { thread_id });
|
||||
@@ -93,7 +93,7 @@ async function abortRun(req, res) {
|
||||
};
|
||||
|
||||
if (res.headersSent && finalEvent) {
|
||||
return sendEvent(res, finalEvent);
|
||||
return sendMessage(res, finalEvent);
|
||||
}
|
||||
|
||||
res.json(finalEvent);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const crypto = require('crypto');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { getResponseSender, Constants } = require('librechat-data-provider');
|
||||
const { sendError } = require('~/server/middleware/error');
|
||||
const { sendMessage, sendError } = require('~/server/utils');
|
||||
const { saveMessage } = require('~/models');
|
||||
|
||||
/**
|
||||
@@ -37,7 +36,7 @@ const denyRequest = async (req, res, errorMessage) => {
|
||||
isCreatedByUser: true,
|
||||
text,
|
||||
};
|
||||
sendEvent(res, { message: userMessage, created: true });
|
||||
sendMessage(res, { message: userMessage, created: true });
|
||||
|
||||
const shouldSaveMessage = _convoId && parentMessageId && parentMessageId !== Constants.NO_PARENT;
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const express = require('express');
|
||||
const { addTool } = require('@librechat/api');
|
||||
const { callTool, verifyToolAuth, getToolCalls } = require('~/server/controllers/tools');
|
||||
const { getAvailableTools } = require('~/server/controllers/PluginController');
|
||||
const { toolCallLimiter } = require('~/server/middleware/limiters');
|
||||
@@ -37,12 +36,4 @@ router.get('/:toolId/auth', verifyToolAuth);
|
||||
*/
|
||||
router.post('/:toolId/call', toolCallLimiter, callTool);
|
||||
|
||||
/**
|
||||
* Add a new tool to the system
|
||||
* @route POST /agents/tools/add
|
||||
* @param {object} req.body - Request body containing tool data
|
||||
* @returns {object} Created tool object
|
||||
*/
|
||||
router.post('/add', addTool);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const { klona } = require('klona');
|
||||
const { sleep } = require('@librechat/agents');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
StepTypes,
|
||||
RunStatus,
|
||||
@@ -14,10 +11,11 @@ const {
|
||||
} = require('librechat-data-provider');
|
||||
const { retrieveAndProcessFile } = require('~/server/services/Files/process');
|
||||
const { processRequiredActions } = require('~/server/services/ToolService');
|
||||
const { createOnProgress, sendMessage, sleep } = require('~/server/utils');
|
||||
const { RunManager, waitForRun } = require('~/server/services/Runs');
|
||||
const { processMessages } = require('~/server/services/Threads');
|
||||
const { createOnProgress } = require('~/server/utils');
|
||||
const { TextStream } = require('~/app/clients');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Sorts, processes, and flattens messages to a single string.
|
||||
@@ -66,7 +64,7 @@ async function createOnTextProgress({
|
||||
};
|
||||
|
||||
logger.debug('Content data:', contentData);
|
||||
sendEvent(openai.res, contentData);
|
||||
sendMessage(openai.res, contentData);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const { isUserProvided } = require('@librechat/api');
|
||||
const { EModelEndpoint } = require('librechat-data-provider');
|
||||
const { generateConfig } = require('~/server/utils/handleText');
|
||||
const { isUserProvided, generateConfig } = require('~/server/utils');
|
||||
|
||||
const {
|
||||
OPENAI_API_KEY: openAIApiKey,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
const path = require('path');
|
||||
const axios = require('axios');
|
||||
const yaml = require('js-yaml');
|
||||
const keyBy = require('lodash/keyBy');
|
||||
const { loadYaml } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
CacheKeys,
|
||||
configSchema,
|
||||
paramSettings,
|
||||
EImageOutputType,
|
||||
agentParamSettings,
|
||||
validateSettingDefinitions,
|
||||
agentParamSettings,
|
||||
paramSettings,
|
||||
} = require('librechat-data-provider');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const loadYaml = require('~/utils/loadYaml');
|
||||
const { logger } = require('~/config');
|
||||
const axios = require('axios');
|
||||
const yaml = require('js-yaml');
|
||||
const keyBy = require('lodash/keyBy');
|
||||
|
||||
const projectRoot = path.resolve(__dirname, '..', '..', '..', '..');
|
||||
const defaultConfigPath = path.resolve(projectRoot, 'librechat.yaml');
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
jest.mock('axios');
|
||||
jest.mock('~/cache/getLogStores');
|
||||
jest.mock('@librechat/api', () => ({
|
||||
...jest.requireActual('@librechat/api'),
|
||||
loadYaml: jest.fn(),
|
||||
}));
|
||||
jest.mock('~/utils/loadYaml');
|
||||
jest.mock('librechat-data-provider', () => {
|
||||
const actual = jest.requireActual('librechat-data-provider');
|
||||
return {
|
||||
@@ -33,22 +30,11 @@ jest.mock('librechat-data-provider', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@librechat/data-schemas', () => {
|
||||
return {
|
||||
logger: {
|
||||
info: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const axios = require('axios');
|
||||
const { loadYaml } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const loadCustomConfig = require('./loadCustomConfig');
|
||||
const getLogStores = require('~/cache/getLogStores');
|
||||
const loadYaml = require('~/utils/loadYaml');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
describe('loadCustomConfig', () => {
|
||||
const mockSet = jest.fn();
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
const initializeClient = require('./initialize');
|
||||
|
||||
jest.mock('@librechat/api', () => ({
|
||||
resolveHeaders: jest.fn(),
|
||||
getOpenAIConfig: jest.fn(),
|
||||
createHandleLLMNewToken: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('librechat-data-provider', () => ({
|
||||
CacheKeys: { TOKEN_CONFIG: 'token_config' },
|
||||
ErrorTypes: { NO_USER_KEY: 'NO_USER_KEY', NO_BASE_URL: 'NO_BASE_URL' },
|
||||
envVarRegex: /\$\{([^}]+)\}/,
|
||||
FetchTokenConfig: {},
|
||||
extractEnvVariable: jest.fn((value) => value),
|
||||
}));
|
||||
|
||||
jest.mock('@librechat/agents', () => ({
|
||||
Providers: { OLLAMA: 'ollama' },
|
||||
}));
|
||||
|
||||
jest.mock('~/server/services/UserService', () => ({
|
||||
getUserKeyValues: jest.fn(),
|
||||
checkUserKeyExpiry: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/server/services/Config', () => ({
|
||||
getCustomEndpointConfig: jest.fn().mockResolvedValue({
|
||||
apiKey: 'test-key',
|
||||
baseURL: 'https://test.com',
|
||||
headers: { 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
||||
models: { default: ['test-model'] },
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('~/server/services/ModelService', () => ({
|
||||
fetchModels: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/app/clients/OpenAIClient', () => {
|
||||
return jest.fn().mockImplementation(() => ({
|
||||
options: {},
|
||||
}));
|
||||
});
|
||||
|
||||
jest.mock('~/server/utils', () => ({
|
||||
isUserProvided: jest.fn().mockReturnValue(false),
|
||||
}));
|
||||
|
||||
jest.mock('~/cache/getLogStores', () =>
|
||||
jest.fn().mockReturnValue({
|
||||
get: jest.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
describe('custom/initializeClient', () => {
|
||||
const mockRequest = {
|
||||
body: { endpoint: 'test-endpoint' },
|
||||
user: { id: 'user-123', email: 'test@example.com' },
|
||||
app: { locals: {} },
|
||||
};
|
||||
const mockResponse = {};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('calls resolveHeaders with headers and user', async () => {
|
||||
const { resolveHeaders } = require('@librechat/api');
|
||||
await initializeClient({ req: mockRequest, res: mockResponse, optionsOnly: true });
|
||||
expect(resolveHeaders).toHaveBeenCalledWith(
|
||||
{ 'x-user': '{{LIBRECHAT_USER_ID}}', 'x-email': '{{LIBRECHAT_USER_EMAIL}}' },
|
||||
{ id: 'user-123', email: 'test@example.com' },
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if endpoint config is missing', async () => {
|
||||
const { getCustomEndpointConfig } = require('~/server/services/Config');
|
||||
getCustomEndpointConfig.mockResolvedValueOnce(null);
|
||||
await expect(
|
||||
initializeClient({ req: mockRequest, res: mockResponse, optionsOnly: true }),
|
||||
).rejects.toThrow('Config not found for the test-endpoint custom endpoint.');
|
||||
});
|
||||
|
||||
it('throws if user is missing', async () => {
|
||||
await expect(
|
||||
initializeClient({
|
||||
req: { ...mockRequest, user: undefined },
|
||||
res: mockResponse,
|
||||
optionsOnly: true,
|
||||
}),
|
||||
).rejects.toThrow("Cannot read properties of undefined (reading 'id')");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,7 @@
|
||||
const { getGoogleConfig, isEnabled } = require('@librechat/api');
|
||||
const { EModelEndpoint, AuthKeys } = require('librechat-data-provider');
|
||||
const { getUserKey, checkUserKeyExpiry } = require('~/server/services/UserService');
|
||||
const { getLLMConfig } = require('~/server/services/Endpoints/google/llm');
|
||||
const { isEnabled } = require('~/server/utils');
|
||||
const { GoogleClient } = require('~/app');
|
||||
|
||||
const initializeClient = async ({ req, res, endpointOption, overrideModel, optionsOnly }) => {
|
||||
@@ -64,7 +65,7 @@ const initializeClient = async ({ req, res, endpointOption, overrideModel, optio
|
||||
if (overrideModel) {
|
||||
clientOptions.modelOptions.model = overrideModel;
|
||||
}
|
||||
return getGoogleConfig(credentials, clientOptions);
|
||||
return getLLMConfig(credentials, clientOptions);
|
||||
}
|
||||
|
||||
const client = new GoogleClient(credentials, clientOptions);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Providers } from '@librechat/agents';
|
||||
import { googleSettings, AuthKeys } from 'librechat-data-provider';
|
||||
import type { GoogleClientOptions, VertexAIClientOptions } from '@librechat/agents';
|
||||
import type * as t from '~/types';
|
||||
import { isEnabled } from '~/utils';
|
||||
const { Providers } = require('@librechat/agents');
|
||||
const { AuthKeys } = require('librechat-data-provider');
|
||||
const { isEnabled } = require('~/server/utils');
|
||||
|
||||
function getThresholdMapping(model: string) {
|
||||
function getThresholdMapping(model) {
|
||||
const gemini1Pattern = /gemini-(1\.0|1\.5|pro$|1\.0-pro|1\.5-pro|1\.5-flash-001)/;
|
||||
const restrictedPattern = /(gemini-(1\.5-flash-8b|2\.0|exp)|learnlm)/;
|
||||
|
||||
if (gemini1Pattern.test(model)) {
|
||||
return (value: string) => {
|
||||
return (value) => {
|
||||
if (value === 'OFF') {
|
||||
return 'BLOCK_NONE';
|
||||
}
|
||||
@@ -18,7 +16,7 @@ function getThresholdMapping(model: string) {
|
||||
}
|
||||
|
||||
if (restrictedPattern.test(model)) {
|
||||
return (value: string) => {
|
||||
return (value) => {
|
||||
if (value === 'OFF' || value === 'HARM_BLOCK_THRESHOLD_UNSPECIFIED') {
|
||||
return 'BLOCK_NONE';
|
||||
}
|
||||
@@ -26,16 +24,19 @@ function getThresholdMapping(model: string) {
|
||||
};
|
||||
}
|
||||
|
||||
return (value: string) => value;
|
||||
return (value) => value;
|
||||
}
|
||||
|
||||
export function getSafetySettings(
|
||||
model?: string,
|
||||
): Array<{ category: string; threshold: string }> | undefined {
|
||||
/**
|
||||
*
|
||||
* @param {string} model
|
||||
* @returns {Array<{category: string, threshold: string}> | undefined}
|
||||
*/
|
||||
function getSafetySettings(model) {
|
||||
if (isEnabled(process.env.GOOGLE_EXCLUDE_SAFETY_SETTINGS)) {
|
||||
return undefined;
|
||||
}
|
||||
const mapThreshold = getThresholdMapping(model ?? '');
|
||||
const mapThreshold = getThresholdMapping(model);
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -73,27 +74,24 @@ export function getSafetySettings(
|
||||
* Replicates core logic from GoogleClient's constructor and setOptions, plus client determination.
|
||||
* Returns an object with the provider label and the final options that would be passed to createLLM.
|
||||
*
|
||||
* @param credentials - Either a JSON string or an object containing Google keys
|
||||
* @param options - The same shape as the "GoogleClient" constructor options
|
||||
* @param {string | object} credentials - Either a JSON string or an object containing Google keys
|
||||
* @param {object} [options={}] - The same shape as the "GoogleClient" constructor options
|
||||
*/
|
||||
|
||||
export function getGoogleConfig(
|
||||
credentials: string | t.GoogleCredentials | undefined,
|
||||
options: t.GoogleConfigOptions = {},
|
||||
) {
|
||||
let creds: t.GoogleCredentials = {};
|
||||
function getLLMConfig(credentials, options = {}) {
|
||||
// 1. Parse credentials
|
||||
let creds = {};
|
||||
if (typeof credentials === 'string') {
|
||||
try {
|
||||
creds = JSON.parse(credentials);
|
||||
} catch (err: unknown) {
|
||||
throw new Error(
|
||||
`Error parsing string credentials: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
||||
);
|
||||
} catch (err) {
|
||||
throw new Error(`Error parsing string credentials: ${err.message}`);
|
||||
}
|
||||
} else if (credentials && typeof credentials === 'object') {
|
||||
creds = credentials;
|
||||
}
|
||||
|
||||
// Extract from credentials
|
||||
const serviceKeyRaw = creds[AuthKeys.GOOGLE_SERVICE_KEY] ?? {};
|
||||
const serviceKey =
|
||||
typeof serviceKeyRaw === 'string' ? JSON.parse(serviceKeyRaw) : (serviceKeyRaw ?? {});
|
||||
@@ -104,15 +102,9 @@ export function getGoogleConfig(
|
||||
const reverseProxyUrl = options.reverseProxyUrl;
|
||||
const authHeader = options.authHeader;
|
||||
|
||||
const {
|
||||
thinking = googleSettings.thinking.default,
|
||||
thinkingBudget = googleSettings.thinkingBudget.default,
|
||||
...modelOptions
|
||||
} = options.modelOptions || {};
|
||||
|
||||
const llmConfig: GoogleClientOptions | VertexAIClientOptions = {
|
||||
...(modelOptions || {}),
|
||||
model: modelOptions?.model ?? '',
|
||||
/** @type {GoogleClientOptions | VertexAIClientOptions} */
|
||||
let llmConfig = {
|
||||
...(options.modelOptions || {}),
|
||||
maxRetries: 2,
|
||||
};
|
||||
|
||||
@@ -129,30 +121,16 @@ export function getGoogleConfig(
|
||||
|
||||
// If we have a GCP project => Vertex AI
|
||||
if (project_id && provider === Providers.VERTEXAI) {
|
||||
(llmConfig as VertexAIClientOptions).authOptions = {
|
||||
/** @type {VertexAIClientOptions['authOptions']} */
|
||||
llmConfig.authOptions = {
|
||||
credentials: { ...serviceKey },
|
||||
projectId: project_id,
|
||||
};
|
||||
(llmConfig as VertexAIClientOptions).location = process.env.GOOGLE_LOC || 'us-central1';
|
||||
llmConfig.location = process.env.GOOGLE_LOC || 'us-central1';
|
||||
} else if (apiKey && provider === Providers.GOOGLE) {
|
||||
llmConfig.apiKey = apiKey;
|
||||
}
|
||||
|
||||
const shouldEnableThinking =
|
||||
thinking && thinkingBudget != null && (thinkingBudget > 0 || thinkingBudget === -1);
|
||||
|
||||
if (shouldEnableThinking && provider === Providers.GOOGLE) {
|
||||
(llmConfig as GoogleClientOptions).thinkingConfig = {
|
||||
thinkingBudget: thinking ? thinkingBudget : googleSettings.thinkingBudget.default,
|
||||
includeThoughts: Boolean(thinking),
|
||||
};
|
||||
} else if (shouldEnableThinking && provider === Providers.VERTEXAI) {
|
||||
(llmConfig as VertexAIClientOptions).thinkingBudget = thinking
|
||||
? thinkingBudget
|
||||
: googleSettings.thinkingBudget.default;
|
||||
(llmConfig as VertexAIClientOptions).includeThoughts = Boolean(thinking);
|
||||
}
|
||||
|
||||
/*
|
||||
let legacyOptions = {};
|
||||
// Filter out any "examples" that are empty
|
||||
@@ -174,11 +152,11 @@ export function getGoogleConfig(
|
||||
*/
|
||||
|
||||
if (reverseProxyUrl) {
|
||||
(llmConfig as GoogleClientOptions).baseUrl = reverseProxyUrl;
|
||||
llmConfig.baseUrl = reverseProxyUrl;
|
||||
}
|
||||
|
||||
if (authHeader) {
|
||||
(llmConfig as GoogleClientOptions).customHeaders = {
|
||||
llmConfig.customHeaders = {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
};
|
||||
}
|
||||
@@ -191,3 +169,8 @@ export function getGoogleConfig(
|
||||
llmConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLLMConfig,
|
||||
getSafetySettings,
|
||||
};
|
||||
@@ -1,6 +1,3 @@
|
||||
const { sleep } = require('@librechat/agents');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const {
|
||||
Constants,
|
||||
StepTypes,
|
||||
@@ -11,8 +8,9 @@ const {
|
||||
} = require('librechat-data-provider');
|
||||
const { retrieveAndProcessFile } = require('~/server/services/Files/process');
|
||||
const { processRequiredActions } = require('~/server/services/ToolService');
|
||||
const { createOnProgress, sendMessage, sleep } = require('~/server/utils');
|
||||
const { processMessages } = require('~/server/services/Threads');
|
||||
const { createOnProgress } = require('~/server/utils');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Implements the StreamRunManager functionality for managing the streaming
|
||||
@@ -128,7 +126,7 @@ class StreamRunManager {
|
||||
conversationId: this.finalMessage.conversationId,
|
||||
};
|
||||
|
||||
sendEvent(this.res, contentData);
|
||||
sendMessage(this.res, contentData);
|
||||
}
|
||||
|
||||
/* <------------------ Misc. Helpers ------------------> */
|
||||
@@ -304,7 +302,7 @@ class StreamRunManager {
|
||||
|
||||
for (const d of delta[key]) {
|
||||
if (typeof d === 'object' && !Object.prototype.hasOwnProperty.call(d, 'index')) {
|
||||
logger.warn("Expected an object with an 'index' for array updates but got:", d);
|
||||
logger.warn('Expected an object with an \'index\' for array updates but got:', d);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ const {
|
||||
defaultAssistantsVersion,
|
||||
defaultAgentCapabilities,
|
||||
} = require('librechat-data-provider');
|
||||
const { sendEvent } = require('@librechat/api');
|
||||
const { Providers } = require('@librechat/agents');
|
||||
const partialRight = require('lodash/partialRight');
|
||||
const { sendMessage } = require('./streamResponse');
|
||||
|
||||
/** Helper function to escape special characters in regex
|
||||
* @param {string} string - The string to escape.
|
||||
@@ -37,7 +37,7 @@ const createOnProgress = (
|
||||
basePayload.text = basePayload.text + chunk;
|
||||
|
||||
const payload = Object.assign({}, basePayload, rest);
|
||||
sendEvent(res, payload);
|
||||
sendMessage(res, payload);
|
||||
if (_onProgress) {
|
||||
_onProgress(payload);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ const createOnProgress = (
|
||||
const sendIntermediateMessage = (res, payload, extraTokens = '') => {
|
||||
basePayload.text = basePayload.text + extraTokens;
|
||||
const message = Object.assign({}, basePayload, payload);
|
||||
sendEvent(res, message);
|
||||
sendMessage(res, message);
|
||||
if (i === 0) {
|
||||
basePayload.initial = false;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const streamResponse = require('./streamResponse');
|
||||
const removePorts = require('./removePorts');
|
||||
const countTokens = require('./countTokens');
|
||||
const handleText = require('./handleText');
|
||||
const sendEmail = require('./sendEmail');
|
||||
const queue = require('./queue');
|
||||
const files = require('./files');
|
||||
const math = require('./math');
|
||||
|
||||
/**
|
||||
* Check if email configuration is set
|
||||
@@ -26,6 +28,7 @@ function checkEmailConfig() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
...streamResponse,
|
||||
checkEmailConfig,
|
||||
...handleText,
|
||||
countTokens,
|
||||
@@ -33,4 +36,5 @@ module.exports = {
|
||||
sendEmail,
|
||||
...files,
|
||||
...queue,
|
||||
math,
|
||||
};
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
* If the input is not a string or contains invalid characters, an error is thrown.
|
||||
* If the evaluated result is not a number, an error is thrown.
|
||||
*
|
||||
* @param str - The mathematical expression to evaluate, or a number.
|
||||
* @param fallbackValue - The default value to return if the input is not a string or number, or if the evaluated result is not a number.
|
||||
* @param {string|number} str - The mathematical expression to evaluate, or a number.
|
||||
* @param {number} [fallbackValue] - The default value to return if the input is not a string or number, or if the evaluated result is not a number.
|
||||
*
|
||||
* @returns The result of the evaluated expression or the input number.
|
||||
* @returns {number} The result of the evaluated expression or the input number.
|
||||
*
|
||||
* @throws Throws an error if the input is not a string or number, contains invalid characters, or does not evaluate to a number.
|
||||
* @throws {Error} Throws an error if the input is not a string or number, contains invalid characters, or does not evaluate to a number.
|
||||
*/
|
||||
export function math(str: string | number, fallbackValue?: number): number {
|
||||
function math(str, fallbackValue) {
|
||||
const fallback = typeof fallbackValue !== 'undefined' && typeof fallbackValue === 'number';
|
||||
if (typeof str !== 'string' && typeof str === 'number') {
|
||||
return str;
|
||||
@@ -43,3 +43,5 @@ export function math(str: string | number, fallbackValue?: number): number {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
module.exports = math;
|
||||
@@ -1,9 +1,31 @@
|
||||
const crypto = require('crypto');
|
||||
const { logger } = require('@librechat/data-schemas');
|
||||
const { parseConvo } = require('librechat-data-provider');
|
||||
const { sendEvent, handleError } = require('@librechat/api');
|
||||
const { saveMessage, getMessages } = require('~/models/Message');
|
||||
const { getConvo } = require('~/models/Conversation');
|
||||
const { logger } = require('~/config');
|
||||
|
||||
/**
|
||||
* Sends error data in Server Sent Events format and ends the response.
|
||||
* @param {object} res - The server response.
|
||||
* @param {string} message - The error message.
|
||||
*/
|
||||
const handleError = (res, message) => {
|
||||
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
|
||||
res.end();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends message data in Server Sent Events format.
|
||||
* @param {Express.Response} res - - The server response.
|
||||
* @param {string | Object} message - The message to be sent.
|
||||
* @param {'message' | 'error' | 'cancel'} event - [Optional] The type of event. Default is 'message'.
|
||||
*/
|
||||
const sendMessage = (res, message, event = 'message') => {
|
||||
if (typeof message === 'string' && message.length === 0) {
|
||||
return;
|
||||
}
|
||||
res.write(`event: ${event}\ndata: ${JSON.stringify(message)}\n\n`);
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes an error with provided options, saves the error message and sends a corresponding SSE response
|
||||
@@ -69,7 +91,7 @@ const sendError = async (req, res, options, callback) => {
|
||||
convo = parseConvo(errorMessage);
|
||||
}
|
||||
|
||||
return sendEvent(res, {
|
||||
return sendMessage(res, {
|
||||
final: true,
|
||||
requestMessage: query?.[0] ? query[0] : requestMessage,
|
||||
responseMessage: errorMessage,
|
||||
@@ -98,10 +120,12 @@ const sendResponse = (req, res, data, errorMessage) => {
|
||||
if (errorMessage) {
|
||||
return sendError(req, res, { ...data, text: errorMessage });
|
||||
}
|
||||
return sendEvent(res, data);
|
||||
return sendMessage(res, data);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
sendError,
|
||||
sendResponse,
|
||||
handleError,
|
||||
sendMessage,
|
||||
sendError,
|
||||
};
|
||||
@@ -1,9 +1,11 @@
|
||||
const loadYaml = require('./loadYaml');
|
||||
const tokenHelpers = require('./tokens');
|
||||
const deriveBaseURL = require('./deriveBaseURL');
|
||||
const extractBaseURL = require('./extractBaseURL');
|
||||
const findMessageContent = require('./findMessageContent');
|
||||
|
||||
module.exports = {
|
||||
loadYaml,
|
||||
deriveBaseURL,
|
||||
extractBaseURL,
|
||||
...tokenHelpers,
|
||||
|
||||
13
api/utils/loadYaml.js
Normal file
13
api/utils/loadYaml.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const fs = require('fs');
|
||||
const yaml = require('js-yaml');
|
||||
|
||||
function loadYaml(filepath) {
|
||||
try {
|
||||
let fileContents = fs.readFileSync(filepath, 'utf8');
|
||||
return yaml.load(fileContents);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = loadYaml;
|
||||
@@ -109,6 +109,9 @@
|
||||
"tailwindcss-radix": "^2.8.0",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jotai": "^2.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-runtime": "^7.22.15",
|
||||
"@babel/preset-env": "^7.22.15",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type RenderProp<
|
||||
P = React.HTMLAttributes<any> & {
|
||||
ref?: React.Ref<any>;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Input, Label, OGDialog, Button } from '~/components/ui';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { Button, Input, Label, OGDialog, OGDialogTemplate } from '~/components';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export interface ConfigFieldDetail {
|
||||
@@ -3,7 +3,7 @@ import { SettingsIcon } from 'lucide-react';
|
||||
import { Constants } from 'librechat-data-provider';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type { TUpdateUserPlugins, TPlugin } from 'librechat-data-provider';
|
||||
import MCPConfigDialog, { type ConfigFieldDetail } from '~/components/ui/MCPConfigDialog';
|
||||
import MCPConfigDialog, { type ConfigFieldDetail } from './MCPConfigDialog';
|
||||
import { useToastContext, useBadgeRowContext } from '~/Providers';
|
||||
import MultiSelect from '~/components/ui/MultiSelect';
|
||||
import { MCPIcon } from '~/components/svg';
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
InputNumber,
|
||||
SelectDropDown,
|
||||
HoverCardTrigger,
|
||||
MultiSelectDropDown,
|
||||
} from '~/components/ui';
|
||||
import {
|
||||
removeFocusOutlines,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
} from '~/utils';
|
||||
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
|
||||
import { useLocalize, useDebouncedInput } from '~/hooks';
|
||||
import MultiSelectDropDown from '~/components/Input/ModelSelect/MultiSelectDropDown';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
import store from '~/store';
|
||||
@@ -165,7 +165,7 @@ export default function Settings({
|
||||
)}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2 ',
|
||||
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Wrench, ArrowRight } from 'lucide-react';
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
@@ -7,12 +8,11 @@ import {
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from '@headlessui/react';
|
||||
import { Wrench, ArrowRight } from 'lucide-react';
|
||||
import { CheckMark } from '~/components/svg';
|
||||
import useOnClickOutside from '~/hooks/useOnClickOutside';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
import { cn } from '~/utils/';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import { useMultiSearch } from '~/components/MultiSearch';
|
||||
import { useOnClickOutside } from '~/hooks';
|
||||
import { CheckMark } from '~/svgs';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
export type TMultiSelectDropDownProps = {
|
||||
title?: string;
|
||||
@@ -142,7 +142,7 @@ function MultiSelectDropDown({
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -2,7 +2,7 @@ import { Wrench } from 'lucide-react';
|
||||
import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import MenuItem from '~/components/Chat/Menus/UI/MenuItem';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
import { useMultiSearch } from '~/components';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
@@ -32,8 +32,6 @@ function MultiSelectPop({
|
||||
optionValueKey = 'value',
|
||||
searchPlaceholder,
|
||||
}: SelectDropDownProps) {
|
||||
// const localize = useLocalize();
|
||||
|
||||
const title = _title;
|
||||
const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins'];
|
||||
|
||||
@@ -54,14 +52,14 @@ function MultiSelectPop({
|
||||
<button
|
||||
data-testid="select-dropdown-button"
|
||||
className={cn(
|
||||
'relative flex flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-gray-700 dark:bg-gray-800 dark:bg-gray-800 sm:text-sm',
|
||||
'relative flex flex-col rounded-md border border-black/10 bg-white py-2 pl-3 pr-10 text-left focus:outline-none focus:ring-0 focus:ring-offset-0 dark:border-gray-700 dark:bg-gray-800 sm:text-sm',
|
||||
'pointer-cursor font-normal',
|
||||
'hover:bg-gray-50 radix-state-open:bg-gray-50 dark:hover:bg-gray-700 dark:radix-state-open:bg-gray-700',
|
||||
)}
|
||||
>
|
||||
{' '}
|
||||
{showLabel && (
|
||||
<label className="block text-xs text-gray-700 dark:text-gray-500 ">{title}</label>
|
||||
<label className="block text-xs text-gray-700 dark:text-gray-500">{title}</label>
|
||||
)}
|
||||
<span className="inline-flex" id={excludeIds[2]}>
|
||||
<span
|
||||
@@ -73,7 +71,7 @@ function MultiSelectPop({
|
||||
{/* {!showLabel && title.length > 0 && (
|
||||
<span className="text-xs text-gray-700 dark:text-gray-500">{title}:</span>
|
||||
)} */}
|
||||
<span className="flex items-center gap-1 ">
|
||||
<span className="flex items-center gap-1">
|
||||
<div className="flex gap-1">
|
||||
{value.length === 0 && 'None selected'}
|
||||
{value.map((v, i) => (
|
||||
@@ -98,7 +96,7 @@ function MultiSelectPop({
|
||||
viewBox="0 0 24 24"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
className="h-4 w-4 text-gray-400"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -4,15 +4,10 @@ import { useState, useEffect, useMemo } from 'react';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import {
|
||||
Button,
|
||||
MultiSelectPop,
|
||||
SelectDropDown,
|
||||
SelectDropDownPop,
|
||||
MultiSelectDropDown,
|
||||
} from '~/components/ui';
|
||||
import { Button, SelectDropDown, SelectDropDownPop, MultiSelectDropDown } from '~/components/ui';
|
||||
import { useSetIndexOptions, useAuthContext, useMediaQuery, useLocalize } from '~/hooks';
|
||||
import { cn, cardStyle, selectPlugins, processPlugins } from '~/utils';
|
||||
import MultiSelectPop from './MultiSelectPop';
|
||||
import store from '~/store';
|
||||
|
||||
export default function PluginsByIndex({
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Root, Trigger, Content, Portal } from '@radix-ui/react-popover';
|
||||
import MenuItem from '~/components/Chat/Menus/UI/MenuItem';
|
||||
import { useMultiSearch } from '~/components';
|
||||
import type { Option } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
import { useMultiSearch } from './MultiSearch';
|
||||
|
||||
type SelectDropDownProps = {
|
||||
id?: string;
|
||||
@@ -1,26 +1,28 @@
|
||||
import { useCallback, useState, useMemo, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { TrashIcon, MessageSquare, ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import type { SharedLinkItem, SharedLinksListParams } from 'librechat-data-provider';
|
||||
import {
|
||||
OGDialog,
|
||||
OGDialogTemplate,
|
||||
OGDialogTrigger,
|
||||
OGDialogContent,
|
||||
OGDialogHeader,
|
||||
OGDialogTitle,
|
||||
TooltipAnchor,
|
||||
DataTable,
|
||||
Spinner,
|
||||
Button,
|
||||
Label,
|
||||
Spinner,
|
||||
} from '~/components';
|
||||
import { useDeleteSharedLinkMutation, useSharedLinksQuery } from '~/data-provider';
|
||||
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
|
||||
import { useLocalize, useMediaQuery } from '~/hooks';
|
||||
import DataTable from '~/components/ui/DataTable';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { formatDate } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
@@ -36,6 +38,7 @@ export default function SharedLinks() {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const isSearchEnabled = useRecoilValue(store.search);
|
||||
const [queryParams, setQueryParams] = useState<SharedLinksListParams>(DEFAULT_PARAMS);
|
||||
const [deleteRow, setDeleteRow] = useState<SharedLinkItem | null>(null);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
@@ -308,6 +311,7 @@ export default function SharedLinks() {
|
||||
onFilterChange={debouncedFilterChange}
|
||||
filterValue={queryParams.search}
|
||||
isLoading={isLoading}
|
||||
enableSearch={isSearchEnabled}
|
||||
/>
|
||||
</OGDialogContent>
|
||||
</OGDialog>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback, useMemo, useEffect } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { TrashIcon, ArchiveRestore, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
|
||||
import type { ConversationListParams, TConversation } from 'librechat-data-provider';
|
||||
import {
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
Label,
|
||||
TooltipAnchor,
|
||||
Spinner,
|
||||
DataTable,
|
||||
} from '~/components';
|
||||
import {
|
||||
useArchiveConvoMutation,
|
||||
@@ -19,10 +21,10 @@ import {
|
||||
} from '~/data-provider';
|
||||
import { useLocalize, useMediaQuery } from '~/hooks';
|
||||
import { MinimalIcon } from '~/components/Endpoints';
|
||||
import DataTable from '~/components/ui/DataTable';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { formatDate } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const DEFAULT_PARAMS: ConversationListParams = {
|
||||
isArchived: true,
|
||||
@@ -39,7 +41,7 @@ export default function ArchivedChatsTable({
|
||||
const localize = useLocalize();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const { showToast } = useToastContext();
|
||||
|
||||
const isSearchEnabled = useRecoilValue(store.search);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
const [queryParams, setQueryParams] = useState<ConversationListParams>(DEFAULT_PARAMS);
|
||||
const [deleteConversation, setDeleteConversation] = useState<TConversation | null>(null);
|
||||
@@ -272,6 +274,7 @@ export default function ArchivedChatsTable({
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
isLoading={isLoading}
|
||||
showCheckboxes={false}
|
||||
enableSearch={isSearchEnabled}
|
||||
/>
|
||||
|
||||
<OGDialog open={isDeleteOpen} onOpenChange={onOpenChange}>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-quer
|
||||
import type { TUpdateUserPlugins } from 'librechat-data-provider';
|
||||
import { Button, Input, Label } from '~/components/ui';
|
||||
import { useGetStartupConfig } from '~/data-provider';
|
||||
import { useAddToolMutation } from '~/data-provider/Tools';
|
||||
import MCPPanelSkeleton from './MCPPanelSkeleton';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { useLocalize } from '~/hooks';
|
||||
@@ -18,12 +17,6 @@ interface ServerConfigWithVars {
|
||||
};
|
||||
}
|
||||
|
||||
interface AddToolFormData {
|
||||
name: string;
|
||||
description: string;
|
||||
type: 'function' | 'code_interpreter' | 'file_search';
|
||||
}
|
||||
|
||||
export default function MCPPanel() {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
@@ -31,7 +24,6 @@ export default function MCPPanel() {
|
||||
const [selectedServerNameForEditing, setSelectedServerNameForEditing] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [showAddToolForm, setShowAddToolForm] = useState(true);
|
||||
|
||||
const mcpServerDefinitions = useMemo(() => {
|
||||
if (!startupConfig?.mcpServers) {
|
||||
@@ -95,25 +87,19 @@ export default function MCPPanel() {
|
||||
|
||||
const handleGoBackToList = () => {
|
||||
setSelectedServerNameForEditing(null);
|
||||
setShowAddToolForm(false);
|
||||
};
|
||||
|
||||
const handleShowAddToolForm = () => {
|
||||
setShowAddToolForm(true);
|
||||
setSelectedServerNameForEditing(null);
|
||||
};
|
||||
|
||||
if (startupConfigLoading) {
|
||||
return <MCPPanelSkeleton />;
|
||||
}
|
||||
|
||||
// if (mcpServerDefinitions.length === 0) {
|
||||
// return (
|
||||
// <div className="p-4 text-center text-sm text-gray-500">
|
||||
// {localize('com_sidepanel_mcp_no_servers_with_vars')}
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
if (mcpServerDefinitions.length === 0) {
|
||||
return (
|
||||
<div className="p-4 text-center text-sm text-gray-500">
|
||||
{localize('com_sidepanel_mcp_no_servers_with_vars')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedServerNameForEditing) {
|
||||
// Editing View
|
||||
@@ -152,32 +138,10 @@ export default function MCPPanel() {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (showAddToolForm) {
|
||||
// Add Tool Form View
|
||||
return (
|
||||
<div className="h-auto max-w-full overflow-x-hidden p-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleGoBackToList}
|
||||
className="mb-3 flex items-center px-3 py-2 text-sm"
|
||||
>
|
||||
<ChevronLeft className="mr-1 h-4 w-4" />
|
||||
{localize('com_ui_back')}
|
||||
</Button>
|
||||
<h3 className="mb-3 text-lg font-medium">{localize('com_ui_add_tool')}</h3>
|
||||
<AddToolForm onCancel={handleGoBackToList} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// Server List View
|
||||
return (
|
||||
<div className="h-auto max-w-full overflow-x-hidden p-3">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<h3 className="text-lg font-medium">{localize('com_ui_mcp_servers')}</h3>
|
||||
<Button variant="outline" onClick={handleShowAddToolForm} className="text-sm">
|
||||
{localize('com_ui_add_tool')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{mcpServerDefinitions.map((server) => (
|
||||
<Button
|
||||
@@ -287,131 +251,3 @@ function MCPVariableEditor({ server, onSave, onRevoke, isSubmitting }: MCPVariab
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
interface AddToolFormProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
function AddToolForm({ onCancel }: AddToolFormProps) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
||||
const addToolMutation = useAddToolMutation({
|
||||
onSuccess: (data) => {
|
||||
showToast({
|
||||
message: localize('com_ui_tool_added_success', { '0': data.function?.name || 'Unknown' }),
|
||||
status: 'success',
|
||||
});
|
||||
onCancel();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error('Error adding tool:', error);
|
||||
showToast({
|
||||
message: localize('com_ui_tool_add_error'),
|
||||
status: 'error',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
formState: { errors, isDirty },
|
||||
} = useForm<AddToolFormData>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
description: '',
|
||||
type: 'function',
|
||||
},
|
||||
});
|
||||
|
||||
const onFormSubmit = (data: AddToolFormData) => {
|
||||
addToolMutation.mutate(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onFormSubmit)} className="mb-4 mt-2 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="tool-name" className="text-sm font-medium">
|
||||
{localize('com_ui_tool_name')}
|
||||
</Label>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
rules={{ required: localize('com_ui_tool_name_required') }}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
id="tool-name"
|
||||
type="text"
|
||||
{...field}
|
||||
placeholder={localize('com_ui_enter_tool_name')}
|
||||
className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name.message}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="tool-description" className="text-sm font-medium">
|
||||
{localize('com_ui_description')}
|
||||
</Label>
|
||||
<Controller
|
||||
name="description"
|
||||
control={control}
|
||||
rules={{ required: localize('com_ui_description_required') }}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
id="tool-description"
|
||||
type="text"
|
||||
{...field}
|
||||
placeholder={localize('com_ui_enter_description')}
|
||||
className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.description && <p className="text-xs text-red-500">{errors.description.message}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="tool-type" className="text-sm font-medium">
|
||||
{localize('com_ui_tool_type')}
|
||||
</Label>
|
||||
<Controller
|
||||
name="type"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<select
|
||||
id="tool-type"
|
||||
{...field}
|
||||
className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm"
|
||||
>
|
||||
<option value="function">{localize('com_ui_function')}</option>
|
||||
<option value="code_interpreter">{localize('com_ui_code_interpreter')}</option>
|
||||
<option value="file_search">{localize('com_ui_file_search')}</option>
|
||||
</select>
|
||||
)}
|
||||
/>
|
||||
{errors.type && <p className="text-xs text-red-500">{errors.type.message}</p>}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onCancel}
|
||||
disabled={addToolMutation.isLoading}
|
||||
>
|
||||
{localize('com_ui_cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="bg-green-500 text-white hover:bg-green-600"
|
||||
disabled={addToolMutation.isLoading || !isDirty}
|
||||
>
|
||||
{addToolMutation.isLoading ? localize('com_ui_saving') : localize('com_ui_save')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,10 +46,6 @@ function DynamicInput({
|
||||
setInputValue(e, !isNaN(Number(e.target.value)));
|
||||
};
|
||||
|
||||
const placeholderText = placeholderCode
|
||||
? localize(placeholder as TranslationKeys) || placeholder
|
||||
: placeholder;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col items-center justify-start gap-6 ${
|
||||
@@ -80,7 +76,11 @@ function DynamicInput({
|
||||
disabled={readonly}
|
||||
value={inputValue ?? defaultValue ?? ''}
|
||||
onChange={handleInputChange}
|
||||
placeholder={placeholderText}
|
||||
placeholder={
|
||||
placeholderCode
|
||||
? localize(placeholder as TranslationKeys) || placeholder
|
||||
: placeholder
|
||||
}
|
||||
className={cn(
|
||||
'flex h-10 max-h-10 w-full resize-none border-none bg-surface-secondary px-3 py-2',
|
||||
)}
|
||||
|
||||
@@ -29,7 +29,7 @@ function OptionHover({
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={side} className={`z-[999] w-80 ${className}`} sideOffset={sideOffset}>
|
||||
<div className="space-y-2">
|
||||
<p className="whitespace-pre-wrap text-sm text-text-secondary">{text}</p>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">{text}</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AnimatedSearchInput = ({
|
||||
value,
|
||||
onChange,
|
||||
isSearching: searching,
|
||||
placeholder,
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
isSearching?: boolean;
|
||||
placeholder: string;
|
||||
}) => {
|
||||
const isSearching = searching === true;
|
||||
const hasValue = value != null && value.length > 0;
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="relative rounded-lg transition-all duration-500 ease-in-out">
|
||||
<div className="relative">
|
||||
{/* Icon on the left */}
|
||||
<div className="absolute left-3 top-1/2 z-50 -translate-y-1/2">
|
||||
<Search
|
||||
className={cn(
|
||||
`
|
||||
h-4 w-4 transition-all duration-500 ease-in-out`,
|
||||
isSearching && hasValue ? 'text-blue-400' : 'text-gray-400',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Input field */}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
className={`
|
||||
peer relative z-20 w-full rounded-lg bg-surface-secondary px-10
|
||||
py-2 outline-none ring-0 backdrop-blur-sm transition-all
|
||||
duration-500 ease-in-out placeholder:text-gray-400
|
||||
focus:outline-none focus:ring-0
|
||||
`}
|
||||
/>
|
||||
|
||||
{/* Gradient overlay */}
|
||||
<div
|
||||
className={`
|
||||
pointer-events-none absolute inset-0 z-20 rounded-lg
|
||||
bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-blue-500/20
|
||||
transition-all duration-500 ease-in-out
|
||||
${isSearching && hasValue ? 'opacity-100 blur-sm' : 'opacity-0 blur-none'}
|
||||
`}
|
||||
/>
|
||||
|
||||
{/* Animated loading indicator */}
|
||||
<div
|
||||
className={`
|
||||
absolute right-3 top-1/2 z-20 -translate-y-1/2
|
||||
transition-all duration-500 ease-in-out
|
||||
${isSearching && hasValue ? 'scale-100 opacity-100' : 'scale-0 opacity-0'}
|
||||
`}
|
||||
>
|
||||
<div className="relative h-2 w-2">
|
||||
<div className="absolute inset-0 animate-ping rounded-full bg-blue-500/60" />
|
||||
<div className="absolute inset-0 rounded-full bg-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Outer glow effect */}
|
||||
<div
|
||||
className={`
|
||||
absolute -inset-8 -z-10
|
||||
transition-all duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'scale-105 opacity-100' : 'scale-100 opacity-0'}
|
||||
`}
|
||||
>
|
||||
<div className="absolute inset-0">
|
||||
<div
|
||||
className={`
|
||||
bg-gradient-radial absolute inset-0 from-blue-500/10 to-transparent
|
||||
transition-opacity duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'animate-pulse-slow opacity-100' : 'opacity-0'}
|
||||
`}
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
absolute inset-0 bg-gradient-to-r from-purple-500/5 via-blue-500/5 to-purple-500/5
|
||||
blur-xl transition-all duration-700 ease-in-out
|
||||
${isSearching && hasValue ? 'animate-gradient-x opacity-100' : 'opacity-0'}
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`
|
||||
absolute inset-0 -z-20 scale-100 bg-gradient-to-r from-blue-500/10
|
||||
via-purple-500/10 to-blue-500/10 opacity-0 blur-xl
|
||||
transition-all duration-500 ease-in-out
|
||||
peer-focus:scale-105 peer-focus:opacity-100
|
||||
`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedSearchInput;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { useDelayedRender } from '~/hooks';
|
||||
|
||||
const DelayedRender = ({ delay, children }) => useDelayedRender(delay)(() => children);
|
||||
|
||||
export default DelayedRender;
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root> & { onDoubleClick?: () => void }
|
||||
>(({ className, onDoubleClick, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-pointer touch-none select-none items-center',
|
||||
className,
|
||||
)}
|
||||
onDoubleClick={onDoubleClick}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
@@ -4,7 +4,7 @@ import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { useAcceptTermsMutation } from '~/data-provider';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { OGDialog } from '~/components/ui';
|
||||
import { OGDialog } from '~/components';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const TermsAndConditionsModal = ({
|
||||
@@ -73,7 +73,7 @@ const TermsAndConditionsModal = ({
|
||||
main={
|
||||
<section
|
||||
// Motivation: This is a dialog, so its content should be focusable
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
|
||||
tabIndex={0}
|
||||
className="max-h-[60vh] overflow-y-auto p-4"
|
||||
aria-label={localize('com_ui_terms_and_conditions')}
|
||||
|
||||
@@ -40,44 +40,3 @@ export const useToolCallMutation = <T extends t.ToolId>(
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface for creating a new tool
|
||||
*/
|
||||
interface CreateToolData {
|
||||
name: string;
|
||||
description: string;
|
||||
type: 'function' | 'code_interpreter' | 'file_search';
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutation hook for adding a new tool to the system
|
||||
* Note: Requires corresponding backend implementation of dataService.createTool
|
||||
*/
|
||||
export const useAddToolMutation = (
|
||||
// options?:
|
||||
// {
|
||||
// onMutate?: (variables: CreateToolData) => void | Promise<unknown>;
|
||||
// onError?: (error: Error, variables: CreateToolData, context: unknown) => void;
|
||||
// onSuccess?: (data: t.Tool, variables: CreateToolData, context: unknown) => void;
|
||||
// }
|
||||
options?: t.MutationOptions<Record<string, unknown>, CreateToolData>,
|
||||
): UseMutationResult<Record<string, unknown>, Error, CreateToolData> => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(toolData: CreateToolData) => {
|
||||
return dataService.createTool(toolData);
|
||||
},
|
||||
{
|
||||
onMutate: (variables) => options?.onMutate?.(variables),
|
||||
onError: (error, variables, context) => options?.onError?.(error, variables, context),
|
||||
onSuccess: (data, variables, context) => {
|
||||
// Invalidate tools list to trigger refetch
|
||||
queryClient.invalidateQueries([QueryKeys.tools]);
|
||||
return options?.onSuccess?.(data, variables, context);
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -194,7 +194,7 @@ export const useConversationTagsQuery = (
|
||||
/**
|
||||
* Hook for getting all available tools for Assistants
|
||||
*/
|
||||
export const useAvailableToolsQuery = <TData = t.TPlugin[]>( // <-- this one
|
||||
export const useAvailableToolsQuery = <TData = t.TPlugin[]>(
|
||||
endpoint: t.AssistantsEndpoint | EModelEndpoint.agents,
|
||||
config?: UseQueryOptions<t.TPlugin[], unknown, TData>,
|
||||
): QueryObserverResult<TData> => {
|
||||
|
||||
@@ -152,20 +152,20 @@ export default function useSideNavLinks({
|
||||
});
|
||||
}
|
||||
|
||||
// if (
|
||||
// startupConfig?.mcpServers &&
|
||||
// Object.values(startupConfig.mcpServers).some(
|
||||
// (server) => server.customUserVars && Object.keys(server.customUserVars).length > 0,
|
||||
// )
|
||||
// ) {
|
||||
links.push({
|
||||
title: 'com_nav_setting_mcp',
|
||||
label: '',
|
||||
icon: MCPIcon,
|
||||
id: 'mcp-settings',
|
||||
Component: MCPPanel,
|
||||
});
|
||||
// }
|
||||
if (
|
||||
startupConfig?.mcpServers &&
|
||||
Object.values(startupConfig.mcpServers).some(
|
||||
(server) => server.customUserVars && Object.keys(server.customUserVars).length > 0,
|
||||
)
|
||||
) {
|
||||
links.push({
|
||||
title: 'com_nav_setting_mcp',
|
||||
label: '',
|
||||
icon: MCPIcon,
|
||||
id: 'mcp-settings',
|
||||
Component: MCPPanel,
|
||||
});
|
||||
}
|
||||
|
||||
links.push({
|
||||
title: 'com_sidepanel_hide_panel',
|
||||
|
||||
@@ -14,24 +14,19 @@ export * from './Prompts';
|
||||
export * from './Roles';
|
||||
export * from './SSE';
|
||||
export * from './AuthContext';
|
||||
export * from './ThemeContext';
|
||||
export * from './ScreenshotContext';
|
||||
export * from './ApiErrorBoundaryContext';
|
||||
export * from './Endpoint';
|
||||
|
||||
export type { TranslationKeys } from './useLocalize';
|
||||
|
||||
export { default as useToast } from './useToast';
|
||||
export { default as useTimeout } from './useTimeout';
|
||||
export { default as useNewConvo } from './useNewConvo';
|
||||
export { default as useLocalize } from './useLocalize';
|
||||
export { default as useMediaQuery } from './useMediaQuery';
|
||||
export { default as useChatBadges } from './useChatBadges';
|
||||
export { default as useScrollToRef } from './useScrollToRef';
|
||||
export { default as useLocalStorage } from './useLocalStorage';
|
||||
export { default as useDocumentTitle } from './useDocumentTitle';
|
||||
export { default as useDelayedRender } from './useDelayedRender';
|
||||
export { default as useOnClickOutside } from './useOnClickOutside';
|
||||
export { default as useSpeechToText } from './Input/useSpeechToText';
|
||||
export { default as useTextToSpeech } from './Input/useTextToSpeech';
|
||||
export { default as useGenerationsByLatest } from './useGenerationsByLatest';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// Translation.spec.ts
|
||||
|
||||
import i18n from './i18n';
|
||||
import English from './en/translation.json';
|
||||
import French from './fr/translation.json';
|
||||
|
||||
@@ -207,8 +207,6 @@
|
||||
"com_endpoint_google_temp": "Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.",
|
||||
"com_endpoint_google_topk": "Top-k changes how the model selects tokens for output. A top-k of 1 means the selected token is the most probable among all tokens in the model's vocabulary (also called greedy decoding), while a top-k of 3 means that the next token is selected from among the 3 most probable tokens (using temperature).",
|
||||
"com_endpoint_google_topp": "Top-p changes how the model selects tokens for output. Tokens are selected from most K (see topK parameter) probable to least until the sum of their probabilities equals the top-p value.",
|
||||
"com_endpoint_google_thinking": "Enables or disables reasoning. This setting is only supported by certain models (2.5 series). For older models, this setting may have no effect.",
|
||||
"com_endpoint_google_thinking_budget": "Guides the number of thinking tokens the model uses. The actual amount may exceed or fall below this value depending on the prompt.\n\nThis setting is only supported by certain models (2.5 series). Gemini 2.5 Pro supports 128-32,768 tokens. Gemini 2.5 Flash supports 0-24,576 tokens. Gemini 2.5 Flash Lite supports 512-24,576 tokens.\n\nLeave blank or set to \"-1\" to let the model automatically decide when and how much to think. By default, Gemini 2.5 Flash Lite does not think.",
|
||||
"com_endpoint_instructions_assistants": "Override Instructions",
|
||||
"com_endpoint_instructions_assistants_placeholder": "Overrides the instructions of the assistant. This is useful for modifying the behavior on a per-run basis.",
|
||||
"com_endpoint_max_output_tokens": "Max Output Tokens",
|
||||
@@ -584,7 +582,6 @@
|
||||
"com_ui_auth_url": "Authorization URL",
|
||||
"com_ui_authentication": "Authentication",
|
||||
"com_ui_authentication_type": "Authentication Type",
|
||||
"com_ui_auto": "Auto",
|
||||
"com_ui_available_tools": "Available Tools",
|
||||
"com_ui_avatar": "Avatar",
|
||||
"com_ui_azure": "Azure",
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
FileMapContext,
|
||||
SetConvoProvider,
|
||||
} from '~/Providers';
|
||||
import TermsAndConditionsModal from '~/components/ui/TermsAndConditionsModal';
|
||||
import TermsAndConditionsModal from '~/components/ui'; // TODO: remove this comment after updating the import path of all components
|
||||
import { useUserTermsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
import { Nav, MobileNav } from '~/components/Nav';
|
||||
import { useHealthCheck } from '~/data-provider';
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
"test/setupTests.js",
|
||||
"env.d.ts",
|
||||
"../config/translations/**/*.ts"
|
||||
]
|
||||
, "../packages/client/src/hooks/useDelayedRender.tsx" ]
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ interface:
|
||||
bookmarks: true
|
||||
multiConvo: true
|
||||
agents: true
|
||||
# Temporary chat retention period in hours (default: 720, min: 1, max: 8760)
|
||||
# temporaryChatRetention: 1
|
||||
|
||||
# Example Cloudflare turnstile (optional)
|
||||
#turnstile:
|
||||
|
||||
552
package-lock.json
generated
552
package-lock.json
generated
@@ -64,7 +64,7 @@
|
||||
"@langchain/google-genai": "^0.2.13",
|
||||
"@langchain/google-vertexai": "^0.2.13",
|
||||
"@langchain/textsplitters": "^0.1.0",
|
||||
"@librechat/agents": "^2.4.46",
|
||||
"@librechat/agents": "^2.4.42",
|
||||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@node-saml/passport-saml": "^5.0.0",
|
||||
@@ -1351,6 +1351,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"api/node_modules/@librechat/agents": {
|
||||
"version": "2.4.42",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.42.tgz",
|
||||
"integrity": "sha512-52ux2PeEAV79yr6/h6GN3omlpqX6H0FYl6qwjJ6gT04MMko/imnLd3bQrX0gm3i0KL5ygHbRjQeonONKjJayHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@langchain/anthropic": "^0.3.23",
|
||||
"@langchain/aws": "^0.1.11",
|
||||
"@langchain/community": "^0.3.47",
|
||||
"@langchain/core": "^0.3.60",
|
||||
"@langchain/deepseek": "^0.0.2",
|
||||
"@langchain/google-genai": "^0.2.13",
|
||||
"@langchain/google-vertexai": "^0.2.13",
|
||||
"@langchain/langgraph": "^0.3.4",
|
||||
"@langchain/mistralai": "^0.2.1",
|
||||
"@langchain/ollama": "^0.2.3",
|
||||
"@langchain/openai": "^0.5.14",
|
||||
"@langchain/xai": "^0.0.3",
|
||||
"cheerio": "^1.0.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"nanoid": "^3.3.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"api/node_modules/@smithy/abort-controller": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.2.tgz",
|
||||
@@ -2625,58 +2652,9 @@
|
||||
"vite-plugin-compression2": "^1.3.3",
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-pwa": "^0.21.2"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.15.tgz",
|
||||
"integrity": "sha512-0V2LkNPFrGRT+SEIiObx/LQjR6v3rR+mKEDUu/3tq7jfCZ+7+6Q6EMR1rFaK+XMkaRY1RWUcj/rRDWAUWnsDww==",
|
||||
"dependencies": {
|
||||
"@ariakit/react-core": "0.4.15"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ariakit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.17.tgz",
|
||||
"integrity": "sha512-kFF6n+gC/5CRQIyaMTFoBPio2xUe0k9rZhMNdUobWRmc/twfeLVkODx+8UVYaNyKilTge8G0JFqwvFKku/jKEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.15",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@ariakit/react-core/node_modules/@ariakit/core": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.15.tgz",
|
||||
"integrity": "sha512-vvxmZvkNhiisKM+Y1TbGMUfVVchV/sWu9F0xw0RYADXcimWPK31dd9JnIZs/OQ5pwAryAHmERHwuGQVESkSjwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"client/node_modules/@ariakit/react/node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.15.tgz",
|
||||
"integrity": "sha512-Up8+U97nAPJdyUh9E8BCEhJYTA+eVztWpHoo1R9zZfHd4cnBWAg5RHxEmMH+MamlvuRxBQA71hFKY/735fDg+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.14",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
"jotai": "^2.12.5"
|
||||
}
|
||||
},
|
||||
"client/node_modules/@babel/compat-data": {
|
||||
@@ -4547,6 +4525,27 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"client/node_modules/class-variance-authority": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz",
|
||||
"integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"clsx": "1.2.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://joebell.co.uk"
|
||||
}
|
||||
},
|
||||
"client/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"client/node_modules/core-js-compat": {
|
||||
"version": "3.40.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz",
|
||||
@@ -4578,6 +4577,33 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"client/node_modules/framer-motion": {
|
||||
"version": "11.18.2",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
||||
"integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^11.18.1",
|
||||
"motion-utils": "^11.18.1",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"client/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
@@ -4588,6 +4614,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"client/node_modules/lucide-react": {
|
||||
"version": "0.394.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.394.0.tgz",
|
||||
"integrity": "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/node-releases": {
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
@@ -4610,16 +4645,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"client/node_modules/react-resizable-panels": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.2.tgz",
|
||||
"integrity": "sha512-j4RNII75fnHkLnbsTb5G5YsDvJsSEZrJK2XSF2z0Tc2jIonYlIVir/Yh/5LvcUFCfs1HqrMAoiBFmIrRjC4XnA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"client/node_modules/ts-jest": {
|
||||
"version": "29.2.5",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
|
||||
@@ -4794,9 +4819,42 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ariakit/core": {
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.14.tgz",
|
||||
"integrity": "sha512-hpzZvyYzGhP09S9jW1XGsU/FD5K3BKsH1eG/QJ8rfgEeUdPS7BvHPt5lHbOeJ2cMrRzBEvsEzLi1ivfDifHsVA=="
|
||||
"version": "0.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/core/-/core-0.4.15.tgz",
|
||||
"integrity": "sha512-vvxmZvkNhiisKM+Y1TbGMUfVVchV/sWu9F0xw0RYADXcimWPK31dd9JnIZs/OQ5pwAryAHmERHwuGQVESkSjwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ariakit/react": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.17.tgz",
|
||||
"integrity": "sha512-HQaIboE2axtlncJz1hRTaiQfJ1GGjhdtNcAnPwdjvl2RybfmlHowIB+HTVBp36LzroKPs/M4hPCxk7XTaqRZGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/react-core": "0.4.17"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/ariakit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ariakit/react-core": {
|
||||
"version": "0.4.17",
|
||||
"resolved": "https://registry.npmjs.org/@ariakit/react-core/-/react-core-0.4.17.tgz",
|
||||
"integrity": "sha512-kFF6n+gC/5CRQIyaMTFoBPio2xUe0k9rZhMNdUobWRmc/twfeLVkODx+8UVYaNyKilTge8G0JFqwvFKku/jKEw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ariakit/core": "0.4.15",
|
||||
"@floating-ui/dom": "^1.0.0",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-crypto/crc32": {
|
||||
"version": "3.0.0",
|
||||
@@ -15608,6 +15666,25 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
"integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "0.7.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
|
||||
"integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
|
||||
@@ -16886,21 +16963,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.2.tgz",
|
||||
"integrity": "sha512-Kb3hgk9gRNRcTZktBrKdHhF3xFhYkca1Rk6e1/im2ENf83dgN54orMW0uSKTXFnUpZOUFZ+wcY05LlipwgZIFQ==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.4.tgz",
|
||||
"integrity": "sha512-lz+OGcAH1dK93rgSMzXmm1qKOJkBUqZf1L4M8TWLNplftQD3IkoEDdUFNfAn4ylsN6WOTVtWaLmvmaHOUk1dTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.16",
|
||||
"@react-aria/focus": "^3.17.1",
|
||||
"@react-aria/interactions": "^3.21.3",
|
||||
"@tanstack/react-virtual": "^3.8.1"
|
||||
"@react-aria/focus": "^3.20.2",
|
||||
"@react-aria/interactions": "^3.25.0",
|
||||
"@tanstack/react-virtual": "^3.13.9",
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
@@ -19126,9 +19205,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@langchain/langgraph": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.3.5.tgz",
|
||||
"integrity": "sha512-7astlgnp6BdMQJqmr+cbDgR10FYWNCaDLnbfEDHpqhKCCajU59m5snOdl4Vtu5UM6V2k3lgatNqWoflBtxhIyg==",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.3.4.tgz",
|
||||
"integrity": "sha512-Vuja8Qtu3Zjx7k4fK7Cnw+p8gtvIRPciWp9btPhAs3aUo6aBgOJOZVcK5Ii3mHfEHK/aQmRElR0x/u/YwykOrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@langchain/langgraph-checkpoint": "~0.0.18",
|
||||
@@ -19436,10 +19515,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@librechat/agents": {
|
||||
"version": "2.4.46",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.46.tgz",
|
||||
"integrity": "sha512-zR27U19/WGF3HN64oBbiaFgjjWHaF7BjYzRFWzQKEkk+iEzCe59IpuEZUizQ54YcY02nhhh6S3MNUjhAJwMYVA==",
|
||||
"version": "2.4.41",
|
||||
"resolved": "https://registry.npmjs.org/@librechat/agents/-/agents-2.4.41.tgz",
|
||||
"integrity": "sha512-kYmdk5WVRp0qZxTx6BuGCs4l0Ir9iBLLx4ZY4/1wxr80al5/vq3P8wbgGdKMeO2qTu4ZaT4RyWRQYWBg5HDkUQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@langchain/anthropic": "^0.3.23",
|
||||
"@langchain/aws": "^0.1.11",
|
||||
@@ -19467,6 +19547,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@langchain/community/-/community-0.3.47.tgz",
|
||||
"integrity": "sha512-Vo42kAfkXpTFSevhEkeqqE55az8NyQgDktCbitXYuhipNbFYx08XVvqEDkFkB20MM/Z7u+cvLb+DxCqnKuH0CQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@langchain/openai": ">=0.2.0 <0.6.0",
|
||||
"@langchain/weaviate": "^0.2.0",
|
||||
@@ -19988,10 +20069,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@librechat/agents/node_modules/@langchain/openai": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.15.tgz",
|
||||
"integrity": "sha512-ANadEHyAj5sufQpz+SOPpKbyoMcTLhnh8/d+afbSPUqWsIMPpEFX3HoSY3nrBPG6l4NQQNG5P5oHb4SdC8+YIg==",
|
||||
"version": "0.5.14",
|
||||
"resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.5.14.tgz",
|
||||
"integrity": "sha512-0GEj5K/qi1MRuZ4nE7NvyI4jTG+RSewLZqsExUwRukWdeqmkPNHGrogTa5ZDt7eaJxAaY7EgLC5ZnvCM3L1oug==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"js-tiktoken": "^1.0.12",
|
||||
"openai": "^5.3.0",
|
||||
@@ -20009,6 +20091,7 @@
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
@@ -20018,6 +20101,7 @@
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
@@ -20027,10 +20111,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@librechat/agents/node_modules/openai": {
|
||||
"version": "5.7.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-5.7.0.tgz",
|
||||
"integrity": "sha512-zXWawZl6J/P5Wz57/nKzVT3kJQZvogfuyuNVCdEp4/XU2UNrjL7SsuNpWAyLZbo6HVymwmnfno9toVzBhelygA==",
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-5.5.1.tgz",
|
||||
"integrity": "sha512-5i19097mGotHA1eFsM6Tjd/tJ8uo9sa5Ysv4Q6bKJ2vtN6rc0MzMrUefXnLXYAJcmMQrC1Efhj0AvfIkXrQamw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
@@ -20056,6 +20141,7 @@
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
@@ -20068,6 +20154,10 @@
|
||||
"resolved": "api",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@librechat/client": {
|
||||
"resolved": "packages/client",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@librechat/data-schemas": {
|
||||
"resolved": "packages/data-schemas",
|
||||
"link": true
|
||||
@@ -22693,49 +22783,43 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus": {
|
||||
"version": "3.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.17.1.tgz",
|
||||
"integrity": "sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==",
|
||||
"version": "3.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.5.tgz",
|
||||
"integrity": "sha512-JpFtXmWQ0Oca7FcvkqgjSyo6xEP7v3oQOLUId6o0xTvm4AD5W0mU2r3lYrbhsJ+XxdUUX4AVR5473sZZ85kU4A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/interactions": "^3.21.3",
|
||||
"@react-aria/utils": "^3.24.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/interactions": "^3.25.3",
|
||||
"@react-aria/utils": "^3.29.1",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/interactions": {
|
||||
"version": "3.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.21.3.tgz",
|
||||
"integrity": "sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==",
|
||||
"version": "3.25.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.3.tgz",
|
||||
"integrity": "sha512-J1bhlrNtjPS/fe5uJQ+0c7/jiXniwa4RQlP+Emjfc/iuqpW2RhbF9ou5vROcLzWIyaW8tVMZ468J68rAs/aZ5A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.4",
|
||||
"@react-aria/utils": "^3.24.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/ssr": "^3.9.9",
|
||||
"@react-aria/utils": "^3.29.1",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/ssr": {
|
||||
"version": "3.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz",
|
||||
"integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==",
|
||||
"version": "3.9.9",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.9.tgz",
|
||||
"integrity": "sha512-2P5thfjfPy/np18e5wD4WPt8ydNXhij1jwA8oehxZTFqlgVMGXzcWKxTb4RtJrLFsqPO7RUQTiY8QJk0M4Vy2g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
@@ -22744,32 +22828,25 @@
|
||||
"node": ">= 12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils": {
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.1.tgz",
|
||||
"integrity": "sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==",
|
||||
"version": "3.29.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.29.1.tgz",
|
||||
"integrity": "sha512-yXMFVJ73rbQ/yYE/49n5Uidjw7kh192WNN9PNQGV0Xoc7EJUlSOxqhnpHmYTyO0EotJ8fdM1fMH8durHjUSI8g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.4",
|
||||
"@react-stately/utils": "^3.10.1",
|
||||
"@react-types/shared": "^3.23.1",
|
||||
"@react-aria/ssr": "^3.9.9",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-stately/utils": "^3.10.7",
|
||||
"@react-types/shared": "^3.30.0",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils/node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
@@ -22879,25 +22956,34 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/flags": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
|
||||
"integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/utils": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz",
|
||||
"integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==",
|
||||
"version": "3.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.7.tgz",
|
||||
"integrity": "sha512-cWvjGAocvy4abO9zbr6PW6taHgF24Mwy/LbQ4TC4Aq3tKdKDntxyD+sh7AkSRfJRT2ccMVaHVv2+FfHThd3PKQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-types/shared": {
|
||||
"version": "3.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz",
|
||||
"integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==",
|
||||
"version": "3.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.30.0.tgz",
|
||||
"integrity": "sha512-COIazDAx1ncDg046cTJ8SFYsX8aS3lB/08LDnbkH/SkdYrFPWDlXMrO/sUam8j1WWM+PJ+4d1mj7tODIKNiFog==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@redis/bloom": {
|
||||
@@ -25146,12 +25232,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz",
|
||||
"integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==",
|
||||
"version": "0.5.17",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/match-sorter-utils": {
|
||||
@@ -25245,20 +25331,20 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.2.tgz",
|
||||
"integrity": "sha512-g78+DA29K0ByAfDkuibfLQqDshf8Aha/zcyEZ+huAX/yS/TWj/CUiEY4IJfDrFacdxIFmsLm0u4VtsLSKTngRw==",
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz",
|
||||
"integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.8.2"
|
||||
"@tanstack/virtual-core": "3.13.12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/table-core": {
|
||||
@@ -25274,9 +25360,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.8.2.tgz",
|
||||
"integrity": "sha512-ffpN6kTaPGwQPoWMcBAHbdv2ZCpj1SugldoYAcY0C4xH+Pej1KCOEUisNeEgbUnXOp8Y/4q6wGPu2tFHthOIQw==",
|
||||
"version": "3.13.12",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
||||
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -27908,14 +27994,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.1.tgz",
|
||||
"integrity": "sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==",
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"clsx": "1.2.1"
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://joebell.co.uk"
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
@@ -28071,9 +28159,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -31465,22 +31554,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.5.4",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.5.4.tgz",
|
||||
"integrity": "sha512-E+tb3/G6SO69POkdJT+3EpdMuhmtCh9EWuK4I1DnIC23L7tFPrl8vxP+LSovwaw6uUr73rUbpb4FgK011wbRJQ==",
|
||||
"version": "10.18.0",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz",
|
||||
"integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@emotion/is-prop-valid": "^0.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
@@ -32013,7 +32102,8 @@
|
||||
"node_modules/hamt_plus": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
|
||||
"integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/handlebars": {
|
||||
"version": "4.7.8",
|
||||
@@ -34441,6 +34531,28 @@
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.12.5",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.12.5.tgz",
|
||||
"integrity": "sha512-G8m32HW3lSmcz/4mbqx0hgJIQ0ekndKWiYP7kWVKi0p6saLXdSoye+FZiOFyonnd7Q482LCzm8sMDl7Ar1NWDw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">=17.0.0",
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
@@ -35801,9 +35913,11 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.394.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.394.0.tgz",
|
||||
"integrity": "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw==",
|
||||
"version": "0.263.1",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz",
|
||||
"integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
@@ -37482,6 +37596,21 @@
|
||||
"color-name": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
|
||||
"integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^11.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
|
||||
"integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mpath": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
|
||||
@@ -40879,6 +41008,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-resizable-panels": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz",
|
||||
"integrity": "sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.22.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz",
|
||||
@@ -40988,6 +41127,15 @@
|
||||
"react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-virtualized/node_modules/clsx": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
|
||||
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -41092,6 +41240,7 @@
|
||||
"version": "0.7.7",
|
||||
"resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
|
||||
"integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hamt_plus": "1.0.2"
|
||||
},
|
||||
@@ -44917,11 +45066,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
|
||||
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf8": {
|
||||
@@ -46534,7 +46684,7 @@
|
||||
},
|
||||
"packages/api": {
|
||||
"name": "@librechat/api",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.4",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
@@ -46566,14 +46716,13 @@
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@librechat/agents": "^2.4.46",
|
||||
"@librechat/agents": "^2.4.41",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.12.3",
|
||||
"axios": "^1.8.2",
|
||||
"diff": "^7.0.0",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^4.21.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"keyv": "^5.3.2",
|
||||
"librechat-data-provider": "*",
|
||||
"node-fetch": "2.7.0",
|
||||
@@ -46657,6 +46806,61 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@librechat/client",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@ariakit/react": "^0.4.17",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"react-resizable-panels": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"rollup": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@radix-ui/react-separator": "^1.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.0.0",
|
||||
"lucide-react": "^0.263.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"packages/client/node_modules/@rollup/plugin-typescript": {
|
||||
"version": "11.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz",
|
||||
"integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"resolve": "^1.22.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^2.14.0||^3.0.0||^4.0.0",
|
||||
"tslib": "*",
|
||||
"typescript": ">=3.7.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
},
|
||||
"tslib": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"packages/data-provider": {
|
||||
"name": "librechat-data-provider",
|
||||
"version": "0.7.88",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@librechat/api",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.4",
|
||||
"type": "commonjs",
|
||||
"description": "MCP services for LibreChat",
|
||||
"main": "dist/index.js",
|
||||
@@ -69,14 +69,13 @@
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@librechat/agents": "^2.4.46",
|
||||
"@librechat/agents": "^2.4.41",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.12.3",
|
||||
"axios": "^1.8.2",
|
||||
"diff": "^7.0.0",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^4.21.2",
|
||||
"js-yaml": "^4.1.0",
|
||||
"keyv": "^5.3.2",
|
||||
"librechat-data-provider": "*",
|
||||
"node-fetch": "2.7.0",
|
||||
|
||||
@@ -46,10 +46,7 @@ export async function createRun({
|
||||
customHandlers?: Record<GraphEvents, EventHandler>;
|
||||
}): Promise<Run<IState>> {
|
||||
const provider =
|
||||
(providerEndpointMap[
|
||||
agent.provider as keyof typeof providerEndpointMap
|
||||
] as unknown as Providers) ?? agent.provider;
|
||||
|
||||
providerEndpointMap[agent.provider as keyof typeof providerEndpointMap] ?? agent.provider;
|
||||
const llmConfig: t.RunLLMConfig = Object.assign(
|
||||
{
|
||||
provider,
|
||||
@@ -69,9 +66,7 @@ export async function createRun({
|
||||
}
|
||||
|
||||
let reasoningKey: 'reasoning_content' | 'reasoning' | undefined;
|
||||
if (provider === Providers.GOOGLE) {
|
||||
reasoningKey = 'reasoning';
|
||||
} else if (
|
||||
if (
|
||||
llmConfig.configuration?.baseURL?.includes(KnownEndpoints.openrouter) ||
|
||||
(agent.endpoint && agent.endpoint.toLowerCase().includes(KnownEndpoints.openrouter))
|
||||
) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './llm';
|
||||
@@ -1,2 +1 @@
|
||||
export * from './google';
|
||||
export * from './openai';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ErrorTypes, EModelEndpoint, mapModelToAzureConfig } from 'librechat-data-provider';
|
||||
import type {
|
||||
LLMConfigOptions,
|
||||
UserKeyValues,
|
||||
OpenAIOptionsResult,
|
||||
OpenAIConfigOptions,
|
||||
InitializeOpenAIOptionsParams,
|
||||
OpenAIOptionsResult,
|
||||
} from '~/types';
|
||||
import { createHandleLLMNewToken } from '~/utils/generators';
|
||||
import { getAzureCredentials } from '~/utils/azure';
|
||||
@@ -64,7 +64,7 @@ export const initializeOpenAI = async ({
|
||||
? userValues?.baseURL
|
||||
: baseURLOptions[endpoint as keyof typeof baseURLOptions];
|
||||
|
||||
const clientOptions: OpenAIConfigOptions = {
|
||||
const clientOptions: LLMConfigOptions = {
|
||||
proxy: PROXY ?? undefined,
|
||||
reverseProxyUrl: baseURL || undefined,
|
||||
streaming: true,
|
||||
@@ -135,7 +135,7 @@ export const initializeOpenAI = async ({
|
||||
user: req.user.id,
|
||||
};
|
||||
|
||||
const finalClientOptions: OpenAIConfigOptions = {
|
||||
const finalClientOptions: LLMConfigOptions = {
|
||||
...clientOptions,
|
||||
modelOptions,
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import { isEnabled } from '~/utils/common';
|
||||
*/
|
||||
export function getOpenAIConfig(
|
||||
apiKey: string,
|
||||
options: t.OpenAIConfigOptions = {},
|
||||
options: t.LLMConfigOptions = {},
|
||||
endpoint?: string | null,
|
||||
): t.LLMConfigResult {
|
||||
const {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
export * from './mcp/manager';
|
||||
export * from './mcp/oauth';
|
||||
export * from './mcp/auth';
|
||||
export * from './mcp/add';
|
||||
/* Utilities */
|
||||
export * from './mcp/utils';
|
||||
export * from './utils';
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { logger } from '@librechat/data-schemas';
|
||||
|
||||
interface CreateToolRequest extends Request {
|
||||
body: {
|
||||
name: string;
|
||||
description: string;
|
||||
type: 'function' | 'code_interpreter' | 'file_search';
|
||||
metadata?: Record<string, unknown>;
|
||||
};
|
||||
user?: {
|
||||
id: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new tool to the system
|
||||
* @route POST /agents/tools/add
|
||||
* @param {object} req.body - Request body containing tool data
|
||||
* @param {string} req.body.name - Tool name
|
||||
* @param {string} req.body.description - Tool description
|
||||
* @param {string} req.body.type - Tool type (function, code_interpreter, file_search)
|
||||
* @param {object} [req.body.metadata] - Optional metadata
|
||||
* @returns {object} Created tool object
|
||||
*/
|
||||
export const addTool = async (req: CreateToolRequest, res: Response) => {
|
||||
try {
|
||||
const { name, description, type, metadata } = req.body;
|
||||
|
||||
// Log the incoming request for development
|
||||
logger.info(
|
||||
'Add Tool Request:' +
|
||||
JSON.stringify({
|
||||
name,
|
||||
description,
|
||||
type,
|
||||
metadata,
|
||||
userId: req.user?.id,
|
||||
}),
|
||||
);
|
||||
|
||||
// Validate required fields
|
||||
if (!name || !description || !type) {
|
||||
return res.status(400).json({
|
||||
error: 'Missing required fields: name, description, and type are required',
|
||||
});
|
||||
}
|
||||
|
||||
// Validate tool type
|
||||
const validTypes = ['function', 'code_interpreter', 'file_search'];
|
||||
if (!validTypes.includes(type)) {
|
||||
return res.status(400).json({
|
||||
error: `Invalid tool type. Must be one of: ${validTypes.join(', ')}`,
|
||||
});
|
||||
}
|
||||
|
||||
// For now, return a mock successful response
|
||||
// TODO: Implement actual tool creation logic
|
||||
const mockTool = {
|
||||
id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
type,
|
||||
function: {
|
||||
name,
|
||||
description,
|
||||
},
|
||||
metadata: metadata || {},
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
logger.info('Tool created successfully:' + JSON.stringify(mockTool));
|
||||
|
||||
res.status(201).json(mockTool);
|
||||
} catch (error) {
|
||||
logger.error('Error adding tool:', error);
|
||||
res.status(500).json({
|
||||
error: 'Internal server error while adding tool',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -7,7 +7,6 @@ const RECOGNIZED_PROVIDERS = new Set([
|
||||
'xai',
|
||||
'deepseek',
|
||||
'ollama',
|
||||
'bedrock',
|
||||
]);
|
||||
const CONTENT_ARRAY_PROVIDERS = new Set(['google', 'anthropic', 'openai']);
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
import { AuthKeys, googleBaseSchema } from 'librechat-data-provider';
|
||||
|
||||
export type GoogleParameters = z.infer<typeof googleBaseSchema>;
|
||||
|
||||
export type GoogleCredentials = {
|
||||
[AuthKeys.GOOGLE_SERVICE_KEY]?: string;
|
||||
[AuthKeys.GOOGLE_API_KEY]?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configuration options for the getLLMConfig function
|
||||
*/
|
||||
export interface GoogleConfigOptions {
|
||||
modelOptions?: Partial<GoogleParameters>;
|
||||
reverseProxyUrl?: string;
|
||||
defaultQuery?: Record<string, string | undefined>;
|
||||
headers?: Record<string, string>;
|
||||
proxy?: string;
|
||||
streaming?: boolean;
|
||||
authHeader?: boolean;
|
||||
addParams?: Record<string, unknown>;
|
||||
dropParams?: string[];
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
export * from './azure';
|
||||
export * from './events';
|
||||
export * from './google';
|
||||
export * from './mistral';
|
||||
export * from './openai';
|
||||
export * from './run';
|
||||
|
||||
@@ -9,7 +9,7 @@ export type OpenAIParameters = z.infer<typeof openAISchema>;
|
||||
/**
|
||||
* Configuration options for the getLLMConfig function
|
||||
*/
|
||||
export interface OpenAIConfigOptions {
|
||||
export interface LLMConfigOptions {
|
||||
modelOptions?: Partial<OpenAIParameters>;
|
||||
reverseProxyUrl?: string;
|
||||
defaultQuery?: Record<string, string | undefined>;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { Providers } from '@librechat/agents';
|
||||
import type { AgentModelParameters } from 'librechat-data-provider';
|
||||
import type { AgentModelParameters, EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { OpenAIConfiguration } from './openai';
|
||||
|
||||
export type RunLLMConfig = {
|
||||
provider: Providers;
|
||||
provider: EModelEndpoint;
|
||||
streaming: boolean;
|
||||
streamUsage: boolean;
|
||||
usage?: boolean;
|
||||
|
||||
@@ -314,116 +314,4 @@ describe('resolveHeaders', () => {
|
||||
'Dot-Header': 'dot-value',
|
||||
});
|
||||
});
|
||||
|
||||
// Additional comprehensive tests for all user field placeholders
|
||||
it('should replace all allowed user field placeholders', () => {
|
||||
const user = {
|
||||
id: 'abc',
|
||||
name: 'Test User',
|
||||
username: 'testuser',
|
||||
email: 'me@example.com',
|
||||
provider: 'google',
|
||||
role: 'admin',
|
||||
googleId: 'gid',
|
||||
facebookId: 'fbid',
|
||||
openidId: 'oid',
|
||||
samlId: 'sid',
|
||||
ldapId: 'lid',
|
||||
githubId: 'ghid',
|
||||
discordId: 'dcid',
|
||||
appleId: 'aid',
|
||||
emailVerified: true,
|
||||
twoFactorEnabled: false,
|
||||
termsAccepted: true,
|
||||
};
|
||||
|
||||
const headers = {
|
||||
'X-User-ID': '{{LIBRECHAT_USER_ID}}',
|
||||
'X-User-Name': '{{LIBRECHAT_USER_NAME}}',
|
||||
'X-User-Username': '{{LIBRECHAT_USER_USERNAME}}',
|
||||
'X-User-Email': '{{LIBRECHAT_USER_EMAIL}}',
|
||||
'X-User-Provider': '{{LIBRECHAT_USER_PROVIDER}}',
|
||||
'X-User-Role': '{{LIBRECHAT_USER_ROLE}}',
|
||||
'X-User-GoogleId': '{{LIBRECHAT_USER_GOOGLEID}}',
|
||||
'X-User-FacebookId': '{{LIBRECHAT_USER_FACEBOOKID}}',
|
||||
'X-User-OpenIdId': '{{LIBRECHAT_USER_OPENIDID}}',
|
||||
'X-User-SamlId': '{{LIBRECHAT_USER_SAMLID}}',
|
||||
'X-User-LdapId': '{{LIBRECHAT_USER_LDAPID}}',
|
||||
'X-User-GithubId': '{{LIBRECHAT_USER_GITHUBID}}',
|
||||
'X-User-DiscordId': '{{LIBRECHAT_USER_DISCORDID}}',
|
||||
'X-User-AppleId': '{{LIBRECHAT_USER_APPLEID}}',
|
||||
'X-User-EmailVerified': '{{LIBRECHAT_USER_EMAILVERIFIED}}',
|
||||
'X-User-TwoFactorEnabled': '{{LIBRECHAT_USER_TWOFACTORENABLED}}',
|
||||
'X-User-TermsAccepted': '{{LIBRECHAT_USER_TERMSACCEPTED}}',
|
||||
};
|
||||
|
||||
const result = resolveHeaders(headers, user);
|
||||
|
||||
expect(result['X-User-ID']).toBe('abc');
|
||||
expect(result['X-User-Name']).toBe('Test User');
|
||||
expect(result['X-User-Username']).toBe('testuser');
|
||||
expect(result['X-User-Email']).toBe('me@example.com');
|
||||
expect(result['X-User-Provider']).toBe('google');
|
||||
expect(result['X-User-Role']).toBe('admin');
|
||||
expect(result['X-User-GoogleId']).toBe('gid');
|
||||
expect(result['X-User-FacebookId']).toBe('fbid');
|
||||
expect(result['X-User-OpenIdId']).toBe('oid');
|
||||
expect(result['X-User-SamlId']).toBe('sid');
|
||||
expect(result['X-User-LdapId']).toBe('lid');
|
||||
expect(result['X-User-GithubId']).toBe('ghid');
|
||||
expect(result['X-User-DiscordId']).toBe('dcid');
|
||||
expect(result['X-User-AppleId']).toBe('aid');
|
||||
expect(result['X-User-EmailVerified']).toBe('true');
|
||||
expect(result['X-User-TwoFactorEnabled']).toBe('false');
|
||||
expect(result['X-User-TermsAccepted']).toBe('true');
|
||||
});
|
||||
|
||||
it('should handle multiple placeholders in one value', () => {
|
||||
const user = { id: 'abc', email: 'me@example.com' };
|
||||
const headers = {
|
||||
'X-Multi': 'User: {{LIBRECHAT_USER_ID}}, Env: ${TEST_API_KEY}, Custom: {{MY_CUSTOM}}',
|
||||
};
|
||||
const customVars = { MY_CUSTOM: 'custom-value' };
|
||||
const result = resolveHeaders(headers, user, customVars);
|
||||
expect(result['X-Multi']).toBe('User: abc, Env: test-api-key-value, Custom: custom-value');
|
||||
});
|
||||
|
||||
it('should leave unknown placeholders unchanged', () => {
|
||||
const user = { id: 'abc' };
|
||||
const headers = {
|
||||
'X-Unknown': '{{SOMETHING_NOT_RECOGNIZED}}',
|
||||
'X-Known': '{{LIBRECHAT_USER_ID}}',
|
||||
};
|
||||
const result = resolveHeaders(headers, user);
|
||||
expect(result['X-Unknown']).toBe('{{SOMETHING_NOT_RECOGNIZED}}');
|
||||
expect(result['X-Known']).toBe('abc');
|
||||
});
|
||||
|
||||
it('should handle a mix of all types', () => {
|
||||
const user = {
|
||||
id: 'abc',
|
||||
email: 'me@example.com',
|
||||
emailVerified: true,
|
||||
twoFactorEnabled: false,
|
||||
};
|
||||
const headers = {
|
||||
'X-User': '{{LIBRECHAT_USER_ID}}',
|
||||
'X-Env': '${TEST_API_KEY}',
|
||||
'X-Custom': '{{MY_CUSTOM}}',
|
||||
'X-Multi': 'ID: {{LIBRECHAT_USER_ID}}, ENV: ${TEST_API_KEY}, CUSTOM: {{MY_CUSTOM}}',
|
||||
'X-Unknown': '{{NOT_A_REAL_PLACEHOLDER}}',
|
||||
'X-Empty': '',
|
||||
'X-Boolean': '{{LIBRECHAT_USER_EMAILVERIFIED}}',
|
||||
};
|
||||
const customVars = { MY_CUSTOM: 'custom-value' };
|
||||
const result = resolveHeaders(headers, user, customVars);
|
||||
|
||||
expect(result['X-User']).toBe('abc');
|
||||
expect(result['X-Env']).toBe('test-api-key-value');
|
||||
expect(result['X-Custom']).toBe('custom-value');
|
||||
expect(result['X-Multi']).toBe('ID: abc, ENV: test-api-key-value, CUSTOM: custom-value');
|
||||
expect(result['X-Unknown']).toBe('{{NOT_A_REAL_PLACEHOLDER}}');
|
||||
expect(result['X-Empty']).toBe('');
|
||||
expect(result['X-Boolean']).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,13 +14,3 @@ export function sendEvent(res: ServerResponse, event: ServerSentEvent): void {
|
||||
}
|
||||
res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends error data in Server Sent Events format and ends the response.
|
||||
* @param res - The server response.
|
||||
* @param message - The error message.
|
||||
*/
|
||||
export function handleError(res: ServerResponse, message: string): void {
|
||||
res.write(`event: error\ndata: ${JSON.stringify(message)}\n\n`);
|
||||
res.end();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,5 @@ export * from './events';
|
||||
export * from './files';
|
||||
export * from './generators';
|
||||
export * from './llm';
|
||||
export * from './math';
|
||||
export * from './openid';
|
||||
export * from './tempChatRetention';
|
||||
export { default as Tokenizer } from './tokenizer';
|
||||
export * from './yaml';
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import {
|
||||
MIN_RETENTION_HOURS,
|
||||
MAX_RETENTION_HOURS,
|
||||
DEFAULT_RETENTION_HOURS,
|
||||
getTempChatRetentionHours,
|
||||
createTempChatExpirationDate,
|
||||
} from './tempChatRetention';
|
||||
import type { TCustomConfig } from 'librechat-data-provider';
|
||||
|
||||
describe('tempChatRetention', () => {
|
||||
const originalEnv = process.env;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
delete process.env.TEMP_CHAT_RETENTION_HOURS;
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('getTempChatRetentionHours', () => {
|
||||
it('should return default retention hours when no config or env var is set', () => {
|
||||
const result = getTempChatRetentionHours();
|
||||
expect(result).toBe(DEFAULT_RETENTION_HOURS);
|
||||
});
|
||||
|
||||
it('should use environment variable when set', () => {
|
||||
process.env.TEMP_CHAT_RETENTION_HOURS = '48';
|
||||
const result = getTempChatRetentionHours();
|
||||
expect(result).toBe(48);
|
||||
});
|
||||
|
||||
it('should use config value when set', () => {
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 12,
|
||||
},
|
||||
};
|
||||
const result = getTempChatRetentionHours(config);
|
||||
expect(result).toBe(12);
|
||||
});
|
||||
|
||||
it('should prioritize config over environment variable', () => {
|
||||
process.env.TEMP_CHAT_RETENTION_HOURS = '48';
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 12,
|
||||
},
|
||||
};
|
||||
const result = getTempChatRetentionHours(config);
|
||||
expect(result).toBe(12);
|
||||
});
|
||||
|
||||
it('should enforce minimum retention period', () => {
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 0,
|
||||
},
|
||||
};
|
||||
const result = getTempChatRetentionHours(config);
|
||||
expect(result).toBe(MIN_RETENTION_HOURS);
|
||||
});
|
||||
|
||||
it('should enforce maximum retention period', () => {
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 10000,
|
||||
},
|
||||
};
|
||||
const result = getTempChatRetentionHours(config);
|
||||
expect(result).toBe(MAX_RETENTION_HOURS);
|
||||
});
|
||||
|
||||
it('should handle invalid environment variable', () => {
|
||||
process.env.TEMP_CHAT_RETENTION_HOURS = 'invalid';
|
||||
const result = getTempChatRetentionHours();
|
||||
expect(result).toBe(DEFAULT_RETENTION_HOURS);
|
||||
});
|
||||
|
||||
it('should handle invalid config value', () => {
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 'invalid' as unknown as number,
|
||||
},
|
||||
};
|
||||
const result = getTempChatRetentionHours(config);
|
||||
expect(result).toBe(DEFAULT_RETENTION_HOURS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTempChatExpirationDate', () => {
|
||||
it('should create expiration date with default retention period', () => {
|
||||
const result = createTempChatExpirationDate();
|
||||
|
||||
const expectedDate = new Date();
|
||||
expectedDate.setHours(expectedDate.getHours() + DEFAULT_RETENTION_HOURS);
|
||||
|
||||
// Allow for small time differences in test execution
|
||||
const timeDiff = Math.abs(result.getTime() - expectedDate.getTime());
|
||||
expect(timeDiff).toBeLessThan(1000); // Less than 1 second difference
|
||||
});
|
||||
|
||||
it('should create expiration date with custom retention period', () => {
|
||||
const config: Partial<TCustomConfig> = {
|
||||
interface: {
|
||||
temporaryChatRetention: 12,
|
||||
},
|
||||
};
|
||||
|
||||
const result = createTempChatExpirationDate(config);
|
||||
|
||||
const expectedDate = new Date();
|
||||
expectedDate.setHours(expectedDate.getHours() + 12);
|
||||
|
||||
// Allow for small time differences in test execution
|
||||
const timeDiff = Math.abs(result.getTime() - expectedDate.getTime());
|
||||
expect(timeDiff).toBeLessThan(1000); // Less than 1 second difference
|
||||
});
|
||||
|
||||
it('should return a Date object', () => {
|
||||
const result = createTempChatExpirationDate();
|
||||
expect(result).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should return a future date', () => {
|
||||
const now = new Date();
|
||||
const result = createTempChatExpirationDate();
|
||||
expect(result.getTime()).toBeGreaterThan(now.getTime());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,77 +0,0 @@
|
||||
import { logger } from '@librechat/data-schemas';
|
||||
import type { TCustomConfig } from 'librechat-data-provider';
|
||||
|
||||
/**
|
||||
* Default retention period for temporary chats in hours
|
||||
*/
|
||||
export const DEFAULT_RETENTION_HOURS = 24 * 30; // 30 days
|
||||
|
||||
/**
|
||||
* Minimum allowed retention period in hours
|
||||
*/
|
||||
export const MIN_RETENTION_HOURS = 1;
|
||||
|
||||
/**
|
||||
* Maximum allowed retention period in hours (1 year = 8760 hours)
|
||||
*/
|
||||
export const MAX_RETENTION_HOURS = 8760;
|
||||
|
||||
/**
|
||||
* Gets the temporary chat retention period from environment variables or config
|
||||
* @param config - The custom configuration object
|
||||
* @returns The retention period in hours
|
||||
*/
|
||||
export function getTempChatRetentionHours(config?: Partial<TCustomConfig> | null): number {
|
||||
let retentionHours = DEFAULT_RETENTION_HOURS;
|
||||
|
||||
// Check environment variable first
|
||||
if (process.env.TEMP_CHAT_RETENTION_HOURS) {
|
||||
const envValue = parseInt(process.env.TEMP_CHAT_RETENTION_HOURS, 10);
|
||||
if (!isNaN(envValue)) {
|
||||
retentionHours = envValue;
|
||||
} else {
|
||||
logger.warn(
|
||||
`Invalid TEMP_CHAT_RETENTION_HOURS environment variable: ${process.env.TEMP_CHAT_RETENTION_HOURS}. Using default: ${DEFAULT_RETENTION_HOURS} hours.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check config file (takes precedence over environment variable)
|
||||
if (config?.interface?.temporaryChatRetention !== undefined) {
|
||||
const configValue = config.interface.temporaryChatRetention;
|
||||
if (typeof configValue === 'number' && !isNaN(configValue)) {
|
||||
retentionHours = configValue;
|
||||
} else {
|
||||
logger.warn(
|
||||
`Invalid temporaryChatRetention in config: ${configValue}. Using ${retentionHours} hours.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the retention period
|
||||
if (retentionHours < MIN_RETENTION_HOURS) {
|
||||
logger.warn(
|
||||
`Temporary chat retention period ${retentionHours} is below minimum ${MIN_RETENTION_HOURS} hours. Using minimum value.`,
|
||||
);
|
||||
retentionHours = MIN_RETENTION_HOURS;
|
||||
} else if (retentionHours > MAX_RETENTION_HOURS) {
|
||||
logger.warn(
|
||||
`Temporary chat retention period ${retentionHours} exceeds maximum ${MAX_RETENTION_HOURS} hours. Using maximum value.`,
|
||||
);
|
||||
retentionHours = MAX_RETENTION_HOURS;
|
||||
}
|
||||
|
||||
return retentionHours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an expiration date for temporary chats
|
||||
* @param config - The custom configuration object
|
||||
* @returns The expiration date
|
||||
*/
|
||||
export function createTempChatExpirationDate(config?: Partial<TCustomConfig>): Date {
|
||||
const retentionHours = getTempChatRetentionHours(config);
|
||||
const expiredAt = new Date();
|
||||
expiredAt.setHours(expiredAt.getHours() + retentionHours);
|
||||
return expiredAt;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
export function loadYaml(filepath: string) {
|
||||
try {
|
||||
const fileContents = fs.readFileSync(filepath, 'utf8');
|
||||
return yaml.load(fileContents);
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
44
packages/client/package.json
Normal file
44
packages/client/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@librechat/client",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"module": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"dev": "rollup -c rollup.config.mjs -w",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@radix-ui/react-separator": "^1.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"framer-motion": "^10.0.0",
|
||||
"lucide-react": "^0.263.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"rollup": "^4.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ariakit/react": "^0.4.17",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"react-resizable-panels": "^3.0.3"
|
||||
}
|
||||
}
|
||||
40
packages/client/rollup.config.mjs
Normal file
40
packages/client/rollup.config.mjs
Normal file
@@ -0,0 +1,40 @@
|
||||
// ESM bundler config for React components
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import alias from '@rollup/plugin-alias';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, resolve as pathResolve } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export default {
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'es',
|
||||
preserveModules: true,
|
||||
},
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react/jsx-runtime',
|
||||
'@radix-ui/react-separator',
|
||||
'@radix-ui/react-slot',
|
||||
'class-variance-authority',
|
||||
'clsx',
|
||||
'framer-motion',
|
||||
'lucide-react',
|
||||
'tailwind-merge',
|
||||
],
|
||||
plugins: [
|
||||
alias({
|
||||
entries: [{ find: '~', replacement: pathResolve(__dirname, 'src') }],
|
||||
}),
|
||||
resolve(),
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
jsx: 'react-jsx',
|
||||
}),
|
||||
],
|
||||
};
|
||||
6
packages/client/src/common/a11y.ts
Normal file
6
packages/client/src/common/a11y.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface AnnounceOptions {
|
||||
message: string;
|
||||
isStatus?: boolean;
|
||||
}
|
||||
|
||||
export const MESSAGE_UPDATE_INTERVAL = 7000;
|
||||
33
packages/client/src/common/agents-types.ts
Normal file
33
packages/client/src/common/agents-types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { AgentCapabilities, ArtifactModes } from 'librechat-data-provider';
|
||||
import type { Agent, AgentProvider, AgentModelParameters } from 'librechat-data-provider';
|
||||
import type { OptionWithIcon, ExtendedFile } from './types';
|
||||
|
||||
export type TAgentOption = OptionWithIcon &
|
||||
Agent & {
|
||||
knowledge_files?: Array<[string, ExtendedFile]>;
|
||||
context_files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
};
|
||||
|
||||
export type TAgentCapabilities = {
|
||||
[AgentCapabilities.web_search]: boolean;
|
||||
[AgentCapabilities.file_search]: boolean;
|
||||
[AgentCapabilities.execute_code]: boolean;
|
||||
[AgentCapabilities.end_after_tools]?: boolean;
|
||||
[AgentCapabilities.hide_sequential_outputs]?: boolean;
|
||||
};
|
||||
|
||||
export type AgentForm = {
|
||||
agent?: TAgentOption;
|
||||
id: string;
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
instructions: string | null;
|
||||
model: string | null;
|
||||
model_parameters: AgentModelParameters;
|
||||
tools?: string[];
|
||||
provider?: AgentProvider | OptionWithIcon;
|
||||
agent_ids?: string[];
|
||||
[AgentCapabilities.artifacts]?: ArtifactModes | string;
|
||||
recursion_limit?: number;
|
||||
} & TAgentCapabilities;
|
||||
27
packages/client/src/common/artifacts.ts
Normal file
27
packages/client/src/common/artifacts.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export interface CodeBlock {
|
||||
id: string;
|
||||
language: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface Artifact {
|
||||
id: string;
|
||||
lastUpdateTime: number;
|
||||
index?: number;
|
||||
messageId?: string;
|
||||
identifier?: string;
|
||||
language?: string;
|
||||
content?: string;
|
||||
title?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export type ArtifactFiles =
|
||||
| {
|
||||
'App.tsx': string;
|
||||
'index.tsx': string;
|
||||
'/components/ui/MermaidDiagram.tsx': string;
|
||||
}
|
||||
| Partial<{
|
||||
[x: string]: string | undefined;
|
||||
}>;
|
||||
31
packages/client/src/common/assistants-types.ts
Normal file
31
packages/client/src/common/assistants-types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Capabilities, EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { Assistant, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { Option, ExtendedFile } from './types';
|
||||
|
||||
export type ActionsEndpoint = AssistantsEndpoint | EModelEndpoint.agents;
|
||||
|
||||
export type TAssistantOption =
|
||||
| string
|
||||
| (Option &
|
||||
Assistant & {
|
||||
files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
});
|
||||
|
||||
export type Actions = {
|
||||
[Capabilities.code_interpreter]: boolean;
|
||||
[Capabilities.image_vision]: boolean;
|
||||
[Capabilities.retrieval]: boolean;
|
||||
};
|
||||
|
||||
export type AssistantForm = {
|
||||
assistant: TAssistantOption;
|
||||
id: string;
|
||||
name: string | null;
|
||||
description: string | null;
|
||||
instructions: string | null;
|
||||
conversation_starters: string[];
|
||||
model: string;
|
||||
functions: string[];
|
||||
append_current_datetime: boolean;
|
||||
} & Actions;
|
||||
10
packages/client/src/common/index.ts
Normal file
10
packages/client/src/common/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export {
|
||||
TShowToast,
|
||||
Option,
|
||||
OptionWithIcon,
|
||||
DropdownValueSetter,
|
||||
MentionOption,
|
||||
NotificationSeverity,
|
||||
} from './types';
|
||||
|
||||
export { MenuItemProps } from './menus';
|
||||
26
packages/client/src/common/mcp.ts
Normal file
26
packages/client/src/common/mcp.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
AuthorizationTypeEnum,
|
||||
AuthTypeEnum,
|
||||
TokenExchangeMethodEnum,
|
||||
} from 'librechat-data-provider';
|
||||
import { MCPForm } from '~/common/types';
|
||||
|
||||
export const defaultMCPFormValues: MCPForm = {
|
||||
type: AuthTypeEnum.None,
|
||||
saved_auth_fields: false,
|
||||
api_key: '',
|
||||
authorization_type: AuthorizationTypeEnum.Basic,
|
||||
custom_auth_header: '',
|
||||
oauth_client_id: '',
|
||||
oauth_client_secret: '',
|
||||
authorization_url: '',
|
||||
client_url: '',
|
||||
scope: '',
|
||||
token_exchange_method: TokenExchangeMethodEnum.DefaultPost,
|
||||
name: '',
|
||||
description: '',
|
||||
url: '',
|
||||
tools: [],
|
||||
icon: '',
|
||||
trust: false,
|
||||
};
|
||||
24
packages/client/src/common/menus.ts
Normal file
24
packages/client/src/common/menus.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
export type RenderProp<
|
||||
P = React.HTMLAttributes<any> & {
|
||||
ref?: React.Ref<any>;
|
||||
},
|
||||
> = (props: P) => React.ReactNode;
|
||||
|
||||
export interface MenuItemProps {
|
||||
id?: string;
|
||||
label?: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement | HTMLDivElement>) => void;
|
||||
icon?: React.ReactNode;
|
||||
kbd?: string;
|
||||
show?: boolean;
|
||||
disabled?: boolean;
|
||||
separate?: boolean;
|
||||
hideOnClick?: boolean;
|
||||
dialog?: React.ReactElement;
|
||||
ref?: React.Ref<any>;
|
||||
render?:
|
||||
| RenderProp<React.HTMLAttributes<any> & { ref?: React.Ref<any> | undefined }>
|
||||
| React.ReactElement<any, string | React.JSXElementConstructor<any>>
|
||||
| undefined;
|
||||
}
|
||||
23
packages/client/src/common/selector.ts
Normal file
23
packages/client/src/common/selector.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import { TModelSpec, TStartupConfig } from 'librechat-data-provider';
|
||||
|
||||
export interface Endpoint {
|
||||
value: string;
|
||||
label: string;
|
||||
hasModels: boolean;
|
||||
models?: Array<{ name: string; isGlobal?: boolean }>;
|
||||
icon: React.ReactNode;
|
||||
agentNames?: Record<string, string>;
|
||||
assistantNames?: Record<string, string>;
|
||||
modelIcons?: Record<string, string | undefined>;
|
||||
}
|
||||
|
||||
export interface SelectedValues {
|
||||
endpoint: string | null;
|
||||
model: string | null;
|
||||
modelSpec: string | null;
|
||||
}
|
||||
|
||||
export interface ModelSelectorProps {
|
||||
startupConfig: TStartupConfig | undefined;
|
||||
}
|
||||
6
packages/client/src/common/tools.ts
Normal file
6
packages/client/src/common/tools.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { AuthType } from 'librechat-data-provider';
|
||||
|
||||
export type ApiKeyFormData = {
|
||||
apiKey: string;
|
||||
authType?: string | AuthType;
|
||||
};
|
||||
624
packages/client/src/common/types.ts
Normal file
624
packages/client/src/common/types.ts
Normal file
@@ -0,0 +1,624 @@
|
||||
import { RefObject } from 'react';
|
||||
import { FileSources, EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type * as InputNumberPrimitive from 'rc-input-number';
|
||||
import type { PrimitiveAtom, WritableAtom } from 'jotai';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import type { TranslationKeys } from '~/hooks';
|
||||
|
||||
export type CodeBarProps = {
|
||||
lang: string;
|
||||
error?: boolean;
|
||||
plugin?: boolean;
|
||||
blockIndex?: number;
|
||||
allowExecution?: boolean;
|
||||
codeRef: RefObject<HTMLElement>;
|
||||
};
|
||||
|
||||
export enum PromptsEditorMode {
|
||||
SIMPLE = 'simple',
|
||||
ADVANCED = 'advanced',
|
||||
}
|
||||
|
||||
export enum STTEndpoints {
|
||||
browser = 'browser',
|
||||
external = 'external',
|
||||
}
|
||||
|
||||
export enum TTSEndpoints {
|
||||
browser = 'browser',
|
||||
external = 'external',
|
||||
}
|
||||
|
||||
export type AudioChunk = {
|
||||
audio: string;
|
||||
isFinal: boolean;
|
||||
alignment: {
|
||||
char_start_times_ms: number[];
|
||||
chars_durations_ms: number[];
|
||||
chars: string[];
|
||||
};
|
||||
normalizedAlignment: {
|
||||
char_start_times_ms: number[];
|
||||
chars_durations_ms: number[];
|
||||
chars: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type BadgeItem = {
|
||||
id: string;
|
||||
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
||||
label: string;
|
||||
atom: PrimitiveAtom<boolean> | WritableAtom<boolean, [boolean], void>;
|
||||
isAvailable: boolean;
|
||||
};
|
||||
|
||||
export type AssistantListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: t.Assistant['metadata'];
|
||||
model: string;
|
||||
};
|
||||
|
||||
export type AgentListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: t.Agent['avatar'];
|
||||
};
|
||||
|
||||
export type TPluginMap = Record<string, t.TPlugin>;
|
||||
|
||||
export type GenericSetter<T> = (value: T | ((currentValue: T) => T)) => void;
|
||||
|
||||
export type LastSelectedModels = Record<t.EModelEndpoint, string>;
|
||||
|
||||
export type LocalizeFunction = (
|
||||
phraseKey: TranslationKeys,
|
||||
options?: Record<string, string | number>,
|
||||
) => string;
|
||||
|
||||
export type ChatFormValues = { text: string };
|
||||
|
||||
export const mainTextareaId = 'prompt-textarea';
|
||||
export const globalAudioId = 'global-audio';
|
||||
|
||||
export enum IconContext {
|
||||
landing = 'landing',
|
||||
menuItem = 'menu-item',
|
||||
nav = 'nav',
|
||||
message = 'message',
|
||||
}
|
||||
|
||||
export type IconMapProps = {
|
||||
className?: string;
|
||||
iconURL?: string;
|
||||
context?: 'landing' | 'menu-item' | 'nav' | 'message';
|
||||
endpoint?: string | null;
|
||||
endpointType?: string;
|
||||
assistantName?: string;
|
||||
agentName?: string;
|
||||
avatar?: string;
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export type IconComponent = React.ComponentType<IconMapProps>;
|
||||
export type AgentIconComponent = React.ComponentType<AgentIconMapProps>;
|
||||
export type IconComponentTypes = IconComponent | AgentIconComponent;
|
||||
export type IconsRecord = {
|
||||
[key in t.EModelEndpoint | 'unknown' | string]: IconComponentTypes | null | undefined;
|
||||
};
|
||||
|
||||
export type AgentIconMapProps = IconMapProps & { agentName?: string };
|
||||
|
||||
export type NavLink = {
|
||||
title: TranslationKeys;
|
||||
label?: string;
|
||||
icon: LucideIcon | React.FC;
|
||||
Component?: React.ComponentType;
|
||||
onClick?: (e?: React.MouseEvent) => void;
|
||||
variant?: 'default' | 'ghost';
|
||||
id: string;
|
||||
};
|
||||
|
||||
export interface NavProps {
|
||||
isCollapsed: boolean;
|
||||
links: NavLink[];
|
||||
resize?: (size: number) => void;
|
||||
defaultActive?: string;
|
||||
}
|
||||
|
||||
export interface DataColumnMeta {
|
||||
meta:
|
||||
| {
|
||||
size: number | string;
|
||||
}
|
||||
| undefined;
|
||||
}
|
||||
|
||||
export enum Panel {
|
||||
advanced = 'advanced',
|
||||
builder = 'builder',
|
||||
actions = 'actions',
|
||||
model = 'model',
|
||||
version = 'version',
|
||||
mcp = 'mcp',
|
||||
}
|
||||
|
||||
export type FileSetter =
|
||||
| GenericSetter<Map<string, ExtendedFile>>
|
||||
| React.Dispatch<React.SetStateAction<Map<string, ExtendedFile>>>;
|
||||
|
||||
export type ActionAuthForm = {
|
||||
/* General */
|
||||
type: t.AuthTypeEnum;
|
||||
saved_auth_fields: boolean;
|
||||
/* API key */
|
||||
api_key: string; // not nested
|
||||
authorization_type: t.AuthorizationTypeEnum;
|
||||
custom_auth_header: string;
|
||||
/* OAuth */
|
||||
oauth_client_id: string; // not nested
|
||||
oauth_client_secret: string; // not nested
|
||||
authorization_url: string;
|
||||
client_url: string;
|
||||
scope: string;
|
||||
token_exchange_method: t.TokenExchangeMethodEnum;
|
||||
};
|
||||
|
||||
export type MCPForm = ActionAuthForm & {
|
||||
name?: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
tools?: string[];
|
||||
icon?: string;
|
||||
trust?: boolean;
|
||||
};
|
||||
|
||||
export type ActionWithNullableMetadata = Omit<t.Action, 'metadata'> & {
|
||||
metadata: t.ActionMetadata | null;
|
||||
};
|
||||
|
||||
export type AssistantPanelProps = {
|
||||
index?: number;
|
||||
action?: ActionWithNullableMetadata;
|
||||
actions?: t.Action[];
|
||||
assistant_id?: string;
|
||||
activePanel?: string;
|
||||
endpoint: t.AssistantsEndpoint;
|
||||
version: number | string;
|
||||
documentsMap: Map<string, t.AssistantDocument> | null;
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
setCurrentAssistantId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
};
|
||||
|
||||
export type AgentPanelProps = {
|
||||
index?: number;
|
||||
agent_id?: string;
|
||||
activePanel?: string;
|
||||
mcp?: t.MCP;
|
||||
mcps?: t.MCP[];
|
||||
action?: t.Action;
|
||||
actions?: t.Action[];
|
||||
createMutation: UseMutationResult<t.Agent, Error, t.AgentCreateParams>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
endpointsConfig?: t.TEndpointsConfig;
|
||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
agentsConfig?: t.TAgentsEndpoint | null;
|
||||
};
|
||||
|
||||
export type AgentPanelContextType = {
|
||||
action?: t.Action;
|
||||
actions?: t.Action[];
|
||||
setAction: React.Dispatch<React.SetStateAction<t.Action | undefined>>;
|
||||
mcp?: t.MCP;
|
||||
mcps?: t.MCP[];
|
||||
setMcp: React.Dispatch<React.SetStateAction<t.MCP | undefined>>;
|
||||
setMcps: React.Dispatch<React.SetStateAction<t.MCP[] | undefined>>;
|
||||
groupedTools: Record<string, t.AgentToolType & { tools?: t.AgentToolType[] }>;
|
||||
tools: t.AgentToolType[];
|
||||
activePanel?: string;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
agent_id?: string;
|
||||
};
|
||||
|
||||
export type AgentModelPanelProps = {
|
||||
agent_id?: string;
|
||||
providers: Option[];
|
||||
models: Record<string, string[] | undefined>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
};
|
||||
|
||||
export type AugmentedColumnDef<TData, TValue> = ColumnDef<TData, TValue> & DataColumnMeta;
|
||||
|
||||
export type TSetOption = t.TSetOption;
|
||||
|
||||
export type TSetExample = (
|
||||
i: number,
|
||||
type: string,
|
||||
newValue: number | string | boolean | null,
|
||||
) => void;
|
||||
|
||||
export type OnInputNumberChange = InputNumberPrimitive.InputNumberProps['onChange'];
|
||||
|
||||
export const defaultDebouncedDelay = 450;
|
||||
|
||||
export enum ESide {
|
||||
Top = 'top',
|
||||
Right = 'right',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
}
|
||||
|
||||
export enum NotificationSeverity {
|
||||
INFO = 'info',
|
||||
SUCCESS = 'success',
|
||||
WARNING = 'warning',
|
||||
ERROR = 'error',
|
||||
}
|
||||
|
||||
export type TShowToast = {
|
||||
message: string;
|
||||
severity?: NotificationSeverity;
|
||||
showIcon?: boolean;
|
||||
duration?: number;
|
||||
status?: 'error' | 'success' | 'warning' | 'info';
|
||||
};
|
||||
|
||||
export type TBaseSettingsProps = {
|
||||
conversation: t.TConversation | t.TPreset | null;
|
||||
className?: string;
|
||||
isPreset?: boolean;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export type TSettingsProps = TBaseSettingsProps & {
|
||||
setOption: TSetOption;
|
||||
};
|
||||
|
||||
export type TModels = {
|
||||
models: string[];
|
||||
showAbove?: boolean;
|
||||
popover?: boolean;
|
||||
};
|
||||
|
||||
export type TModelSelectProps = TSettingsProps & TModels;
|
||||
|
||||
export type TEditPresetProps = {
|
||||
open: boolean;
|
||||
onOpenChange: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
preset: t.TPreset;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type TSetOptions = (options: Record<string, unknown>) => void;
|
||||
export type TSetOptionsPayload = {
|
||||
setOption: TSetOption;
|
||||
setExample: TSetExample;
|
||||
addExample: () => void;
|
||||
removeExample: () => void;
|
||||
setAgentOption: TSetOption;
|
||||
// getConversation: () => t.TConversation | t.TPreset | null;
|
||||
checkPluginSelection: (value: string) => boolean;
|
||||
setTools: (newValue: string, remove?: boolean) => void;
|
||||
setOptions?: TSetOptions;
|
||||
};
|
||||
|
||||
export type TPresetItemProps = {
|
||||
preset: t.TPreset;
|
||||
value: t.TPreset;
|
||||
onSelect: (preset: t.TPreset) => void;
|
||||
onChangePreset: (preset: t.TPreset) => void;
|
||||
onDeletePreset: (preset: t.TPreset) => void;
|
||||
};
|
||||
|
||||
export type TOnClick = (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
|
||||
export type TGenButtonProps = {
|
||||
onClick: TOnClick;
|
||||
};
|
||||
|
||||
export type TAskProps = {
|
||||
text: string;
|
||||
overrideConvoId?: string;
|
||||
overrideUserMessageId?: string;
|
||||
parentMessageId?: string | null;
|
||||
conversationId?: string | null;
|
||||
messageId?: string | null;
|
||||
clientTimestamp?: string;
|
||||
};
|
||||
|
||||
export type TOptions = {
|
||||
editedMessageId?: string | null;
|
||||
editedText?: string | null;
|
||||
isRegenerate?: boolean;
|
||||
isContinued?: boolean;
|
||||
isEdited?: boolean;
|
||||
overrideMessages?: t.TMessage[];
|
||||
/** This value is only true when the user submits a message with "Save & Submit" for a user-created message */
|
||||
isResubmission?: boolean;
|
||||
/** Currently only utilized when `isResubmission === true`, uses that message's currently attached files */
|
||||
overrideFiles?: t.TMessage['files'];
|
||||
};
|
||||
|
||||
export type TAskFunction = (props: TAskProps, options?: TOptions) => void;
|
||||
|
||||
export type TMessageProps = {
|
||||
conversation?: t.TConversation | null;
|
||||
messageId?: string | null;
|
||||
message?: t.TMessage;
|
||||
messagesTree?: t.TMessage[];
|
||||
currentEditId: string | number | null;
|
||||
isSearchView?: boolean;
|
||||
siblingIdx?: number;
|
||||
siblingCount?: number;
|
||||
setCurrentEditId?: React.Dispatch<React.SetStateAction<string | number | null>> | null;
|
||||
setSiblingIdx?: ((value: number) => void | React.Dispatch<React.SetStateAction<number>>) | null;
|
||||
};
|
||||
|
||||
export type TMessageIcon = { endpoint?: string | null; isCreatedByUser?: boolean } & Pick<
|
||||
t.TConversation,
|
||||
'modelLabel'
|
||||
> &
|
||||
Pick<t.TMessage, 'model' | 'iconURL'>;
|
||||
|
||||
export type TInitialProps = {
|
||||
text: string;
|
||||
edit: boolean;
|
||||
error: boolean;
|
||||
unfinished: boolean;
|
||||
isSubmitting: boolean;
|
||||
isLast: boolean;
|
||||
};
|
||||
export type TAdditionalProps = {
|
||||
ask: TAskFunction;
|
||||
message: t.TMessage;
|
||||
isCreatedByUser: boolean;
|
||||
siblingIdx: number;
|
||||
enterEdit: (cancel: boolean) => void;
|
||||
setSiblingIdx: (value: number) => void;
|
||||
};
|
||||
|
||||
export type TMessageContentProps = TInitialProps & TAdditionalProps;
|
||||
|
||||
export type TText = Pick<TInitialProps, 'text'> & { className?: string };
|
||||
export type TEditProps = Pick<TInitialProps, 'isSubmitting'> &
|
||||
Omit<TAdditionalProps, 'isCreatedByUser' | 'siblingIdx'> & {
|
||||
text?: string;
|
||||
index?: number;
|
||||
siblingIdx: number | null;
|
||||
};
|
||||
export type TDisplayProps = TText &
|
||||
Pick<TAdditionalProps, 'isCreatedByUser' | 'message'> & {
|
||||
showCursor?: boolean;
|
||||
};
|
||||
|
||||
export type TConfigProps = {
|
||||
userKey: string;
|
||||
setUserKey: React.Dispatch<React.SetStateAction<string>>;
|
||||
endpoint: t.EModelEndpoint | string;
|
||||
};
|
||||
|
||||
export type TDangerButtonProps = {
|
||||
id: string;
|
||||
confirmClear: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
showText?: boolean;
|
||||
mutation?: UseMutationResult<unknown>;
|
||||
onClick: () => void;
|
||||
infoTextCode: TranslationKeys;
|
||||
actionTextCode: TranslationKeys;
|
||||
dataTestIdInitial: string;
|
||||
dataTestIdConfirm: string;
|
||||
infoDescriptionCode?: TranslationKeys;
|
||||
confirmActionTextCode?: TranslationKeys;
|
||||
};
|
||||
|
||||
export type TDialogProps = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type TPluginStoreDialogProps = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type TResError = {
|
||||
response: { data: { message: string } };
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type TAuthContext = {
|
||||
user: t.TUser | undefined;
|
||||
token: string | undefined;
|
||||
isAuthenticated: boolean;
|
||||
error: string | undefined;
|
||||
login: (data: t.TLoginUser) => void;
|
||||
logout: (redirect?: string) => void;
|
||||
setError: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
roles?: Record<string, t.TRole | null | undefined>;
|
||||
};
|
||||
|
||||
export type TUserContext = {
|
||||
user?: t.TUser | undefined;
|
||||
token: string | undefined;
|
||||
isAuthenticated: boolean;
|
||||
redirect?: string;
|
||||
};
|
||||
|
||||
export type TAuthConfig = {
|
||||
loginRedirect: string;
|
||||
test?: boolean;
|
||||
};
|
||||
|
||||
export type IconProps = Pick<t.TMessage, 'isCreatedByUser' | 'model'> &
|
||||
Pick<t.TConversation, 'chatGptLabel' | 'modelLabel'> & {
|
||||
size?: number;
|
||||
button?: boolean;
|
||||
iconURL?: string;
|
||||
message?: boolean;
|
||||
className?: string;
|
||||
iconClassName?: string;
|
||||
endpoint?: t.EModelEndpoint | string | null;
|
||||
endpointType?: t.EModelEndpoint | null;
|
||||
assistantName?: string;
|
||||
agentName?: string;
|
||||
error?: boolean;
|
||||
};
|
||||
|
||||
export type Option = Record<string, unknown> & {
|
||||
label?: string;
|
||||
value: string | number | null;
|
||||
};
|
||||
|
||||
export type StringOption = Option & { value: string | null };
|
||||
|
||||
export type VoiceOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type TMessageAudio = {
|
||||
isLast?: boolean;
|
||||
index: number;
|
||||
messageId: string;
|
||||
content: string;
|
||||
className?: string;
|
||||
renderButton?: (props: {
|
||||
onClick: (e?: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
title: string;
|
||||
icon: React.ReactNode;
|
||||
isActive?: boolean;
|
||||
isVisible?: boolean;
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
}) => React.ReactNode;
|
||||
};
|
||||
|
||||
export type OptionWithIcon = Option & { icon?: React.ReactNode };
|
||||
export type DropdownValueSetter = (value: string | Option | OptionWithIcon) => void;
|
||||
export type MentionOption = OptionWithIcon & {
|
||||
type: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
};
|
||||
export type PromptOption = MentionOption & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type TOptionSettings = {
|
||||
showExamples?: boolean;
|
||||
isCodeChat?: boolean;
|
||||
};
|
||||
|
||||
export interface ExtendedFile {
|
||||
file?: File;
|
||||
file_id: string;
|
||||
temp_file_id?: string;
|
||||
type?: string;
|
||||
filepath?: string;
|
||||
filename?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
size: number;
|
||||
preview?: string;
|
||||
progress: number;
|
||||
source?: FileSources;
|
||||
attached?: boolean;
|
||||
embedded?: boolean;
|
||||
tool_resource?: string;
|
||||
metadata?: t.TFile['metadata'];
|
||||
}
|
||||
|
||||
export interface ModelItemProps {
|
||||
modelName: string;
|
||||
endpoint: EModelEndpoint;
|
||||
isSelected: boolean;
|
||||
onSelect: () => void;
|
||||
onNavigateBack: () => void;
|
||||
icon?: JSX.Element;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export type ContextType = {
|
||||
navVisible: boolean;
|
||||
setNavVisible: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
export interface SwitcherProps {
|
||||
endpoint?: t.EModelEndpoint | null;
|
||||
endpointKeyProvided: boolean;
|
||||
isCollapsed: boolean;
|
||||
}
|
||||
export type TLoginLayoutContext = {
|
||||
startupConfig: t.TStartupConfig | null;
|
||||
startupConfigError: unknown;
|
||||
isFetching: boolean;
|
||||
error: string | null;
|
||||
setError: React.Dispatch<React.SetStateAction<string | null>>;
|
||||
headerText: string;
|
||||
setHeaderText: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
export type NewConversationParams = {
|
||||
template?: Partial<t.TConversation>;
|
||||
preset?: Partial<t.TPreset>;
|
||||
modelsData?: t.TModelsConfig;
|
||||
buildDefault?: boolean;
|
||||
keepLatestMessage?: boolean;
|
||||
keepAddedConvos?: boolean;
|
||||
disableParams?: boolean;
|
||||
};
|
||||
|
||||
export type ConvoGenerator = (params: NewConversationParams) => void | t.TConversation;
|
||||
|
||||
export type TBaseResData = {
|
||||
plugin?: t.TResPlugin;
|
||||
final?: boolean;
|
||||
initial?: boolean;
|
||||
previousMessages?: t.TMessage[];
|
||||
conversation: t.TConversation;
|
||||
conversationId?: string;
|
||||
runMessages?: t.TMessage[];
|
||||
};
|
||||
|
||||
export type TResData = TBaseResData & {
|
||||
requestMessage: t.TMessage;
|
||||
responseMessage: t.TMessage;
|
||||
};
|
||||
|
||||
export type TFinalResData = Omit<TBaseResData, 'conversation'> & {
|
||||
conversation: Partial<t.TConversation> & Pick<t.TConversation, 'conversationId'>;
|
||||
requestMessage?: t.TMessage;
|
||||
responseMessage?: t.TMessage;
|
||||
};
|
||||
|
||||
export type TVectorStore = {
|
||||
_id: string;
|
||||
object: 'vector_store';
|
||||
created_at: string | Date;
|
||||
name: string;
|
||||
bytes?: number;
|
||||
file_counts?: {
|
||||
in_progress: number;
|
||||
completed: number;
|
||||
failed: number;
|
||||
cancelled: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type TThread = { id: string; createdAt: string };
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
google_tag_manager?: unknown;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
@@ -28,7 +27,7 @@ const AccordionTrigger = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="text-muted-foreground h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
||||
|
||||
import { cn } from '../../utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root;
|
||||
|
||||
79
packages/client/src/components/AnimatedSearchInput.tsx
Normal file
79
packages/client/src/components/AnimatedSearchInput.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
import { Search } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AnimatedSearchInput = ({
|
||||
value,
|
||||
onChange,
|
||||
isSearching: searching,
|
||||
placeholder,
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
isSearching?: boolean;
|
||||
placeholder: string;
|
||||
}) => {
|
||||
const isSearching = searching === true;
|
||||
const hasValue = value != null && value.length > 0;
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div className="relative rounded-lg transition-all duration-500 ease-in-out">
|
||||
<div className="relative">
|
||||
{/* Icon on the left */}
|
||||
<div className="absolute left-3 top-1/2 z-50 -translate-y-1/2">
|
||||
<Search
|
||||
className={cn(
|
||||
`h-4 w-4 transition-all duration-500 ease-in-out`,
|
||||
isSearching && hasValue ? 'text-blue-400' : 'text-gray-400',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Input field */}
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
className={`peer relative z-20 w-full rounded-lg bg-surface-secondary px-10 py-2 outline-none ring-0 backdrop-blur-sm transition-all duration-500 ease-in-out placeholder:text-gray-400 focus:outline-none focus:ring-0`}
|
||||
/>
|
||||
|
||||
{/* Gradient overlay */}
|
||||
<div
|
||||
className={`pointer-events-none absolute inset-0 z-20 rounded-lg bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-blue-500/20 transition-all duration-500 ease-in-out ${isSearching && hasValue ? 'opacity-100 blur-sm' : 'opacity-0 blur-none'} `}
|
||||
/>
|
||||
|
||||
{/* Animated loading indicator */}
|
||||
<div
|
||||
className={`absolute right-3 top-1/2 z-20 -translate-y-1/2 transition-all duration-500 ease-in-out ${isSearching && hasValue ? 'scale-100 opacity-100' : 'scale-0 opacity-0'} `}
|
||||
>
|
||||
<div className="relative h-2 w-2">
|
||||
<div className="absolute inset-0 animate-ping rounded-full bg-blue-500/60" />
|
||||
<div className="absolute inset-0 rounded-full bg-blue-500" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Outer glow effect */}
|
||||
<div
|
||||
className={`absolute -inset-8 -z-10 transition-all duration-700 ease-in-out ${isSearching && hasValue ? 'scale-105 opacity-100' : 'scale-100 opacity-0'} `}
|
||||
>
|
||||
<div className="absolute inset-0">
|
||||
<div
|
||||
className={`bg-gradient-radial absolute inset-0 from-blue-500/10 to-transparent transition-opacity duration-700 ease-in-out ${isSearching && hasValue ? 'animate-pulse-slow opacity-100' : 'opacity-0'} `}
|
||||
/>
|
||||
<div
|
||||
className={`absolute inset-0 bg-gradient-to-r from-purple-500/5 via-blue-500/5 to-purple-500/5 blur-xl transition-all duration-700 ease-in-out ${isSearching && hasValue ? 'animate-gradient-x opacity-100' : 'opacity-0'} `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`absolute inset-0 -z-20 scale-100 bg-gradient-to-r from-blue-500/10 via-purple-500/10 to-blue-500/10 opacity-0 blur-xl transition-all duration-500 ease-in-out peer-focus:scale-105 peer-focus:opacity-100`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnimatedSearchInput;
|
||||
@@ -26,7 +26,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.animated-tab[data-state="active"] {
|
||||
.animated-tab[data-state='active'] {
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type React from 'react';
|
||||
|
||||
import { X, Plus } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { ButtonHTMLAttributes } from 'react';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
interface BadgeProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
interface BadgeProps
|
||||
extends Omit<
|
||||
ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
'onAnimationStart' | 'onDragStart' | 'onDragEnd' | 'onDrag'
|
||||
> {
|
||||
icon?: LucideIcon;
|
||||
label: string;
|
||||
id?: string;
|
||||
@@ -71,7 +74,7 @@ export default function Badge({
|
||||
}}
|
||||
whileTap={{ scale: isDragging ? 1.1 : isDisabled ? 1 : 0.97 }}
|
||||
transition={{ type: 'tween', duration: 0.1, ease: 'easeOut' }}
|
||||
{...props}
|
||||
{...(props as React.ComponentProps<typeof motion.button>)}
|
||||
>
|
||||
{Icon && <Icon className={cn('relative h-5 w-5 md:h-4 md:w-4', !label && 'mx-auto')} />}
|
||||
<span className="relative hidden md:inline">{label}</span>
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { ChevronRight, MoreHorizontal } from 'lucide-react';
|
||||
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
@@ -17,7 +16,7 @@ const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWi
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5',
|
||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -44,7 +43,7 @@ const BreadcrumbLink = React.forwardRef<
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn('hover:text-foreground transition-colors', className)}
|
||||
className={cn('transition-colors hover:text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -58,7 +57,7 @@ const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWit
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn('text-foreground font-normal', className)}
|
||||
className={cn('font-normal text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user