Skip to content

Commit 1fe4145

Browse files
committed
Squashed commit of Jun 20, 2025:
commit 0103b4b Author: Danny Avila <[email protected]> Date: Fri Jun 13 18:17:25 2025 -0400 🧹 chore: Cleanup base64 Handling for Azure Mistral OCR (danny-avila#7892) * 🧹 chore: Remove Comments and Cleanup base64 handling for Azure Mistral OCR * chore: Remove unnecessary await from MCP instructions formatting in AgentClient * ci: Update document_url regex in MistralOCR tests to support PDF format commit 5eb0703 Author: richzw <[email protected]> Date: Sat Jun 14 05:51:02 2025 +0800 🌐 fix: Support `global` location for Google VertexAI (danny-avila#7768) * fix: Check if loc is 'global' and set the endpoint prefix accordingly * fix: ESLint error --------- Co-authored-by: Danny Avila <[email protected]> commit 4419e2c Author: Danny Avila <[email protected]> Date: Fri Jun 13 15:47:41 2025 -0400 ✨ feat: Agent Panel UI Enhancements (danny-avila#7800) * feat: add MCP Panel to Agent Builder - Add MCP server panel and configuration UI - Implement MCP input forms and tool lists - Add MCP icon and metadata support - Integrate MCP with agent configuration - Add localization support for MCP features - Refactor components for better reusability - Update types and add MCP-related mutations - Fix small issues with Actions and AgentSelect - Refactor AgentPanelSwitch and related components to use new AgentPanelContext to reduce prop drilling * chore: import order * chore: clean up import statements and unused var in ActionsPanel component * refactor: AgentPanelContext with actions query, remove unnecessary `actions` state - Added actions query using `useGetActionsQuery` to fetch actions based on the current agent ID. - Removed now unused `setActions` state and related logic from `AgentPanelContext` and `AgentPanelSwitch` components. - Updated `AgentPanelContextType` to reflect the removal of `setActions`. * chore: re-order import statements in AgentConfig component * chore: re-order import statements in ModelPanel component * chore: update ModelPanel props to consolidated props to avoid passing unnecessary props * chore: update import statements in Providers index file to include ToastProvider and AgentPanelContext exports * chore: clean up import statements in VersionPanel component * refactor: streamline AgentConfig and AgentPanel components - Consolidated props in AgentConfig to only include necessary fields. - Updated AgentPanel to remove unused state and props, enhancing clarity and maintainability. - Reorganized import statements for better structure and readability. * refactor: replace default agent form values with utility function - Updated AgentsProvider, AgentPanel, AgentSelect, and DeleteButton components to use getDefaultAgentFormValues utility function instead of directly importing defaultAgentFormValues. - Enhanced the initialization of agent forms by incorporating localStorage values for model and provider in the new utility function. * chore: comment out rendering MCPSection --------- Co-authored-by: Dustin Healy <[email protected]> commit 5f2d1c5 Author: Danny Avila <[email protected]> Date: Fri Jun 13 15:14:57 2025 -0400 👁️ feat: Azure Mistral OCR Strategy (danny-avila#7888) * 👁️ feat: Add Azure Mistral OCR strategy and endpoint integration This commit introduces a new OCR strategy named 'azure_mistral_ocr', allowing the use of a Mistral OCR endpoint deployed on Azure. The configuration, schemas, and file upload strategies have been updated to support this integration, enabling seamless OCR processing via Azure-hosted Mistral services. * 🗑️ chore: Clean up .gitignore by removing commented-out uncommon directory name * chore: remove unused vars * refactor: Move createAxiosInstance to packages/api/utils and update imports - Removed the createAxiosInstance function from the config module and relocated it to a new utils module for better organization. - Updated import paths in relevant files to reflect the new location of createAxiosInstance. - Added tests for createAxiosInstance to ensure proper functionality and proxy configuration handling. * chore: move axios helpers to packages/api - Added logAxiosError function to @librechat/api for centralized error logging. - Updated imports across various files to use the new logAxiosError function. - Removed the old axios.js utility file as it is no longer needed. * chore: Update Jest moduleNameMapper for improved path resolution - Added a new mapping for '~/' to resolve module paths in Jest configuration, enhancing import handling for the project. * feat: Implement Mistral OCR API integration in TS * chore: Update MistralOCR tests based on new imports * fix: Enhance MistralOCR configuration handling and tests - Introduced helper functions for resolving configuration values from environment variables or hardcoded settings. - Updated the uploadMistralOCR and uploadAzureMistralOCR functions to utilize the new configuration resolution logic. - Improved test cases to ensure correct behavior when mixing environment variables and hardcoded values. - Mocked file upload and signed URL responses in tests to validate functionality without external dependencies. * feat: Enhance MistralOCR functionality with improved configuration and error handling - Introduced helper functions for loading authentication configuration and resolving values from environment variables. - Updated uploadMistralOCR and uploadAzureMistralOCR functions to utilize the new configuration logic. - Added utility functions for processing OCR results and creating error messages. - Improved document type determination and result aggregation for better OCR processing. * refactor: Reorganize OCR type imports in Mistral CRUD file - Moved OCRResult, OCRResultPage, and OCRImage imports to a more logical grouping for better readability and maintainability. * feat: Add file exports to API and create files index * chore: Update OCR types for enhanced structure and clarity - Redesigned OCRImage interface to include mandatory fields and improved naming conventions. - Added PageDimensions interface for better representation of page metrics. - Updated OCRResultPage to include dimensions and mandatory images array. - Refined OCRResult to include document annotation and usage information. * refactor: use TS counterpart of uploadOCR methods * ci: Update MistralOCR tests to reflect new OCR result structure * chore: Bump version of @librechat/api to 1.2.3 in package.json and package-lock.json * chore: Update CONFIG_VERSION to 1.2.8 * chore: remove unused sendEvent function from config module (now imported from '@librechat/api') * chore: remove MistralOCR service files and tests (now in '@librechat/api') * ci: update logger import in ModelService tests to use @librechat/data-schemas --------- Co-authored-by: arthurolivierfortin <[email protected]> commit 46ff008 Author: Marco Beretta <[email protected]> Date: Thu Jun 12 23:34:04 2025 +0200 🤖 refactor: Improve Speech Settings Initialization (danny-avila#7869) * ✨ feat: Implement speech settings initialization and update settings handling * 🔧 fix: Ensure setters reference is included in useEffect dependencies for speech settings initialization * chore: Update setter reference in useSpeechSettingsInit for improved type safety --------- Co-authored-by: Danny Avila <[email protected]> commit 55f79bd Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu Jun 12 08:25:10 2025 -0400 🌍 i18n: Update translation.json with latest translations (danny-avila#7727) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> commit 1bd8745 Author: Danny Avila <[email protected]> Date: Wed Jun 11 22:12:50 2025 -0400 🔧 feat: Add Basic Token Exchange Method for Actions OAuth flow (danny-avila#7844) - Enhanced the OAuth callback and action creation processes to include the `token_exchange_method` parameter. - Updated the `TokenService` to handle different token exchange methods, allowing for either 'default_post' or 'basic_auth_header' approaches. - Improved the handling of access tokens and refresh tokens based on the specified exchange method. commit 6488873 Author: Samuel Path <[email protected]> Date: Wed Jun 11 20:27:27 2025 +0200 🔧 fix: Properly handle Token Expiry Defaults when Env Variable not set (danny-avila#7834) commit 13c7ceb Author: Danny Avila <[email protected]> Date: Wed Jun 11 14:17:48 2025 -0400 📋 fix: Agent Resource Deduplication & Sharing Duplicate False Positive (danny-avila#7835) * fix: `primeResources` to Prevent Duplicate Files Across Sources - Added multiple test cases to ensure that the `primeResources` function correctly handles duplicate files from OCR and attachments, including scenarios with shared files, files without IDs, and duplicates within attachments. - Implemented logic to categorize files into appropriate tool resources while preventing duplicates across different categories. - Enhanced error handling and ensured that unique files are returned in the final attachments array. * fix: Update ToolService to handle single OCR tool case (no loaded tool necessary) * refactor: Add skipVersioning option to updateAgent for isolated updates - for now, mainly concerns sharing/unsharing of agents * chore: Update translation for shared agent message in UI commit cdf42b3 Author: Danny Avila <[email protected]> Date: Tue Jun 10 22:20:41 2025 -0400 ✨ feat: Add Dynamic User Field Placeholder Support in MCP Variables (danny-avila#7825) * chore: linting in mcp.spec.ts * chore: linting in mcp.ts * feat(mcp): support dynamic user field placeholders in MCP environment variables - Added user object handling in MCP options, allowing for dynamic user field processing in environment variables, headers, and URLs. - Updated `processMCPEnv` to utilize user fields for more flexible configurations. * chore: update backend review workflow to include unit tests for @librechat/data-schemas commit c2a18f6 Author: Sebastien Bruel <[email protected]> Date: Wed Jun 11 11:12:13 2025 +0900 ⏱️ refactor: Retry `/api/convos/gen_title` every 1s for up to 20s (danny-avila#7807)
1 parent 2e16cdf commit 1fe4145

File tree

126 files changed

+14689
-2324
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+14689
-2324
lines changed

.github/workflows/unused-packages.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ jobs:
9898
cd client
9999
UNUSED=$(depcheck --json | jq -r '.dependencies | join("\n")' || echo "")
100100
UNUSED=$(comm -23 <(echo "$UNUSED" | sort) <(cat ../client_used_deps.txt ../client_used_code.txt | sort) || echo "")
101+
# Filter out false positives
102+
UNUSED=$(echo "$UNUSED" | grep -v "^micromark-extension-llm-math$" || echo "")
101103
echo "CLIENT_UNUSED<<EOF" >> $GITHUB_ENV
102104
echo "$UNUSED" >> $GITHUB_ENV
103105
echo "EOF" >> $GITHUB_ENV

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ bower_components/
5555
# AI
5656
.clineignore
5757
.cursor
58+
.aider*
5859

5960
# Floobits
6061
.floo

api/app/clients/tools/util/handleTools.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
const { mcpToolPattern } = require('@librechat/api');
2+
const { logger } = require('@librechat/data-schemas');
13
const { SerpAPI } = require('@langchain/community/tools/serpapi');
24
const { Calculator } = require('@langchain/community/tools/calculator');
35
const { EnvVar, createCodeExecutionTool, createSearchTool } = require('@librechat/agents');
46
const {
57
Tools,
6-
Constants,
78
EToolResources,
89
loadWebSearchAuth,
910
replaceSpecialVars,
1011
} = require('librechat-data-provider');
11-
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
1212
const {
1313
availableTools,
1414
manifestToolMap,
@@ -28,11 +28,10 @@ const {
2828
} = require('../');
2929
const { primeFiles: primeCodeFiles } = require('~/server/services/Files/Code/process');
3030
const { createFileSearchTool, primeFiles: primeSearchFiles } = require('./fileSearch');
31+
const { getUserPluginAuthValue } = require('~/server/services/PluginService');
3132
const { loadAuthValues } = require('~/server/services/Tools/credentials');
33+
const { getCachedTools } = require('~/server/services/Config');
3234
const { createMCPTool } = require('~/server/services/MCP');
33-
const { logger } = require('~/config');
34-
35-
const mcpToolPattern = new RegExp(`^.+${Constants.mcp_delimiter}.+$`);
3635

3736
/**
3837
* Validates the availability and authentication of tools for a user based on environment variables or user-specific plugin authentication values.
@@ -93,7 +92,7 @@ const validateTools = async (user, tools = []) => {
9392
return Array.from(validToolsSet.values());
9493
} catch (err) {
9594
logger.error('[validateTools] There was a problem validating tools', err);
96-
throw new Error('There was a problem validating tools');
95+
throw new Error(err);
9796
}
9897
};
9998

@@ -236,7 +235,7 @@ const loadTools = async ({
236235

237236
/** @type {Record<string, string>} */
238237
const toolContextMap = {};
239-
const appTools = options.req?.app?.locals?.availableTools ?? {};
238+
const appTools = (await getCachedTools({ includeGlobal: true })) ?? {};
240239

241240
for (const tool of tools) {
242241
if (tool === Tools.execute_code) {
@@ -299,6 +298,7 @@ Current Date & Time: ${replaceSpecialVars({ text: '{{iso_datetime}}' })}
299298
requestedTools[tool] = async () =>
300299
createMCPTool({
301300
req: options.req,
301+
res: options.res,
302302
toolKey: tool,
303303
model: agent?.model ?? model,
304304
provider: agent?.provider ?? endpoint,

api/cache/getLogStores.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const roles = isRedisEnabled
2929
? new Keyv({ store: keyvRedis })
3030
: new Keyv({ namespace: CacheKeys.ROLES });
3131

32+
const mcpTools = isRedisEnabled
33+
? new Keyv({ store: keyvRedis })
34+
: new Keyv({ namespace: CacheKeys.MCP_TOOLS });
35+
3236
const audioRuns = isRedisEnabled
3337
? new Keyv({ store: keyvRedis, ttl: Time.TEN_MINUTES })
3438
: new Keyv({ namespace: CacheKeys.AUDIO_RUNS, ttl: Time.TEN_MINUTES });
@@ -67,6 +71,7 @@ const openIdExchangedTokensCache = isRedisEnabled
6771

6872
const namespaces = {
6973
[CacheKeys.ROLES]: roles,
74+
[CacheKeys.MCP_TOOLS]: mcpTools,
7075
[CacheKeys.CONFIG_STORE]: config,
7176
[CacheKeys.PENDING_REQ]: pending_req,
7277
[ViolationTypes.BAN]: new Keyv({ store: keyvMongo, namespace: CacheKeys.BANS, ttl: duration }),

api/config/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ let flowManager = null;
1515
*/
1616
function getMCPManager(userId) {
1717
if (!mcpManager) {
18-
mcpManager = MCPManager.getInstance(logger);
18+
mcpManager = MCPManager.getInstance();
1919
} else {
2020
mcpManager.checkIdleConnections(userId);
2121
}
@@ -30,7 +30,6 @@ function getFlowStateManager(flowsCache) {
3030
if (!flowManager) {
3131
flowManager = new FlowStateManager(flowsCache, {
3232
ttl: Time.ONE_MINUTE * 3,
33-
logger,
3433
});
3534
}
3635
return flowManager;

api/models/Agent.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
removeAgentIdsFromProject,
1212
removeAgentFromAllProjects,
1313
} = require('./Project');
14+
const { getCachedTools } = require('~/server/services/Config');
1415
const getLogStores = require('~/cache/getLogStores');
1516
const { getActions } = require('./Action');
1617
const { Agent } = require('~/db/models');
@@ -55,12 +56,12 @@ const getAgent = async (searchParameter) => await Agent.findOne(searchParameter)
5556
* @param {string} params.agent_id
5657
* @param {string} params.endpoint
5758
* @param {import('@librechat/agents').ClientOptions} [params.model_parameters]
58-
* @returns {Agent|null} The agent document as a plain object, or null if not found.
59+
* @returns {Promise<Agent|null>} The agent document as a plain object, or null if not found.
5960
*/
60-
const loadEphemeralAgent = ({ req, agent_id, endpoint, model_parameters: _m }) => {
61+
const loadEphemeralAgent = async ({ req, agent_id, endpoint, model_parameters: _m }) => {
6162
const { model, ...model_parameters } = _m;
6263
/** @type {Record<string, FunctionTool>} */
63-
const availableTools = req.app.locals.availableTools;
64+
const availableTools = await getCachedTools({ includeGlobal: true });
6465
/** @type {TEphemeralAgent | null} */
6566
const ephemeralAgent = req.body.ephemeralAgent;
6667
const mcpServers = new Set(ephemeralAgent?.mcp);
@@ -111,7 +112,7 @@ const loadAgent = async ({ req, agent_id, endpoint, model_parameters }) => {
111112
return null;
112113
}
113114
if (agent_id === EPHEMERAL_AGENT_ID) {
114-
return loadEphemeralAgent({ req, agent_id, endpoint, model_parameters });
115+
return await loadEphemeralAgent({ req, agent_id, endpoint, model_parameters });
115116
}
116117
const agent = await getAgent({
117118
id: agent_id,

api/models/Agent.spec.js

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ const originalEnv = {
66
process.env.CREDS_KEY = '0123456789abcdef0123456789abcdef';
77
process.env.CREDS_IV = '0123456789abcdef';
88

9+
jest.mock('~/server/services/Config', () => ({
10+
getCachedTools: jest.fn(),
11+
}));
12+
913
const mongoose = require('mongoose');
1014
const { v4: uuidv4 } = require('uuid');
1115
const { agentSchema } = require('@librechat/data-schemas');
@@ -23,6 +27,7 @@ const {
2327
generateActionMetadataHash,
2428
revertAgentVersion,
2529
} = require('./Agent');
30+
const { getCachedTools } = require('~/server/services/Config');
2631

2732
/**
2833
* @type {import('mongoose').Model<import('@librechat/data-schemas').IAgent>}
@@ -406,6 +411,7 @@ describe('models/Agent', () => {
406411
beforeAll(async () => {
407412
mongoServer = await MongoMemoryServer.create();
408413
const mongoUri = mongoServer.getUri();
414+
Agent = mongoose.models.Agent || mongoose.model('Agent', agentSchema);
409415
await mongoose.connect(mongoUri);
410416
});
411417

@@ -1546,6 +1552,12 @@ describe('models/Agent', () => {
15461552
test('should test ephemeral agent loading logic', async () => {
15471553
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
15481554

1555+
getCachedTools.mockResolvedValue({
1556+
tool1_mcp_server1: {},
1557+
tool2_mcp_server2: {},
1558+
another_tool: {},
1559+
});
1560+
15491561
const mockReq = {
15501562
user: { id: 'user123' },
15511563
body: {
@@ -1556,15 +1568,6 @@ describe('models/Agent', () => {
15561568
mcp: ['server1', 'server2'],
15571569
},
15581570
},
1559-
app: {
1560-
locals: {
1561-
availableTools: {
1562-
tool1_mcp_server1: {},
1563-
tool2_mcp_server2: {},
1564-
another_tool: {},
1565-
},
1566-
},
1567-
},
15681571
};
15691572

15701573
const result = await loadAgent({
@@ -1657,6 +1660,8 @@ describe('models/Agent', () => {
16571660
test('should handle ephemeral agent with no MCP servers', async () => {
16581661
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
16591662

1663+
getCachedTools.mockResolvedValue({});
1664+
16601665
const mockReq = {
16611666
user: { id: 'user123' },
16621667
body: {
@@ -1667,11 +1672,6 @@ describe('models/Agent', () => {
16671672
mcp: [],
16681673
},
16691674
},
1670-
app: {
1671-
locals: {
1672-
availableTools: {},
1673-
},
1674-
},
16751675
};
16761676

16771677
const result = await loadAgent({
@@ -1692,16 +1692,13 @@ describe('models/Agent', () => {
16921692
test('should handle ephemeral agent with undefined ephemeralAgent in body', async () => {
16931693
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
16941694

1695+
getCachedTools.mockResolvedValue({});
1696+
16951697
const mockReq = {
16961698
user: { id: 'user123' },
16971699
body: {
16981700
promptPrefix: 'Basic instructions',
16991701
},
1700-
app: {
1701-
locals: {
1702-
availableTools: {},
1703-
},
1704-
},
17051702
};
17061703

17071704
const result = await loadAgent({
@@ -1734,6 +1731,13 @@ describe('models/Agent', () => {
17341731
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
17351732

17361733
const largeToolList = Array.from({ length: 100 }, (_, i) => `tool_${i}_mcp_server1`);
1734+
const availableTools = largeToolList.reduce((acc, tool) => {
1735+
acc[tool] = {};
1736+
return acc;
1737+
}, {});
1738+
1739+
getCachedTools.mockResolvedValue(availableTools);
1740+
17371741
const mockReq = {
17381742
user: { id: 'user123' },
17391743
body: {
@@ -1744,14 +1748,6 @@ describe('models/Agent', () => {
17441748
mcp: ['server1'],
17451749
},
17461750
},
1747-
app: {
1748-
locals: {
1749-
availableTools: largeToolList.reduce((acc, tool) => {
1750-
acc[tool] = {};
1751-
return acc;
1752-
}, {}),
1753-
},
1754-
},
17551751
};
17561752

17571753
const result = await loadAgent({
@@ -2272,6 +2268,13 @@ describe('models/Agent', () => {
22722268
test('should handle loadEphemeralAgent with malformed MCP tool names', async () => {
22732269
const { EPHEMERAL_AGENT_ID } = require('librechat-data-provider').Constants;
22742270

2271+
getCachedTools.mockResolvedValue({
2272+
malformed_tool_name: {}, // No mcp delimiter
2273+
tool__server1: {}, // Wrong delimiter
2274+
tool_mcp_server1: {}, // Correct format
2275+
tool_mcp_server2: {}, // Different server
2276+
});
2277+
22752278
const mockReq = {
22762279
user: { id: 'user123' },
22772280
body: {
@@ -2282,16 +2285,6 @@ describe('models/Agent', () => {
22822285
mcp: ['server1'],
22832286
},
22842287
},
2285-
app: {
2286-
locals: {
2287-
availableTools: {
2288-
malformed_tool_name: {}, // No mcp delimiter
2289-
tool__server1: {}, // Wrong delimiter
2290-
tool_mcp_server1: {}, // Correct format
2291-
tool_mcp_server2: {}, // Different server
2292-
},
2293-
},
2294-
},
22952288
};
22962289

22972290
const result = await loadAgent({

0 commit comments

Comments
 (0)