Skip to content

Commit 7f1d01c

Browse files
authored
🔀 fix: MCP Improvements, Auto-Save Drafts, Artifact Markup (danny-avila#7040)
* feat: Update MCP tool creation to use lowercase provider name * refactor: handle MCP image output edge cases where tool outputs must contain string responses * feat: Drop 'anyOf' and 'oneOf' fields from JSON schema conversion * feat: Transform 'oneOf' and 'anyOf' fields to Zod union in JSON schema conversion * fix: artifactPlugin to replace textDirective with expected text, closes danny-avila#7029 * fix: auto-save functionality to handle conversation transitions from pending drafts, closes danny-avila#7027 * refactor: improve async handling during user disconnection process * fix: use correct user ID variable for MCP tool calling * fix: improve handling of pending drafts in auto-save functionality * fix: add support for additional model names in getValueKey function * fix: reset form values on agent deletion when no agents remain
1 parent 150116e commit 7f1d01c

File tree

12 files changed

+855
-72
lines changed

12 files changed

+855
-72
lines changed

api/models/tx.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ const getValueKey = (model, endpoint) => {
180180
return 'gpt-3.5-turbo-1106';
181181
} else if (modelName.includes('gpt-3.5')) {
182182
return '4k';
183+
} else if (modelName.includes('o4-mini')) {
184+
return 'o4-mini';
185+
} else if (modelName.includes('o4')) {
186+
return 'o4';
187+
} else if (modelName.includes('o3-mini')) {
188+
return 'o3-mini';
189+
} else if (modelName.includes('o3')) {
190+
return 'o3';
183191
} else if (modelName.includes('o1-preview')) {
184192
return 'o1-preview';
185193
} else if (modelName.includes('o1-mini')) {

api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"@langchain/google-genai": "^0.2.2",
4949
"@langchain/google-vertexai": "^0.2.3",
5050
"@langchain/textsplitters": "^0.1.0",
51-
"@librechat/agents": "^2.4.20",
51+
"@librechat/agents": "^2.4.22",
5252
"@librechat/data-schemas": "*",
5353
"@waylaidwanderer/fetch-event-source": "^3.0.1",
5454
"axios": "^1.8.2",

api/server/services/MCP.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,18 @@ const { logger, getMCPManager } = require('~/config');
1919
* @param {string} params.model - The model for the tool.
2020
* @returns { Promise<typeof tool | { _call: (toolInput: Object | string) => unknown}> } An object with `_call` method to execute the tool input.
2121
*/
22-
async function createMCPTool({ req, toolKey, provider }) {
22+
async function createMCPTool({ req, toolKey, provider: _provider }) {
2323
const toolDefinition = req.app.locals.availableTools[toolKey]?.function;
2424
if (!toolDefinition) {
2525
logger.error(`Tool ${toolKey} not found in available tools`);
2626
return null;
2727
}
2828
/** @type {LCTool} */
2929
const { description, parameters } = toolDefinition;
30-
const isGoogle = provider === Providers.VERTEXAI || provider === Providers.GOOGLE;
30+
const isGoogle = _provider === Providers.VERTEXAI || _provider === Providers.GOOGLE;
3131
let schema = convertJsonSchemaToZod(parameters, {
3232
allowEmptyObject: !isGoogle,
33+
transformOneOfAnyOf: true,
3334
});
3435

3536
if (!schema) {
@@ -49,7 +50,8 @@ async function createMCPTool({ req, toolKey, provider }) {
4950
const _call = async (toolArguments, config) => {
5051
try {
5152
const derivedSignal = config?.signal ? AbortSignal.any([config.signal]) : undefined;
52-
const mcpManager = getMCPManager(config?.userId);
53+
const mcpManager = getMCPManager(config?.configurable?.user_id);
54+
const provider = (config?.metadata?.provider || _provider)?.toLowerCase();
5355
const result = await mcpManager.callTool({
5456
serverName,
5557
toolName,
@@ -70,7 +72,7 @@ async function createMCPTool({ req, toolKey, provider }) {
7072
return result;
7173
} catch (error) {
7274
logger.error(
73-
`[MCP][User: ${config?.userId}][${serverName}] Error calling "${toolName}" MCP tool:`,
75+
`[MCP][User: ${config?.configurable?.user_id}][${serverName}] Error calling "${toolName}" MCP tool:`,
7476
error,
7577
);
7678
throw new Error(

client/src/components/Artifacts/Artifact.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,16 @@ import ArtifactButton from './ArtifactButton';
1111

1212
export const artifactPlugin: Pluggable = () => {
1313
return (tree) => {
14-
visit(tree, ['textDirective', 'leafDirective', 'containerDirective'], (node) => {
14+
visit(tree, ['textDirective', 'leafDirective', 'containerDirective'], (node, index, parent) => {
15+
if (node.type === 'textDirective') {
16+
const replacementText = `:${node.name}`;
17+
if (parent && Array.isArray(parent.children) && typeof index === 'number') {
18+
parent.children[index] = {
19+
type: 'text',
20+
value: replacementText,
21+
};
22+
}
23+
}
1524
if (node.name !== 'artifact') {
1625
return;
1726
}
@@ -26,7 +35,6 @@ export const artifactPlugin: Pluggable = () => {
2635
};
2736

2837
export function Artifact({
29-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3038
node,
3139
...props
3240
}: Artifact & {

client/src/components/SidePanel/Agents/DeleteButton.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { useFormContext } from 'react-hook-form';
2+
import { defaultAgentFormValues } from 'librechat-data-provider';
13
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
24
import type { UseMutationResult } from '@tanstack/react-query';
35
import { OGDialog, OGDialogTrigger, Label } from '~/components/ui';
@@ -18,6 +20,7 @@ export default function DeleteButton({
1820
createMutation: UseMutationResult<Agent, Error, AgentCreateParams>;
1921
}) {
2022
const localize = useLocalize();
23+
const { reset } = useFormContext();
2124
const { showToast } = useToastContext();
2225
const { conversation } = useChatContext();
2326
const { setOption } = useSetIndexOptions();
@@ -41,6 +44,10 @@ export default function DeleteButton({
4144

4245
const firstAgent = updatedList[0] as Agent | undefined;
4346
if (!firstAgent) {
47+
setCurrentAgentId(undefined);
48+
reset({
49+
...defaultAgentFormValues,
50+
});
4451
return setOption('agent_id')('');
4552
}
4653

client/src/hooks/Input/useAutoSave.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import debounce from 'lodash/debounce';
22
import { SetterOrUpdater, useRecoilValue } from 'recoil';
3-
import { useState, useEffect, useMemo, useCallback } from 'react';
3+
import { useState, useEffect, useMemo, useCallback, useRef } from 'react';
44
import { LocalStorageKeys, Constants } from 'librechat-data-provider';
55
import type { TFile } from 'librechat-data-provider';
66
import type { ExtendedFile } from '~/common';
@@ -159,6 +159,8 @@ export const useAutoSave = ({
159159
};
160160
}, [conversationId, saveDrafts, textAreaRef]);
161161

162+
const prevConversationIdRef = useRef<string | null>(null);
163+
162164
useEffect(() => {
163165
// This useEffect is responsible for saving the current conversation's draft and
164166
// restoring the new conversation's draft when switching between conversations.
@@ -176,7 +178,28 @@ export const useAutoSave = ({
176178
setFiles(new Map());
177179

178180
try {
179-
if (currentConversationId != null && currentConversationId) {
181+
// Check for transition from PENDING_CONVO to a valid conversationId
182+
if (
183+
prevConversationIdRef.current === Constants.PENDING_CONVO &&
184+
conversationId !== Constants.PENDING_CONVO &&
185+
conversationId.length > 3
186+
) {
187+
const pendingDraft = localStorage.getItem(
188+
`${LocalStorageKeys.TEXT_DRAFT}${Constants.PENDING_CONVO}`,
189+
);
190+
191+
// Clear the pending draft, if it exists, and save the current draft to the new conversationId;
192+
// otherwise, save the current text area value to the new conversationId
193+
localStorage.removeItem(`${LocalStorageKeys.TEXT_DRAFT}${Constants.PENDING_CONVO}`);
194+
if (pendingDraft) {
195+
localStorage.setItem(`${LocalStorageKeys.TEXT_DRAFT}${conversationId}`, pendingDraft);
196+
} else if (textAreaRef?.current?.value) {
197+
localStorage.setItem(
198+
`${LocalStorageKeys.TEXT_DRAFT}${conversationId}`,
199+
encodeBase64(textAreaRef.current.value),
200+
);
201+
}
202+
} else if (currentConversationId != null && currentConversationId) {
180203
saveText(currentConversationId);
181204
}
182205

@@ -186,11 +209,13 @@ export const useAutoSave = ({
186209
console.error(e);
187210
}
188211

212+
prevConversationIdRef.current = conversationId;
189213
setCurrentConversationId(conversationId);
190214
}, [
191-
conversationId,
192215
currentConversationId,
216+
conversationId,
193217
restoreFiles,
218+
textAreaRef,
194219
restoreText,
195220
saveDrafts,
196221
saveText,

package-lock.json

Lines changed: 42 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)