mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🌐 fix: Prevent MCP Body/Header Timeouts at 5-Minute mark (#9476)
* chore: improve error log for tool error * fix: add undici as fetch method with agent to prevent body/header timeouts at 5-minute mark
This commit is contained in:
parent
4dd2998592
commit
1869854d70
3 changed files with 38 additions and 12 deletions
|
|
@ -7,11 +7,12 @@ const {
|
||||||
createRun,
|
createRun,
|
||||||
Tokenizer,
|
Tokenizer,
|
||||||
checkAccess,
|
checkAccess,
|
||||||
|
logAxiosError,
|
||||||
resolveHeaders,
|
resolveHeaders,
|
||||||
getBalanceConfig,
|
getBalanceConfig,
|
||||||
getTransactionsConfig,
|
|
||||||
memoryInstructions,
|
memoryInstructions,
|
||||||
formatContentStrings,
|
formatContentStrings,
|
||||||
|
getTransactionsConfig,
|
||||||
createMemoryProcessor,
|
createMemoryProcessor,
|
||||||
} = require('@librechat/api');
|
} = require('@librechat/api');
|
||||||
const {
|
const {
|
||||||
|
|
@ -88,11 +89,10 @@ function createTokenCounter(encoding) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function logToolError(graph, error, toolId) {
|
function logToolError(graph, error, toolId) {
|
||||||
logger.error(
|
logAxiosError({
|
||||||
'[api/server/controllers/agents/client.js #chatCompletion] Tool Error',
|
|
||||||
error,
|
error,
|
||||||
toolId,
|
message: `[api/server/controllers/agents/client.js #chatCompletion] Tool Error "${toolId}"`,
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class AgentClient extends BaseClient {
|
class AgentClient extends BaseClient {
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,7 @@ Please follow these instructions when using tools from the respective MCP server
|
||||||
CallToolResultSchema,
|
CallToolResultSchema,
|
||||||
{
|
{
|
||||||
timeout: connection.timeout,
|
timeout: connection.timeout,
|
||||||
|
resetTimeoutOnProgress: true,
|
||||||
...options,
|
...options,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
import { fetch as undiciFetch, Agent } from 'undici';
|
||||||
import {
|
import {
|
||||||
StdioClientTransport,
|
StdioClientTransport,
|
||||||
getDefaultEnvironment,
|
getDefaultEnvironment,
|
||||||
|
|
@ -11,10 +12,17 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||||
import { logger } from '@librechat/data-schemas';
|
import { logger } from '@librechat/data-schemas';
|
||||||
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
||||||
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
||||||
|
import type {
|
||||||
|
RequestInit as UndiciRequestInit,
|
||||||
|
RequestInfo as UndiciRequestInfo,
|
||||||
|
Response as UndiciResponse,
|
||||||
|
} from 'undici';
|
||||||
import type { MCPOAuthTokens } from './oauth/types';
|
import type { MCPOAuthTokens } from './oauth/types';
|
||||||
import { mcpConfig } from './mcpConfig';
|
import { mcpConfig } from './mcpConfig';
|
||||||
import type * as t from './types';
|
import type * as t from './types';
|
||||||
|
|
||||||
|
type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;
|
||||||
|
|
||||||
function isStdioOptions(options: t.MCPOptions): options is t.StdioOptions {
|
function isStdioOptions(options: t.MCPOptions): options is t.StdioOptions {
|
||||||
return 'command' in options;
|
return 'command' in options;
|
||||||
}
|
}
|
||||||
|
|
@ -141,11 +149,18 @@ export class MCPConnection extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
private createFetchFunction(
|
private createFetchFunction(
|
||||||
getHeaders: () => Record<string, string> | null | undefined,
|
getHeaders: () => Record<string, string> | null | undefined,
|
||||||
): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> {
|
): (input: UndiciRequestInfo, init?: UndiciRequestInit) => Promise<UndiciResponse> {
|
||||||
return function customFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
return function customFetch(
|
||||||
|
input: UndiciRequestInfo,
|
||||||
|
init?: UndiciRequestInit,
|
||||||
|
): Promise<UndiciResponse> {
|
||||||
const requestHeaders = getHeaders();
|
const requestHeaders = getHeaders();
|
||||||
|
const agent = new Agent({
|
||||||
|
bodyTimeout: 0,
|
||||||
|
headersTimeout: 0,
|
||||||
|
});
|
||||||
if (!requestHeaders) {
|
if (!requestHeaders) {
|
||||||
return fetch(input, init);
|
return undiciFetch(input, { ...init, dispatcher: agent });
|
||||||
}
|
}
|
||||||
|
|
||||||
let initHeaders: Record<string, string> = {};
|
let initHeaders: Record<string, string> = {};
|
||||||
|
|
@ -159,12 +174,13 @@ export class MCPConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(input, {
|
return undiciFetch(input, {
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
...initHeaders,
|
...initHeaders,
|
||||||
...requestHeaders,
|
...requestHeaders,
|
||||||
},
|
},
|
||||||
|
dispatcher: agent,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -235,13 +251,20 @@ export class MCPConnection extends EventEmitter {
|
||||||
eventSourceInit: {
|
eventSourceInit: {
|
||||||
fetch: (url, init) => {
|
fetch: (url, init) => {
|
||||||
const fetchHeaders = new Headers(Object.assign({}, init?.headers, headers));
|
const fetchHeaders = new Headers(Object.assign({}, init?.headers, headers));
|
||||||
return fetch(url, {
|
const agent = new Agent({
|
||||||
|
bodyTimeout: 0,
|
||||||
|
headersTimeout: 0,
|
||||||
|
});
|
||||||
|
return undiciFetch(url, {
|
||||||
...init,
|
...init,
|
||||||
|
dispatcher: agent,
|
||||||
headers: fetchHeaders,
|
headers: fetchHeaders,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fetch: this.createFetchFunction(this.getRequestHeaders.bind(this)),
|
fetch: this.createFetchFunction(
|
||||||
|
this.getRequestHeaders.bind(this),
|
||||||
|
) as unknown as FetchLike,
|
||||||
});
|
});
|
||||||
|
|
||||||
transport.onclose = () => {
|
transport.onclose = () => {
|
||||||
|
|
@ -279,7 +302,9 @@ export class MCPConnection extends EventEmitter {
|
||||||
headers,
|
headers,
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
},
|
},
|
||||||
fetch: this.createFetchFunction(this.getRequestHeaders.bind(this)),
|
fetch: this.createFetchFunction(
|
||||||
|
this.getRequestHeaders.bind(this),
|
||||||
|
) as unknown as FetchLike,
|
||||||
});
|
});
|
||||||
|
|
||||||
transport.onclose = () => {
|
transport.onclose = () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue